Lineageos 22.1(Android 15)实现负一屏

一、前言

方案是参考的这位大佬的,大家可以去付费订阅支持一波。我大概理一下Android15的修改。
大佬的方案代码

二、Android15适配调整

1.bp调整,加入aidl引入,这样make之后就可以索引代码了

c 复制代码
filegroup {
    name: "launcher-src",
    srcs: [
        "src/**/*.java",
        "src/**/*.kt",
        "src/**/*.aidl"
    ],
}

客户端端按照文章来就行,服务端我微调了一下代码,让切换变得更加平顺,具体还的项目中再调整。其实还差手机重复按的时候停止动画之类的东西处理,遇到的时候再说吧。

java 复制代码
package com.google.test;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.google.android.libraries.launcherclient.ILauncherOverlay;
import com.google.android.libraries.launcherclient.ILauncherOverlayCallback;

import androidx.annotation.NonNull;

/**
 * Created by cczheng on 2022/5/25.
 */

public class ScreenService extends Service {

    private Context mContext;
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams mLayoutParams;
    private int screenWidth;
    ILauncherOverlayCallback overlayCallback;

    private String TAG = "ScreenService";
    private float progress;

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
        mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        mLayoutParams = new WindowManager.LayoutParams();
        DisplayMetrics outMetrics = new DisplayMetrics();
        mWindowManager.getDefaultDisplay().getMetrics(outMetrics);
        screenWidth = outMetrics.widthPixels;
        Log.d(TAG, "onCreate");
    }

    @Override
    public IBinder onBind(Intent intent) {
        IBinder iBinder = new ILauncherOverlayImpl().asBinder();
        Log.i(TAG, "onBind");
        return iBinder;
    }


    public class ILauncherOverlayImpl extends ILauncherOverlay.Stub {

        private Handler mainThreadHandler;

        @Override
        public String getVoiceSearchLanguage() throws RemoteException {
            return null;
        }

        @Override
        public boolean isVoiceDetectionRunning() throws RemoteException {
            return false;
        }

        @Override
        public void onPause() throws RemoteException {
            Log.d(TAG, "onPause");
        }

        @Override
        public void onResume() throws RemoteException {
            Log.d(TAG, "onResume");
        }

        @Override
        public void requestVoiceDetection(boolean start) throws RemoteException {
            Log.d(TAG, "requestVoiceDetection");
        }

        @Override
        public void openOverlay(int options) throws RemoteException {
            Log.i(TAG, "openOverlay");
        }

        @Override
        public void closeOverlay(int options) throws RemoteException {
            Log.i(TAG, "closeOverlay");
        }

        @Override
        public void startScroll() throws RemoteException {
            Log.e(TAG, "startScroll");
            Message.obtain(this.mainThreadHandler, OverlayCallback.START_SCROLL).sendToTarget();
        }

        @Override
        public void onScroll(float progress) throws RemoteException {
//            Log.i(TAG,"onScroll=" + progress);
            Message.obtain(this.mainThreadHandler, OverlayCallback.UPDATE_SCROLL, progress).sendToTarget();
        }

        @Override
        public void endScroll() throws RemoteException {
            Log.e(TAG, "endScroll");
            Message.obtain(this.mainThreadHandler, OverlayCallback.END_SCROLL).sendToTarget();
        }

        @Override
        public void windowAttached(WindowManager.LayoutParams attrs, ILauncherOverlayCallback callbacks,
                                   int options) throws RemoteException {
            Log.i(TAG, "windowAttached.....");
//            doWindowAttached(attrs, callbacks, options);

            overlayCallback = callbacks;
            Bundle bundle = new Bundle();
            bundle.putParcelable("layout_params", attrs);
            bundle.putInt("client_options", options);
            OverlayCallback overlayCallback = new OverlayCallback(ScreenService.this);
            mainThreadHandler = new Handler(Looper.getMainLooper(), overlayCallback);
            Message.obtain(this.mainThreadHandler, OverlayCallback.WINDOW_ATTACHED,
                    Pair.create(bundle, callbacks)).sendToTarget();
        }

        @Override
        public void windowDetached(boolean isChangingConfigurations) throws RemoteException {
            Log.d(TAG, "windowDetached");
            Message.obtain(this.mainThreadHandler, OverlayCallback.WINDOW_DETACHCHED, isChangingConfigurations).sendToTarget();
        }
    }
    private void applyScroll(View view)  {
        try {
            overlayCallback.overlayScrollChanged(progress);
            mLayoutParams.x = (int) (-screenWidth*(1-progress));
            mWindowManager.updateViewLayout(view, mLayoutParams);

        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    }

    private void startProgressAnimation(boolean open,View view) {
        ValueAnimator valueAnimator=(open)? ValueAnimator.ofFloat(progress, 1f):ValueAnimator.ofFloat(progress, 0f);
        valueAnimator.setDuration(500);

        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                try {
                    overlayCallback.overlayScrollChanged(0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onAnimationCancel(Animator animation) {
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                progress= (float) valueAnimator.getAnimatedValue();
                applyScroll(view);
            }
        });
        valueAnimator.start();
    }
    class OverlayCallback implements Handler.Callback {
        public static final int WINDOW_ATTACHED = 100;
        public static final int START_SCROLL = 101;
        public static final int UPDATE_SCROLL = 102;
        public static final int END_SCROLL = 103;
        public static final int WINDOW_DETACHCHED = 104;

        private ScreenService screenService;
        private LinearLayout mOverlayDecorView;

        public OverlayCallback(ScreenService screenService) {
            this.screenService = screenService;
        }

        @Override
        public boolean handleMessage(@NonNull Message msg) {
            try {
                if (msg.what == WINDOW_ATTACHED) {
//                bundle.putParcelable("layout_params", attrs);
//                bundle.putInt("client_options", options);
                    Pair<Bundle, ILauncherOverlayCallback> pair = (Pair<Bundle, ILauncherOverlayCallback>) msg.obj;
                    WindowManager.LayoutParams layoutParams = pair.first.getParcelable("layout_params");
//                    overlayCallback = pair.second;

                    doWindowAttached(layoutParams, pair.second, 1);
                } else if (msg.what == START_SCROLL) {

                } else if (msg.what == UPDATE_SCROLL) {
                     progress = (float) msg.obj;
                    Log.d(TAG, "progress=" + progress);
                    applyScroll(mOverlayDecorView);
                } else if (msg.what == END_SCROLL) {
                    startProgressAnimation(progress>0.4f,mOverlayDecorView);



                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            return false;
        }




        public void doWindowAttached(WindowManager.LayoutParams lp, ILauncherOverlayCallback cb,
                                     int flags) throws RemoteException {
            mLayoutParams = new WindowManager.LayoutParams();
            mLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
            mLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
            mLayoutParams.gravity = Gravity.START;
            // 负一屏的 Window 层级比 Launcher 的大就可以
            mLayoutParams.type = lp.type + 1;
            mLayoutParams.token = lp.token;
            mLayoutParams.flags = WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS |
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS |
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;

            mLayoutParams.x = -screenWidth;
            Log.d(TAG, "doWindowAttached." + lp.type + "   " + lp.token + "   " + (-screenWidth));
            mLayoutParams.format = PixelFormat.TRANSLUCENT;

            mOverlayDecorView = new LinearLayout(mContext);
            mOverlayDecorView.setGravity(Gravity.CENTER);
            mOverlayDecorView.setBackgroundColor(Color.RED);

            TextView textView = new TextView(mContext);
            textView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));
            textView.setText("XXXXXXXX");
            textView.setTextSize(25);
            textView.setTextColor(Color.WHITE);
            mOverlayDecorView.addView(textView);

            mWindowManager.addView(mOverlayDecorView, mLayoutParams);

            mOverlayDecorView.setOnTouchListener(new OverlayOnTouchListener());

            if (cb != null) {
                cb.overlayStatusChanged(1);
            }
        }
    }


    private boolean isDrag;

    private class OverlayOnTouchListener implements View.OnTouchListener {
        private int firstX;
        private int lastX;

        @Override
        public boolean onTouch(View view, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    isDrag = false;
                    firstX = (int) event.getRawX();
                    lastX = (int) event.getRawX();
                    break;

                case MotionEvent.ACTION_MOVE:
                    isDrag = true;
                    int nowX = (int) event.getRawX();
                    int movedX = nowX - lastX;
                    lastX = nowX;
//                    Log.d(TAG,"movedX=" + movedX);
                    if (movedX < 0) {//只能左滑,滑到launcher中
                        mLayoutParams.x = mLayoutParams.x + movedX;
                        progress=1-((float) -mLayoutParams.x )/((float) screenWidth);
                        applyScroll(view);
//                        mWindowManager.updateViewLayout(view, mLayoutParams);
//                        try {
//                            overlayCallback.overlayScrollChanged();
//                        } catch (RemoteException e) {
//                            throw new RuntimeException(e);
//                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    int stopX = (int) event.getRawX();
                    int movedFX = stopX - firstX;
                    Log.d(TAG,"lastX=" + lastX + "  isDrag=" + isDrag + "  movedFX=" + movedFX);
                    if (isDrag && movedFX < 0) {
                        progress=1-((float) -mLayoutParams.x )/((float) screenWidth);
                        startProgressAnimation(false,view);
                    }
                    break;
            }
            return isDrag || view.onTouchEvent(event);
        }


}

}
相关推荐
阿巴斯甜10 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker10 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952711 小时前
Andorid Google 登录接入文档
android
黄林晴13 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android