如何在设备 运行 Oreo 和 Pie 中时在锁定屏幕上叠加布局

How to overlay a layout on lock screen when in devices running Oreo and Pie

在我的应用程序中,当我在 运行 服务时,我试图在此处用服务覆盖布局,当目标设备低于 oreo 时,它成功执行并在锁定屏幕上显示布局,但当我我试图从晚于 oreo 的版本中覆盖相同的内容,但它不起作用,但是当我按照其他答案中的建议将标志更改为 Type_Application_Overlay 时,它只在屏幕解锁时显示布局,但我想在锁定屏幕上显示它尝试了很多搜索但没有找到任何有用的答案来解决这个问题一些答案建议在锁屏上显示 activity 我也试过了但仍然没有证明有任何帮助!

此外, 要补充一点,Play 商店中有一些应用程序可以轻松地在锁定屏幕上显示任何布局,即使是在 oreo 和 pie 上也是如此:

你可以看看这个应用程序:

https://play.google.com/store/apps/details?id=com.wifihacker.whousemywifi.wifirouter.wifisecurity

即使不要求任何覆盖权限或设备管理员权限,此应用程序也可以轻松地在锁屏上显示自定义全屏布局。

如果这个应用程序能够覆盖锁定屏幕,为什么我不能这样做?

任何对此的帮助和建议将不胜感激!

这是我当前的锁屏服务代码:

class LockScreenService : Service() {

    private lateinit var mReceiver: BroadcastReceiver
    private var isShowing = false

    private lateinit var windowManager: WindowManager
    lateinit var  params: WindowManager.LayoutParams
    lateinit var myview: View
    var  downX:Int = 0
    lateinit var locklayout:RelativeLayout
     var upX:Int = 0
    var indicator: WaveLoadingView?=null
    lateinit var date:TextView
    lateinit var time:TextView
    lateinit var settings:ImageView
    lateinit var unlock:LinearLayout
    var r:Runnable?=null
    companion object{

    }
    private val mBatInfoReceiver = object : BroadcastReceiver() {
        override fun onReceive(ctxt: Context, intent: Intent) {
            val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
            if (indicator!=null)
            {
                indicator!!.progressValue = level
                indicator!!.setAnimDuration(3000)
                indicator!!.startAnimation()
            }
            }
    }

    override fun onBind(intent: Intent): IBinder? {
        // TODO Auto-generated method stub
        return null
    }

    override fun onCreate() {
        super.onCreate()
        try {
            EventBus.getDefault().register(this)
            windowManager = applicationContext.getSystemService(WINDOW_SERVICE) as WindowManager

            val li = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

            myview = li.inflate(R.layout.lockscreenlayout, null)
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                params = WindowManager.LayoutParams(
                    WindowManager.LayoutParams.MATCH_PARENT,
                    WindowManager.LayoutParams.MATCH_PARENT,
                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                    PixelFormat.TRANSLUCENT
                )
            } else {
                params = WindowManager.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
                    WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                            or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                            or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                            or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
                    PixelFormat.TRANSLUCENT
                )
                myview.setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            or View.SYSTEM_UI_FLAG_FULLSCREEN
                            or View.SYSTEM_UI_FLAG_VISIBLE
                            or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            or View.SYSTEM_UI_FLAG_IMMERSIVE
                            or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                )
            }
            myview.setVisibility(View.VISIBLE)
            settings = myview.findViewById(R.id.imgsetting)
            settings.setOnClickListener {
                var intent = Intent(this, Settings::class.java)
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                this.startActivity(intent)
                windowManager.removeViewImmediate(myview)
            }

             indicator = myview.findViewById(R.id.indicator)
            locklayout = myview.findViewById(R.id.locklay)
            this.registerReceiver(this.mBatInfoReceiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
            time = myview.findViewById(R.id.time)
            date = myview.findViewById(R.id.date)

            date.text =
                SimpleDateFormat("EEEE").format(Calendar.getInstance().time).toString() + "," + SimpleDateFormat("dd").format(
                    Calendar.getInstance().time
                ).toString() + " " + SimpleDateFormat("MMMM").format(Calendar.getInstance().time).toString()
            try {
                unlock.setOnTouchListener(object : View.OnTouchListener {
                    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
                        if (event!!.getAction() == MotionEvent.ACTION_DOWN) {
                            downX = event.getX().toInt()

                            return true
                        } else if (event.getAction() == MotionEvent.ACTION_UP) {
                            upX = event.getX().toInt()
                            if (upX - downX > 100) {
                                val animation = AnimationUtils.loadAnimation(applicationContext, R.anim.left_right_anim)
                                animation.setAnimationListener(object : Animation.AnimationListener {
                                    override fun onAnimationStart(animation: Animation) {}

                                    override fun onAnimationRepeat(animation: Animation) {}

                                    override fun onAnimationEnd(animation: Animation) {
                                        windowManager.removeViewImmediate(myview)
                                    }
                                })
                                locklayout.startAnimation(animation)
                                // swipe right
                            } else if (downX - upX > -100) {

                            }
                            return true

                        }
                        return false
                    }
                })
            } catch (ex: Exception)
            {}


            //Register receiver for determining screen off and if user is present
            mReceiver = LockScreenStateReceiver()
            val filter = IntentFilter(Intent.ACTION_SCREEN_OFF)
            filter.addAction(Intent.ACTION_USER_PRESENT)

            registerReceiver(mReceiver, filter)
        }
        catch (ex:Exception)
        {

        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
         return START_STICKY
       }

    inner class LockScreenStateReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            try {
                if (intent.action == Intent.ACTION_SCREEN_OFF) {
                    //if screen is turn off show the textview
                    if (!isShowing) {
                        windowManager.addView(myview, params)
                        isShowing = true
                    }
                } else if (intent.action == Intent.ACTION_USER_PRESENT) {
                    //Handle resuming events if user is present/screen is unlocked remove the textview immediately
                    if (isShowing) {
                        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) {
                            //windowManager.removeViewImmediate(myview)
                        }
                        isShowing = false
                    }
                }
            }
            catch (ex:Exception){

            }
        }

    }

    override fun onDestroy() {
        //unregister receiver when the service is destroy
        try {
            EventBus.getDefault().unregister(this)
            if (mReceiver != null) {
                unregisterReceiver(mReceiver)
            }

            //remove view if it is showing and the service is destroy
            if (isShowing) {
                if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) {
                    //windowManager.removeViewImmediate(myview)
                }
                isShowing = false
            }
        }
        catch (ex:Exception)
        {

        }
        super.onDestroy()
    }

}

我正在研究 VOIP,基本上我所做的是当应用程序收到远程通知时,它会启动我的 IncomingCallActivity 以提醒用户有来电。它不需要覆盖或唤醒锁定权限。

您需要做的就是为您的锁屏覆盖创建一个 Activity,从您的服务中启动 activity。在 onCreate 方法上添加相关 WindowManager flags to your activity's Window

 Window window = getWindow();
    window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
            | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
            | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

我在 Oreo 上测试过,效果很好。

我非常感谢@Emir 的回答,但是他提供的代码在某些设备中不是 运行 并且 activity 没有在锁定屏幕上启动

Window window = getWindow();
    window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
            | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
            | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

这里的问题是代码的 'OR' 部分导致某些设备出现问题,因此为了解决这个问题,我所做的是而不是在我调用的 'OR' 部分中调用 window 标志这里所有这些标志都是我在 oncreate 中为解决这个问题所做的。

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lock_screen)
   //execute all flags individually to solve this issue
     window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
        window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD)
        window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        val mUIFlag = ( View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                or View.SYSTEM_UI_FLAG_FULLSCREEN
                or View.SYSTEM_UI_FLAG_VISIBLE
                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_IMMERSIVE
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)

        window.decorView.systemUiVisibility = mUIFlag

        }