完善桌面歌词

This commit is contained in:
lyswhut 2021-08-06 22:13:39 +08:00
parent 6bd59b800f
commit 7f344631a8
16 changed files with 425 additions and 70 deletions

View File

@ -1,16 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="cn.toside.music.mobile"> package="cn.toside.music.mobile">
<!--获取读写外置存储权限--> <!-- 获取读写外置存储权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application <application

View File

@ -1,5 +1,9 @@
package com.lxmusicmobile.lyric; package com.lxmusicmobile.lyric;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log; import android.util.Log;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
@ -9,20 +13,92 @@ import java.util.List;
public class Lyric extends LyricPlayer { public class Lyric extends LyricPlayer {
LyricView lyricView = null; LyricView lyricView = null;
LyricEvent lyricEvent = null;
ReactApplicationContext reactAppContext;
public void showLyric(ReactApplicationContext reactContext, boolean isLock) { boolean isShowLyric = false;
if (lyricView == null) { boolean isLock = false;
lyricView = new LyricView(reactContext); String themeColor = "#07c556";
String lastText = "LX Music ^-^";
int lyricViewX = 0;
int lyricViewY = 0;
Lyric(ReactApplicationContext reactContext) {
this.reactAppContext = reactContext;
registerScreenBroadcastReceiver();
} }
lyricView.showLyricView(isLock);
private void registerScreenBroadcastReceiver() {
final IntentFilter theFilter = new IntentFilter();
/** System Defined Broadcast */
theFilter.addAction(Intent.ACTION_SCREEN_ON);
theFilter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver screenOnOffReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String strAction = intent.getAction();
switch (strAction) {
case Intent.ACTION_SCREEN_OFF:
Log.d("Lyric", "ACTION_SCREEN_OFF");
handleScreenOff();
break;
case Intent.ACTION_SCREEN_ON:
Log.d("Lyric", "ACTION_SCREEN_ON");
handleScreenOn();
break;
}
}
};
reactAppContext.registerReceiver(screenOnOffReceiver, theFilter);
}
private void handleScreenOff() {
if (!isShowLyric) return;
setTempPause(true);
if (lyricView != null) {
lyricView.runOnUiThread(new Runnable() {
@Override
public void run() {
lyricView.destroy();
}
});
}
}
private void handleScreenOn() {
if (!isShowLyric) return;
if (lyricView == null) lyricView = new LyricView(reactAppContext, lyricEvent);
lyricView.runOnUiThread(new Runnable() {
@Override
public void run() {
lyricView.showLyricView(isLock, themeColor, lyricViewX, lyricViewY);
lyricView.setLyric(lastText);
setTempPause(false);
}
});
}
public void showLyric(boolean isLock, String themeColor, int lyricViewX, int lyricViewY) {
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);
isShowLyric = true;
} }
public void hideLyric() { public void hideLyric() {
this.pause(); this.pause();
if (lyricView != null) { if (lyricView != null) {
lyricView.destroy(); lyricView.destroy();
lyricView = null;
} }
isShowLyric = false;
} }
@Override @Override
@ -34,29 +110,37 @@ public class Lyric extends LyricPlayer {
public void onSetLyric(List lines) { public void onSetLyric(List lines) {
for (int i = 0; i < lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
HashMap line = (HashMap) lines.get(i); HashMap line = (HashMap) lines.get(i);
Log.e("Lyric", (String) line.get("text") + " " + (String) line.get("translation")); // Log.d("Lyric", (String) line.get("text") + " " + (String) line.get("translation"));
} }
} }
@Override @Override
public void onPlay(int lineNum, String text) { public void onPlay(int lineNum, String text) {
HashMap line = (HashMap) lines.get(lineNum); // HashMap line = (HashMap) lines.get(lineNum);
lastText = text;
if (lyricView == null) return; if (lyricView == null) return;
lyricView.setLyric(text); lyricView.setLyric(text);
Log.e("Lyric", lineNum + " " + text + " " + (String) line.get("translation")); // Log.d("Lyric", lineNum + " " + text + " " + (String) line.get("translation"));
} }
public void lockLyric() { public void lockLyric() {
if (lyricView == null) return; if (lyricView == null) return;
this.isLock = true;
lyricView.lockView(); lyricView.lockView();
} }
public void unlockLyric() { public void unlockLyric() {
if (lyricView == null) return; if (lyricView == null) return;
this.isLock = false;
lyricView.unlockView(); lyricView.unlockView();
} }
public void toggleTranslation(boolean isShowTranslation) { public void toggleTranslation(boolean isShowTranslation) {
} }
public void setColor(String color) {
lyricView.setColor(color);
this.themeColor = color;
}
} }

View File

@ -0,0 +1,23 @@
package com.lxmusicmobile.lyric;
import android.util.Log;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
public class LyricEvent {
final String SET_VIEW_POSITION = "set-position";
private final ReactApplicationContext reactContext;
LyricEvent(ReactApplicationContext reactContext) { this.reactContext = reactContext; }
public void sendEvent(String eventName, @Nullable WritableMap params) {
Log.d("Lyric", "senEvent: " + eventName);
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
}

View File

@ -10,11 +10,20 @@ import com.facebook.react.bridge.ReactMethod;
public class LyricModule extends ReactContextBaseJavaModule { public class LyricModule extends ReactContextBaseJavaModule {
private final ReactApplicationContext reactContext; private final ReactApplicationContext reactContext;
Lyric lyric; Lyric lyric;
// final Map<String, Object> constants = new HashMap<>();
LyricModule(ReactApplicationContext reactContext) { LyricModule(ReactApplicationContext reactContext) {
super(reactContext); super(reactContext);
this.reactContext = reactContext; this.reactContext = reactContext;
lyric = new Lyric();
// constants.put("THEME_GREEN", "#07c556");
// constants.put("THEME_YELLOW", "#fffa12");
// constants.put("THEME_BLUE", "#19b5fe");
// constants.put("THEME_RED", "#ff1222");
// constants.put("THEME_PINK", "#f1828d");
// constants.put("THEME_PURPLE", "#c851d4");
// constants.put("THEME_ORANGE", "#fffa12");
// constants.put("THEME_GREY", "#bdc3c7");
} }
@Override @Override
@ -22,9 +31,15 @@ public class LyricModule extends ReactContextBaseJavaModule {
return "LyricModule"; return "LyricModule";
} }
// @Override
// public Map<String, Object> getConstants() {
// return constants;
// }
@ReactMethod @ReactMethod
public void showLyric(boolean isLook, Promise promise) { public void showLyric(boolean isLook, String themeColor, int lyricViewX, int lyricViewY, Promise promise) {
lyric.showLyric(reactContext, isLook); if (lyric == null) lyric = new Lyric(reactContext);
lyric.showLyric(isLook, themeColor, lyricViewX, lyricViewY);
promise.resolve(null); promise.resolve(null);
} }
@ -37,8 +52,8 @@ public class LyricModule extends ReactContextBaseJavaModule {
@ReactMethod @ReactMethod
public void setLyric(String lyric, String translation, Promise promise) { public void setLyric(String lyric, String translation, Promise promise) {
Log.e("Lyric", "set lyric: " + lyric); // Log.d("Lyric", "set lyric: " + lyric);
Log.e("Lyric", "set lyric translation: " + translation); // Log.d("Lyric", "set lyric translation: " + translation);
this.lyric.setLyric(lyric, translation); this.lyric.setLyric(lyric, translation);
promise.resolve(null); promise.resolve(null);
} }
@ -51,14 +66,14 @@ public class LyricModule extends ReactContextBaseJavaModule {
@ReactMethod @ReactMethod
public void play(int time, Promise promise) { public void play(int time, Promise promise) {
Log.e("Lyric", "play lyric: " + time); Log.d("Lyric", "play lyric: " + time);
lyric.play(time); lyric.play(time);
promise.resolve(null); promise.resolve(null);
} }
@ReactMethod @ReactMethod
public void pause(Promise promise) { public void pause(Promise promise) {
Log.e("Lyric", "play pause"); Log.d("Lyric", "play pause");
lyric.pause(); lyric.pause();
promise.resolve(null); promise.resolve(null);
} }
@ -70,5 +85,12 @@ public class LyricModule extends ReactContextBaseJavaModule {
} else { } else {
lyric.unlockLyric(); lyric.unlockLyric();
} }
promise.resolve(null);
}
@ReactMethod
public void setColor(String themeColor, Promise promise) {
lyric.setColor(themeColor);
promise.resolve(null);
} }
} }

View File

@ -1,7 +1,5 @@
package com.lxmusicmobile.lyric; package com.lxmusicmobile.lyric;
import android.util.Log;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -26,10 +24,12 @@ public class LyricPlayer {
int curLineNum = 0; int curLineNum = 0;
int maxLine = 0; int maxLine = 0;
int offset = 150; int offset = 150;
boolean isOffseted = false; boolean isOffered = false;
long performanceTime = 0; long performanceTime = 0;
int delay = 0; int delay = 0;
Object tid = null; Object tid = null;
boolean tempPause = false;
boolean tempPaused = false;
LyricPlayer() { LyricPlayer() {
// tagRegMap = new HashMap<String, String>(); // tagRegMap = new HashMap<String, String>();
@ -43,6 +43,17 @@ public class LyricPlayer {
timePattern = Pattern.compile(timeExp); timePattern = Pattern.compile(timeExp);
} }
public void setTempPause(boolean isPaused) {
if (isPaused) {
tempPause = true;
} else {
tempPause = false;
if (tempPaused) {
tempPaused = false;
if (isPlay) refresh();
}
}
}
// @RequiresApi(api = Build.VERSION_CODES.N) // @RequiresApi(api = Build.VERSION_CODES.N)
// private void initTag() { // private void initTag() {
@ -167,7 +178,8 @@ public class LyricPlayer {
public void pause() { public void pause() {
if (!isPlay) return; if (!isPlay) return;
isPlay = false; isPlay = false;
isOffseted = false; isOffered = false;
tempPaused = false;
stopTimeout(); stopTimeout();
if (curLineNum == maxLine) return; if (curLineNum == maxLine) return;
int curLineNum = this.findCurLineNum(getCurrentTime()); int curLineNum = this.findCurLineNum(getCurrentTime());
@ -192,7 +204,7 @@ public class LyricPlayer {
private int findCurLineNum(int curTime) { private int findCurLineNum(int curTime) {
int length = lines.size(); int length = lines.size();
for (int index = 0; index < length; index++) { for (int index = 0; index < length; index++) {
if (curTime <= (Integer) ((HashMap)lines.get(index)).get("time")) return index == 0 ? 0 : index - 1; if (curTime <= (int) ((HashMap)lines.get(index)).get("time")) return index == 0 ? 0 : index - 1;
} }
return length - 1; return length - 1;
} }
@ -203,6 +215,9 @@ public class LyricPlayer {
} }
private void refresh() { private void refresh() {
if (tempPaused) tempPaused = false;
// Log.d("Lyric", "refresh: " + curLineNum);
curLineNum++; curLineNum++;
if (curLineNum == maxLine) { if (curLineNum == maxLine) {
handleMaxLine(); handleMaxLine();
@ -211,20 +226,26 @@ public class LyricPlayer {
HashMap curLine = lines.get(curLineNum); HashMap curLine = lines.get(curLineNum);
HashMap nextLine = lines.get(curLineNum + 1); HashMap nextLine = lines.get(curLineNum + 1);
int currentTime = getCurrentTime(); int currentTime = getCurrentTime();
int driftTime = currentTime - (Integer) curLine.get("time"); int driftTime = currentTime - (int) curLine.get("time");
Log.e("Lyric", "driftTime: " + driftTime); // Log.d("Lyric", "driftTime: " + driftTime);
if (driftTime >= 0 || curLineNum == 0) { if (driftTime >= 0 || curLineNum == 0) {
delay = (Integer) nextLine.get("time") - (Integer) curLine.get("time") - driftTime; delay = (int) nextLine.get("time") - (int) curLine.get("time") - driftTime;
Log.e("Lyric", "delay: " + delay + " driftTime: " + driftTime); // Log.d("Lyric", "delay: " + delay + " driftTime: " + driftTime);
if (delay > 0) { if (delay > 0) {
if (!isOffseted && delay >= offset) { if (!isOffered && delay >= offset) {
delay -= offset; delay -= offset;
isOffseted = true; isOffered = true;
} }
if (isPlay) {
startTimeout(() -> { startTimeout(() -> {
if (tempPause) {
tempPaused = true;
return;
}
refresh(); refresh();
}, delay); }, delay);
}
onPlay(curLineNum, (String) curLine.get("text")); onPlay(curLineNum, (String) curLine.get("text"));
return; return;
} }

View File

@ -5,20 +5,29 @@ import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.os.Build; import android.os.Build;
import android.text.TextUtils;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity; import android.view.Gravity;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.TextView; import android.widget.TextView;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import cn.toside.music.mobile.R;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
public class LyricView extends Activity implements View.OnTouchListener { public class LyricView extends Activity implements View.OnTouchListener {
TextView textView = null; TextView textView = null;
WindowManager windowManager = null; WindowManager windowManager = null;
WindowManager.LayoutParams layoutParams = null; WindowManager.LayoutParams layoutParams = null;
ReactApplicationContext reactContext; final private ReactApplicationContext reactContext;
final private LyricEvent lyricEvent;
private int winWidth = 0; private int winWidth = 0;
@ -28,14 +37,22 @@ public class LyricView extends Activity implements View.OnTouchListener {
private float nowY; private float nowY;
private float tranX; //悬浮窗移动位置的相对值 private float tranX; //悬浮窗移动位置的相对值
private float tranY; private float tranY;
private int prevViewX = 0;
private int prevViewY = 0;
private float preY = 0; private float preY = 0;
// private static boolean isVibrated = false; // private static boolean isVibrated = false;
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469; LyricView(ReactApplicationContext reactContext, LyricEvent lyricEvent) {
LyricView(ReactApplicationContext reactContext) {
this.reactContext = reactContext; this.reactContext = reactContext;
this.lyricEvent = lyricEvent;
}
private void sendPositionEvent(int x, int y) {
WritableMap params = Arguments.createMap();
params.putInt("x", x);
params.putInt("y", y);
lyricEvent.sendEvent(lyricEvent.SET_VIEW_POSITION, params);
} }
// public void permission(){ // public void permission(){
@ -58,7 +75,7 @@ public class LyricView extends Activity implements View.OnTouchListener {
// } // }
// } // }
public void showLyricView(boolean isLock) { public void showLyricView(boolean isLock, String themeColor, int lyricViewX, int lyricViewY) {
if (windowManager == null) { if (windowManager == null) {
windowManager = (WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE); windowManager = (WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE);
//设置TextView的属性 //设置TextView的属性
@ -66,7 +83,7 @@ public class LyricView extends Activity implements View.OnTouchListener {
DisplayMetrics outMetrics = new DisplayMetrics(); DisplayMetrics outMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(outMetrics); windowManager.getDefaultDisplay().getMetrics(outMetrics);
winWidth = (int)(outMetrics.widthPixels * 0.6); winWidth = (int)(outMetrics.widthPixels * 0.92);
} }
// 注意悬浮窗只有一个而当打开应用的时候才会产生悬浮窗所以要判断悬浮窗是否已经存在 // 注意悬浮窗只有一个而当打开应用的时候才会产生悬浮窗所以要判断悬浮窗是否已经存在
@ -77,14 +94,15 @@ public class LyricView extends Activity implements View.OnTouchListener {
// 使用Application context // 使用Application context
// 创建UI控件避免Activity销毁导致上下文出现问题,因为现在的悬浮窗是系统级别的不依赖与Activity存在 // 创建UI控件避免Activity销毁导致上下文出现问题,因为现在的悬浮窗是系统级别的不依赖与Activity存在
//创建自定义的TextView //创建自定义的TextView
View view = new View(reactContext);
textView = new TextView(reactContext); textView = new TextView(reactContext);
textView.setText("LX Music ^-^"); textView.setText("LX Music ^-^");
textView.setTextColor(Color.rgb(205, 220, 57)); textView.setTextSize(18);
// textView.setGravity(Gravity.CENTER);
textView.setTextColor(Color.parseColor(themeColor));
textView.setShadowLayer(1, 0, 0, Color.BLACK); textView.setShadowLayer(1, 0, 0, Color.BLACK);
textView.setMaxLines(2);
textView.setEllipsize(TextUtils.TruncateAt.END);
// textView.setBackgroundColor(0x66000000);
//监听 OnTouch 事件 为了实现"移动歌词"功能 //监听 OnTouch 事件 为了实现"移动歌词"功能
textView.setOnTouchListener(this); textView.setOnTouchListener(this);
@ -94,9 +112,16 @@ public class LyricView extends Activity implements View.OnTouchListener {
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
layoutParams.flags = isLock // layoutParams.flags = isLock
? WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE // ? WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
: WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; // : WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
if (isLock) {
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
textView.setBackgroundColor(Color.TRANSPARENT);
} else {
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
textView.setBackgroundResource(R.drawable.rounded_corner);
}
// TYPE_SYSTEM_ALERT 系统提示,它总是出现在应用程序窗口之上 // TYPE_SYSTEM_ALERT 系统提示,它总是出现在应用程序窗口之上
// TYPE_SYSTEM_OVERLAY 系统顶层窗口显示在其他一切内容之上此窗口不能获得输入焦点否则影响锁屏 // TYPE_SYSTEM_OVERLAY 系统顶层窗口显示在其他一切内容之上此窗口不能获得输入焦点否则影响锁屏
@ -105,15 +130,16 @@ public class LyricView extends Activity implements View.OnTouchListener {
layoutParams.gravity = Gravity.TOP | Gravity.CENTER_VERTICAL; //显示在屏幕上中部 layoutParams.gravity = Gravity.TOP | Gravity.CENTER_VERTICAL; //显示在屏幕上中部
//显示位置与指定位置的相对位置差 //显示位置与指定位置的相对位置差
layoutParams.x = 0; layoutParams.x = this.prevViewX = lyricViewX;
layoutParams.y = 0; layoutParams.y = this.prevViewY = lyricViewY;
//悬浮窗的宽高 //悬浮窗的宽高
// layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; // layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
// layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; // layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
// layoutParams.width= DisplayUtil.dp2px(mContext,55); // layoutParams.width= DisplayUtil.dp2px(mContext,55);
// layoutParams.height= DisplayUtil.dp2px(mContext,55); // layoutParams.height= DisplayUtil.dp2px(mContext,55);
layoutParams.width = winWidth; layoutParams.width = MATCH_PARENT;
layoutParams.height = 80; // layoutParams.height = 100;
layoutParams.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, reactContext.getResources().getDisplayMetrics());
//设置透明 //设置透明
layoutParams.format = PixelFormat.TRANSPARENT; layoutParams.format = PixelFormat.TRANSPARENT;
@ -123,6 +149,7 @@ public class LyricView extends Activity implements View.OnTouchListener {
} }
public void setLyric(String text) { public void setLyric(String text) {
if (textView == null) return;
textView.setText(text); textView.setText(text);
} }
@ -158,7 +185,7 @@ public class LyricView extends Activity implements View.OnTouchListener {
break; break;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
// float dy = nowY - preY; // float dy = nowY - preY;
// Log.e("Lyric","dy: " + dy); // Log.d("Lyric","dy: " + dy);
// if (isVibrated){ // if (isVibrated){
// if (dy > 10){ // if (dy > 10){
// //down // //down
@ -175,6 +202,11 @@ public class LyricView extends Activity implements View.OnTouchListener {
//根据移动的位置来判断 //根据移动的位置来判断
// dy = 0; // dy = 0;
tranY = 0; tranY = 0;
if (layoutParams.x != prevViewX || layoutParams.y != prevViewX) {
prevViewX = layoutParams.x;
prevViewY = layoutParams.y;
sendPositionEvent(prevViewX, prevViewY);
}
break; break;
} }
return ret; return ret;
@ -183,12 +215,20 @@ public class LyricView extends Activity implements View.OnTouchListener {
public void lockView() { public void lockView() {
if (windowManager == null || textView == null) return; if (windowManager == null || textView == null) return;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
textView.setBackgroundColor(Color.TRANSPARENT);
windowManager.updateViewLayout(textView, layoutParams); windowManager.updateViewLayout(textView, layoutParams);
} }
public void unlockView() { public void unlockView() {
if (windowManager == null || textView == null) return; if (windowManager == null || textView == null) return;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
textView.setBackgroundResource(R.drawable.rounded_corner);
windowManager.updateViewLayout(textView, layoutParams);
}
public void setColor(String color) {
if (windowManager == null || textView == null) return;
textView.setTextColor(Color.parseColor(color));
windowManager.updateViewLayout(textView, layoutParams); windowManager.updateViewLayout(textView, layoutParams);
} }

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#29000000" />
<padding
android:left="1dp"
android:right="1dp"
android:bottom="1dp"
android:top="1dp" />
<corners android:radius="4dp" />
</shape>

View File

@ -17,7 +17,7 @@ import { action as playerAction } from '@/store/modules/player'
import { action as listAction } from '@/store/modules/list' import { action as listAction } from '@/store/modules/list'
import { init as initMusicTools } from '@/utils/music' import { init as initMusicTools } from '@/utils/music'
import { init as initLyric, toggleTranslation } from '@/utils/lyric' import { init as initLyric, toggleTranslation } from '@/utils/lyric'
import { showLyric } from '@/utils/lyricDesktop' import { showLyric, onPositionChange } from '@/utils/lyricDesktop'
import { init as initI18n, supportedLngs } from '@/plugins/i18n' import { init as initI18n, supportedLngs } from '@/plugins/i18n'
import { deviceLanguage, getPlayInfo, toast } from '@/utils/tools' import { deviceLanguage, getPlayInfo, toast } from '@/utils/tools'
import { LIST_ID_PLAY_TEMP } from '@/config/constant' import { LIST_ID_PLAY_TEMP } from '@/config/constant'
@ -44,7 +44,10 @@ const init = () => {
let setting = store.getState().common.setting let setting = store.getState().common.setting
toggleTranslation(setting.player.isShowTranslation) toggleTranslation(setting.player.isShowTranslation)
if (setting.sync.enable) connect() if (setting.sync.enable) connect()
if (setting.desktopLyric.enable) showLyric(setting.desktopLyric.isLock) if (setting.desktopLyric.enable) showLyric(setting.desktopLyric.isLock, setting.desktopLyric.theme, setting.desktopLyric.position.x, setting.desktopLyric.position.y)
onPositionChange(position => {
store.dispatch(commonAction.setDesktopLyricPosition(position))
})
let lang = setting.langId let lang = setting.langId
let needSetLang = false let needSetLang = false

View File

@ -3,7 +3,7 @@
// const { isMac } = require('./utils') // const { isMac } = require('./utils')
const defaultSetting = { const defaultSetting = {
version: '1.8', version: '1.10',
player: { player: {
togglePlayMethod: 'listLoop', togglePlayMethod: 'listLoop',
highQuality: false, highQuality: false,
@ -17,11 +17,13 @@ const defaultSetting = {
desktopLyric: { desktopLyric: {
enable: false, enable: false,
isLock: false, isLock: false,
theme: 'green',
// width: 380, // width: 380,
// height: 420, // height: 420,
// x: null, position: {
// y: null, x: 0,
// theme: 0, y: 0,
},
// style: { // style: {
// fontSize: 120, // fontSize: 120,
// opacity: 95, // opacity: 95,

View File

@ -0,0 +1,68 @@
import React, { memo, useMemo } from 'react'
import { StyleSheet, View, TouchableOpacity } from 'react-native'
import { useGetter, useDispatch } from '@/store'
import SubTitle from '../components/SubTitle'
import { useTranslation } from '@/plugins/i18n'
import { themes } from '@/utils/lyricDesktop'
const useActive = id => {
const themeDesktopLyric = useGetter('common', 'themeDesktopLyric')
const isActive = useMemo(() => themeDesktopLyric == id, [themeDesktopLyric, id])
return isActive
}
const ThemeItem = ({ id, color, setTheme }) => {
const theme = useGetter('common', 'theme')
const isActive = useActive(id)
return (
<TouchableOpacity style={styles.item} activeOpacity={0.5} onPress={() => setTheme(id)}>
<View style={{ ...styles.colorContent, backgroundColor: theme.primary, borderColor: isActive ? color : 'transparent' }}>
<View style={{ ...styles.image, backgroundColor: color }}></View>
</View>
</TouchableOpacity>
)
}
export default memo(() => {
const { t } = useTranslation()
const setThemeDesktopLyric = useDispatch('common', 'setThemeDesktopLyric')
return (
<SubTitle title={t('setting_basic_theme')}>
<View style={styles.list}>
{
themes.map(({ id, value }) => <ThemeItem key={id} color={value} id={id} setTheme={setThemeDesktopLyric} />)
}
</View>
</SubTitle>
)
})
const styles = StyleSheet.create({
list: {
flexDirection: 'row',
flexWrap: 'wrap',
},
item: {
marginRight: 15,
marginTop: 5,
alignItems: 'center',
width: 26,
// backgroundColor: 'rgba(0,0,0,0.2)',
},
colorContent: {
width: 26,
height: 26,
borderRadius: 4,
borderWidth: 1.6,
alignItems: 'center',
justifyContent: 'center',
},
image: {
width: 20,
height: 20,
borderRadius: 4,
},
})

View File

@ -3,6 +3,7 @@ import React, { memo } from 'react'
import Section from '../components/Section' import Section from '../components/Section'
import IsShowLyric from './IsShowLyric' import IsShowLyric from './IsShowLyric'
import IsLockLyric from './IsLockLyric' import IsLockLyric from './IsLockLyric'
import Theme from './Theme'
import { useTranslation } from '@/plugins/i18n' import { useTranslation } from '@/plugins/i18n'
export default memo(() => { export default memo(() => {
@ -12,6 +13,7 @@ export default memo(() => {
<Section title={t('setting_lyric_desktop')}> <Section title={t('setting_lyric_desktop')}>
<IsShowLyric /> <IsShowLyric />
<IsLockLyric /> <IsLockLyric />
<Theme />
</Section> </Section>
) )
}) })

View File

@ -39,6 +39,8 @@ export const TYPES = {
setSyncStatus: null, setSyncStatus: null,
setIsShowDesktopLyric: null, setIsShowDesktopLyric: null,
setIsLockDesktopLyric: null, setIsLockDesktopLyric: null,
setThemeDesktopLyric: null,
setDesktopLyricPosition: null,
} }
for (const key of Object.keys(TYPES)) { for (const key of Object.keys(TYPES)) {
TYPES[key] = `common__${key}` TYPES[key] = `common__${key}`
@ -313,6 +315,23 @@ export const setIsLockDesktopLyric = flag => async(dispatch, getState) => {
const { common } = getState() const { common } = getState()
await setData(settingKey, common.setting) await setData(settingKey, common.setting)
} }
export const setThemeDesktopLyric = theme => async(dispatch, getState) => {
dispatch(playerAction.setDesktopLyricTheme(theme))
dispatch({
type: TYPES.setThemeDesktopLyric,
payload: theme,
})
const { common } = getState()
await setData(settingKey, common.setting)
}
export const setDesktopLyricPosition = position => async(dispatch, getState) => {
dispatch({
type: TYPES.setDesktopLyricPosition,
payload: position,
})
const { common } = getState()
await setData(settingKey, common.setting)
}
export const setIsEnableSync = flag => async(dispatch, getState) => { export const setIsEnableSync = flag => async(dispatch, getState) => {
dispatch({ dispatch({

View File

@ -44,6 +44,8 @@ export const sourceNameType = state => state.common.setting.sourceNameType
export const isEnableDesktopLyric = state => state.common.setting.desktopLyric.enable export const isEnableDesktopLyric = state => state.common.setting.desktopLyric.enable
export const isLockDesktopLyric = state => state.common.setting.desktopLyric.isLock 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 timeoutExit = state => state.common.setting.player.timeoutExit export const timeoutExit = state => state.common.setting.player.timeoutExit
export const timeoutExitPlayed = state => state.common.setting.player.timeoutExitPlayed export const timeoutExitPlayed = state => state.common.setting.player.timeoutExitPlayed

View File

@ -314,6 +314,30 @@ const mutations = {
}, },
} }
}, },
[TYPES.setThemeDesktopLyric](state, theme) {
return {
...state,
setting: {
...state.setting,
desktopLyric: {
...state.setting.desktopLyric,
theme,
},
},
}
},
[TYPES.setDesktopLyricPosition](state, position) {
return {
...state,
setting: {
...state.setting,
desktopLyric: {
...state.setting.desktopLyric,
position,
},
},
}
},
[TYPES.setVersionInfo](state, versionInfo) { [TYPES.setVersionInfo](state, versionInfo) {
return { return {
...state, ...state,

View File

@ -18,7 +18,7 @@ import { getRandom } from '@/utils'
import { getMusicUrl, saveMusicUrl, getLyric, saveLyric, assertApiSupport, savePlayInfo, saveList } from '@/utils/tools' import { getMusicUrl, saveMusicUrl, getLyric, saveLyric, assertApiSupport, savePlayInfo, saveList } from '@/utils/tools'
import { playInfo as playInfoGetter } from './getter' import { playInfo as playInfoGetter } from './getter'
import { play as lrcPlay, setLyric, pause as lrcPause, toggleTranslation as lrcToggleTranslation } from '@/utils/lyric' import { play as lrcPlay, setLyric, pause as lrcPause, toggleTranslation as lrcToggleTranslation } from '@/utils/lyric'
import { showLyric, hideLyric, setLyric as lrcdSetLyric, toggleLock } from '@/utils/lyricDesktop' import { showLyric, hideLyric, setLyric as lrcdSetLyric, toggleLock, setTheme } from '@/utils/lyricDesktop'
import { action as listAction } from '@/store/modules/list' import { action as listAction } from '@/store/modules/list'
import { LIST_ID_PLAY_LATER } from '@/config/constant' import { LIST_ID_PLAY_LATER } from '@/config/constant'
// import { defaultList } from '../list/getter' // import { defaultList } from '../list/getter'
@ -782,11 +782,12 @@ export const toggleTranslation = isShow => async(dispatch, getState) => {
export const toggleDesktopLyric = isShow => async(dispatch, getState) => { export const toggleDesktopLyric = isShow => async(dispatch, getState) => {
if (isShow) { if (isShow) {
const { common, player } = getState() const { common, player } = getState()
const desktopLyric = common.setting.desktopLyric
const [{ lyric, tlyric }] = await Promise.all([ const [{ lyric, tlyric }] = await Promise.all([
_playMusicInfo _playMusicInfo
? getLyric(_playMusicInfo).catch(() => ({ lyric: '', tlyric: '' })) ? getLyric(_playMusicInfo).catch(() => ({ lyric: '', tlyric: '' }))
: Promise.resolve({ lyric: '', tlyric: '' }), : Promise.resolve({ lyric: '', tlyric: '' }),
showLyric(common.setting.desktopLyric.isLock), showLyric(desktopLyric.isLock, desktopLyric.theme, desktopLyric.position.x, desktopLyric.position.y),
]) ])
await lrcdSetLyric(lyric, tlyric) await lrcdSetLyric(lyric, tlyric)
if (player.status == STATUS.playing && !player.isGettingUrl) { if (player.status == STATUS.playing && !player.isGettingUrl) {
@ -802,6 +803,9 @@ export const toggleDesktopLyric = isShow => async(dispatch, getState) => {
export const toggleDesktopLyricLock = isLock => async(dispatch, getState) => { export const toggleDesktopLyricLock = isLock => async(dispatch, getState) => {
toggleLock(isLock) toggleLock(isLock)
} }
export const setDesktopLyricTheme = theme => async(dispatch, getState) => {
setTheme(theme)
}
export const checkPlayList = listIds => async(dispatch, getState) => { export const checkPlayList = listIds => async(dispatch, getState) => {
const { player, list: listState } = getState() const { player, list: listState } = getState()

View File

@ -1,17 +1,31 @@
import { NativeModules } from 'react-native' import { NativeModules, NativeEventEmitter } from 'react-native'
const { LyricModule } = NativeModules const { LyricModule } = NativeModules
let isShowLyric = false let isShowLyric = false
export const themes = [
{ id: 'green', value: '#07c556' },
{ id: 'yellow', value: '#fffa12' },
{ id: 'blue', value: '#19b5fe' },
{ id: 'red', value: '#ff1222' },
{ id: 'pink', value: '#f1828d' },
{ id: 'purple', value: '#c851d4' },
{ id: 'orange', value: '#ffad12' },
{ id: 'grey', value: '#bdc3c7' },
]
const getThemeColor = themeId => (themes.find(t => t.id == themeId) || themes[0]).value
/** /**
* show lyric * show lyric
* @param {Number} isLock is lock lyric window * @param {Number} isLock is lock lyric window
* @returns {Promise} Promise * @returns {Promise} Promise
*/ */
export const showLyric = (isLock = false) => { export const showLyric = (isLock = false, themeId, lyricViewX, lyricViewY) => {
if (isShowLyric) return Promise.resolve() if (isShowLyric) return Promise.resolve()
return LyricModule.showLyric(isLock).then(() => { return LyricModule.showLyric(isLock, getThemeColor(themeId), lyricViewX, lyricViewY).then(() => {
isShowLyric = true isShowLyric = true
}) })
} }
@ -78,3 +92,20 @@ export const toggleLock = isLock => {
return LyricModule.toggleLock(isLock) return LyricModule.toggleLock(isLock)
} }
export const setTheme = themeId => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setColor(getThemeColor(themeId))
}
export const onPositionChange = callback => {
console.log('onPositionChange')
const eventEmitter = new NativeEventEmitter(LyricModule)
const eventListener = eventEmitter.addListener('set-position', event => {
callback(event)
})
return () => {
eventListener.remove()
}
}