From 1e991a5caae68c9de4fa42146a2620c73926d882 Mon Sep 17 00:00:00 2001 From: Raymond Yang Date: Fri, 30 Jun 2023 17:50:20 +0800 Subject: [PATCH] first commit --- .gitignore | 15 ++ .idea/.gitignore | 3 + .idea/compiler.xml | 6 + .idea/gradle.xml | 19 ++ .idea/kotlinc.xml | 6 + .idea/misc.xml | 10 + app/.gitignore | 1 + app/build.gradle.kts | 54 +++++ app/proguard-rules.pro | 21 ++ .../ExampleInstrumentedTest.kt | 24 +++ app/src/main/AndroidManifest.xml | 44 +++++ .../ray650128/floatwindowdemo/MainActivity.kt | 138 +++++++++++++ .../service/SuspendwindowService.kt | 94 +++++++++ .../service/WorkAccessibilityService.kt | 112 +++++++++++ .../utils/ItemViewTouchListener.kt | 44 +++++ .../ray650128/floatwindowdemo/utils/Utils.kt | 105 ++++++++++ .../floatwindowdemo/utils/ViewModleMain.kt | 23 +++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++++++ .../res/drawable/ic_launcher_foreground.xml | 30 +++ app/src/main/res/drawable/icon_logo.jpg | Bin 0 -> 28625 bytes .../main/res/layout/activity_float_item.xml | 10 + app/src/main/res/layout/activity_main.xml | 43 ++++ .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes app/src/main/res/values-night/themes.xml | 7 + app/src/main/res/values/colors.xml | 5 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/themes.xml | 9 + app/src/main/res/xml/allocation.xml | 8 + app/src/main/res/xml/backup_rules.xml | 13 ++ .../main/res/xml/data_extraction_rules.xml | 19 ++ .../floatwindowdemo/ExampleUnitTest.kt | 17 ++ build.gradle.kts | 5 + gradle.properties | 23 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 185 ++++++++++++++++++ gradlew.bat | 89 +++++++++ settings.gradle.kts | 18 ++ 49 files changed, 1391 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/compiler.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/misc.xml create mode 100644 app/.gitignore create mode 100644 app/build.gradle.kts create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/com/ray650128/floatwindowdemo/ExampleInstrumentedTest.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/ray650128/floatwindowdemo/MainActivity.kt create mode 100644 app/src/main/java/com/ray650128/floatwindowdemo/service/SuspendwindowService.kt create mode 100644 app/src/main/java/com/ray650128/floatwindowdemo/service/WorkAccessibilityService.kt create mode 100644 app/src/main/java/com/ray650128/floatwindowdemo/utils/ItemViewTouchListener.kt create mode 100644 app/src/main/java/com/ray650128/floatwindowdemo/utils/Utils.kt create mode 100644 app/src/main/java/com/ray650128/floatwindowdemo/utils/ViewModleMain.kt create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/icon_logo.jpg create mode 100644 app/src/main/res/layout/activity_float_item.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/values-night/themes.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/allocation.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/test/java/com/ray650128/floatwindowdemo/ExampleUnitTest.kt create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..6d89050 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..2b8a50f --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0ad17cb --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..78627a2 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,54 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "com.ray650128.floatwindowdemo" + compileSdk = 33 + + defaultConfig { + applicationId = "com.ray650128.floatwindowdemo" + minSdk = 26 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + + viewBinding.enable = true +} + +dependencies { + + implementation("androidx.core:core-ktx:1.9.0") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.9.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + + + + implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") + +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/ray650128/floatwindowdemo/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/ray650128/floatwindowdemo/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..99768e2 --- /dev/null +++ b/app/src/androidTest/java/com/ray650128/floatwindowdemo/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.ray650128.floatwindowdemo + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.ray650128.floatwindowdemo", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..16d335e --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/ray650128/floatwindowdemo/MainActivity.kt b/app/src/main/java/com/ray650128/floatwindowdemo/MainActivity.kt new file mode 100644 index 0000000..3eda2b9 --- /dev/null +++ b/app/src/main/java/com/ray650128/floatwindowdemo/MainActivity.kt @@ -0,0 +1,138 @@ +package com.ray650128.floatwindowdemo + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.Build +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.provider.Settings +import android.view.* +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.widget.Toast +import androidx.annotation.RequiresApi +import com.ray650128.floatwindowdemo.databinding.ActivityMainBinding +import com.ray650128.floatwindowdemo.utils.Utils.REQUEST_FLOAT_CODE +import com.ray650128.floatwindowdemo.service.SuspendwindowService +import com.ray650128.floatwindowdemo.utils.ItemViewTouchListener +import com.ray650128.floatwindowdemo.utils.Utils +import com.ray650128.floatwindowdemo.utils.ViewModleMain + +/** + * @功能: 悬浮窗口Demo + * @User Lmy + * @Creat 4/16/21 9:28 AM + * @Compony 永远相信美好的事情即将发生 + */ +class MainActivity : AppCompatActivity(), View.OnClickListener { + + private var floatRootView: View? = null//悬浮窗View + private var isReceptionShow = false + + private lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + startService(Intent(this, SuspendwindowService::class.java)) + + binding.apply { + bt01.setOnClickListener(this@MainActivity) + bt02.setOnClickListener(this@MainActivity) + bt03.setOnClickListener(this@MainActivity) + bt04.setOnClickListener(this@MainActivity) + bt05.setOnClickListener(this@MainActivity) + } + } + + override fun onClick(v: View?) { + 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 -> { + Utils.checkAccessibilityPermission(this) { + ViewModleMain.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?) { + if (requestCode == REQUEST_FLOAT_CODE) { + if (Settings.canDrawOverlays(this)) { + Toast.makeText(this, "悬浮窗权限已经打开", Toast.LENGTH_SHORT).show() + } + } + super.onActivityResult(requestCode, resultCode, data) + } + + override fun onResume() { + super.onResume() + if (isReceptionShow) { + ViewModleMain.isVisible.postValue(true) + } + } + + override fun onStop() { + super.onStop() + if (isReceptionShow) { + ViewModleMain.isVisible.postValue(false) + } + } + +} diff --git a/app/src/main/java/com/ray650128/floatwindowdemo/service/SuspendwindowService.kt b/app/src/main/java/com/ray650128/floatwindowdemo/service/SuspendwindowService.kt new file mode 100644 index 0000000..ff74542 --- /dev/null +++ b/app/src/main/java/com/ray650128/floatwindowdemo/service/SuspendwindowService.kt @@ -0,0 +1,94 @@ +package com.ray650128.floatwindowdemo.service + +import android.annotation.SuppressLint +import android.graphics.PixelFormat +import android.os.Build +import android.util.DisplayMetrics +import android.view.* +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import androidx.lifecycle.LifecycleService +import com.ray650128.floatwindowdemo.R +import com.ray650128.floatwindowdemo.utils.Utils +import com.ray650128.floatwindowdemo.utils.ViewModleMain +import com.ray650128.floatwindowdemo.utils.ItemViewTouchListener + +/** + * @功能:应用外打开Service 有局限性 特殊界面无法显示 + * @User Lmy + * @Creat 4/15/21 5:28 PM + * @Compony 永远相信美好的事情即将发生 + */ +class SuspendwindowService : LifecycleService() { + private lateinit var windowManager: WindowManager + private var floatRootView: View? = null//悬浮窗View + + override fun onCreate() { + super.onCreate() + initObserve() + } + + /** + * 初始化订阅 + */ + private fun initObserve() { + ViewModleMain.apply { + /** + * 悬浮窗按钮的显示和隐藏 + */ + isVisible.observe(this@SuspendwindowService, { + floatRootView?.visibility = if (it) View.VISIBLE else View.GONE + }) + /** + * 悬浮窗按钮的创建和移除 + */ + isShowSuspendWindow.observe(this@SuspendwindowService, { + if (it) { + showWindow() + } else { + if (!Utils.isNull(floatRootView)) { + if (!Utils.isNull(floatRootView?.windowToken)) { + if (!Utils.isNull(windowManager)) { + windowManager?.removeView(floatRootView) + } + } + } + } + }) + } + } + + /** + * 创建悬浮窗 + */ + @SuppressLint("ClickableViewAccessibility") + private fun showWindow() { + //获取WindowManager + windowManager = getSystemService(WINDOW_SERVICE) as WindowManager + val outMetrics = DisplayMetrics() + windowManager.defaultDisplay.getMetrics(outMetrics) + var layoutParam = WindowManager.LayoutParams().apply { + /** + * 设置type 这里进行了兼容 + */ + type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY + } else { + WindowManager.LayoutParams.TYPE_PHONE + } + format = PixelFormat.RGBA_8888 + flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + //位置大小设置 + width = WRAP_CONTENT + height = WRAP_CONTENT + gravity = Gravity.LEFT or Gravity.TOP + //设置剧中屏幕显示 + x = outMetrics.widthPixels / 2 - width / 2 + y = outMetrics.heightPixels / 2 - height / 2 + } + // 新建悬浮窗控件 + floatRootView = LayoutInflater.from(this).inflate(R.layout.activity_float_item, null) + floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager)) + // 将悬浮窗控件添加到WindowManager + windowManager.addView(floatRootView, layoutParam) + } +} diff --git a/app/src/main/java/com/ray650128/floatwindowdemo/service/WorkAccessibilityService.kt b/app/src/main/java/com/ray650128/floatwindowdemo/service/WorkAccessibilityService.kt new file mode 100644 index 0000000..898beb6 --- /dev/null +++ b/app/src/main/java/com/ray650128/floatwindowdemo/service/WorkAccessibilityService.kt @@ -0,0 +1,112 @@ +package com.ray650128.floatwindowdemo.service + +import android.accessibilityservice.AccessibilityService +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.PixelFormat +import android.os.Build +import android.util.DisplayMetrics +import android.view.* +import android.view.accessibility.AccessibilityEvent +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import com.ray650128.floatwindowdemo.R +import com.ray650128.floatwindowdemo.utils.ItemViewTouchListener +import com.ray650128.floatwindowdemo.utils.Utils.isNull +import com.ray650128.floatwindowdemo.utils.ViewModleMain + +/** + * @功能:利用无障碍打开悬浮窗口 无局限性 任何界面可以显示 + * @User Lmy + * @Creat 4/15/21 5:57 PM + * @Compony 永远相信美好的事情即将发生 + */ +class WorkAccessibilityService : AccessibilityService(), LifecycleOwner { + private lateinit var windowManager: WindowManager + private var floatRootView: View? = null//悬浮窗View + private val mLifecycleRegistry = LifecycleRegistry(this) + override fun onCreate() { + super.onCreate() + mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); + initObserve() + } + + /** + * 打开关闭的订阅 + */ + private fun initObserve() { + ViewModleMain.isShowWindow.observe(this) { + if (it) { + showWindow() + } else { + if (!isNull(floatRootView)) { + if (!isNull(floatRootView?.windowToken)) { + if (!isNull(windowManager)) { + windowManager.removeView(floatRootView) + } + } + } + } + } + } + + @SuppressLint("ClickableViewAccessibility") + private fun showWindow() { + // 设置LayoutParam + // 获取WindowManager服务 + windowManager = getSystemService(WINDOW_SERVICE) as WindowManager + val outMetrics = DisplayMetrics() + windowManager.defaultDisplay.getMetrics(outMetrics) + var layoutParam = WindowManager.LayoutParams() + layoutParam.apply { + //显示的位置 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY + //刘海屏延伸到刘海里面 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + } + } else { + type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT + } + flags = + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + width = WindowManager.LayoutParams.WRAP_CONTENT + height = WindowManager.LayoutParams.WRAP_CONTENT + format = PixelFormat.TRANSPARENT + } + floatRootView = LayoutInflater.from(this).inflate(R.layout.activity_float_item, null) + floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager)) + windowManager.addView(floatRootView, layoutParam) + } + + + override fun onServiceConnected() { + super.onServiceConnected() + mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) + } + + override fun getLifecycle(): Lifecycle = mLifecycleRegistry + override fun onStart(intent: Intent?, startId: Int) { + super.onStart(intent, startId) + mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) + } + + override fun onUnbind(intent: Intent?): Boolean { + mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP) + return super.onUnbind(intent) + } + + override fun onDestroy() { + mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) + super.onDestroy() + } + + override fun onAccessibilityEvent(event: AccessibilityEvent?) { + } + + override fun onInterrupt() { + } +} diff --git a/app/src/main/java/com/ray650128/floatwindowdemo/utils/ItemViewTouchListener.kt b/app/src/main/java/com/ray650128/floatwindowdemo/utils/ItemViewTouchListener.kt new file mode 100644 index 0000000..df09dfe --- /dev/null +++ b/app/src/main/java/com/ray650128/floatwindowdemo/utils/ItemViewTouchListener.kt @@ -0,0 +1,44 @@ +package com.ray650128.floatwindowdemo.utils + +import android.view.MotionEvent +import android.view.View +import android.view.WindowManager + +/** + * @功能:处理悬浮窗拖动更新位置 + * @User Lmy + * @Creat 4/16/21 9:41 AM + * @Compony 永远相信美好的事情即将发生 + */ +class ItemViewTouchListener(val wl: WindowManager.LayoutParams, val windowManager: WindowManager) : + View.OnTouchListener { + private var x = 0 + private var y = 0 + override fun onTouch(view: View, motionEvent: MotionEvent): Boolean { + when (motionEvent.action) { + MotionEvent.ACTION_DOWN -> { + x = motionEvent.rawX.toInt() + y = motionEvent.rawY.toInt() + + } + MotionEvent.ACTION_MOVE -> { + val nowX = motionEvent.rawX.toInt() + val nowY = motionEvent.rawY.toInt() + val movedX = nowX - x + val movedY = nowY - y + x = nowX + y = nowY + wl.apply { + x += movedX + y += movedY + } + //更新悬浮球控件位置 + windowManager?.updateViewLayout(view, wl) + } + else -> { + + } + } + return false + } +} diff --git a/app/src/main/java/com/ray650128/floatwindowdemo/utils/Utils.kt b/app/src/main/java/com/ray650128/floatwindowdemo/utils/Utils.kt new file mode 100644 index 0000000..53388ec --- /dev/null +++ b/app/src/main/java/com/ray650128/floatwindowdemo/utils/Utils.kt @@ -0,0 +1,105 @@ +package com.ray650128.floatwindowdemo.utils + +import android.app.Activity +import android.app.ActivityManager +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.provider.Settings +import android.text.TextUtils +import android.util.Log +import android.widget.Toast +import com.ray650128.floatwindowdemo.service.WorkAccessibilityService +import java.util.* + +/** + * @功能: 工具类 + * @User Lmy + * @Creat 4/16/21 8:33 AM + * @Compony 永远相信美好的事情即将发生 + */ +object Utils { + const val REQUEST_FLOAT_CODE=1001 + /** + * 跳转到设置页面申请打开无障碍辅助功能 + */ + private fun accessibilityToSettingPage(context: Context) { + //开启辅助功能页面 + try { + val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(intent) + } catch (e: Exception) { + val intent = Intent(Settings.ACTION_SETTINGS) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(intent) + e.printStackTrace() + } + } + + /** + * 判断Service是否开启 + * + */ + fun isServiceRunning(context: Context, ServiceName: String): Boolean { + if (TextUtils.isEmpty(ServiceName)) { + return false + } + val myManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + val runningService = + myManager.getRunningServices(1000) as ArrayList + for (i in runningService.indices) { + if (runningService[i].service.className == ServiceName) { + return true + } + } + return false + } + + /** + * 判断悬浮窗权限权限 + */ + private fun commonROMPermissionCheck(context: Context?): Boolean { + var result = true + if (Build.VERSION.SDK_INT >= 23) { + try { + val clazz: Class<*> = Settings::class.java + val canDrawOverlays = + clazz.getDeclaredMethod("canDrawOverlays", Context::class.java) + result = canDrawOverlays.invoke(null, context) as Boolean + } catch (e: Exception) { + Log.e("ServiceUtils", Log.getStackTraceString(e)) + } + } + return result + } + + /** + * 检查悬浮窗权限是否开启 + */ + fun checkSuspendedWindowPermission(context: Activity, block: () -> Unit) { + if (commonROMPermissionCheck(context)) { + block() + } else { + Toast.makeText(context, "请开启悬浮窗权限", Toast.LENGTH_SHORT).show() + context.startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply { + data = Uri.parse("package:${context.packageName}") + }, REQUEST_FLOAT_CODE) + } + } + + /** + * 检查无障碍服务权限是否开启 + */ + fun checkAccessibilityPermission(context: Activity, block: () -> Unit) { + if (isServiceRunning(context, WorkAccessibilityService::class.java.canonicalName)) { + block() + } else { + accessibilityToSettingPage(context) + } + } + + fun isNull(any: Any?): Boolean = any == null + +} diff --git a/app/src/main/java/com/ray650128/floatwindowdemo/utils/ViewModleMain.kt b/app/src/main/java/com/ray650128/floatwindowdemo/utils/ViewModleMain.kt new file mode 100644 index 0000000..f0162e8 --- /dev/null +++ b/app/src/main/java/com/ray650128/floatwindowdemo/utils/ViewModleMain.kt @@ -0,0 +1,23 @@ +package com.ray650128.floatwindowdemo.utils + +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +/** + * @功能: 用于和Service通信 + * @User Lmy + * @Creat 4/16/21 8:37 AM + * @Compony 永远相信美好的事情即将发生 + */ +object ViewModleMain : ViewModel() { + //悬浮窗口创建 移除 基于无障碍服务 + var isShowWindow = MutableLiveData() + //悬浮窗口创建 移除 + + var isShowSuspendWindow = MutableLiveData() + + //悬浮窗口显示 隐藏 + var isVisible = MutableLiveData() + +} diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_logo.jpg b/app/src/main/res/drawable/icon_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9f01ebe594c2ebd66f68de61565f83a851b7e6c6 GIT binary patch literal 28625 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 + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..4dcae42 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,43 @@ + + +