diff --git a/app/(auth)/(tabs)/(home)/settings/appearance/hide-libraries/page.tsx b/app/(auth)/(tabs)/(home)/settings/appearance/hide-libraries/page.tsx
index d0109fb7..e4a7e3b9 100644
--- a/app/(auth)/(tabs)/(home)/settings/appearance/hide-libraries/page.tsx
+++ b/app/(auth)/(tabs)/(home)/settings/appearance/hide-libraries/page.tsx
@@ -2,8 +2,9 @@ import { getUserViewsApi } from "@jellyfin/sdk/lib/utils/api";
import { useQuery } from "@tanstack/react-query";
import { useAtomValue } from "jotai";
import { useTranslation } from "react-i18next";
-import { ScrollView, Switch, View } from "react-native";
+import { ScrollView, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { Text } from "@/components/common/Text";
import { Loader } from "@/components/Loader";
import { ListGroup } from "@/components/list/ListGroup";
@@ -50,12 +51,12 @@ export default function AppearanceHideLibrariesPage() {
>
{data?.map((view) => (
{}}>
- {
updateSettings({
diff --git a/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx b/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx
index fe777462..5aba233a 100644
--- a/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx
+++ b/app/(auth)/(tabs)/(home)/settings/hide-libraries/page.tsx
@@ -2,7 +2,8 @@ import { getUserViewsApi } from "@jellyfin/sdk/lib/utils/api";
import { useQuery } from "@tanstack/react-query";
import { useAtomValue } from "jotai";
import { useTranslation } from "react-i18next";
-import { Switch, View } from "react-native";
+import { View } from "react-native";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { Text } from "@/components/common/Text";
import { Loader } from "@/components/Loader";
import { ListGroup } from "@/components/list/ListGroup";
@@ -46,7 +47,7 @@ export default function HideLibrariesPage() {
{data?.map((view) => (
{}}>
- {
updateSettings({
diff --git a/app/(auth)/(tabs)/(home)/settings/music/page.tsx b/app/(auth)/(tabs)/(home)/settings/music/page.tsx
index 70467b4d..7dc5e22e 100644
--- a/app/(auth)/(tabs)/(home)/settings/music/page.tsx
+++ b/app/(auth)/(tabs)/(home)/settings/music/page.tsx
@@ -3,9 +3,9 @@ import { useQuery } from "@tanstack/react-query";
import { useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Platform, ScrollView, View } from "react-native";
-import { Switch } from "react-native-gesture-handler";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { toast } from "sonner-native";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { Text } from "@/components/common/Text";
import { ListGroup } from "@/components/list/ListGroup";
import { ListItem } from "@/components/list/ListItem";
@@ -136,7 +136,7 @@ export default function MusicSettingsPage() {
title={t("home.settings.music.prefer_downloaded")}
disabled={pluginSettings?.preferLocalAudio?.locked}
>
-
@@ -159,7 +159,7 @@ export default function MusicSettingsPage() {
title={t("home.settings.music.lookahead_enabled")}
disabled={pluginSettings?.audioLookaheadEnabled?.locked}
>
-
@@ -233,7 +233,7 @@ export default function MusicSettingsPage() {
})}
/>
-
+
-
+
diff --git a/app/(auth)/(tabs)/(home)/settings/plugins/kefinTweaks/page.tsx b/app/(auth)/(tabs)/(home)/settings/plugins/kefinTweaks/page.tsx
index b6fd8c2e..059afbf0 100644
--- a/app/(auth)/(tabs)/(home)/settings/plugins/kefinTweaks/page.tsx
+++ b/app/(auth)/(tabs)/(home)/settings/plugins/kefinTweaks/page.tsx
@@ -13,7 +13,7 @@ export default function KefinTweaksPage() {
paddingRight: insets.right,
}}
>
-
+
diff --git a/app/(auth)/(tabs)/(home)/settings/plugins/marlin-search/page.tsx b/app/(auth)/(tabs)/(home)/settings/plugins/marlin-search/page.tsx
index d9e737ec..b36a493b 100644
--- a/app/(auth)/(tabs)/(home)/settings/plugins/marlin-search/page.tsx
+++ b/app/(auth)/(tabs)/(home)/settings/plugins/marlin-search/page.tsx
@@ -4,13 +4,13 @@ import { useTranslation } from "react-i18next";
import {
Linking,
ScrollView,
- Switch,
TextInput,
TouchableOpacity,
View,
} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { toast } from "sonner-native";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { Text } from "@/components/common/Text";
import { ListGroup } from "@/components/list/ListGroup";
import { ListItem } from "@/components/list/ListItem";
@@ -65,7 +65,7 @@ export default function MarlinSearchPage() {
paddingRight: insets.right,
}}
>
-
+
{/* disabledByAdmin renders the "Disabled by admin" notice as the row's
subtitle (same pattern as the Streamystats settings) — no clipping. */}
@@ -79,7 +79,7 @@ export default function MarlinSearchPage() {
queryClient.invalidateQueries({ queryKey: ["search"] });
}}
>
- {
diff --git a/app/(auth)/(tabs)/(home)/settings/plugins/streamystats/page.tsx b/app/(auth)/(tabs)/(home)/settings/plugins/streamystats/page.tsx
index a5a45519..7774a925 100644
--- a/app/(auth)/(tabs)/(home)/settings/plugins/streamystats/page.tsx
+++ b/app/(auth)/(tabs)/(home)/settings/plugins/streamystats/page.tsx
@@ -4,13 +4,13 @@ import { useTranslation } from "react-i18next";
import {
Linking,
ScrollView,
- Switch,
TextInput,
TouchableOpacity,
View,
} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { toast } from "sonner-native";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { Text } from "@/components/common/Text";
import { ListGroup } from "@/components/list/ListGroup";
import { ListItem } from "@/components/list/ListItem";
@@ -132,7 +132,7 @@ export default function StreamystatsPage() {
paddingRight: insets.right,
}}
>
-
+
-
-
-
- = scaled visual width to avoid clipping the switch sideways.
+ */
+const BOX_W = 40;
+const BOX_H = 30;
+const SCALE = 0.9;
+
+export const SettingSwitch: React.FC = (props) => {
+ if (Platform.OS !== "android") return ;
+ return (
+
+
+
+ );
+};
diff --git a/components/list/ListGroup.tsx b/components/list/ListGroup.tsx
index b9752bac..d3f955f0 100644
--- a/components/list/ListGroup.tsx
+++ b/components/list/ListGroup.tsx
@@ -23,9 +23,11 @@ export const ListGroup: React.FC> = ({
return (
-
- {title}
-
+ {title ? (
+
+ {title}
+
+ ) : null}
> = ({
}) => {
const effectiveSubtitle = disabledByAdmin ? "Disabled by admin" : subtitle;
const isDisabled = disabled || disabledByAdmin;
+ // Keep the row floor uniform; Android trims padding slightly (its native
+ // controls sit taller). Switch height is capped via SettingSwitch so toggle
+ // rows match non-toggle rows.
+ const rowSizing =
+ Platform.OS === "android" ? "min-h-[42px] py-1.5" : "min-h-[42px] py-2";
if (onPress)
return (
> = ({
);
return (
{
)
}
>
-
@@ -45,13 +46,23 @@ export const AppearanceSettings: React.FC = () => {
-
updateSettings({ mergeNextUpAndContinueWatching: value })
}
/>
+
+
+ updateSettings({ hideRemoteSessionButton: value })
+ }
+ />
+
router.push("/settings/appearance/hide-libraries/page")
@@ -59,16 +70,6 @@ export const AppearanceSettings: React.FC = () => {
title={t("home.settings.other.hide_libraries")}
showArrow
/>
-
-
- updateSettings({ hideRemoteSessionButton: value })
- }
- />
-
);
diff --git a/components/settings/AudioToggles.tsx b/components/settings/AudioToggles.tsx
index 93a267bd..e34d5a98 100644
--- a/components/settings/AudioToggles.tsx
+++ b/components/settings/AudioToggles.tsx
@@ -2,7 +2,7 @@ import { Ionicons } from "@expo/vector-icons";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Platform, View, type ViewProps } from "react-native";
-import { Switch } from "react-native-gesture-handler";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { AudioTranscodeMode, useSettings } from "@/utils/atoms/settings";
import { Text } from "../common/Text";
import { ListGroup } from "../list/ListGroup";
@@ -135,7 +135,7 @@ export const AudioToggles: React.FC = ({ ...props }) => {
title={t("home.settings.audio.set_audio_track")}
disabled={pluginSettings?.rememberAudioSelections?.locked}
>
-
diff --git a/components/settings/ChromecastSettings.tsx b/components/settings/ChromecastSettings.tsx
index 4da06332..6842528e 100644
--- a/components/settings/ChromecastSettings.tsx
+++ b/components/settings/ChromecastSettings.tsx
@@ -1,4 +1,5 @@
-import { Switch, View } from "react-native";
+import { View } from "react-native";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { useSettings } from "@/utils/atoms/settings";
import { ListGroup } from "../list/ListGroup";
import { ListItem } from "../list/ListItem";
@@ -9,7 +10,7 @@ export const ChromecastSettings: React.FC = ({ ...props }) => {
-
updateSettings({ enableH265ForChromecast })
diff --git a/components/settings/GestureControls.tsx b/components/settings/GestureControls.tsx
index b9c39ef4..8d30a157 100644
--- a/components/settings/GestureControls.tsx
+++ b/components/settings/GestureControls.tsx
@@ -2,7 +2,7 @@ import type React from "react";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import type { ViewProps } from "react-native";
-import { Switch } from "react-native";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import DisabledSetting from "@/components/settings/DisabledSetting";
import { useSettings } from "@/utils/atoms/settings";
import { ListGroup } from "../list/ListGroup";
@@ -39,7 +39,7 @@ export const GestureControls: React.FC = ({ ...props }) => {
)}
disabled={pluginSettings?.enableHorizontalSwipeSkip?.locked}
>
-
@@ -55,7 +55,7 @@ export const GestureControls: React.FC = ({ ...props }) => {
)}
disabled={pluginSettings?.enableLeftSideBrightnessSwipe?.locked}
>
-
@@ -71,7 +71,7 @@ export const GestureControls: React.FC = ({ ...props }) => {
)}
disabled={pluginSettings?.enableRightSideVolumeSwipe?.locked}
>
-
@@ -87,7 +87,7 @@ export const GestureControls: React.FC = ({ ...props }) => {
)}
disabled={pluginSettings?.hideVolumeSlider?.locked}
>
-
@@ -103,7 +103,7 @@ export const GestureControls: React.FC = ({ ...props }) => {
)}
disabled={pluginSettings?.hideBrightnessSlider?.locked}
>
-
diff --git a/components/settings/KefinTweaks.tsx b/components/settings/KefinTweaks.tsx
index 0021859c..5a12cc69 100644
--- a/components/settings/KefinTweaks.tsx
+++ b/components/settings/KefinTweaks.tsx
@@ -1,5 +1,5 @@
import { useTranslation } from "react-i18next";
-import { Switch } from "react-native";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { useSettings } from "@/utils/atoms/settings";
import { ListGroup } from "../list/ListGroup";
import { ListItem } from "../list/ListItem";
@@ -17,7 +17,7 @@ export const KefinTweaksSettings = () => {
title={t("home.settings.plugins.kefinTweaks.watchlist_enabler")}
disabledByAdmin={locked}
>
- updateSettings({ useKefinTweaks: value })}
diff --git a/components/settings/LocalNetworkSettings.tsx b/components/settings/LocalNetworkSettings.tsx
index 8bf99dbd..804185c7 100644
--- a/components/settings/LocalNetworkSettings.tsx
+++ b/components/settings/LocalNetworkSettings.tsx
@@ -2,8 +2,9 @@ import { Ionicons } from "@expo/vector-icons";
import type React from "react";
import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
-import { Switch, TouchableOpacity, View } from "react-native";
+import { TouchableOpacity, View } from "react-native";
import { toast } from "sonner-native";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { useWifiSSID } from "@/hooks/useWifiSSID";
import { useServerUrl } from "@/providers/ServerUrlProvider";
import { storage } from "@/utils/mmkv";
@@ -147,7 +148,10 @@ export function LocalNetworkSettings(): React.ReactElement | null {
title={t("home.settings.network.auto_switch_enabled")}
subtitle={t("home.settings.network.auto_switch_description")}
>
-
+
diff --git a/components/settings/MpvSubtitleSettings.tsx b/components/settings/MpvSubtitleSettings.tsx
index c481a080..e3b2feb8 100644
--- a/components/settings/MpvSubtitleSettings.tsx
+++ b/components/settings/MpvSubtitleSettings.tsx
@@ -1,7 +1,8 @@
import { Ionicons } from "@expo/vector-icons";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
-import { Platform, Switch, View, type ViewProps } from "react-native";
+import { Platform, View, type ViewProps } from "react-native";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { Stepper } from "@/components/inputs/Stepper";
import { Text } from "../common/Text";
import { ListGroup } from "../list/ListGroup";
@@ -126,7 +127,7 @@ export const MpvSubtitleSettings: React.FC = ({ ...props }) => {
)}
-
updateSettings({ mpvSubtitleBackgroundEnabled: value })
diff --git a/components/settings/OtherSettings.tsx b/components/settings/OtherSettings.tsx
index bd339c54..2bb26e6c 100644
--- a/components/settings/OtherSettings.tsx
+++ b/components/settings/OtherSettings.tsx
@@ -3,8 +3,9 @@ import { TFunction } from "i18next";
import type React from "react";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
-import { Linking, Switch, View } from "react-native";
+import { Linking, View } from "react-native";
import { BITRATES } from "@/components/BitrateSelector";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { PlatformDropdown } from "@/components/PlatformDropdown";
import DisabledSetting from "@/components/settings/DisabledSetting";
import useRouter from "@/hooks/useAppRouter";
@@ -132,7 +133,7 @@ export const OtherSettings: React.FC = () => {
title={t("home.settings.other.safe_area_in_controls")}
disabled={pluginSettings?.safeAreaInControlsEnabled?.locked}
>
-
@@ -150,7 +151,7 @@ export const OtherSettings: React.FC = () => {
)
}
>
-
@@ -188,7 +189,7 @@ export const OtherSettings: React.FC = () => {
title={t("home.settings.other.disable_haptic_feedback")}
disabled={pluginSettings?.disableHapticFeedback?.locked}
>
-
diff --git a/components/settings/PlaybackControlsSettings.tsx b/components/settings/PlaybackControlsSettings.tsx
index 17ee5367..9fa39414 100644
--- a/components/settings/PlaybackControlsSettings.tsx
+++ b/components/settings/PlaybackControlsSettings.tsx
@@ -3,8 +3,9 @@ import { TFunction } from "i18next";
import type React from "react";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
-import { Switch, View } from "react-native";
+import { View } from "react-native";
import { BITRATES } from "@/components/BitrateSelector";
+import { SettingSwitch } from "@/components/common/SettingSwitch";
import { PlatformDropdown } from "@/components/PlatformDropdown";
import { PLAYBACK_SPEEDS } from "@/components/PlaybackSpeedSelector";
import DisabledSetting from "@/components/settings/DisabledSetting";
@@ -115,7 +116,7 @@ export const PlaybackControlsSettings: React.FC = () => {
return (
-
+
{
title={t("home.settings.other.safe_area_in_controls")}
disabled={pluginSettings?.safeAreaInControlsEnabled?.locked}
>
-
@@ -205,7 +206,7 @@ export const PlaybackControlsSettings: React.FC = () => {
title={t("home.settings.other.disable_haptic_feedback")}
disabled={pluginSettings?.disableHapticFeedback?.locked}
>
-
@@ -218,7 +219,7 @@ export const PlaybackControlsSettings: React.FC = () => {
title={t("home.settings.other.auto_play_next_episode")}
disabled={pluginSettings?.autoPlayNextEpisode?.locked}
>
-
diff --git a/components/settings/StorageSettings.tsx b/components/settings/StorageSettings.tsx
index 82a97d50..e61586cf 100644
--- a/components/settings/StorageSettings.tsx
+++ b/components/settings/StorageSettings.tsx
@@ -125,7 +125,7 @@ export const StorageSettings = () => {
{!Platform.isTV && (
-
+
= ({ ...props }) => {
return (
@@ -152,7 +153,7 @@ export const SubtitleToggles: React.FC = ({ ...props }) => {
title={t("home.settings.subtitles.set_subtitle_track")}
disabled={pluginSettings?.rememberSubtitleSelections?.locked}
>
-