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);
        }


}

}
相关推荐
大G哥43 分钟前
Kotlin Lambda语法错误修复
android·java·开发语言·kotlin
鸿蒙布道师4 小时前
鸿蒙NEXT开发动画案例2
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
androidwork4 小时前
Kotlin Android工程Mock数据方法总结
android·开发语言·kotlin
xiangxiongfly9156 小时前
Android setContentView()源码分析
android·setcontentview
人间有清欢7 小时前
Android开发补充内容
android·okhttp·rxjava·retrofit·hilt·jetpack compose
人间有清欢8 小时前
Android开发报错解决
android
每次的天空10 小时前
Android学习总结之kotlin协程面试篇
android·学习·kotlin
每次的天空12 小时前
Android学习总结之Binder篇
android·学习·binder
峥嵘life12 小时前
Android 有线网开发调试总结
android
是店小二呀13 小时前
【算法-链表】链表操作技巧:常见算法
android·c++·算法·链表