Lineageos 22.1(Android 15)实现负一屏

news2025/3/23 2:20:28

一、前言

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

二、Android15适配调整

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

filegroup {
    name: "launcher-src",
    srcs: [
        "src/**/*.java",
        "src/**/*.kt",
        "src/**/*.aidl"
    ],
}

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

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


}

}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2319891.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《深度学习》——YOLOv3详解

文章目录 YOLOv3简介YOLOv3核心原理YOLOv3改进YOLOv3网络结构 YOLOv3简介 YOLOv3&#xff08;You Only Look Once, version 3&#xff09;是一种先进的实时目标检测算法&#xff0c;由 Joseph Redmon 和 Ali Farhadi 开发。它在目标检测领域表现出色&#xff0c;具有速度快、精…

【设计模式】三十一、状态模式

系列文章|源码 https://github.com/tyronczt/design-mode-learn 文章目录 系列文章|源码一、模式核心思想二、模式结构三、Java代码示例&#xff1a;订单状态管理1. 定义状态接口2. 实现具体状态类3. 上下文类&#xff08;Context&#xff09;4. 客户端调用5. 运行截图 四、状…

vue 获取当前时间并自动刷新

新增需求&#xff0c;需要在大屏的右上角展示当前时间&#xff0c;并实时按秒刷新&#xff0c;通过通义千问搜索关键js代码后&#xff0c;整理出如下代码。 【效果图】 【HTML】 <div class"time-wrap">{{ formattedDateTime }}<span> {{ weekTime }}&…

C 语 言 --- 扫 雷 游 戏(初 阶 版)

C 语 言 --- 扫 雷 游 戏 初 阶 版 代 码 全 貌 与 功 能 介 绍扫雷游戏的功能说明游 戏 效 果 展 示游 戏 代 码 详 解game.htest.cgame.c 总结 &#x1f4bb;作 者 简 介&#xff1a;曾 与 你 一 样 迷 茫&#xff0c;现 以 经 验 助 你 入 门 C 语 言 &#x1f4a1;个 人 主…

WebDeveloper靶机详解

一、主机发现 arp-scan -l靶机ip为192.168.55.163 二、端口扫描、目录枚举、漏洞扫描、指纹识别 2.1端口扫描 nmap --min-rate 10000 -p- 192.168.55.163发现并无特殊端口开放 扫描一下UDP端口 nmap -sU --min-rate 10000 -p- 192.168.55.163没有扫描到UDP端口 2.2目录枚…

来源于胡椒的亚甲二氧桥CYP450-文献精读119

Piper nigrum CYP719A37 Catalyzes the Decisive Methylenedioxy Bridge Formation in Piperine Biosynthesis 胡椒 (Piper nigrum) CYP719A37 催化胡椒碱生物合成中关键的亚甲二氧桥形成 摘要 胡椒 (Piper nigrum) 是世界上最受欢迎的香料之一。其主要辛辣成分胡椒碱 (piper…

梦回杭州...

她对我说&#xff0c;烟雨中的西湖更别有情趣&#xff0c;我也怀着对‘人间天堂’的憧憬踏上了向往之旅。第一次亲密接触没有感觉中那么好&#xff0c;现在想起来是那时的人和心情都没能安静下来&#xff0c;去慢慢品味它的美。 六下杭州&#xff0c;亲历每一片风景&#xff0c…

NAT 实验:多私网环境下 NAPT、Easy IP 配置及 FTP 服务公网映射

NAT基本概念 定义&#xff1a;网络地址转换&#xff08;Network Address Translation&#xff0c;NAT&#xff09;是一种将私有&#xff08;保留&#xff09;地址转化为合法公网 IP 地址的转换技术&#xff0c;它被广泛应用于各种类型 Internet 接入方式和各种类型的网络中。作…

YOLO数据集分割训练集、测试集和验证集

记录一下自己的分割代码。 注意&#xff1a; 这是在windows环境&#xff0c;请Linux的同学们注意。标签为txt&#xff0c;图像为jpg&#xff0c;其他的我没试过喔。 训练集、验证集、测试集&#xff08;7:2:1&#xff09; import os import shutil import random from tqdm…

Debug-037-table列表勾选回显方案

效果展示&#xff1a; 图1 图2 最近实现一个支持勾选的el-table可以回显之前勾选项的功能。实现了一个“编辑”的功能&#xff1a; 在图1中的列表中有三行数据&#xff0c;当点击“更换设备”按钮时&#xff0c;打开抽屉显示el-table组件如图2所示&#xff0c;可以直接回显勾选…

人脸表情识别系统分享(基于深度学习+OpenCV+PyQt5)

最近终于把毕业大论文忙完了&#xff0c;众所周知硕士大论文需要有三个工作点&#xff0c;表情识别领域的第三个工作点一般是做一个表情识别系统出来&#xff0c;如下图所示。 这里分享一下这个表情识别系统&#xff1a; 采用 深度学习OpenCVPyQt5 构建&#xff0c;主要功能包…

AtCoder - arc086_d Shift and Decrement分析与实现

分析与思路 可以把操作流程表示成下图 以进行四次除法操作为例&#xff1a; 这里有一个关键点&#xff1a;对于每个p_i (0< i <x-1) &#xff0c;x是除法操作的次数&#xff0c;如果p_i>2&#xff0c;可以将2个p_i的减法操作去掉&#xff0c;在p_(i1)中增加一个减法…

tcping 命令的使用,ping IP 和端口

1. ‌Windows系统安装‌ ‌下载tcping工具‌&#xff1a;根据系统位数&#xff08;32位或64位&#xff09;下载对应的tcping.exe文件。‌安装步骤‌&#xff1a; 将下载的tcping.exe文件复制到C:\Windows\System32目录下。如果下载的是64位版本&#xff0c;需将文件名改为tcpi…

天地图InfoWindow插入React自定义组件

截至2025年03月21日天地图的Marker不支持添加Label; 同时Label和Icon是不支持自定义HTMLElement只支持String&#xff1b;目前只有InfoWindow支持自定义HTMLElement; 效果图 React核心api import ReactDOM from react-dom/client const content document.createElement(div);…

003-掌控命令行-CLI11-C++开源库108杰

首选的现代C风格命令行参数解析器! &#xff08;本课程包含两段教学视频。&#xff09; 以文件对象监控程序为实例&#xff0c;五分钟实现从命令行读入多个监控目标路径&#xff1b;区分两大时机&#xff0c;学习 CLI11 构建与解析参数两大场景下的异常处理&#xff1b;区分三…

鸿蒙HarmonyOS NEXT应用崩溃分析及修复

鸿蒙HarmonyOS NEXT应用崩溃分析及修复 如何保证应用的健壮性&#xff0c;其中一个指标就是看崩溃率&#xff0c;如何降低崩溃率&#xff0c;就需要知道存在哪些崩溃&#xff0c;然后对症下药&#xff0c;解决崩溃。那么鸿蒙应用中存在哪些崩溃类型呢&#xff1f;又改如何解决…

机器学习——分类、回归、聚类、LASSO回归、Ridge回归(自用)

纠正自己的误区&#xff1a;机器学习是一个大范围&#xff0c;并不是一个小的方向&#xff0c;比如&#xff1a;线性回归预测、卷积神经网络和强化学都是机器学习算法在不同场景的应用。 机器学习最为关键的是要有数据&#xff0c;也就是数据集 名词解释&#xff1a;数据集中的…

【canvas】一键自动布局:如何让流程图节点自动找到最佳位置

一键自动布局&#xff1a;如何让流程图节点自动找到最佳位置 引言 在流程图、拓扑图和系统架构图设计中&#xff0c;节点布局往往是最令人头疼的问题。如果手动调整每个节点位置&#xff0c;不仅耗时费力&#xff0c;还难以保证美观性和一致性。本文将深入解析如何实现自动布…

[每周一更]-(第137期):Go + Gin 实战:Docker Compose + Apache 反向代理全流程

文章目录 **1. Go 代码示例&#xff08;main.go&#xff09;****2. Dockerfile 多段构建**3.构建 Docker 镜像**4. docker-compose.yml 直接拉取镜像****5. 运行容器****6. 测试 API**7、配置域名访问**DNS解析&#xff1a;将域名转换为IP地址****DNS寻址示例** 8.错误记录 访问…

SpringCache小记

Spring Cache 小记 官方文档&#xff1a;https://springdoc.cn/spring-cache-tutorial/ 基础知识 常用注解 EnableCaching&#xff1a;开启缓存功能&#xff0c;一般放在启动类上。 Cacheable&#xff1a;表示该方法支持缓存。当调用被注解的方法时&#xff0c;如果对应的键已…