Runt
2022-05-01 1c757c667d4d827cc0bcf692dae663f7ca49b01c
悬浮窗  位置移动
6 files modified
4 files added
455 ■■■■■ changed files
app/src/main/AndroidManifest.xml 25 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/MainActivity.java 77 ●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/service/FloatingWindowService.java 108 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/util/DeviceUtil.java 18 ●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/widgets/LyricView.java 151 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/widgets/Sentence.java 25 ●●●●● patch | view | raw | blame | history
app/src/main/res/layout/float_view.xml 38 ●●●● patch | view | raw | blame | history
app/src/main/res/values/colors.xml 1 ●●●● patch | view | raw | blame | history
app/src/main/res/values/strings.xml 1 ●●●● patch | view | raw | blame | history
app/src/main/res/xml/simulateinput.xml 11 ●●●●● 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,47 +1,58 @@
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())) {
                //启动Activity让用户授权
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                intent.setData(Uri.parse("package:" + getPackageName()));
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
                startActivity(intent);
            }else if(!isAccessibilitySettingsOn()){//辅助权限
                Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
                startActivity(intent);
            }else{//开启监听服务
                startService(service);
            //申请权限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                //开启悬浮窗
               /* if(!Settings.canDrawOverlays(getApplicationContext())) {
                    //启动Activity让用户授权
                    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                    intent.setData(Uri.parse("package:" + getPackageName()));
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
                    startActivity(intent);
                }else if(!isAccessibilitySettingsOn()){//辅助权限
                    Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
                    startActivity(intent);
                }else{//开启监听服务
                    //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"
    />