diff --git a/android/app/src/main/java/com/lxmusicmobile/lyric/Lyric.java b/android/app/src/main/java/com/lxmusicmobile/lyric/Lyric.java index df8e94f..28e2963 100644 --- a/android/app/src/main/java/com/lxmusicmobile/lyric/Lyric.java +++ b/android/app/src/main/java/com/lxmusicmobile/lyric/Lyric.java @@ -22,6 +22,8 @@ public class Lyric extends LyricPlayer { String lastText = "LX Music ^-^"; int lyricViewX = 0; int lyricViewY = 0; + String textX = "LEFT"; + String textY = "TOP"; Lyric(ReactApplicationContext reactContext) { this.reactAppContext = reactContext; @@ -75,21 +77,23 @@ public class Lyric extends LyricPlayer { lyricView.runOnUiThread(new Runnable() { @Override public void run() { - lyricView.showLyricView(isLock, themeColor, lyricViewX, lyricViewY); + lyricView.showLyricView(isLock, themeColor, lyricViewX, lyricViewY, textX, textY); lyricView.setLyric(lastText); setTempPause(false); } }); } - public void showLyric(boolean isLock, String themeColor, int lyricViewX, int lyricViewY) { + public void showLyric(boolean isLock, String themeColor, int lyricViewX, int lyricViewY, String textX, String textY) { if (lyricEvent == null) lyricEvent = new LyricEvent(reactAppContext); if (lyricView == null) lyricView = new LyricView(reactAppContext, lyricEvent); this.isLock = isLock; this.themeColor = themeColor; this.lyricViewX = lyricViewX; this.lyricViewY = lyricViewY; - lyricView.showLyricView(isLock, themeColor, lyricViewX, lyricViewY); + this.textX = textX; + this.textY = textY; + lyricView.showLyricView(isLock, themeColor, lyricViewX, lyricViewY, textX, textY); isShowLyric = true; } @@ -143,4 +147,10 @@ public class Lyric extends LyricPlayer { lyricView.setColor(color); this.themeColor = color; } + + public void setLyricTextPosition(String positionX, String positionY) { + this.textX = positionX; + this.textY = positionY; + lyricView.setLyricTextPosition(positionX, positionY); + } } diff --git a/android/app/src/main/java/com/lxmusicmobile/lyric/LyricModule.java b/android/app/src/main/java/com/lxmusicmobile/lyric/LyricModule.java index 313fc5f..8e50a15 100644 --- a/android/app/src/main/java/com/lxmusicmobile/lyric/LyricModule.java +++ b/android/app/src/main/java/com/lxmusicmobile/lyric/LyricModule.java @@ -37,9 +37,9 @@ public class LyricModule extends ReactContextBaseJavaModule { // } @ReactMethod - public void showLyric(boolean isLook, String themeColor, int lyricViewX, int lyricViewY, Promise promise) { + public void showLyric(boolean isLook, String themeColor, int lyricViewX, int lyricViewY, String textX, String textY , Promise promise) { if (lyric == null) lyric = new Lyric(reactContext); - lyric.showLyric(isLook, themeColor, lyricViewX, lyricViewY); + lyric.showLyric(isLook, themeColor, lyricViewX, lyricViewY, textX, textY); promise.resolve(null); } @@ -93,4 +93,10 @@ public class LyricModule extends ReactContextBaseJavaModule { lyric.setColor(themeColor); promise.resolve(null); } + + @ReactMethod + public void setLyricTextPosition(String positionX, String positionY, Promise promise) { + lyric.setLyricTextPosition(positionX, positionY); + promise.resolve(null); + } } diff --git a/android/app/src/main/java/com/lxmusicmobile/lyric/LyricView.java b/android/app/src/main/java/com/lxmusicmobile/lyric/LyricView.java index fc1159c..82d87bd 100644 --- a/android/app/src/main/java/com/lxmusicmobile/lyric/LyricView.java +++ b/android/app/src/main/java/com/lxmusicmobile/lyric/LyricView.java @@ -75,7 +75,7 @@ public class LyricView extends Activity implements View.OnTouchListener { // } // } - public void showLyricView(boolean isLock, String themeColor, int lyricViewX, int lyricViewY) { + public void showLyricView(boolean isLock, String themeColor, int lyricViewX, int lyricViewY, String textX, String textY) { if (windowManager == null) { windowManager = (WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE); //设置TextView的属性 @@ -97,7 +97,34 @@ public class LyricView extends Activity implements View.OnTouchListener { textView = new TextView(reactContext); textView.setText("LX Music ^-^"); textView.setTextSize(18); - // textView.setGravity(Gravity.CENTER); + // Log.d("Lyric", "textX: " + textX + " textY: " + textY); + int textPositionX; + int textPositionY; + switch (textX) { + case "CENTER": + textPositionX = Gravity.CENTER; + break; + case "RIGHT": + textPositionX = Gravity.RIGHT; + break; + case "left": + default: + textPositionX = Gravity.LEFT; + break; + } + switch (textY) { + case "CENTER": + textPositionY = Gravity.CENTER; + break; + case "BOTTOM": + textPositionY = Gravity.BOTTOM; + break; + case "TOP": + default: + textPositionY = Gravity.TOP; + break; + } + textView.setGravity(textPositionX | textPositionY); textView.setTextColor(Color.parseColor(themeColor)); textView.setShadowLayer(1, 0, 0, Color.BLACK); textView.setMaxLines(2); @@ -232,6 +259,39 @@ public class LyricView extends Activity implements View.OnTouchListener { windowManager.updateViewLayout(textView, layoutParams); } + public void setLyricTextPosition(String textX, String textY) { + if (windowManager == null || textView == null) return; + int textPositionX; + int textPositionY; + // Log.d("Lyric", "textX: " + textX + " textY: " + textY); + switch (textX) { + case "CENTER": + textPositionX = Gravity.CENTER; + break; + case "RIGHT": + textPositionX = Gravity.RIGHT; + break; + case "left": + default: + textPositionX = Gravity.LEFT; + break; + } + switch (textY) { + case "CENTER": + textPositionY = Gravity.CENTER; + break; + case "BOTTOM": + textPositionY = Gravity.BOTTOM; + break; + case "TOP": + default: + textPositionY = Gravity.TOP; + break; + } + textView.setGravity(textPositionX | textPositionY); + windowManager.updateViewLayout(textView, layoutParams); + } + public void destroy() { windowManager.removeView(textView); windowManager = null; diff --git a/index.js b/index.js index 9ee31de..5ae04a7 100644 --- a/index.js +++ b/index.js @@ -44,7 +44,16 @@ const init = () => { let setting = store.getState().common.setting toggleTranslation(setting.player.isShowTranslation) if (setting.sync.enable) connect() - if (setting.desktopLyric.enable) showLyric(setting.desktopLyric.isLock, setting.desktopLyric.theme, setting.desktopLyric.position.x, setting.desktopLyric.position.y) + if (setting.desktopLyric.enable) { + showLyric( + setting.desktopLyric.isLock, + setting.desktopLyric.theme, + setting.desktopLyric.position.x, + setting.desktopLyric.position.y, + setting.desktopLyric.textPosition.x, + setting.desktopLyric.textPosition.y, + ) + } onPositionChange(position => { store.dispatch(commonAction.setDesktopLyricPosition(position)) }) diff --git a/src/config/defaultSetting.js b/src/config/defaultSetting.js index daa3be2..d14119d 100644 --- a/src/config/defaultSetting.js +++ b/src/config/defaultSetting.js @@ -3,7 +3,7 @@ // const { isMac } = require('./utils') const defaultSetting = { - version: '1.10', + version: '1.11', player: { togglePlayMethod: 'listLoop', highQuality: false, @@ -24,6 +24,10 @@ const defaultSetting = { x: 0, y: 0, }, + textPosition: { + x: 'left', + y: 'top', + }, // style: { // fontSize: 120, // opacity: 95, diff --git a/src/lang/en_us.json b/src/lang/en_us.json index 99f4557..be113b1 100644 --- a/src/lang/en_us.json +++ b/src/lang/en_us.json @@ -98,10 +98,20 @@ "setting_basic_sourcename_title": "Select the name of music source", "setting_basic_theme": "Theme", "setting_list": "List settings", + "setting_list_add_music_location_type": "Position when the song was added to the list", "setting_list_add_music_location_type_bottom": "Bottom", "setting_list_add_music_location_type_top": "Top", "setting_lyric_desktop": "Desktop lyrics", + "setting_lyric_desktop_enable": "Show desktop lyrics", "setting_lyric_desktop_lock": "Lock lyrics", + "setting_lyric_desktop_text_x": "Lyrics horizontal alignment", + "setting_lyric_desktop_text_x_center": "Center", + "setting_lyric_desktop_text_x_left": "Left", + "setting_lyric_desktop_text_x_right": "Right", + "setting_lyric_desktop_text_y": "Lyrics vertical alignment", + "setting_lyric_desktop_text_y_bottom": "Bottom", + "setting_lyric_desktop_text_y_center": "Center", + "setting_lyric_desktop_text_y_top": "Top", "setting_other": "Other", "setting_other_cache": "Cache management (including the cache of songs, lyrics, error logs, etc., it is not recommended to clean up if there is no problem related to song playback)", "setting_other_cache_clear_btn": "Clear Cache", diff --git a/src/lang/zh_cn.json b/src/lang/zh_cn.json index 88b0830..79b44a8 100644 --- a/src/lang/zh_cn.json +++ b/src/lang/zh_cn.json @@ -104,6 +104,14 @@ "setting_lyric_desktop": "桌面歌词", "setting_lyric_desktop_enable": "显示桌面歌词", "setting_lyric_desktop_lock": "锁定歌词", + "setting_lyric_desktop_text_x": "歌词水平对齐方式", + "setting_lyric_desktop_text_x_left": "居左", + "setting_lyric_desktop_text_x_center": "居中", + "setting_lyric_desktop_text_x_right": "居右", + "setting_lyric_desktop_text_y": "歌词垂直对齐方式", + "setting_lyric_desktop_text_y_top": "居上", + "setting_lyric_desktop_text_y_center": "居中", + "setting_lyric_desktop_text_y_bottom": "居底", "setting_other": "其他", "setting_other_cache": "缓存管理(包括歌曲、歌词、错误日志等缓存,没有歌曲播放相关的问题不建议清理)", "setting_other_cache_clear_btn": "清理缓存", diff --git a/src/screens/Home/Setting/LyricDesktop/TextPositionX.js b/src/screens/Home/Setting/LyricDesktop/TextPositionX.js new file mode 100644 index 0000000..56af568 --- /dev/null +++ b/src/screens/Home/Setting/LyricDesktop/TextPositionX.js @@ -0,0 +1,48 @@ +import React, { memo, useCallback, useMemo } from 'react' + +import { StyleSheet, View } from 'react-native' +import { useGetter, useDispatch } from '@/store' + +import SubTitle from '../components/SubTitle' +import { useTranslation } from '@/plugins/i18n' +import CheckBox from '@/components/common/CheckBox' +import { textPositionX } from '@/utils/lyricDesktop' + +const useActive = id => { + const { x } = useGetter('common', 'desktopLyricTextPosition') + const isActive = useMemo(() => x == id, [x, id]) + return isActive +} + +const Item = ({ id, name, change }) => { + const isActive = useActive(id) + // const [toggleCheckBox, setToggleCheckBox] = useState(false) + return change({ x: id })} need /> +} + +export default memo(() => { + const { t } = useTranslation() + const setDesktopLyricTextPosition = useDispatch('common', 'setDesktopLyricTextPosition') + const list = useMemo(() => { + return textPositionX.map(({ id, value }) => ({ id, name: t('setting_lyric_desktop_text_x_' + id) })) + }, [t]) + + return ( + + + { + list.map(({ id, name }) => ) + } + + + ) +}) + +const styles = StyleSheet.create({ + list: { + flexGrow: 0, + flexShrink: 1, + flexDirection: 'row', + flexWrap: 'wrap', + }, +}) diff --git a/src/screens/Home/Setting/LyricDesktop/TextPositionY.js b/src/screens/Home/Setting/LyricDesktop/TextPositionY.js new file mode 100644 index 0000000..15996ad --- /dev/null +++ b/src/screens/Home/Setting/LyricDesktop/TextPositionY.js @@ -0,0 +1,48 @@ +import React, { memo, useCallback, useMemo } from 'react' + +import { StyleSheet, View } from 'react-native' +import { useGetter, useDispatch } from '@/store' + +import SubTitle from '../components/SubTitle' +import { useTranslation } from '@/plugins/i18n' +import CheckBox from '@/components/common/CheckBox' +import { textPositionY } from '@/utils/lyricDesktop' + +const useActive = id => { + const { y } = useGetter('common', 'desktopLyricTextPosition') + const isActive = useMemo(() => y == id, [y, id]) + return isActive +} + +const Item = ({ id, name, change }) => { + const isActive = useActive(id) + // const [toggleCheckBox, setToggleCheckBox] = useState(false) + return change({ y: id })} need /> +} + +export default memo(() => { + const { t } = useTranslation() + const setDesktopLyricTextPosition = useDispatch('common', 'setDesktopLyricTextPosition') + const list = useMemo(() => { + return textPositionY.map(({ id }) => ({ id, name: t('setting_lyric_desktop_text_y_' + id) })) + }, [t]) + + return ( + + + { + list.map(({ id, name }) => ) + } + + + ) +}) + +const styles = StyleSheet.create({ + list: { + flexGrow: 0, + flexShrink: 1, + flexDirection: 'row', + flexWrap: 'wrap', + }, +}) diff --git a/src/screens/Home/Setting/LyricDesktop/index.js b/src/screens/Home/Setting/LyricDesktop/index.js index 67b36ba..7c16abf 100644 --- a/src/screens/Home/Setting/LyricDesktop/index.js +++ b/src/screens/Home/Setting/LyricDesktop/index.js @@ -3,6 +3,8 @@ import React, { memo } from 'react' import Section from '../components/Section' import IsShowLyric from './IsShowLyric' import IsLockLyric from './IsLockLyric' +import TextPositionX from './TextPositionX' +import TextPositionY from './TextPositionY' import Theme from './Theme' import { useTranslation } from '@/plugins/i18n' @@ -13,6 +15,8 @@ export default memo(() => {
+ +
) diff --git a/src/store/modules/common/action.js b/src/store/modules/common/action.js index abd3038..62fbec7 100644 --- a/src/store/modules/common/action.js +++ b/src/store/modules/common/action.js @@ -41,6 +41,7 @@ export const TYPES = { setIsLockDesktopLyric: null, setThemeDesktopLyric: null, setDesktopLyricPosition: null, + setDesktopLyricTextPosition: null, } for (const key of Object.keys(TYPES)) { TYPES[key] = `common__${key}` @@ -332,6 +333,16 @@ export const setDesktopLyricPosition = position => async(dispatch, getState) => const { common } = getState() await setData(settingKey, common.setting) } +export const setDesktopLyricTextPosition = position => async(dispatch, getState) => { + const textPosition = { ...getState().common.setting.desktopLyric.textPosition, ...position } + dispatch(playerAction.setDesktopLyricTextPosition(textPosition)) + dispatch({ + type: TYPES.setDesktopLyricTextPosition, + payload: textPosition, + }) + const { common } = getState() + await setData(settingKey, common.setting) +} export const setIsEnableSync = flag => async(dispatch, getState) => { dispatch({ diff --git a/src/store/modules/common/getter.js b/src/store/modules/common/getter.js index c646a5d..26152cf 100644 --- a/src/store/modules/common/getter.js +++ b/src/store/modules/common/getter.js @@ -46,6 +46,7 @@ export const isEnableDesktopLyric = state => state.common.setting.desktopLyric.e export const isLockDesktopLyric = state => state.common.setting.desktopLyric.isLock export const themeDesktopLyric = state => state.common.setting.desktopLyric.theme export const desktopLyricPosition = state => state.common.setting.desktopLyric.position +export const desktopLyricTextPosition = state => state.common.setting.desktopLyric.textPosition export const timeoutExit = state => state.common.setting.player.timeoutExit export const timeoutExitPlayed = state => state.common.setting.player.timeoutExitPlayed diff --git a/src/store/modules/common/reducer.js b/src/store/modules/common/reducer.js index bc097ad..bfec9da 100644 --- a/src/store/modules/common/reducer.js +++ b/src/store/modules/common/reducer.js @@ -338,6 +338,18 @@ const mutations = { }, } }, + [TYPES.setDesktopLyricTextPosition](state, textPosition) { + return { + ...state, + setting: { + ...state.setting, + desktopLyric: { + ...state.setting.desktopLyric, + textPosition, + }, + }, + } + }, [TYPES.setVersionInfo](state, versionInfo) { return { ...state, diff --git a/src/store/modules/player/action.js b/src/store/modules/player/action.js index 4433dfc..dcbdd94 100644 --- a/src/store/modules/player/action.js +++ b/src/store/modules/player/action.js @@ -18,7 +18,7 @@ import { getRandom } from '@/utils' import { getMusicUrl, saveMusicUrl, getLyric, saveLyric, assertApiSupport, savePlayInfo, saveList } from '@/utils/tools' import { playInfo as playInfoGetter } from './getter' import { play as lrcPlay, setLyric, pause as lrcPause, toggleTranslation as lrcToggleTranslation } from '@/utils/lyric' -import { showLyric, hideLyric, setLyric as lrcdSetLyric, toggleLock, setTheme } from '@/utils/lyricDesktop' +import { showLyric, hideLyric, setLyric as lrcdSetLyric, toggleLock, setTheme, setLyricTextPosition } from '@/utils/lyricDesktop' import { action as listAction } from '@/store/modules/list' import { LIST_ID_PLAY_LATER } from '@/config/constant' // import { defaultList } from '../list/getter' @@ -787,7 +787,14 @@ export const toggleDesktopLyric = isShow => async(dispatch, getState) => { _playMusicInfo ? getLyric(_playMusicInfo).catch(() => ({ lyric: '', tlyric: '' })) : Promise.resolve({ lyric: '', tlyric: '' }), - showLyric(desktopLyric.isLock, desktopLyric.theme, desktopLyric.position.x, desktopLyric.position.y), + showLyric( + desktopLyric.isLock, + desktopLyric.theme, + desktopLyric.position.x, + desktopLyric.position.y, + desktopLyric.textPosition.x, + desktopLyric.textPosition.y, + ), ]) await lrcdSetLyric(lyric, tlyric) if (player.status == STATUS.playing && !player.isGettingUrl) { @@ -806,6 +813,9 @@ export const toggleDesktopLyricLock = isLock => async(dispatch, getState) => { export const setDesktopLyricTheme = theme => async(dispatch, getState) => { setTheme(theme) } +export const setDesktopLyricTextPosition = position => async(dispatch, getState) => { + setLyricTextPosition(position.x, position.y) +} export const checkPlayList = listIds => async(dispatch, getState) => { const { player, list: listState } = getState() diff --git a/src/utils/lyricDesktop.js b/src/utils/lyricDesktop.js index 52afd9a..3cef9ca 100644 --- a/src/utils/lyricDesktop.js +++ b/src/utils/lyricDesktop.js @@ -16,16 +16,37 @@ export const themes = [ { id: 'grey', value: '#bdc3c7' }, ] +export const textPositionX = [ + { id: 'left', value: 'LEFT' }, + { id: 'center', value: 'CENTER' }, + { id: 'right', value: 'RIGHT' }, +] +export const textPositionY = [ + { id: 'top', value: 'TOP' }, + { id: 'center', value: 'CENTER' }, + { id: 'bottom', value: 'BOTTOM' }, +] + const getThemeColor = themeId => (themes.find(t => t.id == themeId) || themes[0]).value +const getTextPositionX = x => (textPositionX.find(t => t.id == x) || textPositionX[0]).value +const getTextPositionY = y => (textPositionY.find(t => t.id == y) || textPositionY[0]).value + /** * show lyric * @param {Number} isLock is lock lyric window * @returns {Promise} Promise */ -export const showLyric = (isLock = false, themeId, lyricViewX, lyricViewY) => { +export const showLyric = (isLock = false, themeId, lyricViewX, lyricViewY, textX, textY) => { if (isShowLyric) return Promise.resolve() - return LyricModule.showLyric(isLock, getThemeColor(themeId), lyricViewX, lyricViewY).then(() => { + return LyricModule.showLyric( + isLock, + getThemeColor(themeId), + lyricViewX, + lyricViewY, + getTextPositionX(textX), + getTextPositionY(textY), + ).then(() => { isShowLyric = true }) } @@ -98,8 +119,12 @@ export const setTheme = themeId => { return LyricModule.setColor(getThemeColor(themeId)) } +export const setLyricTextPosition = (textX, textY) => { + if (!isShowLyric) return Promise.resolve() + return LyricModule.setLyricTextPosition(getTextPositionX(textX), getTextPositionY(textY)) +} + export const onPositionChange = callback => { - console.log('onPositionChange') const eventEmitter = new NativeEventEmitter(LyricModule) const eventListener = eventEmitter.addListener('set-position', event => { callback(event)