chahinebrini 4a013bc43b feat(android-protection): präzise Tamper-Lock + a11y-Onboarding-Guide
Tamper-Lock von Keyword-Scanning auf präzise Einzel-Surfaces umgebaut:
blockt nur ReBreaks eigene Screens (Admin-Deaktivierung via DeviceAdminAdd,
a11y-Ausschalten, VPN-Trennen/Surface), nie Listen oder fremde Apps.

- Deny-Removal = Admin-only: OS graut Uninstall+Force-Stop für aktiven
  Device-Admin aus; einziger Bypass (Admin deaktivieren) bleibt a11y-gesperrt.
  Andere Apps verwalten/force-stoppen/deinstallieren bleibt komplett frei.
- a11y-Onboarding: passiver Bottom-Overlay-Hinweis + Settings-Reset auf
  Startseite nach Aktivierung + 1s-Delay vor App-Rückkehr.
- VPN-Trennen-Dialog + a11y-Ausschalten neu abgedeckt.
- a11y-Service-Icon im Plugin (klar als ReBreak erkennbar).

Verifiziert auf A50 per logcat: alle 4 Surfaces blocken, Listen + fremde
Apps frei, keine False-Positives.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 04:05:41 +02:00

79 lines
2.1 KiB
TypeScript

import { useEffect, useRef } from 'react';
import { Animated, Text, View } from 'react-native';
import { useTranslation } from 'react-i18next';
import { useColors } from '../../lib/theme';
interface DeviceProgressBarProps {
count: number;
max: number;
atLimit: boolean;
/** Optionales Label (z.B. "Mobil" / "Computer") statt generischem progress_label */
label?: string;
}
export function DeviceProgressBar({ count, max, atLimit, label }: DeviceProgressBarProps) {
const { t } = useTranslation();
const colors = useColors();
const fillAnim = useRef(new Animated.Value(0)).current;
const ratio = max > 0 ? Math.min(count / max, 1) : 0;
useEffect(() => {
Animated.timing(fillAnim, {
toValue: ratio,
duration: 380,
useNativeDriver: false,
}).start();
}, [ratio]);
const fillColor = atLimit ? colors.brandOrange : colors.success;
return (
<View style={{ gap: 5 }}>
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
<Text
style={{
fontSize: 12,
color: atLimit ? colors.brandOrange : colors.textMuted,
fontFamily: 'Nunito_600SemiBold',
}}
>
{atLimit
? (label ? `${label}${t('devices.progress_at_limit')}` : t('devices.progress_at_limit'))
: (label ?? t('devices.progress_label', { count, max }))}
</Text>
<Text
style={{
fontSize: 11,
color: colors.textMuted,
fontFamily: 'Nunito_400Regular',
}}
>
{count}/{max}
</Text>
</View>
<View
style={{
height: 6,
borderRadius: 3,
backgroundColor: colors.surfaceElevated,
overflow: 'hidden',
}}
>
<Animated.View
style={{
height: '100%',
borderRadius: 3,
backgroundColor: fillColor,
width: fillAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0%', '100%'],
}),
}}
/>
</View>
</View>
);
}