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


}

}
相关推荐
消失的旧时光-194316 分钟前
kotlin 函数引用
android·开发语言·kotlin
zhangphil18 分钟前
Android Coil3阶梯preload批量Bitmap拼接扁平宽图,Kotlin
android·kotlin
粤M温同学1 小时前
Android 根据Url使用Retrofit框架进行文件下载
android·retrofit
weixin_460783872 小时前
Execution failed for task ‘:path_provider_android:compileDebugJavaWithJavac‘.
android
每次的天空2 小时前
Android卷笔试题目总结
android·java·算法
硬件学长森哥6 小时前
Android音视频多媒体开源库基础大全
android·计算机视觉·开源·音视频
开开心心就好6 小时前
免费提供多样风格手机壁纸及自动更换功能的软件
android·python·网络协议·tcp/ip·macos·智能手机·pdf
EasyCVR12 小时前
EasyRTC嵌入式音视频通话SDK:如何解决跨平台(Linix、Windows、ARM、物联网)、跨设备(Android、ios等)的兼容性难题?
android·ios·音视频
每次的天空13 小时前
Android第五次面试总结(HR面)
android·面试·职场和发展
消失的旧时光-194314 小时前
浅谈跨平台框架的演变(H5混合开发->RN->Flutter)
android·开发语言·flutter·react native·跨平台