Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bottom tab #211

Merged
merged 14 commits into from
Oct 19, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,36 @@ class NoxAndroidAutoModule(reactContext: ReactApplicationContext) : ReactContext
override fun getName() = "NoxAndroidAutoModule"
@RequiresApi(Build.VERSION_CODES.O_MR1)
@ReactMethod fun disableShowWhenLocked() {
val activity = reactApplicationContext.currentActivity;
activity?.setShowWhenLocked(false);
activity?.setTurnScreenOn(false);
val activity = reactApplicationContext.currentActivity
activity?.setShowWhenLocked(false)
activity?.setTurnScreenOn(false)
}
@ReactMethod fun getDrawOverAppsPermission(callback: Promise) {
val context = reactApplicationContext;
val activity = context.currentActivity;
callback.resolve(Settings.canDrawOverlays(activity));
val context = reactApplicationContext
val activity = context.currentActivity
callback.resolve(Settings.canDrawOverlays(activity))
}

@ReactMethod fun askDrawOverAppsPermission() {
val context = reactApplicationContext;
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:com.noxplay.noxplayer"));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
val context = reactApplicationContext
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:com.noxplay.noxplayer"))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
}

@ReactMethod fun keepScreenOn(screenOn: Boolean = true) {
val context = reactApplicationContext;
val activity = context.currentActivity;
val window = activity?.window;
val context = reactApplicationContext
val activity = context.currentActivity
val window = activity?.window
if (screenOn) {
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}

@ReactMethod fun isGestureNavigationMode(callback: Promise) {
val context = reactApplicationContext
callback.resolve(Settings.Secure.getInt(context.contentResolver, "navigation_mode", 0) == 2)
}
}
2 changes: 0 additions & 2 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
const path = require('path');

module.exports = api => {
const isTest = api.env('test');
if (isTest) {
Expand Down
17 changes: 11 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"@react-native-async-storage/async-storage": "^1.18.1",
"@react-native-community/netinfo": "^9.3.10",
"@react-native-cookies/cookies": "git+https://github.com/alexhernandez/cookies.git#patch-1",
"@react-native/metro-config": "0.72.9",
"@react-native/metro-config": "^0.74.0",
"@react-navigation/drawer": "^6.6.2",
"@react-navigation/material-top-tabs": "^6.6.2",
"@react-navigation/native": "^6.1.6",
Expand Down Expand Up @@ -55,10 +55,11 @@
"lottie-react-native": "^6.2.0",
"lru-cache": "7.14.0",
"md5": "^2.3.0",
"metro": "^0.79.1",
"qs": "^6.11.2",
"react": "18.2.0",
"react-i18next": "^12.2.2",
"react-native": "0.72.4",
"react-native": "0.72.6",
"react-native-app-auth": "^7.0.0",
"react-native-background-timer": "git+https://github.com/lovegaoshi/react-native-background-timer.git",
"react-native-blob-jsi-helper": "git+https://github.com/lovegaoshi/react-native-blob-jsi-helper.git",
Expand All @@ -85,17 +86,21 @@
"react-native-vector-icons": "^9.2.0",
"react-native-video": "^5.2.1",
"react-native-webview": "^13.3.0",
"react-native-windows": "^0.65.0-0",
"react-native-windows": "^0.72.14",
"react-use": "^17.4.0",
"use-debounce": "^9.0.4",
"uuid": "^9.0.0",
"ytdl-core": "git+https://[email protected]/lovegaoshi/node-ytdl-core.git",
"zustand": "^4.3.7"
},
"resolutions": {
"metro": "^0.79.1"
},
"devDependencies": {
"@babel/core": "^7.22.11",
"@babel/preset-env": "^7.22.10",
"@babel/runtime": "^7.20.0",
"@babel/core": "^7.23.2",
"@babel/preset-env": "^7.23.2",
"@babel/preset-typescript": "^7.23.2",
"@babel/runtime": "^7.23.2",
"@react-native-community/eslint-config": "^3.2.0",
"@tsconfig/react-native": "^2.0.2",
"@types/jest": "^29.2.1",
Expand Down
27 changes: 23 additions & 4 deletions src/AzusaPlayer.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import React from 'react';
import React, { useEffect } from 'react';
import { View } from 'react-native';
import {
NavigationContainer,
DarkTheme as NavigationDarkTheme,
DefaultTheme as NavigationDefaultTheme,
ParamListBase,
} from '@react-navigation/native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import {
createDrawerNavigator,
DrawerNavigationProp,
} from '@react-navigation/drawer';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
import {
IconButton,
Expand All @@ -16,6 +20,7 @@ import {
} from 'react-native-paper';
import merge from 'deepmerge';
import { useTranslation } from 'react-i18next';

import { Player } from './components/player/View';
import Playlist from './components/playlist/View';
import PlayerBottomPanel from './components/player/PlayerProgressControls';
Expand All @@ -27,6 +32,7 @@ import DummySettings from './components/setting/DummySettings';
import './localization/i18n';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { ICONS } from '@enums/Icons';
import NoxAndroidBottomTab from './components/bottomtab/NoxBottomTab';

const { LightTheme, DarkTheme } = adaptNavigationTheme({
reactNavigationLight: NavigationDefaultTheme,
Expand All @@ -37,9 +43,15 @@ const CombinedDefaultTheme = merge(MD3LightTheme, LightTheme);
const CombinedDarkTheme = merge(MD3DarkTheme, DarkTheme);
const PlayerStyle = { backgroundColor: 'transparent' };

const NoxPlayer = () => {
interface Props {
navigation: DrawerNavigationProp<ParamListBase>;
setNavigation?: (val: DrawerNavigationProp<ParamListBase>) => void;
}
const NoxPlayer = ({ navigation, setNavigation = () => undefined }: Props) => {
const Tab = createMaterialTopTabNavigator();

useEffect(() => setNavigation(navigation), []);

return (
<View style={{ flex: 1, justifyContent: 'flex-end' }}>
<Tab.Navigator style={PlayerStyle}>
Expand Down Expand Up @@ -67,6 +79,12 @@ const AzusaPlayer = () => {
? CombinedDarkTheme
: CombinedDefaultTheme;
const insets = useSafeAreaInsets();
const [navigation, setNavigation] = React.useState<
DrawerNavigationProp<ParamListBase> | undefined
>(undefined);

const NoxPlayer2 = ({ navigation }: Props) =>
NoxPlayer({ navigation, setNavigation });

return (
<PaperProvider
Expand Down Expand Up @@ -107,7 +125,7 @@ const AzusaPlayer = () => {
title: String(t('appDrawer.homeScreenName')),
header: () => null,
}}
component={NoxPlayer}
component={NoxPlayer2}
/>
<Drawer.Screen
name={ViewEnum.EXPORE}
Expand All @@ -127,6 +145,7 @@ const AzusaPlayer = () => {
component={Settings}
/>
</Drawer.Navigator>
<NoxAndroidBottomTab navigation={navigation} />
</View>
</NavigationContainer>
</PaperProvider>
Expand Down
121 changes: 121 additions & 0 deletions src/components/bottomtab/NoxBottomTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React, { useEffect } from 'react';
import { NativeModules, Platform, StyleSheet, View } from 'react-native';
import { IconButton } from 'react-native-paper';
import { useNavigation, ParamListBase } from '@react-navigation/native';
import {
DrawerNavigationProp,
getDrawerStatusFromState,
} from '@react-navigation/drawer';

import { ViewEnum } from '@enums/View';
import { useNoxSetting } from '@hooks/useSetting';

const { NoxAndroidAutoModule } = NativeModules;

interface IconProps {
icon: string;
onPress: () => void;
}
const BottomIconButton = ({ icon, onPress }: IconProps) => {
return (
<IconButton
icon={icon}
style={styles.iconButton}
size={40}
onPress={onPress}
/>
);
};

enum Routes {
playlist = 'playlist-music',
music = 'music-note',
explore = 'compass',
setting = 'cog',
}

interface Props {
navigation?: DrawerNavigationProp<ParamListBase>;
}
const NoxAndroidBottomTab = ({ navigation }: Props) => {
const [gestureMode, setGestureMode] = React.useState(false);
const navigationGlobal = useNavigation();
const playerStyle = useNoxSetting(state => state.playerStyle);
const [route, setRoute] = React.useState('music');

useEffect(() => {
// TODO: how to use await instead of states and useEffect?
if (Platform.OS === 'android') {
NoxAndroidAutoModule.isGestureNavigationMode().then(setGestureMode);
}
}, []);

const isDrawerOpen = () => {
if (navigation === undefined) return false;
return getDrawerStatusFromState(navigation.getState()) === 'open';
};

const onDrawerPress = () => {
if (navigation === undefined) return;
setRoute(Routes.playlist);
if (isDrawerOpen()) {
navigation.closeDrawer();
return;
}
navigation.openDrawer();
};

const renderIcon = (icon: Routes) =>
route === icon ? icon : `${icon}-outline`;

if (gestureMode) {
return (
<View
style={[
styles.panel,
{ backgroundColor: playerStyle.colors.background },
]}
>
<BottomIconButton
icon={renderIcon(Routes.playlist)}
onPress={onDrawerPress}
/>
<BottomIconButton
icon={renderIcon(Routes.music)}
onPress={() => {
navigationGlobal.navigate(ViewEnum.PLAYER_HOME as never);
setRoute(Routes.music);
}}
/>
<BottomIconButton
icon={renderIcon(Routes.explore)}
onPress={() => {
navigationGlobal.navigate(ViewEnum.EXPORE as never);
setRoute(Routes.explore);
}}
/>
<BottomIconButton
icon={renderIcon(Routes.setting)}
onPress={() => {
navigationGlobal.navigate(ViewEnum.SETTINGS as never);
setRoute(Routes.setting);
}}
/>
</View>
);
}
return <></>;
};

const styles = StyleSheet.create({
panel: {
flexDirection: 'row',
},
iconButton: {
flex: 1,
borderRadius: 30,
marginVertical: 3,
},
});

export default NoxAndroidBottomTab;
1 change: 1 addition & 0 deletions src/components/player/Lyric.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export const LyricView = ({
if (hasLrcFromLocal()) {
logger.log('Loading Lrc from localStorage...');
const lrcDetail = lyricMapping.get(track?.song.id);
if (lrcDetail === undefined) return;
searchLyric(lrcDetail?.lyricKey, setLrc);
setLrcOption({ key: lrcDetail?.lyricKey });
setCurrentTimeOffset(lrcDetail!.lyricOffset);
Expand Down
2 changes: 1 addition & 1 deletion src/components/player/PlayerControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,5 @@ const styles = StyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
},
btnSpacer: { width: 6 },
btnSpacer: { width: 8 },
});
7 changes: 1 addition & 6 deletions src/components/player/TrackInfo/SongMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,15 +182,10 @@ export default ({
title={t('SongOperations.songR128gain')}
/>
<ABSliderMenu song={song} closeMenu={closeMenu} />
<Menu.Item
leadingIcon={ICONS.REMOVE}
onPress={() => removeSongs()}
title={t('SongOperations.songRemoveTitle')}
/>
<Menu.Item
leadingIcon={ICONS.REMOVE_AND_BAN_BVID}
onPress={() => removeSongs(true)}
title={t('SongOperations.songRemoveNBanTitle')}
title={t('SongOperations.songRemoveTitle')}
/>
</Menu>
);
Expand Down
27 changes: 4 additions & 23 deletions src/components/playlist/PlaylistInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ export default ({
const searchBkgrdWidth = useRef(new Animated.Value(0)).current;
const searchBkgrdHeight = useRef(new Animated.Value(0)).current;
const [searchVisible, setSearchVisible] = useState(search);
// TODO: a more elegant way to signal content update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const playlistInfoUpdate = useNoxSetting(state => state.playlistInfoUpdate);
const playlistSearchAutoFocus: boolean = useNoxSetting(
state => state.playlistSearchAutoFocus
);
Expand Down Expand Up @@ -155,27 +158,7 @@ export default ({
]).start(() => setSearchVisible(false));
}
}, [search]);

/**
* pull down menu:
<Animated.View
style={[
{
position: 'absolute',
backgroundColor: 'grey',
width: searchBkgrdWidth.interpolate({
inputRange: [0, 100],
outputRange: ['0%', '100%'],
}),
height: searchBkgrdHeight.interpolate({
inputRange: [0, 100],
outputRange: [50, 100],
}),
},
]}
></Animated.View>
*/


return (
<View style={styles.container}>
<Animated.View
Expand All @@ -199,7 +182,6 @@ export default ({
style={styles.textInput}
inputStyle={styles.searchInput}
ref={searchContainerRef}
// autoFocus={playlistSearchAutoFocus}
selectTextOnFocus
selectionColor={playerStyle.customColors.textInputSelectionColor}
icon={search ? 'format-list-checkbox' : () => undefined}
Expand Down Expand Up @@ -240,6 +222,5 @@ const styles = StyleSheet.create({
position: 'absolute',
paddingLeft: 15,
zIndex: -1,
// Add any additional styles for the Pressable component here
},
});
Loading
Loading