diff --git a/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java b/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java index ad7fd04c1..10cbda63a 100644 --- a/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java +++ b/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java @@ -16,6 +16,7 @@ import java.util.Arrays; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; +import app.revanced.extension.shared.ui.Dim; import com.amazon.video.sdk.player.Player; @@ -64,9 +65,8 @@ public class PlaybackSpeedPatch { SpeedIconDrawable speedIcon = new SpeedIconDrawable(); speedButton.setImageDrawable(speedIcon); - int buttonSize = Utils.dipToPixels(48); - speedButton.setMinimumWidth(buttonSize); - speedButton.setMinimumHeight(buttonSize); + speedButton.setMinimumWidth(Dim.dp48); + speedButton.setMinimumHeight(Dim.dp48); return speedButton; } @@ -197,11 +197,11 @@ class SpeedIconDrawable extends Drawable { @Override public int getIntrinsicWidth() { - return Utils.dipToPixels(32); + return Dim.dp32; } @Override public int getIntrinsicHeight() { - return Utils.dipToPixels(32); + return Dim.dp32; } } \ No newline at end of file diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java index 9ec217b64..551d89753 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java @@ -23,9 +23,7 @@ import android.os.Looper; import android.preference.Preference; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; -import android.util.DisplayMetrics; import android.util.Pair; -import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -63,6 +61,7 @@ import app.revanced.extension.shared.settings.AppLanguage; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BooleanSetting; import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference; +import app.revanced.extension.shared.ui.Dim; @SuppressWarnings("NewApi") public class Utils { @@ -801,13 +800,10 @@ public class Utils { public static void setDialogWindowParameters(Window window, int gravity, int yOffsetDip, int widthPercentage, boolean dimAmount) { WindowManager.LayoutParams params = window.getAttributes(); - DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics(); - int portraitWidth = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels); - - params.width = (int) (portraitWidth * (widthPercentage / 100.0f)); // Set width based on parameters. + params.width = Dim.pctPortraitWidth(widthPercentage); params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.gravity = gravity; - params.y = yOffsetDip > 0 ? dipToPixels(yOffsetDip) : 0; + params.y = yOffsetDip > 0 ? Dim.dp(yOffsetDip) : 0; if (dimAmount) { params.dimAmount = 0f; } @@ -816,18 +812,6 @@ public class Utils { window.setBackgroundDrawable(null); // Remove default dialog background } - /** - * Creates an array of corner radii for a rounded rectangle shape. - * - * @param dp Radius in density-independent pixels (dip) to apply to all corners. - * @return An array of eight float values representing the corner radii - * (top-left, top-right, bottom-right, bottom-left). - */ - public static float[] createCornerRadii(float dp) { - final float radius = dipToPixels(dp); - return new float[]{radius, radius, radius, radius, radius, radius, radius, radius}; - } - /** * Sets the theme light color used by the app. */ @@ -1132,42 +1116,6 @@ public class Utils { return getResourceColor(colorString); } - /** - * Converts dip value to actual device pixels. - * - * @param dip The density-independent pixels value. - * @return The device pixel value. - */ - public static int dipToPixels(float dip) { - return (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - dip, - Resources.getSystem().getDisplayMetrics() - ); - } - - /** - * Converts a percentage of the screen height to actual device pixels. - * - * @param percentage The percentage of the screen height (e.g., 30 for 30%). - * @return The device pixel value corresponding to the percentage of screen height. - */ - public static int percentageHeightToPixels(int percentage) { - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - return (int) (metrics.heightPixels * (percentage / 100.0f)); - } - - /** - * Converts a percentage of the screen width to actual device pixels. - * - * @param percentage The percentage of the screen width (e.g., 30 for 30%). - * @return The device pixel value corresponding to the percentage of screen width. - */ - public static int percentageWidthToPixels(int percentage) { - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - return (int) (metrics.widthPixels * (percentage / 100.0f)); - } - /** * Uses {@link #adjustColorBrightness(int, float)} depending if light or dark mode is active. */ diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/EnableDebuggingPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/EnableDebuggingPatch.java index 424ba7b12..e0551ed04 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/EnableDebuggingPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/EnableDebuggingPatch.java @@ -1,5 +1,9 @@ package app.revanced.extension.shared.patches; +import static java.lang.Boolean.TRUE; + +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -21,12 +25,28 @@ public final class EnableDebuggingPatch { ? new ConcurrentHashMap<>(800, 0.5f, 1) : null; + private static final Set DISABLED_FEATURE_FLAGS = parseFlags(BaseSettings.DISABLED_FEATURE_FLAGS.get()); + + // Log all disabled flags on app startup. + static { + if (LOG_FEATURE_FLAGS && !DISABLED_FEATURE_FLAGS.isEmpty()) { + StringBuilder sb = new StringBuilder("Disabled feature flags:\n"); + for (Long flag : DISABLED_FEATURE_FLAGS) { + sb.append(" ").append(flag).append('\n'); + } + Logger.printDebug(sb::toString); + } + } + /** * Injection point. */ public static boolean isBooleanFeatureFlagEnabled(boolean value, Long flag) { if (LOG_FEATURE_FLAGS && value) { - if (featureFlags.putIfAbsent(flag, true) == null) { + if (DISABLED_FEATURE_FLAGS.contains(flag)) { + return false; + } + if (featureFlags.putIfAbsent(flag, TRUE) == null) { Logger.printDebug(() -> "boolean feature is enabled: " + flag); } } @@ -70,10 +90,44 @@ public final class EnableDebuggingPatch { if (LOG_FEATURE_FLAGS && !defaultValue.equals(value)) { if (featureFlags.putIfAbsent(flag, true) == null) { Logger.printDebug(() -> " string feature is enabled: " + flag - + " value: " + value + (defaultValue.isEmpty() ? "" : " default: " + defaultValue)); + + " value: " + value + (defaultValue.isEmpty() ? "" : " default: " + defaultValue)); } } return value; } + + /** + * Get all logged feature flags. + * @return Set of all known flags + */ + public static Set getAllLoggedFlags() { + if (featureFlags != null) { + return new HashSet<>(featureFlags.keySet()); + } + + return new HashSet<>(); + } + + /** + * Public method for parsing flags. + * @param flags String containing newline-separated flag IDs + * @return Set of parsed flag IDs + */ + public static Set parseFlags(String flags) { + Set parsedFlags = new HashSet<>(); + if (!flags.isBlank()) { + for (String flag : flags.split("\n")) { + String trimmedFlag = flag.trim(); + if (trimmedFlag.isEmpty()) continue; // Skip empty lines. + try { + parsedFlags.add(Long.parseLong(trimmedFlag)); + } catch (NumberFormatException e) { + Logger.printException(() -> "Invalid flag ID: " + flag); + } + } + } + + return parsedFlags; + } } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseActivityHook.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseActivityHook.java index 062949db7..fb068d8ed 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseActivityHook.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseActivityHook.java @@ -7,7 +7,6 @@ import android.app.Activity; import android.content.Context; import android.graphics.drawable.Drawable; import android.preference.PreferenceFragment; -import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; @@ -16,6 +15,7 @@ import android.widget.Toolbar; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.preference.ToolbarPreferenceFragment; +import app.revanced.extension.shared.ui.Dim; /** * Base class for hooking activities to inject a custom PreferenceFragment with a toolbar. @@ -109,13 +109,12 @@ public abstract class BaseActivityHook extends Activity { toolbar.setNavigationOnClickListener(getNavigationClickListener(activity)); toolbar.setTitle(STRING_REVANCED_SETTINGS_TITLE); - final int margin = Utils.dipToPixels(16); - toolbar.setTitleMarginStart(margin); - toolbar.setTitleMarginEnd(margin); + toolbar.setTitleMarginStart(Dim.dp16); + toolbar.setTitleMarginEnd(Dim.dp16); TextView toolbarTextView = Utils.getChildView(toolbar, false, view -> view instanceof TextView); if (toolbarTextView != null) { toolbarTextView.setTextColor(Utils.getAppForegroundColor()); - toolbarTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20); + toolbarTextView.setTextSize(20); } setToolbarLayoutParams(toolbar); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java index c1bc849c1..e9dc280a8 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java @@ -42,4 +42,6 @@ public class BaseSettings { public static final EnumSetting CUSTOM_BRANDING_ICON = new EnumSetting<>("revanced_custom_branding_icon", BrandingTheme.ORIGINAL, true); public static final IntegerSetting CUSTOM_BRANDING_NAME = new IntegerSetting("revanced_custom_branding_name", 1, true); + + public static final StringSetting DISABLED_FEATURE_FLAGS = new StringSetting("revanced_disabled_feature_flags", "", true, parent(DEBUG)); } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerPreference.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerPreference.java index 63cf5eb0f..e3d6abee2 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerPreference.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerPreference.java @@ -1,7 +1,6 @@ package app.revanced.extension.shared.settings.preference; import static app.revanced.extension.shared.StringRef.str; -import static app.revanced.extension.shared.Utils.dipToPixels; import static app.revanced.extension.shared.Utils.getResourceIdentifierOrThrow; import android.app.Dialog; @@ -37,6 +36,7 @@ import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.settings.StringSetting; import app.revanced.extension.shared.ui.ColorDot; import app.revanced.extension.shared.ui.CustomDialog; +import app.revanced.extension.shared.ui.Dim; /** * A custom preference for selecting a color via a hexadecimal code or a color picker dialog. @@ -310,11 +310,8 @@ public class ColorPickerPreference extends EditTextPreference { inputLayout.setGravity(Gravity.CENTER_VERTICAL); dialogColorDot = new View(context); - LinearLayout.LayoutParams previewParams = new LinearLayout.LayoutParams( - dipToPixels(20), - dipToPixels(20) - ); - previewParams.setMargins(dipToPixels(16), 0, dipToPixels(10), 0); + LinearLayout.LayoutParams previewParams = new LinearLayout.LayoutParams(Dim.dp20,Dim.dp20); + previewParams.setMargins(Dim.dp16, 0, Dim.dp10, 0); dialogColorDot.setLayoutParams(previewParams); inputLayout.addView(dialogColorDot); updateDialogColorDot(); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerView.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerView.java index 810ddb3a6..b8c957711 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerView.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerView.java @@ -1,6 +1,5 @@ package app.revanced.extension.shared.settings.preference; -import static app.revanced.extension.shared.Utils.dipToPixels; import static app.revanced.extension.shared.settings.preference.ColorPickerPreference.getColorString; import android.annotation.SuppressLint; @@ -21,6 +20,7 @@ import androidx.annotation.ColorInt; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; +import app.revanced.extension.shared.ui.Dim; /** * A custom color picker view that allows the user to select a color using a hue slider, a saturation-value selector @@ -54,28 +54,28 @@ public class ColorPickerView extends View { } /** Expanded touch area for the hue and opacity bars to increase the touch-sensitive area. */ - public static final float TOUCH_EXPANSION = dipToPixels(20f); + public static final float TOUCH_EXPANSION = Dim.dp20; /** Margin between different areas of the view (saturation-value selector, hue bar, and opacity slider). */ - private static final float MARGIN_BETWEEN_AREAS = dipToPixels(24); + private static final float MARGIN_BETWEEN_AREAS = Dim.dp24; /** Padding around the view. */ - private static final float VIEW_PADDING = dipToPixels(16); + private static final float VIEW_PADDING = Dim.dp16; /** Height of the hue bar. */ - private static final float HUE_BAR_HEIGHT = dipToPixels(12); + private static final float HUE_BAR_HEIGHT = Dim.dp12; /** Height of the opacity slider. */ - private static final float OPACITY_BAR_HEIGHT = dipToPixels(12); + private static final float OPACITY_BAR_HEIGHT = Dim.dp12; /** Corner radius for the hue bar. */ - private static final float HUE_CORNER_RADIUS = dipToPixels(6); + private static final float HUE_CORNER_RADIUS = Dim.dp6; /** Corner radius for the opacity slider. */ - private static final float OPACITY_CORNER_RADIUS = dipToPixels(6); + private static final float OPACITY_CORNER_RADIUS = Dim.dp6; /** Radius of the selector handles. */ - private static final float SELECTOR_RADIUS = dipToPixels(12); + private static final float SELECTOR_RADIUS = Dim.dp12; /** Stroke width for the selector handle outlines. */ private static final float SELECTOR_STROKE_WIDTH = 8; @@ -202,7 +202,7 @@ public class ColorPickerView extends View { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final float DESIRED_ASPECT_RATIO = 0.8f; // height = width * 0.8 - final int minWidth = dipToPixels(250); + final int minWidth = Dim.dp(250); final int minHeight = (int) (minWidth * DESIRED_ASPECT_RATIO) + (int) (HUE_BAR_HEIGHT + MARGIN_BETWEEN_AREAS) + (opacitySliderEnabled ? (int) (OPACITY_BAR_HEIGHT + MARGIN_BETWEEN_AREAS) : 0); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/FeatureFlagsManagerPreference.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/FeatureFlagsManagerPreference.java new file mode 100644 index 000000000..2be74ee00 --- /dev/null +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/FeatureFlagsManagerPreference.java @@ -0,0 +1,621 @@ +package app.revanced.extension.shared.settings.preference; + +import static app.revanced.extension.shared.StringRef.str; +import static app.revanced.extension.shared.Utils.getResourceIdentifierOrThrow; + +import android.annotation.SuppressLint; +import android.app.Dialog; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.preference.Preference; +import android.text.Editable; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.util.Pair; +import android.util.SparseBooleanArray; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.Space; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.Utils; +import app.revanced.extension.shared.patches.EnableDebuggingPatch; +import app.revanced.extension.shared.settings.BaseSettings; +import app.revanced.extension.shared.ui.CustomDialog; +import app.revanced.extension.shared.ui.Dim; + +/** + * A custom preference that opens a dialog for managing feature flags. + * Allows moving boolean flags between active and blocked states with advanced selection. + */ +@SuppressWarnings({"deprecation", "unused"}) +public class FeatureFlagsManagerPreference extends Preference { + + private static final int DRAWABLE_REVANCED_SETTINGS_SELECT_ALL = + getResourceIdentifierOrThrow("revanced_settings_select_all", "drawable"); + private static final int DRAWABLE_REVANCED_SETTINGS_DESELECT_ALL = + getResourceIdentifierOrThrow("revanced_settings_deselect_all", "drawable"); + private static final int DRAWABLE_REVANCED_SETTINGS_COPY_ALL = + getResourceIdentifierOrThrow("revanced_settings_copy_all", "drawable"); + private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_RIGHT_ONE = + getResourceIdentifierOrThrow("revanced_settings_arrow_right_one", "drawable"); + private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_RIGHT_DOUBLE = + getResourceIdentifierOrThrow("revanced_settings_arrow_right_double", "drawable"); + private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_LEFT_ONE = + getResourceIdentifierOrThrow("revanced_settings_arrow_left_one", "drawable"); + private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_LEFT_DOUBLE = + getResourceIdentifierOrThrow("revanced_settings_arrow_left_double", "drawable"); + + /** + * Flags to hide from the UI. + */ + private static final Set FLAGS_TO_IGNORE = Set.of( + 45386834L // 'You' tab settings icon. + ); + + /** + * Tracks state for range selection in ListView. + */ + private static class ListViewSelectionState { + int lastClickedPosition = -1; // Position of the last clicked item. + boolean isRangeSelecting = false; // True while a range is being selected. + } + + /** + * Helper class to pass ListView and Adapter together. + */ + private record ColumnViews(ListView listView, FlagAdapter adapter) {} + + { + setOnPreferenceClickListener(pref -> { + showFlagsManagerDialog(); + return true; + }); + } + + public FeatureFlagsManagerPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public FeatureFlagsManagerPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public FeatureFlagsManagerPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public FeatureFlagsManagerPreference(Context context) { + super(context); + } + + /** + * Shows the main dialog for managing feature flags. + */ + private void showFlagsManagerDialog() { + if (!BaseSettings.DEBUG.get()) { + Utils.showToastShort(str("revanced_debug_logs_disabled")); + return; + } + + Context context = getContext(); + + // Load all known and disabled flags. + TreeSet allKnownFlags = new TreeSet<>(EnableDebuggingPatch.getAllLoggedFlags()); + allKnownFlags.removeAll(FLAGS_TO_IGNORE); + + TreeSet disabledFlags = new TreeSet<>(EnableDebuggingPatch.parseFlags( + BaseSettings.DISABLED_FEATURE_FLAGS.get())); + disabledFlags.removeAll(FLAGS_TO_IGNORE); + + if (allKnownFlags.isEmpty() && disabledFlags.isEmpty()) { + // String does not need to be localized because it's basically impossible + // to reach the settings menu without encountering at least 1 flag. + Utils.showToastShort("No feature flags logged yet"); + return; + } + + TreeSet availableFlags = new TreeSet<>(allKnownFlags); + availableFlags.removeAll(disabledFlags); + TreeSet blockedFlags = new TreeSet<>(disabledFlags); + + Pair dialogPair = CustomDialog.create( + context, + getTitle() != null ? getTitle().toString() : "", + null, + null, + str("revanced_settings_save"), + () -> saveFlags(blockedFlags), + () -> {}, + str("revanced_settings_reset"), + this::resetFlags, + true + ); + + LinearLayout mainLayout = dialogPair.second; + LinearLayout.LayoutParams contentParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.0f); + + // Insert content before the dialog button row. + View contentView = createContentView(context, availableFlags, blockedFlags); + mainLayout.addView(contentView, mainLayout.getChildCount() - 1, contentParams); + + Dialog dialog = dialogPair.first; + dialog.show(); + + Window window = dialog.getWindow(); + if (window != null) { + Utils.setDialogWindowParameters(window, Gravity.CENTER, 0, 100, false); + } + } + + /** + * Creates the main content view with two columns. + */ + private View createContentView(Context context, TreeSet availableFlags, TreeSet blockedFlags) { + LinearLayout contentLayout = new LinearLayout(context); + contentLayout.setOrientation(LinearLayout.VERTICAL); + + // Headers. + TextView availableHeader = createHeader(context, "revanced_debug_feature_flags_manager_active_header"); + TextView blockedHeader = createHeader(context, "revanced_debug_feature_flags_manager_blocked_header"); + + LinearLayout headersLayout = new LinearLayout(context); + headersLayout.setOrientation(LinearLayout.HORIZONTAL); + headersLayout.addView(availableHeader, new LinearLayout.LayoutParams( + 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)); + headersLayout.addView(blockedHeader, new LinearLayout.LayoutParams( + 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)); + + // Columns. + View leftColumn = createColumn(context, availableFlags, availableHeader); + View rightColumn = createColumn(context, blockedFlags, blockedHeader); + + ColumnViews leftViews = (ColumnViews) leftColumn.getTag(); + ColumnViews rightViews = (ColumnViews) rightColumn.getTag(); + + updateHeaderCount(availableHeader, leftViews.adapter); + updateHeaderCount(blockedHeader, rightViews.adapter); + + // Main columns layout. + LinearLayout columnsLayout = new LinearLayout(context); + columnsLayout.setOrientation(LinearLayout.HORIZONTAL); + columnsLayout.setLayoutParams(new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)); + columnsLayout.addView(leftColumn, new LinearLayout.LayoutParams( + 0, ViewGroup.LayoutParams.MATCH_PARENT, 1f)); + + Space spaceBetweenColumns = new Space(context); + spaceBetweenColumns.setLayoutParams(new LinearLayout.LayoutParams(Dim.dp8, ViewGroup.LayoutParams.MATCH_PARENT)); + columnsLayout.addView(spaceBetweenColumns); + + columnsLayout.addView(rightColumn, new LinearLayout.LayoutParams( + 0, ViewGroup.LayoutParams.MATCH_PARENT, 1f)); + + // Move buttons below columns. + Pair moveButtons = createMoveButtons(context, + leftViews.listView, rightViews.listView, + availableFlags, blockedFlags, availableHeader, blockedHeader); + + // Layout for buttons row. + LinearLayout buttonsRow = new LinearLayout(context); + buttonsRow.setOrientation(LinearLayout.HORIZONTAL); + buttonsRow.setLayoutParams(new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + buttonsRow.addView(moveButtons.first, new LinearLayout.LayoutParams( + 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)); + + Space spaceBetweenButtons = new Space(context); + spaceBetweenButtons.setLayoutParams(new LinearLayout.LayoutParams(Dim.dp8, ViewGroup.LayoutParams.WRAP_CONTENT)); + buttonsRow.addView(spaceBetweenButtons); + + buttonsRow.addView(moveButtons.second, new LinearLayout.LayoutParams( + 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)); + + contentLayout.addView(headersLayout); + contentLayout.addView(columnsLayout); + contentLayout.addView(buttonsRow); + + return contentLayout; + } + + /** + * Creates a header TextView. + */ + private TextView createHeader(Context context, String tag) { + TextView textview = new TextView(context); + textview.setTag(tag); + textview.setTextSize(16); + textview.setTextColor(Utils.getAppForegroundColor()); + textview.setGravity(Gravity.CENTER); + + return textview; + } + + /** + * Creates a single column (search + buttons + list). + */ + private View createColumn(Context context, TreeSet flags, TextView countText) { + LinearLayout wrapper = new LinearLayout(context); + wrapper.setOrientation(LinearLayout.VERTICAL); + + Pair pair = createListView(context, flags, countText); + ListView listView = pair.first; + FlagAdapter adapter = pair.second; + + EditText search = createSearchBox(context, adapter, listView, countText); + LinearLayout buttons = createActionButtons(context, listView, adapter); + + listView.setLayoutParams(new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)); + ShapeDrawable background = new ShapeDrawable(new RoundRectShape( + Dim.roundedCorners(10), null, null)); + background.getPaint().setColor(Utils.getEditTextBackground()); + listView.setPadding(0, Dim.dp4, 0, Dim.dp4); + listView.setBackground(background); + listView.setOverScrollMode(View.OVER_SCROLL_NEVER); + + wrapper.addView(search); + wrapper.addView(buttons); + wrapper.addView(listView); + + // Save references for move buttons. + wrapper.setTag(new ColumnViews(listView, adapter)); + + return wrapper; + } + + /** + * Updates the header text with the current count. + */ + private void updateHeaderCount(TextView header, FlagAdapter adapter) { + header.setText(str((String) header.getTag(), adapter.getCount())); + } + + /** + * Creates a search box that filters the list. + */ + @SuppressLint("ClickableViewAccessibility") + private EditText createSearchBox(Context context, FlagAdapter adapter, ListView listView, TextView countText) { + EditText search = new EditText(context); + search.setInputType(InputType.TYPE_CLASS_NUMBER); + search.setTextSize(16); + search.setHint(str("revanced_debug_feature_flags_manager_search_hint")); + search.setHapticFeedbackEnabled(false); + search.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); + + search.addTextChangedListener(new TextWatcher() { + @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override public void onTextChanged(CharSequence s, int start, int before, int count) { + adapter.setSearchQuery(s.toString()); + listView.clearChoices(); + updateHeaderCount(countText, adapter); + Drawable clearIcon = context.getResources().getDrawable(android.R.drawable.ic_menu_close_clear_cancel); + clearIcon.setBounds(0, 0, Dim.dp20, Dim.dp20); + search.setCompoundDrawables(null, null, TextUtils.isEmpty(s) ? null : clearIcon, null); + } + @Override public void afterTextChanged(Editable s) {} + }); + + search.setOnTouchListener((v, event) -> { + if (event.getAction() == MotionEvent.ACTION_UP) { + Drawable[] compoundDrawables = search.getCompoundDrawables(); + if (compoundDrawables[2] != null && + event.getRawX() >= (search.getRight() - compoundDrawables[2].getBounds().width())) { + search.setText(""); + return true; + } + } + return false; + }); + + return search; + } + + /** + * Creates action buttons. + */ + private LinearLayout createActionButtons(Context context, ListView listView, FlagAdapter adapter) { + LinearLayout row = new LinearLayout(context); + row.setOrientation(LinearLayout.HORIZONTAL); + row.setGravity(Gravity.CENTER); + row.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); + + ImageButton selectAll = createButton(context, DRAWABLE_REVANCED_SETTINGS_SELECT_ALL, + () -> { + for (int i = 0, count = adapter.getCount(); i < count; i++) { + listView.setItemChecked(i, true); + } + }); + + ImageButton clearAll = createButton(context, DRAWABLE_REVANCED_SETTINGS_DESELECT_ALL, + () -> { + listView.clearChoices(); + adapter.notifyDataSetChanged(); + }); + + ImageButton copy = createButton(context, DRAWABLE_REVANCED_SETTINGS_COPY_ALL, + () -> { + List items = new ArrayList<>(); + SparseBooleanArray checked = listView.getCheckedItemPositions(); + + if (checked.size() > 0) { + for (int i = 0, count = adapter.getCount(); i < count; i++) { + if (checked.get(i)) { + items.add(adapter.getItem(i)); + } + } + } else { + for (Long flag : adapter.getFullFlags()) { + items.add(String.valueOf(flag)); + } + } + + Utils.setClipboard(TextUtils.join("\n", items)); + + Utils.showToastShort(str("revanced_debug_feature_flags_manager_toast_copied")); + }); + + row.addView(selectAll); + row.addView(clearAll); + row.addView(copy); + + return row; + } + + /** + * Creates the move buttons (left and right groups). + */ + private Pair createMoveButtons(Context context, + ListView availableListView, ListView blockedListView, + TreeSet availableFlags, TreeSet blockedFlags, + TextView availableCountText, TextView blockedCountText) { + // Left group: >> > + LinearLayout leftButtons = new LinearLayout(context); + leftButtons.setOrientation(LinearLayout.HORIZONTAL); + leftButtons.setGravity(Gravity.CENTER); + + ImageButton moveAllRight = createButton(context, DRAWABLE_REVANCED_SETTINGS_ARROW_RIGHT_DOUBLE, + () -> moveFlags(availableListView, blockedListView, availableFlags, blockedFlags, + availableCountText, blockedCountText, true)); + + ImageButton moveOneRight = createButton(context, DRAWABLE_REVANCED_SETTINGS_ARROW_RIGHT_ONE, + () -> moveFlags(availableListView, blockedListView, availableFlags, blockedFlags, + availableCountText, blockedCountText, false)); + + leftButtons.addView(moveAllRight); + leftButtons.addView(moveOneRight); + + // Right group: < << + LinearLayout rightButtons = new LinearLayout(context); + rightButtons.setOrientation(LinearLayout.HORIZONTAL); + rightButtons.setGravity(Gravity.CENTER); + + ImageButton moveOneLeft = createButton(context, DRAWABLE_REVANCED_SETTINGS_ARROW_LEFT_ONE, + () -> moveFlags(blockedListView, availableListView, blockedFlags, availableFlags, + blockedCountText, availableCountText, false)); + + ImageButton moveAllLeft = createButton(context, DRAWABLE_REVANCED_SETTINGS_ARROW_LEFT_DOUBLE, + () -> moveFlags(blockedListView, availableListView, blockedFlags, availableFlags, + blockedCountText, availableCountText, true)); + + rightButtons.addView(moveOneLeft); + rightButtons.addView(moveAllLeft); + + return new Pair<>(leftButtons, rightButtons); + } + + /** + * Creates a styled ImageButton. + */ + @SuppressLint("ResourceType") + private ImageButton createButton(Context context, int drawableResId, Runnable action) { + ImageButton button = new ImageButton(context); + + button.setImageResource(drawableResId); + button.setScaleType(ImageView.ScaleType.CENTER); + int[] attrs = {android.R.attr.selectableItemBackgroundBorderless}; + try (TypedArray ripple = context.obtainStyledAttributes(attrs)) { + button.setBackgroundDrawable(ripple.getDrawable(0)); + } + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(Dim.dp32, Dim.dp32); + params.setMargins(Dim.dp8, Dim.dp8, Dim.dp8, Dim.dp8); + button.setLayoutParams(params); + + button.setOnClickListener(v -> action.run()); + + return button; + } + + /** + * Custom adapter with search filtering. + */ + private static class FlagAdapter extends ArrayAdapter { + private final TreeSet fullFlags; + private String searchQuery = ""; + + public FlagAdapter(Context context, TreeSet fullFlags) { + super(context, android.R.layout.simple_list_item_multiple_choice, new ArrayList<>()); + this.fullFlags = fullFlags; + updateFiltered(); + } + + public void setSearchQuery(String query) { + searchQuery = query == null ? "" : query.trim(); + updateFiltered(); + } + + private void updateFiltered() { + clear(); + for (Long flag : fullFlags) { + String flagString = String.valueOf(flag); + if (searchQuery.isEmpty() || flagString.contains(searchQuery)) { + add(flagString); + } + } + notifyDataSetChanged(); + } + + public void refresh() { + updateFiltered(); + } + + public List getFullFlags() { + return new ArrayList<>(fullFlags); + } + } + + /** + * Creates a ListView with filtering, multi-select, and range selection. + */ + @SuppressLint("ClickableViewAccessibility") + private Pair createListView(Context context, + TreeSet flags, TextView countText) { + ListView listView = new ListView(context); + listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + listView.setDividerHeight(0); + + FlagAdapter adapter = new FlagAdapter(context, flags); + listView.setAdapter(adapter); + + final ListViewSelectionState state = new ListViewSelectionState(); + + listView.setOnItemClickListener((parent, view, position, id) -> { + if (!state.isRangeSelecting) { + state.lastClickedPosition = position; + } else { + state.isRangeSelecting = false; + } + }); + + listView.setOnItemLongClickListener((parent, view, position, id) -> { + if (state.lastClickedPosition == -1) { + listView.setItemChecked(position, true); + state.lastClickedPosition = position; + } else { + int start = Math.min(state.lastClickedPosition, position); + int end = Math.max(state.lastClickedPosition, position); + for (int i = start; i <= end; i++) { + listView.setItemChecked(i, true); + } + state.isRangeSelecting = true; + } + return true; + }); + + listView.setOnTouchListener((view, event) -> { + if (event.getAction() == MotionEvent.ACTION_UP && state.isRangeSelecting) { + state.isRangeSelecting = false; + } + return false; + }); + + return new Pair<>(listView, adapter); + } + + /** + * Moves selected or all flags from one list to another. + * + * @param fromListView Source ListView. + * @param toListView Destination ListView. + * @param fromFlags Source flag set. + * @param toFlags Destination flag set. + * @param fromCountText Header showing count of source items. + * @param toCountText Header showing count of destination items. + * @param moveAll If true, move all items; if false, move only selected. + */ + private void moveFlags(ListView fromListView, ListView toListView, + TreeSet fromFlags, TreeSet toFlags, + TextView fromCountText, TextView toCountText, + boolean moveAll) { + if (fromListView == null || toListView == null) return; + + List flagsToMove = new ArrayList<>(); + FlagAdapter fromAdapter = (FlagAdapter) fromListView.getAdapter(); + + if (moveAll) { + flagsToMove.addAll(fromFlags); + } else { + SparseBooleanArray checked = fromListView.getCheckedItemPositions(); + for (int i = 0, count = fromAdapter.getCount(); i < count; i++) { + if (checked.get(i)) { + String item = fromAdapter.getItem(i); + if (item != null) { + flagsToMove.add(Long.parseLong(item)); + } + } + } + } + + if (flagsToMove.isEmpty()) return; + + for (Long flag : flagsToMove) { + fromFlags.remove(flag); + toFlags.add(flag); + } + + // Clear selections before refreshing. + fromListView.clearChoices(); + toListView.clearChoices(); + + // Refresh both adapters. + fromAdapter.refresh(); + ((FlagAdapter) toListView.getAdapter()).refresh(); + + // Update headers. + updateHeaderCount(fromCountText, fromAdapter); + updateHeaderCount(toCountText, (FlagAdapter) toListView.getAdapter()); + } + + /** + * Saves blocked flags to settings. + */ + private void saveFlags(TreeSet blockedFlags) { + StringBuilder flagsString = new StringBuilder(); + for (Long flag : blockedFlags) { + if (flagsString.length() > 0) { + flagsString.append("\n"); + } + flagsString.append(flag); + } + + BaseSettings.DISABLED_FEATURE_FLAGS.save(flagsString.toString()); + Utils.showToastShort(str("revanced_debug_feature_flags_manager_toast_saved")); + Logger.printDebug(() -> "Feature flags saved. Blocked: " + blockedFlags.size()); + + AbstractPreferenceFragment.showRestartDialog(getContext()); + } + + /** + * Resets all blocked flags. + */ + private void resetFlags() { + BaseSettings.DISABLED_FEATURE_FLAGS.save(""); + Utils.showToastShort(str("revanced_debug_feature_flags_manager_toast_reset")); + + AbstractPreferenceFragment.showRestartDialog(getContext()); + } +} diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ImportExportPreference.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ImportExportPreference.java index 1c8580241..6ae43018e 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ImportExportPreference.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ImportExportPreference.java @@ -11,7 +11,6 @@ import android.preference.Preference; import android.text.InputType; import android.util.AttributeSet; import android.util.Pair; -import android.util.TypedValue; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.LinearLayout; @@ -20,6 +19,7 @@ import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.ui.CustomDialog; +import app.revanced.extension.shared.ui.Dim; @SuppressWarnings({"unused", "deprecation"}) public class ImportExportPreference extends EditTextPreference implements Preference.OnPreferenceClickListener { @@ -35,7 +35,7 @@ public class ImportExportPreference extends EditTextPreference implements Prefer editText.setAutofillHints((String) null); } editText.setInputType(editText.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); - editText.setTextSize(TypedValue.COMPLEX_UNIT_PT, 7); // Use a smaller font to reduce text wrap. + editText.setTextSize(14); setOnPreferenceClickListener(this); } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ReVancedAboutPreference.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ReVancedAboutPreference.java index 4f4d3ef92..0d4003b91 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ReVancedAboutPreference.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ReVancedAboutPreference.java @@ -1,7 +1,6 @@ package app.revanced.extension.shared.settings.preference; import static app.revanced.extension.shared.StringRef.str; -import static app.revanced.extension.shared.Utils.dipToPixels; import static app.revanced.extension.shared.requests.Route.Method.GET; import android.annotation.SuppressLint; @@ -41,6 +40,7 @@ import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.requests.Requester; import app.revanced.extension.shared.requests.Route; +import app.revanced.extension.shared.ui.Dim; /** * Opens a dialog showing official links. @@ -222,11 +222,10 @@ class WebViewDialog extends Dialog { LinearLayout mainLayout = new LinearLayout(getContext()); mainLayout.setOrientation(LinearLayout.VERTICAL); - final int padding = dipToPixels(10); - mainLayout.setPadding(padding, padding, padding, padding); + mainLayout.setPadding(Dim.dp10, Dim.dp10, Dim.dp10, Dim.dp10); // Set rounded rectangle background. ShapeDrawable mainBackground = new ShapeDrawable(new RoundRectShape( - Utils.createCornerRadii(28), null, null)); + Dim.roundedCorners(28), null, null)); mainBackground.getPaint().setColor(Utils.getDialogBackgroundColor()); mainLayout.setBackground(mainBackground); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java index e299613dc..5c595a97a 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java @@ -8,7 +8,6 @@ import android.os.Build; import android.preference.Preference; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; -import android.util.TypedValue; import android.view.ViewGroup; import android.view.Window; import android.view.WindowInsets; @@ -20,6 +19,7 @@ import androidx.annotation.Nullable; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseActivityHook; +import app.revanced.extension.shared.ui.Dim; @SuppressWarnings({"deprecation", "NewApi"}) public class ToolbarPreferenceFragment extends AbstractPreferenceFragment { @@ -88,14 +88,13 @@ public class ToolbarPreferenceFragment extends AbstractPreferenceFragment { toolbar.setNavigationIcon(getBackButtonDrawable()); toolbar.setNavigationOnClickListener(view -> preferenceScreenDialog.dismiss()); - final int margin = Utils.dipToPixels(16); - toolbar.setTitleMargin(margin, 0, margin, 0); + toolbar.setTitleMargin(Dim.dp16, 0, Dim.dp16, 0); TextView toolbarTextView = Utils.getChildView(toolbar, true, TextView.class::isInstance); if (toolbarTextView != null) { toolbarTextView.setTextColor(Utils.getAppForegroundColor()); - toolbarTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20); + toolbarTextView.setTextSize(20); } // Allow package-specific toolbar customization. diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultsAdapter.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultsAdapter.java index f1893a232..cb5e792e8 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultsAdapter.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultsAdapter.java @@ -484,7 +484,7 @@ public abstract class BaseSearchResultsAdapter extends ArrayAdapter= Build.VERSION_CODES.Q) { @@ -149,7 +149,7 @@ public abstract class BaseSearchViewController { // Create cursor drawable. GradientDrawable cursorDrawable = new GradientDrawable(); cursorDrawable.setShape(GradientDrawable.RECTANGLE); - cursorDrawable.setSize(Utils.dipToPixels(2), -1); // Width: 2dp, Height: match text height. + cursorDrawable.setSize(Dim.dp2, -1); // Width: 2dp, Height: match text height. cursorDrawable.setColor(cursorColor); // Set cursor drawable. @@ -164,7 +164,7 @@ public abstract class BaseSearchViewController { overlayContainer = new FrameLayout(activity); overlayContainer.setVisibility(View.GONE); overlayContainer.setBackgroundColor(Utils.getAppBackgroundColor()); - overlayContainer.setElevation(Utils.dipToPixels(8)); + overlayContainer.setElevation(Dim.dp8); // Container for search results. FrameLayout searchResultsContainer = new FrameLayout(activity); @@ -669,7 +669,7 @@ public abstract class BaseSearchViewController { protected static GradientDrawable createBackgroundDrawable() { GradientDrawable background = new GradientDrawable(); background.setShape(GradientDrawable.RECTANGLE); - background.setCornerRadius(Utils.dipToPixels(28)); + background.setCornerRadius(Dim.dp28); background.setColor(getSearchViewBackground()); return background; } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/ui/ColorDot.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/ui/ColorDot.java index f0eeef408..07e9b4113 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/ui/ColorDot.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/ui/ColorDot.java @@ -1,7 +1,6 @@ package app.revanced.extension.shared.ui; import static app.revanced.extension.shared.Utils.adjustColorBrightness; -import static app.revanced.extension.shared.Utils.dipToPixels; import static app.revanced.extension.shared.Utils.getAppBackgroundColor; import static app.revanced.extension.shared.Utils.isDarkModeEnabled; import static app.revanced.extension.shared.settings.preference.ColorPickerPreference.DISABLED_ALPHA; @@ -13,7 +12,7 @@ import android.view.View; import androidx.annotation.ColorInt; public class ColorDot { - private static final int STROKE_WIDTH = dipToPixels(1.5f); // Stroke width in dp. + private static final int STROKE_WIDTH = Dim.dp(1.5f); /** * Creates a circular drawable with a main fill and a stroke. @@ -55,7 +54,7 @@ public class ColorDot { targetView.setAlpha(enabled ? 1.0f : DISABLED_ALPHA); if (!isDarkModeEnabled()) { targetView.setClipToOutline(true); - targetView.setElevation(dipToPixels(2)); + targetView.setElevation(Dim.dp2); } } } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/ui/CustomDialog.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/ui/CustomDialog.java index 1b65bea32..15d80c916 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/ui/CustomDialog.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/ui/CustomDialog.java @@ -1,7 +1,5 @@ package app.revanced.extension.shared.ui; -import static app.revanced.extension.shared.Utils.dipToPixels; - import android.app.Dialog; import android.content.Context; import android.graphics.Color; @@ -37,7 +35,6 @@ public class CustomDialog { private final Context context; private final Dialog dialog; private final LinearLayout mainLayout; - private final int dip4, dip8, dip16, dip24, dip36; /** * Creates a custom dialog with a styled layout, including a title, message, buttons, and an optional EditText. @@ -93,13 +90,6 @@ public class CustomDialog { this.dialog = new Dialog(context); this.dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); // Remove default title bar. - // Preset size constants. - dip4 = dipToPixels(4); - dip8 = dipToPixels(8); - dip16 = dipToPixels(16); - dip24 = dipToPixels(24); - dip36 = dipToPixels(36); - // Create main layout. mainLayout = createMainLayout(); addTitle(title); @@ -122,11 +112,11 @@ public class CustomDialog { private LinearLayout createMainLayout() { LinearLayout layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); - layout.setPadding(dip24, dip16, dip24, dip24); + layout.setPadding(Dim.dp24, Dim.dp16, Dim.dp24, Dim.dp24); // Set rounded rectangle background. ShapeDrawable background = new ShapeDrawable(new RoundRectShape( - Utils.createCornerRadii(28), null, null)); + Dim.roundedCorners(28), null, null)); // Dialog background. background.getPaint().setColor(Utils.getDialogBackgroundColor()); layout.setBackground(background); @@ -152,7 +142,7 @@ public class CustomDialog { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - params.setMargins(0, 0, 0, dip16); + params.setMargins(0, 0, 0, Dim.dp16); titleView.setLayoutParams(params); mainLayout.addView(titleView); @@ -180,9 +170,9 @@ public class CustomDialog { // EditText (if provided). if (editText != null) { ShapeDrawable background = new ShapeDrawable(new RoundRectShape( - Utils.createCornerRadii(10), null, null)); + Dim.roundedCorners(10), null, null)); background.getPaint().setColor(Utils.getEditTextBackground()); - scrollView.setPadding(dip8, dip8, dip8, dip8); + scrollView.setPadding(Dim.dp8, Dim.dp8, Dim.dp8, Dim.dp8); scrollView.setBackground(background); scrollView.setClipToOutline(true); @@ -241,7 +231,7 @@ public class CustomDialog { LinearLayout.LayoutParams buttonContainerParams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - buttonContainerParams.setMargins(0, dip16, 0, 0); + buttonContainerParams.setMargins(0, Dim.dp16, 0, 0); buttonContainer.setLayoutParams(buttonContainerParams); List