mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 00:32:04 +00:00
Merge pull request #279 from AdityasahuX07/patch-6
Modern UI for dynamic volume and brightness overlays and fix brightness control does not work on Android
This commit is contained in:
commit
371aacd734
1 changed files with 122 additions and 212 deletions
|
|
@ -133,6 +133,20 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
} as any;
|
} as any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Helper to get dynamic volume icon
|
||||||
|
const getVolumeIcon = (value: number) => {
|
||||||
|
if (value === 0) return 'volume-off';
|
||||||
|
if (value < 0.3) return 'volume-mute';
|
||||||
|
if (value < 0.6) return 'volume-down';
|
||||||
|
return 'volume-up';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to get dynamic brightness icon
|
||||||
|
const getBrightnessIcon = (value: number) => {
|
||||||
|
if (value < 0.3) return 'brightness-low';
|
||||||
|
if (value < 0.7) return 'brightness-medium';
|
||||||
|
return 'brightness-high';
|
||||||
|
};
|
||||||
|
|
||||||
// Get appropriate headers based on stream type
|
// Get appropriate headers based on stream type
|
||||||
const getStreamHeaders = () => {
|
const getStreamHeaders = () => {
|
||||||
|
|
@ -2568,7 +2582,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
|
|
||||||
logger.log('[AndroidVideoPlayer] Fetching streams for next episode:', nextEpisodeId);
|
logger.log('[AndroidVideoPlayer] Fetching streams for next episode:', nextEpisodeId);
|
||||||
|
|
||||||
// Import stremio service
|
// Import stremio service
|
||||||
const stremioService = require('../../services/stremioService').default;
|
const stremioService = require('../../services/stremioService').default;
|
||||||
|
|
||||||
let bestStream: any = null;
|
let bestStream: any = null;
|
||||||
|
|
@ -3136,7 +3150,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{/* Left side gesture handler - tap + long press (brightness gesture disabled) */}
|
{/* Left side gesture handler - brightness + tap + long press (Android and iOS) */}
|
||||||
<LongPressGestureHandler
|
<LongPressGestureHandler
|
||||||
onActivated={onLongPressActivated}
|
onActivated={onLongPressActivated}
|
||||||
onEnded={onLongPressEnd}
|
onEnded={onLongPressEnd}
|
||||||
|
|
@ -3145,20 +3159,29 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
shouldCancelWhenOutside={false}
|
shouldCancelWhenOutside={false}
|
||||||
simultaneousHandlers={[]}
|
simultaneousHandlers={[]}
|
||||||
>
|
>
|
||||||
<TapGestureHandler
|
<PanGestureHandler
|
||||||
onActivated={toggleControls}
|
onGestureEvent={gestureControls.onBrightnessGestureEvent}
|
||||||
|
activeOffsetY={[-10, 10]}
|
||||||
|
failOffsetX={[-30, 30]}
|
||||||
shouldCancelWhenOutside={false}
|
shouldCancelWhenOutside={false}
|
||||||
simultaneousHandlers={[]}
|
simultaneousHandlers={[]}
|
||||||
|
maxPointers={1}
|
||||||
>
|
>
|
||||||
<View style={{
|
<TapGestureHandler
|
||||||
position: 'absolute',
|
onActivated={toggleControls}
|
||||||
top: screenDimensions.height * 0.15,
|
shouldCancelWhenOutside={false}
|
||||||
left: 0,
|
simultaneousHandlers={[]}
|
||||||
width: screenDimensions.width * 0.4,
|
>
|
||||||
height: screenDimensions.height * 0.7,
|
<View style={{
|
||||||
zIndex: 10,
|
position: 'absolute',
|
||||||
}} />
|
top: screenDimensions.height * 0.15,
|
||||||
</TapGestureHandler>
|
left: 0,
|
||||||
|
width: screenDimensions.width * 0.4,
|
||||||
|
height: screenDimensions.height * 0.7,
|
||||||
|
zIndex: 10,
|
||||||
|
}} />
|
||||||
|
</TapGestureHandler>
|
||||||
|
</PanGestureHandler>
|
||||||
</LongPressGestureHandler>
|
</LongPressGestureHandler>
|
||||||
|
|
||||||
{/* Combined gesture handler for right side - volume + tap + long press */}
|
{/* Combined gesture handler for right side - volume + tap + long press */}
|
||||||
|
|
@ -3409,8 +3432,57 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
buffered={buffered}
|
buffered={buffered}
|
||||||
formatTime={formatTime}
|
formatTime={formatTime}
|
||||||
playerBackend={useVLC ? 'VLC' : 'ExoPlayer'}
|
playerBackend={useVLC ? 'VLC' : 'ExoPlayer'}
|
||||||
|
nextLoadingTitle={nextLoadingTitle}
|
||||||
|
controlsFixedOffset={Math.min(Dimensions.get('window').width, Dimensions.get('window').height) >= 768 ? 120 : 100}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Combined Volume & Brightness Gesture Indicator - NEW PILL STYLE (No Bar) */}
|
||||||
|
{(gestureControls.showVolumeOverlay || gestureControls.showBrightnessOverlay) && (
|
||||||
|
<View style={localStyles.gestureIndicatorContainer}>
|
||||||
|
{/* Dynamic Icon */}
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
localStyles.iconWrapper,
|
||||||
|
{
|
||||||
|
// Conditional Background Color Logic
|
||||||
|
backgroundColor: gestureControls.showVolumeOverlay && volume === 0
|
||||||
|
? 'rgba(242, 184, 181)'
|
||||||
|
: 'rgba(59, 59, 59)'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<MaterialIcons
|
||||||
|
name={
|
||||||
|
gestureControls.showVolumeOverlay
|
||||||
|
? getVolumeIcon(volume)
|
||||||
|
: getBrightnessIcon(brightness)
|
||||||
|
}
|
||||||
|
size={24} // Reduced size to fit inside a 32-40px circle better
|
||||||
|
color={
|
||||||
|
gestureControls.showVolumeOverlay && volume === 0
|
||||||
|
? 'rgba(96, 20, 16)' // Bright RED for MUTE icon itself
|
||||||
|
: 'rgba(255, 255, 255)' // White for all other states
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Text Label: Shows "Muted" or percentage */}
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
localStyles.gestureText,
|
||||||
|
// Conditional Text Color Logic
|
||||||
|
gestureControls.showVolumeOverlay && volume === 0 && { color: 'rgba(242, 184, 181)' } // Light RED for "Muted"
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{/* Conditional Text Content Logic */}
|
||||||
|
{gestureControls.showVolumeOverlay && volume === 0
|
||||||
|
? "Muted" // Display "Muted" when volume is 0
|
||||||
|
: `${Math.round((gestureControls.showVolumeOverlay ? volume : brightness) * 100)}%` // Display percentage otherwise
|
||||||
|
}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{showPauseOverlay && (
|
{showPauseOverlay && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
activeOpacity={1}
|
activeOpacity={1}
|
||||||
|
|
@ -3753,214 +3825,20 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
controlsFixedOffset={Math.min(Dimensions.get('window').width, Dimensions.get('window').height) >= 768 ? 120 : 100}
|
controlsFixedOffset={Math.min(Dimensions.get('window').width, Dimensions.get('window').height) >= 768 ? 120 : 100}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Volume Overlay */}
|
|
||||||
{gestureControls.showVolumeOverlay && (
|
|
||||||
<Animated.View
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: screenDimensions.width / 2 - 60,
|
|
||||||
top: screenDimensions.height / 2 - 60,
|
|
||||||
opacity: gestureControls.volumeOverlayOpacity,
|
|
||||||
zIndex: 1000,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View style={{
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.9)',
|
|
||||||
borderRadius: 12,
|
|
||||||
padding: 16,
|
|
||||||
alignItems: 'center',
|
|
||||||
width: 120,
|
|
||||||
height: 120,
|
|
||||||
justifyContent: 'center',
|
|
||||||
shadowColor: '#000',
|
|
||||||
shadowOffset: { width: 0, height: 4 },
|
|
||||||
shadowOpacity: 0.5,
|
|
||||||
shadowRadius: 8,
|
|
||||||
elevation: 10,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
}}>
|
|
||||||
<MaterialIcons
|
|
||||||
name={volume === 0 ? "volume-off" : volume < 0.3 ? "volume-mute" : volume < 0.7 ? "volume-down" : "volume-up"}
|
|
||||||
size={24}
|
|
||||||
color={volume === 0 ? "#FF6B6B" : "#FFFFFF"}
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Horizontal Dotted Progress Bar */}
|
|
||||||
<View style={{
|
|
||||||
width: 80,
|
|
||||||
height: 6,
|
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
|
||||||
borderRadius: 3,
|
|
||||||
position: 'relative',
|
|
||||||
overflow: 'hidden',
|
|
||||||
marginBottom: 8,
|
|
||||||
}}>
|
|
||||||
{/* Dotted background */}
|
|
||||||
<View style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
paddingHorizontal: 1,
|
|
||||||
}}>
|
|
||||||
{Array.from({ length: 16 }, (_, i) => (
|
|
||||||
<View
|
|
||||||
key={i}
|
|
||||||
style={{
|
|
||||||
width: 1.5,
|
|
||||||
height: 1.5,
|
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.3)',
|
|
||||||
borderRadius: 0.75,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Progress fill */}
|
|
||||||
<View style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: `${volume * 100}%`,
|
|
||||||
height: 6,
|
|
||||||
backgroundColor: volume === 0 ? '#FF6B6B' : '#E50914',
|
|
||||||
borderRadius: 3,
|
|
||||||
shadowColor: volume === 0 ? '#FF6B6B' : '#E50914',
|
|
||||||
shadowOffset: { width: 0, height: 1 },
|
|
||||||
shadowOpacity: 0.6,
|
|
||||||
shadowRadius: 2,
|
|
||||||
}} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Text style={{
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: '600',
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
}}>
|
|
||||||
{Math.round(volume * 100)}%
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</Animated.View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Brightness Overlay */}
|
|
||||||
{gestureControls.showBrightnessOverlay && (
|
|
||||||
<Animated.View
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: screenDimensions.width / 2 - 60,
|
|
||||||
top: screenDimensions.height / 2 - 60,
|
|
||||||
opacity: gestureControls.brightnessOverlayOpacity,
|
|
||||||
zIndex: 1000,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View style={{
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.9)',
|
|
||||||
borderRadius: 12,
|
|
||||||
padding: 16,
|
|
||||||
alignItems: 'center',
|
|
||||||
width: 120,
|
|
||||||
height: 120,
|
|
||||||
justifyContent: 'center',
|
|
||||||
shadowColor: '#000',
|
|
||||||
shadowOffset: { width: 0, height: 4 },
|
|
||||||
shadowOpacity: 0.5,
|
|
||||||
shadowRadius: 8,
|
|
||||||
elevation: 10,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: 'rgba(255, 255, 255, 0.1)',
|
|
||||||
}}>
|
|
||||||
<MaterialIcons
|
|
||||||
name={brightness < 0.2 ? "brightness-low" : brightness < 0.5 ? "brightness-medium" : brightness < 0.8 ? "brightness-high" : "brightness-auto"}
|
|
||||||
size={24}
|
|
||||||
color={brightness < 0.2 ? "#FFD700" : "#FFFFFF"}
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Horizontal Dotted Progress Bar */}
|
|
||||||
<View style={{
|
|
||||||
width: 80,
|
|
||||||
height: 6,
|
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
|
||||||
borderRadius: 3,
|
|
||||||
position: 'relative',
|
|
||||||
overflow: 'hidden',
|
|
||||||
marginBottom: 8,
|
|
||||||
}}>
|
|
||||||
{/* Dotted background */}
|
|
||||||
<View style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
paddingHorizontal: 1,
|
|
||||||
}}>
|
|
||||||
{Array.from({ length: 16 }, (_, i) => (
|
|
||||||
<View
|
|
||||||
key={i}
|
|
||||||
style={{
|
|
||||||
width: 1.5,
|
|
||||||
height: 1.5,
|
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.3)',
|
|
||||||
borderRadius: 0.75,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Progress fill */}
|
|
||||||
<View style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: `${brightness * 100}%`,
|
|
||||||
height: 6,
|
|
||||||
backgroundColor: brightness < 0.2 ? '#FFD700' : '#FFA500',
|
|
||||||
borderRadius: 3,
|
|
||||||
shadowColor: brightness < 0.2 ? '#FFD700' : '#FFA500',
|
|
||||||
shadowOffset: { width: 0, height: 1 },
|
|
||||||
shadowOpacity: 0.6,
|
|
||||||
shadowRadius: 2,
|
|
||||||
}} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Text style={{
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: '600',
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
}}>
|
|
||||||
{Math.round(brightness * 100)}%
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</Animated.View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Speed Activated Overlay */}
|
{/* Speed Activated Overlay */}
|
||||||
{showSpeedActivatedOverlay && (
|
{showSpeedActivatedOverlay && (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: screenDimensions.height * 0.1,
|
top: screenDimensions.height * 0.06,
|
||||||
left: screenDimensions.width / 2 - 40,
|
left: screenDimensions.width / 2 - 40,
|
||||||
opacity: speedActivatedOverlayOpacity,
|
opacity: speedActivatedOverlayOpacity,
|
||||||
zIndex: 1000,
|
zIndex: 1000,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View style={{
|
<View style={{
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
backgroundColor: 'rgba(25, 25, 25, 0.6)',
|
||||||
borderRadius: 8,
|
borderRadius: 35,
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
paddingVertical: 6,
|
paddingVertical: 6,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|
@ -3977,7 +3855,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
}}>
|
}}>
|
||||||
{holdToSpeedValue}x Speed Activated
|
{holdToSpeedValue}x Speed
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
|
|
@ -4186,4 +4064,36 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AndroidVideoPlayer;
|
// New styles for the gesture indicator
|
||||||
|
const localStyles = StyleSheet.create({
|
||||||
|
gestureIndicatorContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '4%', // Adjust this for vertical position
|
||||||
|
alignSelf: 'center', // Adjust this for horizontal position
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: 'rgba(25, 25, 25)', // Dark pill background
|
||||||
|
borderRadius: 70,
|
||||||
|
paddingHorizontal: 15,
|
||||||
|
paddingVertical: 15,
|
||||||
|
zIndex: 2000, // Very high z-index to ensure visibility
|
||||||
|
minWidth: 120, // Adjusted min width since bar is removed
|
||||||
|
},
|
||||||
|
iconWrapper: {
|
||||||
|
borderRadius: 50, // Makes it a perfect circle (set to a high number)
|
||||||
|
width: 40, // Define the diameter of the circle
|
||||||
|
height: 40, // Define the diameter of the circle
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginRight: 12, // Margin to separate icon circle from percentage text
|
||||||
|
},
|
||||||
|
gestureText: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 'normal',
|
||||||
|
minWidth: 35,
|
||||||
|
textAlign: 'right',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AndroidVideoPlayer;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue