app/src/main/AndroidManifest.xml | ●●●●● patch | view | raw | blame | history | |
app/src/main/java/com/auto/lyric/MainActivity.java | ●●●●● patch | view | raw | blame | history | |
app/src/main/java/com/auto/lyric/service/FloatingWindowService.java | ●●●●● patch | view | raw | blame | history | |
app/src/main/java/com/auto/lyric/util/DeviceUtil.java | ●●●●● patch | view | raw | blame | history | |
app/src/main/java/com/auto/lyric/widgets/LyricView.java | ●●●●● patch | view | raw | blame | history | |
app/src/main/java/com/auto/lyric/widgets/Sentence.java | ●●●●● patch | view | raw | blame | history | |
app/src/main/res/layout/float_view.xml | ●●●●● patch | view | raw | blame | history | |
app/src/main/res/values/colors.xml | ●●●●● patch | view | raw | blame | history | |
app/src/main/res/values/strings.xml | ●●●●● patch | view | raw | blame | history | |
app/src/main/res/xml/simulateinput.xml | ●●●●● patch | view | raw | blame | history |
app/src/main/AndroidManifest.xml
@@ -25,6 +25,7 @@ tools:ignore="ProtectedPermissions" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" /> <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" /> <application android:allowBackup="true" @@ -42,6 +43,30 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".service.AutoInputService" android:enabled="true" android:exported="true" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" android:resource="@xml/simulateinput" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/simulateinput"/> </service> <service android:name=".service.FloatingWindowService" android:enabled="true" android:exported="true" android:permission="android.permission.SYSTEM_OVERLAY_WINDOW|android.permission.SYSTEM_ALERT_WINDOW"> <intent-filter> <action android:name="float.service" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/simulateinput"/> </service> </application> </manifest> app/src/main/java/com/auto/lyric/MainActivity.java
@@ -1,34 +1,42 @@ package com.auto.lyric; import android.content.Intent; import android.graphics.PixelFormat; import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.Message; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import androidx.annotation.NonNull; import com.auto.lyric.base.activities.BaseActivity; import com.auto.lyric.databinding.ActivityMainBinding; import com.auto.lyric.databinding.FloatViewBinding; import com.auto.lyric.service.AutoInputService; import com.auto.lyric.service.FloatingWindowService; import com.auto.lyric.vm.MainViewModel; public class MainActivity extends BaseActivity<ActivityMainBinding, MainViewModel> { Intent service = new Intent(getApplicationContext(), AutoInputService.class); @Override public void initViews() { } Intent inputService = new Intent(getApplicationContext(), AutoInputService.class); @Override protected void onResume() { super.onResume(); Intent floatService = new Intent(getApplicationContext(), FloatingWindowService.class); binding.button.setOnClickListener(v -> { //申请权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //开启悬浮窗 if(!Settings.canDrawOverlays(getApplicationContext())) { /* if(!Settings.canDrawOverlays(getApplicationContext())) { //启动Activity让用户授权 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); intent.setData(Uri.parse("package:" + getPackageName())); @@ -39,9 +47,12 @@ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK ); startActivity(intent); }else{//开启监听服务 startService(service); //startService(floatService); }*/ startService(floatService); //showFloatView(); } } }); } public boolean isAccessibilitySettingsOn() { @@ -82,5 +93,27 @@ return false; } private void showFloatView(){ //View view = LayoutInflater.from(this).inflate(R.layout.float_view,null); FloatViewBinding binding = FloatViewBinding.inflate(getLayoutInflater()); WindowManager manager = (WindowManager) getApplicationContext().getSystemService(WINDOW_SERVICE); WindowManager.LayoutParams params = new WindowManager.LayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; }else { params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; } params.format = PixelFormat.RGBA_8888; params.gravity = Gravity.CENTER; params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; params.width = WindowManager.LayoutParams.MATCH_PARENT; params.height = WindowManager.LayoutParams.WRAP_CONTENT; binding.btnBack.setOnClickListener(v ->{ stopService(new Intent(this,this.getClass())); }); manager.addView(binding.getRoot(),params); } } app/src/main/java/com/auto/lyric/service/FloatingWindowService.java
New file @@ -0,0 +1,108 @@ package com.auto.lyric.service; import android.app.Service; import android.content.Intent; import android.graphics.PixelFormat; import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.LayoutInflaterFactory; import com.auto.lyric.R; import com.auto.lyric.databinding.FloatViewBinding; import com.auto.lyric.util.DeviceUtil; /** * 全局悬浮窗 * Created by Runt (qingingrunt2010@qq.com) on 2022/4/30. */ public class FloatingWindowService extends Service { private WindowManager manager; private WindowManager.LayoutParams params; FloatViewBinding binding; Handler handler; final String TAG = "FloatingWindowService"; @Override public void onCreate() { super.onCreate(); Log.e(TAG,"onCreate "); View view = LayoutInflater.from(getBaseContext()).inflate(R.layout.float_view,null); binding = FloatViewBinding.bind(view); manager = (WindowManager) getSystemService(WINDOW_SERVICE); params = new WindowManager.LayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; }else { params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; } params.format = PixelFormat.RGBA_8888; params.gravity = Gravity.LEFT | Gravity.TOP; params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; params.width = WindowManager.LayoutParams.MATCH_PARENT; params.height = DeviceUtil.convertDpToPixel(200,getBaseContext()); handler = new Handler(this.getMainLooper()){ @Override public void handleMessage(@NonNull Message msg) { binding.lyric.setText(msg.obj.toString()); int offset = binding.lyric.getLineCount() * binding.lyric.getLineHeight(); } }; binding.close.setOnClickListener(v ->{ stopService(new Intent(this,this.getClass())); stopService(new Intent(this,AutoInputService.class)); }); binding.floating.setOnTouchListener(new View.OnTouchListener() { int startY; @Override public boolean onTouch(View v, MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN){ startY = (int) event.getY(); }else if(event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_UP){ params.y = (int) event.getRawY() - DeviceUtil.getStatusBarHeight(getBaseContext()) - startY; manager.updateViewLayout(binding.getRoot(), params); Log.e(TAG,"ACTION_MOVE V:"+params.y); } Log.e(TAG,"onTouch event:"+event); Log.e(TAG,"onTouch getRawY:"+event.getRawY()); return true; } }); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e(TAG,"onStartCommand flags:"+flags+" startId:"+startId+ " intent:"+intent); manager.addView(binding.getRoot(),params); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.e(TAG,"onDestroy "); manager.removeView(binding.getRoot()); } } app/src/main/java/com/auto/lyric/util/DeviceUtil.java
@@ -175,20 +175,12 @@ * @return */ public static int getStatusBarHeight(Context context) { Class<?> c = null; Object obj = null; Field field = null; int x = 0, statusBarHeight = 0; try { c = Class.forName("com.android.internal.R$dimen"); obj = c.newInstance(); field = c.getField("status_bar_height"); x = Integer.parseInt(field.get(obj).toString()); statusBarHeight = context.getResources().getDimensionPixelSize(x); } catch (Exception e1) { e1.printStackTrace(); int result = 0; int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resId > 0) { result = context.getResources().getDimensionPixelOffset(resId); } return statusBarHeight; return result; } /** app/src/main/java/com/auto/lyric/widgets/LyricView.java
New file @@ -0,0 +1,151 @@ package com.auto.lyric.widgets; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Handler; import android.util.AttributeSet; import android.widget.TextView; import com.auto.lyric.R; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * 歌词 * Created by Runt (qingingrunt2010@qq.com) on 2022/4/30. */ public class LyricView extends androidx.appcompat.widget.AppCompatTextView { private Paint mPaint; private float mX; private Paint mPathPaint; public int index = 0; private List<Sentence> list; public float mTouchHistoryY; private int mY; private float middleY;// private static final int DY = 40; // public LyricView(Context context) { super(context); init(); } public LyricView(Context context, AttributeSet attr) { super(context, attr); init(); } public LyricView(Context context, AttributeSet attr, int i) { super(context, attr, i); init(); } private void init() { setFocusable(true); if(list==null){ list=new ArrayList<Sentence>(); Sentence sen=new Sentence(0," "); list.add(0, sen); } // mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setTextSize(24); mPaint.setColor(Color.BLACK); mPaint.setAlpha(80); mPaint.setTypeface(Typeface.SERIF); // mPathPaint = new Paint(); mPathPaint.setAntiAlias(true); mPathPaint.setColor(Color.RED); mPathPaint.setTextSize(24); mPathPaint.setTypeface(Typeface.SANS_SERIF); } protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(0xEFeffff); Paint p = mPaint; Paint p2 = mPathPaint; p.setTextAlign(Paint.Align.LEFT); if (index == -1) return; p2.setTextAlign(Paint.Align.LEFT); // canvas.drawText(list.get(index).getName(), mX, middleY, p2); float tempY = middleY; // for (int i = index - 1; i >= 0; i--) { tempY = tempY - DY; if (tempY < 0) { break; } canvas.drawText(list.get(i).getName(), mX, tempY, p); } tempY = middleY; // for (int i = index + 1; i < list.size(); i++) { // tempY = tempY + DY; if (tempY > mY) { break; } canvas.drawText(list.get(i).getName(), mX, tempY, p); } } protected void onSizeChanged(int w, int h, int ow, int oh) { super.onSizeChanged(w, h, ow, oh); mX = w * 0.3f; mY = h; middleY = h * 0.5f; } public long updateIndex(int index) { if (index == -1) return -1; this.index=index; return index; } public List<Sentence> getList() { return list; } public void setList(List<Sentence> list) { this.list = list; } public void updateUI(){ new Thread(new updateThread()).start(); } class updateThread implements Runnable { long time = 300; int i=0; public void run() { while (true) { long sleeptime = updateIndex(i); time += sleeptime; mHandler.post(mUpdateResults); if (sleeptime == -1) return; try { Thread.sleep(time); i++; if(i==getList().size()) { i=0; time = 300; } } catch (InterruptedException e) { e.printStackTrace(); } } } } Handler mHandler = new Handler(); Runnable mUpdateResults = new Runnable() { public void run() { invalidate(); // } }; } app/src/main/java/com/auto/lyric/widgets/Sentence.java
New file @@ -0,0 +1,25 @@ package com.auto.lyric.widgets; /** * Created by Runt (qingingrunt2010@qq.com) on 2022/4/30. */ public class Sentence { private String name; private int index; public Sentence(int index,String name){ this.name=name; this.index=index; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } } app/src/main/res/layout/float_view.xml
@@ -2,45 +2,67 @@ <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/black_trans" xmlns:app="http://schemas.android.com/apk/res-auto"> <com.google.android.material.floatingactionbutton.FloatingActionButton <ImageButton android:id="@+id/floating" android:layout_width="40dp" android:layout_height="wrap_content" android:alpha="0.9" android:src="@android:drawable/ic_notification_overlay" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" /> app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintBottom_toTopOf="@id/lyric"/> <ImageButton android:id="@+id/close" android:layout_width="40dp" android:layout_height="wrap_content" android:alpha="0.9" android:src="@android:drawable/ic_menu_close_clear_cancel" app:layout_constraintTop_toTopOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toTopOf="@id/lyric"/> <com.auto.lyric.widgets.LyricView android:id="@+id/lyric" android:layout_width="match_parent" android:layout_height="0dp" android:text="歌词大意" android:gravity="center" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/close" app:layout_constraintBottom_toTopOf="@id/btn_back"/> <com.google.android.material.button.MaterialButton <Button android:id="@+id/btn_back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="后退" android:textColor="@color/black" android:alpha="0.2" app:layout_constraintTop_toBottomOf="@id/lyric" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent"/> <com.google.android.material.button.MaterialButton <Button android:id="@+id/btn_pause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="暂停" android:textColor="@color/black" android:alpha="0.2" app:layout_constraintTop_toBottomOf="@id/lyric" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@id/btn_back" app:layout_constraintRight_toLeftOf="@id/btn_fast"/> <com.google.android.material.button.MaterialButton <Button android:id="@+id/btn_fast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="快进" android:textColor="@color/black" android:alpha="0.2" app:layout_constraintTop_toBottomOf="@id/lyric" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout> app/src/main/res/values/colors.xml
@@ -6,6 +6,7 @@ <color name="teal_200">#FF03DAC5</color> <color name="teal_700">#FF018786</color> <color name="black">#FF000000</color> <color name="black_trans">#61000000</color> <color name="white">#FFFFFFFF</color> app/src/main/res/values/strings.xml
@@ -1,2 +1,3 @@ <resources> <string name="simulate_input_description">simulate_input</string> </resources> app/src/main/res/xml/simulateinput.xml
New file @@ -0,0 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeAllMask" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagReportViewIds|flagRetrieveInteractiveWindows" android:canRetrieveWindowContent="true" android:description="@string/simulate_input_description" android:packageNames="com.ss.android.ugc.aweme" android:notificationTimeout="1" />