Compare commits
3 Commits
9ef79d0e5e
...
fbe86451ba
| Author | SHA1 | Date | |
|---|---|---|---|
| fbe86451ba | |||
| 7ac9063248 | |||
| bbe99a1ca5 |
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import android.os.Bundle
|
|||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import com.ray650128.floatwindowdemo.databinding.ActivityMainBinding
|
import com.ray650128.floatwindowdemo.databinding.ActivityMainBinding
|
||||||
@ -15,7 +16,7 @@ import com.ray650128.floatwindowdemo.utils.Utils.REQUEST_FLOAT_CODE
|
|||||||
import com.ray650128.floatwindowdemo.service.SuspendwindowService
|
import com.ray650128.floatwindowdemo.service.SuspendwindowService
|
||||||
import com.ray650128.floatwindowdemo.utils.ItemViewTouchListener
|
import com.ray650128.floatwindowdemo.utils.ItemViewTouchListener
|
||||||
import com.ray650128.floatwindowdemo.utils.Utils
|
import com.ray650128.floatwindowdemo.utils.Utils
|
||||||
import com.ray650128.floatwindowdemo.utils.ViewModleMain
|
import com.ray650128.floatwindowdemo.utils.ViewModelMain
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @功能: 悬浮窗口Demo
|
* @功能: 悬浮窗口Demo
|
||||||
@ -25,7 +26,6 @@ import com.ray650128.floatwindowdemo.utils.ViewModleMain
|
|||||||
*/
|
*/
|
||||||
class MainActivity : AppCompatActivity(), View.OnClickListener {
|
class MainActivity : AppCompatActivity(), View.OnClickListener {
|
||||||
|
|
||||||
private var floatRootView: View? = null//悬浮窗View
|
|
||||||
private var isReceptionShow = false
|
private var isReceptionShow = false
|
||||||
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
@ -38,80 +38,20 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
|
|||||||
startService(Intent(this, SuspendwindowService::class.java))
|
startService(Intent(this, SuspendwindowService::class.java))
|
||||||
|
|
||||||
binding.apply {
|
binding.apply {
|
||||||
bt01.setOnClickListener(this@MainActivity)
|
|
||||||
bt02.setOnClickListener(this@MainActivity)
|
|
||||||
bt03.setOnClickListener(this@MainActivity)
|
bt03.setOnClickListener(this@MainActivity)
|
||||||
bt04.setOnClickListener(this@MainActivity)
|
|
||||||
bt05.setOnClickListener(this@MainActivity)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View?) {
|
override fun onClick(v: View?) {
|
||||||
when (v?.id) {
|
when (v?.id) {
|
||||||
R.id.bt_01 -> {
|
|
||||||
showCurrentWindow()
|
|
||||||
}
|
|
||||||
R.id.bt_02 -> {
|
|
||||||
Utils.checkSuspendedWindowPermission(this) {
|
|
||||||
isReceptionShow = false
|
|
||||||
ViewModleMain.isShowSuspendWindow.postValue(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
R.id.bt_03 -> {
|
R.id.bt_03 -> {
|
||||||
Utils.checkAccessibilityPermission(this) {
|
Utils.checkAccessibilityPermission(this) {
|
||||||
ViewModleMain.isShowWindow.postValue(true)
|
ViewModelMain.isShowWindow.postValue(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.bt_04 -> {
|
|
||||||
Utils.checkSuspendedWindowPermission(this) {
|
|
||||||
isReceptionShow = true
|
|
||||||
ViewModleMain.isShowSuspendWindow.postValue(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
R.id.bt_05 -> {
|
|
||||||
closeAllSuspendWindow()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 应用界面内显示悬浮球
|
|
||||||
*/
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
private fun showCurrentWindow() {
|
|
||||||
var layoutParam = WindowManager.LayoutParams().apply {
|
|
||||||
//设置大小 自适应
|
|
||||||
width = WRAP_CONTENT
|
|
||||||
height = WRAP_CONTENT
|
|
||||||
flags =
|
|
||||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
|
||||||
}
|
|
||||||
// 新建悬浮窗控件
|
|
||||||
floatRootView = LayoutInflater.from(this).inflate(R.layout.activity_float_item, null)
|
|
||||||
//设置拖动事件
|
|
||||||
floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager))
|
|
||||||
// 将悬浮窗控件添加到WindowManager
|
|
||||||
windowManager.addView(floatRootView, layoutParam)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭所有悬浮窗
|
|
||||||
*/
|
|
||||||
fun closeAllSuspendWindow() {
|
|
||||||
if (!Utils.isNull(floatRootView)) {
|
|
||||||
if (!Utils.isNull(floatRootView?.windowToken)) {
|
|
||||||
if (!Utils.isNull(windowManager)) {
|
|
||||||
windowManager?.removeView(floatRootView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ViewModleMain.isShowSuspendWindow.postValue(false)
|
|
||||||
ViewModleMain.isShowWindow.postValue(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
if (requestCode == REQUEST_FLOAT_CODE) {
|
if (requestCode == REQUEST_FLOAT_CODE) {
|
||||||
if (Settings.canDrawOverlays(this)) {
|
if (Settings.canDrawOverlays(this)) {
|
||||||
@ -124,21 +64,20 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
|
|||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
if (isReceptionShow) {
|
if (isReceptionShow) {
|
||||||
ViewModleMain.isVisible.postValue(true)
|
ViewModelMain.isVisible.postValue(true)
|
||||||
}
|
}
|
||||||
binding.textView.text = "onResume"
|
binding.textView.text = "onResume"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
|
||||||
binding.textView.text = "onPause"
|
binding.textView.text = "onPause"
|
||||||
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
if (isReceptionShow) {
|
if (isReceptionShow) {
|
||||||
ViewModleMain.isVisible.postValue(false)
|
ViewModelMain.isVisible.postValue(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.ray650128.floatwindowdemo
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
|
||||||
|
val Int.dp: Int
|
||||||
|
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
|
||||||
|
|
||||||
|
val Int.px: Int
|
||||||
|
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
|
||||||
@ -9,18 +9,18 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
|||||||
import androidx.lifecycle.LifecycleService
|
import androidx.lifecycle.LifecycleService
|
||||||
import com.ray650128.floatwindowdemo.R
|
import com.ray650128.floatwindowdemo.R
|
||||||
import com.ray650128.floatwindowdemo.utils.Utils
|
import com.ray650128.floatwindowdemo.utils.Utils
|
||||||
import com.ray650128.floatwindowdemo.utils.ViewModleMain
|
import com.ray650128.floatwindowdemo.utils.ViewModelMain
|
||||||
import com.ray650128.floatwindowdemo.utils.ItemViewTouchListener
|
import com.ray650128.floatwindowdemo.utils.ItemViewTouchListener
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @功能:应用外打开Service 有局限性 特殊界面无法显示
|
* @功能:應用外打開Service 有侷限性 特殊界面無法顯示
|
||||||
* @User Lmy
|
* @User Lmy
|
||||||
* @Creat 4/15/21 5:28 PM
|
* @Creat 4/15/21 5:28 PM
|
||||||
* @Compony 永远相信美好的事情即将发生
|
* @Compony 永遠相信美好的事情即將發生
|
||||||
*/
|
*/
|
||||||
class SuspendwindowService : LifecycleService() {
|
class SuspendwindowService : LifecycleService() {
|
||||||
private lateinit var windowManager: WindowManager
|
private lateinit var windowManager: WindowManager
|
||||||
private var floatRootView: View? = null//悬浮窗View
|
private var floatRootView: View? = null//懸浮窗View
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
@ -28,18 +28,18 @@ class SuspendwindowService : LifecycleService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化订阅
|
* 初始化訂閱
|
||||||
*/
|
*/
|
||||||
private fun initObserve() {
|
private fun initObserve() {
|
||||||
ViewModleMain.apply {
|
ViewModelMain.apply {
|
||||||
/**
|
/**
|
||||||
* 悬浮窗按钮的显示和隐藏
|
* 懸浮窗按鈕的顯示和隱藏
|
||||||
*/
|
*/
|
||||||
isVisible.observe(this@SuspendwindowService, {
|
isVisible.observe(this@SuspendwindowService, {
|
||||||
floatRootView?.visibility = if (it) View.VISIBLE else View.GONE
|
floatRootView?.visibility = if (it) View.VISIBLE else View.GONE
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* 悬浮窗按钮的创建和移除
|
* 懸浮窗按鈕的建立和移除
|
||||||
*/
|
*/
|
||||||
isShowSuspendWindow.observe(this@SuspendwindowService, {
|
isShowSuspendWindow.observe(this@SuspendwindowService, {
|
||||||
if (it) {
|
if (it) {
|
||||||
@ -58,17 +58,17 @@ class SuspendwindowService : LifecycleService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建悬浮窗
|
* 建立懸浮窗
|
||||||
*/
|
*/
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private fun showWindow() {
|
private fun showWindow() {
|
||||||
//获取WindowManager
|
//獲取WindowManager
|
||||||
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
|
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
|
||||||
val outMetrics = DisplayMetrics()
|
val outMetrics = DisplayMetrics()
|
||||||
windowManager.defaultDisplay.getMetrics(outMetrics)
|
windowManager.defaultDisplay.getMetrics(outMetrics)
|
||||||
var layoutParam = WindowManager.LayoutParams().apply {
|
var layoutParam = WindowManager.LayoutParams().apply {
|
||||||
/**
|
/**
|
||||||
* 设置type 这里进行了兼容
|
* 設定type 這裡進行了相容
|
||||||
*/
|
*/
|
||||||
type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
||||||
@ -77,18 +77,18 @@ class SuspendwindowService : LifecycleService() {
|
|||||||
}
|
}
|
||||||
format = PixelFormat.RGBA_8888
|
format = PixelFormat.RGBA_8888
|
||||||
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||||
//位置大小设置
|
//位置大小設定
|
||||||
width = WRAP_CONTENT
|
width = WRAP_CONTENT
|
||||||
height = WRAP_CONTENT
|
height = WRAP_CONTENT
|
||||||
gravity = Gravity.LEFT or Gravity.TOP
|
gravity = Gravity.LEFT or Gravity.TOP
|
||||||
//设置剧中屏幕显示
|
//設定劇中螢幕顯示
|
||||||
x = outMetrics.widthPixels / 2 - width / 2
|
x = outMetrics.widthPixels / 2 - width / 2
|
||||||
y = outMetrics.heightPixels / 2 - height / 2
|
y = outMetrics.heightPixels / 2 - height / 2
|
||||||
}
|
}
|
||||||
// 新建悬浮窗控件
|
// 新建懸浮窗控制元件
|
||||||
floatRootView = LayoutInflater.from(this).inflate(R.layout.activity_float_item, null)
|
floatRootView = LayoutInflater.from(this).inflate(R.layout.activity_float_item, null)
|
||||||
floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager))
|
floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager))
|
||||||
// 将悬浮窗控件添加到WindowManager
|
// 將懸浮窗控制元件新增到WindowManager
|
||||||
windowManager.addView(floatRootView, layoutParam)
|
windowManager.addView(floatRootView, layoutParam)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,27 +5,48 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.PixelFormat
|
import android.graphics.PixelFormat
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.accessibility.AccessibilityEvent
|
import android.view.accessibility.AccessibilityEvent
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.LifecycleRegistry
|
import androidx.lifecycle.LifecycleRegistry
|
||||||
import com.ray650128.floatwindowdemo.R
|
import com.ray650128.floatwindowdemo.R
|
||||||
|
import com.ray650128.floatwindowdemo.dp
|
||||||
|
import com.ray650128.floatwindowdemo.px
|
||||||
import com.ray650128.floatwindowdemo.utils.ItemViewTouchListener
|
import com.ray650128.floatwindowdemo.utils.ItemViewTouchListener
|
||||||
import com.ray650128.floatwindowdemo.utils.Utils.isNull
|
import com.ray650128.floatwindowdemo.utils.Utils.isNull
|
||||||
import com.ray650128.floatwindowdemo.utils.ViewModleMain
|
import com.ray650128.floatwindowdemo.utils.ViewModelMain
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @功能:利用无障碍打开悬浮窗口 无局限性 任何界面可以显示
|
* @功能:利用無障礙打開懸浮視窗 無侷限性 任何界面可以顯示
|
||||||
* @User Lmy
|
* @User Lmy
|
||||||
* @Creat 4/15/21 5:57 PM
|
* @Creat 4/15/21 5:57 PM
|
||||||
* @Compony 永远相信美好的事情即将发生
|
* @Compony 永遠相信美好的事情即將發生
|
||||||
*/
|
*/
|
||||||
class WorkAccessibilityService : AccessibilityService(), LifecycleOwner {
|
class WorkAccessibilityService : AccessibilityService(), LifecycleOwner {
|
||||||
|
|
||||||
private lateinit var windowManager: WindowManager
|
private lateinit var windowManager: WindowManager
|
||||||
private var floatRootView: View? = null//悬浮窗View
|
private var floatRootView: View? = null//懸浮窗View
|
||||||
private val mLifecycleRegistry = LifecycleRegistry(this)
|
private val mLifecycleRegistry = LifecycleRegistry(this)
|
||||||
|
|
||||||
|
|
||||||
|
private val timeUpdateHandler = Handler(Looper.getMainLooper())
|
||||||
|
private val timeUpdateRunnable = object : Runnable {
|
||||||
|
@SuppressLint("SimpleDateFormat")
|
||||||
|
override fun run() {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val simpleDateFormat = SimpleDateFormat("HH:mm:ss")
|
||||||
|
floatRootView?.findViewById<TextView>(R.id.tv_time)?.text = simpleDateFormat.format(now)
|
||||||
|
timeUpdateHandler.postDelayed(this, 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
|
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
|
||||||
@ -33,10 +54,10 @@ class WorkAccessibilityService : AccessibilityService(), LifecycleOwner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开关闭的订阅
|
* 打開關閉的訂閱
|
||||||
*/
|
*/
|
||||||
private fun initObserve() {
|
private fun initObserve() {
|
||||||
ViewModleMain.isShowWindow.observe(this) {
|
ViewModelMain.isShowWindow.observe(this) {
|
||||||
if (it) {
|
if (it) {
|
||||||
showWindow()
|
showWindow()
|
||||||
} else {
|
} else {
|
||||||
@ -44,6 +65,7 @@ class WorkAccessibilityService : AccessibilityService(), LifecycleOwner {
|
|||||||
if (!isNull(floatRootView?.windowToken)) {
|
if (!isNull(floatRootView?.windowToken)) {
|
||||||
if (!isNull(windowManager)) {
|
if (!isNull(windowManager)) {
|
||||||
windowManager.removeView(floatRootView)
|
windowManager.removeView(floatRootView)
|
||||||
|
timeUpdateHandler.removeCallbacks(timeUpdateRunnable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,33 +75,36 @@ class WorkAccessibilityService : AccessibilityService(), LifecycleOwner {
|
|||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private fun showWindow() {
|
private fun showWindow() {
|
||||||
// 设置LayoutParam
|
// 設定LayoutParam
|
||||||
// 获取WindowManager服务
|
// 獲取WindowManager服務
|
||||||
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
|
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
|
||||||
val outMetrics = DisplayMetrics()
|
val outMetrics = DisplayMetrics()
|
||||||
windowManager.defaultDisplay.getMetrics(outMetrics)
|
windowManager.defaultDisplay.getMetrics(outMetrics)
|
||||||
var layoutParam = WindowManager.LayoutParams()
|
val layoutParam = WindowManager.LayoutParams()
|
||||||
layoutParam.apply {
|
layoutParam.apply {
|
||||||
//显示的位置
|
//顯示的位置
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
||||||
type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY
|
//劉海屏延伸到劉海里面
|
||||||
//刘海屏延伸到刘海里面
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
layoutInDisplayCutoutMode =
|
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
||||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
|
||||||
}
|
}
|
||||||
} else {
|
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
|
||||||
type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
|
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
|
||||||
}
|
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||||
flags =
|
//flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
//flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||||
width = WindowManager.LayoutParams.WRAP_CONTENT
|
width = 145.px
|
||||||
height = WindowManager.LayoutParams.WRAP_CONTENT
|
height = 38.px
|
||||||
format = PixelFormat.TRANSPARENT
|
format = PixelFormat.RGBA_8888
|
||||||
}
|
}
|
||||||
floatRootView = LayoutInflater.from(this).inflate(R.layout.activity_float_item, null)
|
floatRootView = LayoutInflater.from(this).inflate(R.layout.activity_float_item, null)
|
||||||
|
floatRootView?.findViewById<ImageView>(R.id.iv_close)?.setOnClickListener {
|
||||||
|
windowManager.removeView(floatRootView)
|
||||||
|
timeUpdateHandler.removeCallbacks(timeUpdateRunnable)
|
||||||
|
}
|
||||||
floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager))
|
floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager))
|
||||||
windowManager.addView(floatRootView, layoutParam)
|
windowManager.addView(floatRootView, layoutParam)
|
||||||
|
timeUpdateHandler.post(timeUpdateRunnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -89,9 +114,10 @@ class WorkAccessibilityService : AccessibilityService(), LifecycleOwner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getLifecycle(): Lifecycle = mLifecycleRegistry
|
override fun getLifecycle(): Lifecycle = mLifecycleRegistry
|
||||||
override fun onStart(intent: Intent?, startId: Int) {
|
|
||||||
super.onStart(intent, startId)
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
|
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
|
||||||
|
return super.onStartCommand(intent, flags, startId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnbind(intent: Intent?): Boolean {
|
override fun onUnbind(intent: Intent?): Boolean {
|
||||||
|
|||||||
@ -1,25 +1,30 @@
|
|||||||
package com.ray650128.floatwindowdemo.utils
|
package com.ray650128.floatwindowdemo.utils
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @功能:处理悬浮窗拖动更新位置
|
* @功能:處理懸浮窗拖動更新位置
|
||||||
* @User Lmy
|
* @User Lmy
|
||||||
* @Creat 4/16/21 9:41 AM
|
* @Creat 4/16/21 9:41 AM
|
||||||
* @Compony 永远相信美好的事情即将发生
|
* @Compony 永遠相信美好的事情即將發生
|
||||||
*/
|
*/
|
||||||
class ItemViewTouchListener(val wl: WindowManager.LayoutParams, val windowManager: WindowManager) :
|
class ItemViewTouchListener(val wl: WindowManager.LayoutParams, val windowManager: WindowManager) :
|
||||||
View.OnTouchListener {
|
View.OnTouchListener {
|
||||||
private var x = 0
|
private var x = 0
|
||||||
private var y = 0
|
private var y = 0
|
||||||
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
|
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
|
||||||
|
Log.d("com.kldp.floating.floatingtools", "MotionEvent: $motionEvent")
|
||||||
when (motionEvent.action) {
|
when (motionEvent.action) {
|
||||||
MotionEvent.ACTION_DOWN -> {
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
wl.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
|
||||||
|
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
|
||||||
|
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||||
|
windowManager.updateViewLayout(view, wl)
|
||||||
x = motionEvent.rawX.toInt()
|
x = motionEvent.rawX.toInt()
|
||||||
y = motionEvent.rawY.toInt()
|
y = motionEvent.rawY.toInt()
|
||||||
|
|
||||||
}
|
}
|
||||||
MotionEvent.ACTION_MOVE -> {
|
MotionEvent.ACTION_MOVE -> {
|
||||||
val nowX = motionEvent.rawX.toInt()
|
val nowX = motionEvent.rawX.toInt()
|
||||||
@ -32,13 +37,19 @@ class ItemViewTouchListener(val wl: WindowManager.LayoutParams, val windowManage
|
|||||||
x += movedX
|
x += movedX
|
||||||
y += movedY
|
y += movedY
|
||||||
}
|
}
|
||||||
//更新悬浮球控件位置
|
//更新懸浮球控制元件位置
|
||||||
windowManager?.updateViewLayout(view, wl)
|
windowManager.updateViewLayout(view, wl)
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_OUTSIDE -> {
|
||||||
|
wl.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
|
||||||
|
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
|
||||||
|
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||||
|
windowManager.updateViewLayout(view, wl)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,18 +14,18 @@ import com.ray650128.floatwindowdemo.service.WorkAccessibilityService
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @功能: 工具类
|
* @功能: 工具類
|
||||||
* @User Lmy
|
* @User Lmy
|
||||||
* @Creat 4/16/21 8:33 AM
|
* @Creat 4/16/21 8:33 AM
|
||||||
* @Compony 永远相信美好的事情即将发生
|
* @Compony 永遠相信美好的事情即將發生
|
||||||
*/
|
*/
|
||||||
object Utils {
|
object Utils {
|
||||||
const val REQUEST_FLOAT_CODE=1001
|
const val REQUEST_FLOAT_CODE=1001
|
||||||
/**
|
/**
|
||||||
* 跳转到设置页面申请打开无障碍辅助功能
|
* 跳轉到設定頁面申請打開無障礙輔助功能
|
||||||
*/
|
*/
|
||||||
private fun accessibilityToSettingPage(context: Context) {
|
private fun accessibilityToSettingPage(context: Context) {
|
||||||
//开启辅助功能页面
|
//開啟輔助功能頁面
|
||||||
try {
|
try {
|
||||||
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
|
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
@ -39,7 +39,7 @@ object Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断Service是否开启
|
* 判斷Service是否開啟
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
fun isServiceRunning(context: Context, ServiceName: String): Boolean {
|
fun isServiceRunning(context: Context, ServiceName: String): Boolean {
|
||||||
@ -47,8 +47,7 @@ object Utils {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
val myManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
val myManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
val runningService =
|
val runningService = myManager.getRunningServices(1000) as ArrayList<ActivityManager.RunningServiceInfo>
|
||||||
myManager.getRunningServices(1000) as ArrayList<ActivityManager.RunningServiceInfo>
|
|
||||||
for (i in runningService.indices) {
|
for (i in runningService.indices) {
|
||||||
if (runningService[i].service.className == ServiceName) {
|
if (runningService[i].service.className == ServiceName) {
|
||||||
return true
|
return true
|
||||||
@ -58,11 +57,10 @@ object Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断悬浮窗权限权限
|
* 判斷懸浮窗許可權許可權
|
||||||
*/
|
*/
|
||||||
private fun commonROMPermissionCheck(context: Context?): Boolean {
|
private fun commonROMPermissionCheck(context: Context?): Boolean {
|
||||||
var result = true
|
var result = true
|
||||||
if (Build.VERSION.SDK_INT >= 23) {
|
|
||||||
try {
|
try {
|
||||||
val clazz: Class<*> = Settings::class.java
|
val clazz: Class<*> = Settings::class.java
|
||||||
val canDrawOverlays =
|
val canDrawOverlays =
|
||||||
@ -71,18 +69,17 @@ object Utils {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("ServiceUtils", Log.getStackTraceString(e))
|
Log.e("ServiceUtils", Log.getStackTraceString(e))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查悬浮窗权限是否开启
|
* 檢查懸浮窗許可權是否開啟
|
||||||
*/
|
*/
|
||||||
fun checkSuspendedWindowPermission(context: Activity, block: () -> Unit) {
|
fun checkSuspendedWindowPermission(context: Activity, block: () -> Unit) {
|
||||||
if (commonROMPermissionCheck(context)) {
|
if (commonROMPermissionCheck(context)) {
|
||||||
block()
|
block()
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(context, "请开启悬浮窗权限", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "請開啟懸浮窗許可權", Toast.LENGTH_SHORT).show()
|
||||||
context.startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply {
|
context.startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply {
|
||||||
data = Uri.parse("package:${context.packageName}")
|
data = Uri.parse("package:${context.packageName}")
|
||||||
}, REQUEST_FLOAT_CODE)
|
}, REQUEST_FLOAT_CODE)
|
||||||
@ -90,7 +87,7 @@ object Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查无障碍服务权限是否开启
|
* 檢查無障礙服務許可權是否開啟
|
||||||
*/
|
*/
|
||||||
fun checkAccessibilityPermission(context: Activity, block: () -> Unit) {
|
fun checkAccessibilityPermission(context: Activity, block: () -> Unit) {
|
||||||
if (isServiceRunning(context, WorkAccessibilityService::class.java.canonicalName)) {
|
if (isServiceRunning(context, WorkAccessibilityService::class.java.canonicalName)) {
|
||||||
|
|||||||
@ -1,23 +1,22 @@
|
|||||||
package com.ray650128.floatwindowdemo.utils
|
package com.ray650128.floatwindowdemo.utils
|
||||||
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @功能: 用于和Service通信
|
* @功能: 用於和Service通訊
|
||||||
* @User Lmy
|
* @User Lmy
|
||||||
* @Creat 4/16/21 8:37 AM
|
* @Creat 4/16/21 8:37 AM
|
||||||
* @Compony 永远相信美好的事情即将发生
|
* @Compony 永遠相信美好的事情即將發生
|
||||||
*/
|
*/
|
||||||
object ViewModleMain : ViewModel() {
|
object ViewModelMain : ViewModel() {
|
||||||
//悬浮窗口创建 移除 基于无障碍服务
|
//懸浮視窗建立 移除 基於無障礙服務
|
||||||
var isShowWindow = MutableLiveData<Boolean>()
|
var isShowWindow = MutableLiveData<Boolean>()
|
||||||
//悬浮窗口创建 移除
|
//懸浮視窗建立 移除
|
||||||
|
|
||||||
var isShowSuspendWindow = MutableLiveData<Boolean>()
|
var isShowSuspendWindow = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
//悬浮窗口显示 隐藏
|
//懸浮視窗顯示 隱藏
|
||||||
var isVisible = MutableLiveData<Boolean>()
|
var isVisible = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
}
|
}
|
||||||
11
app/src/main/res/drawable/ic_baseline_cancel_24.xml
Normal file
11
app/src/main/res/drawable/ic_baseline_cancel_24.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#ffffff"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z" />
|
||||||
|
</vector>
|
||||||
6
app/src/main/res/drawable/realtime_clock_background.xml
Normal file
6
app/src/main/res/drawable/realtime_clock_background.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#77000000" />
|
||||||
|
<corners android:radius="18dp" />
|
||||||
|
</shape>
|
||||||
@ -1,10 +1,32 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/realtime_clock_background">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_time"
|
||||||
|
android:layout_width="110dp"
|
||||||
|
android:layout_height="38dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:letterSpacing="0.1"
|
||||||
|
android:paddingStart="15dp"
|
||||||
|
android:text="22:22:22"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="100dp"
|
android:id="@+id/iv_close"
|
||||||
android:layout_height="100dp"
|
android:layout_width="24dp"
|
||||||
android:src="@drawable/icon_logo" />
|
android:layout_height="24dp"
|
||||||
</RelativeLayout>
|
android:layout_marginEnd="12dp"
|
||||||
|
android:src="@drawable/ic_baseline_cancel_24"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toRightOf="@+id/tv_time"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@ -9,23 +9,6 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingTop="50dp"
|
android:paddingTop="50dp"
|
||||||
tools:context=".MainActivity">
|
tools:context=".MainActivity">
|
||||||
<Button
|
|
||||||
android:id="@+id/bt_01"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="应用内悬浮窗口(当前页面)" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/bt_02"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="系统悬浮窗口(全局显示)[锁屏无法显示]" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/bt_04"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="系统悬浮窗口(前台显示)" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/bt_03"
|
android:id="@+id/bt_03"
|
||||||
@ -33,12 +16,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="系统悬浮窗口(全局显示)[无限制]" />
|
android:text="系统悬浮窗口(全局显示)[无限制]" />
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/bt_05"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="恢复初始状态" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textView"
|
android:id="@+id/textView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user