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


}

}
相关推荐
冉冉同学7 分钟前
【HarmonyOS NEXT】解决微信浏览器无法唤起APP的问题
android·前端·harmonyos
韶博雅1 小时前
mysql表类型查询
android·数据库·mysql
studyForMokey1 小时前
【Android学习记录】工具使用
android·学习
小wanga1 小时前
【MySQL】索引特性
android·数据库·mysql
牛了爷爷2 小时前
php伪协议
android·开发语言·php
鸿蒙布道师2 小时前
鸿蒙NEXT开发文件预览工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师2 小时前
鸿蒙NEXT开发全局上下文管理类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
树獭非懒2 小时前
ContentProvider存在的意义:从Android沙箱机制看安全数据共享的设计哲学
android·客户端
恋猫de小郭2 小时前
IntelliJ IDEA 2025.1 发布 ,默认 K2 模式 | Android Studio 也将跟进
android·前端·flutter
tan &2 小时前
Android开发案例——简单计算器
android