如何在设备 运行 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
}
在我的应用程序中,当我在 运行 服务时,我试图在此处用服务覆盖布局,当目标设备低于 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
}