commit 49fbe33f8de86d21092927dd40e867e6563cce4f Author: Raymond Yang Date: Mon Jul 3 14:39:30 2023 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a7057f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,137 @@ +*.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 +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Example user template template +### Example user template + +# IntelliJ project files +.idea +*.iml +out +gen +### Android template +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Log/OS Files +*.log + +# Android Studio generated files and folders +captures/ +.externalNativeBuild/ +.cxx/ +*.apk +output.json + +# IntelliJ +*.iml +.idea/ +misc.xml +deploymentTargetDropDown.xml +render.experimental.xml + +# Keystore files +*.jks +*.keystore + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Android Profiling +*.hprof + 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..8b6b21b --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,52 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "com.ray650128.easywindowtest" + compileSdk = 33 + + defaultConfig { + applicationId = "com.ray650128.easywindowtest" + 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("com.github.getActivity:EasyWindow:10.2") +} \ 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/easywindowtest/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/ray650128/easywindowtest/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..62e1766 --- /dev/null +++ b/app/src/androidTest/java/com/ray650128/easywindowtest/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.ray650128.easywindowtest + +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.easywindowtest", 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..d77cd08 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/ray650128/easywindowtest/MainActivity.kt b/app/src/main/java/com/ray650128/easywindowtest/MainActivity.kt new file mode 100644 index 0000000..d0ff28b --- /dev/null +++ b/app/src/main/java/com/ray650128/easywindowtest/MainActivity.kt @@ -0,0 +1,111 @@ +package com.ray650128.easywindowtest + +import android.app.Activity +import android.content.Intent +import android.net.Uri +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.provider.Settings +import android.util.Log +import android.view.Gravity +import android.view.MotionEvent +import android.view.View +import android.view.WindowManager +import android.widget.TextView +import androidx.activity.result.contract.ActivityResultContracts +import com.hjq.window.EasyWindow +import com.ray650128.easywindowtest.databinding.ActivityMainBinding + +class MainActivity : AppCompatActivity() { + + private lateinit var binding: ActivityMainBinding + + private val floatingWindowPermission = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == Activity.RESULT_CANCELED) { + if (!Settings.canDrawOverlays(this)) { + binding.btnStep1.isEnabled = !Settings.canDrawOverlays(this) + binding.btnStep2.isEnabled = Settings.canDrawOverlays(this) + binding.btnStep3.isEnabled = isFloatWindowShowing + return@registerForActivityResult + } + showOverlayWindow() + } + } + + private var isFloatWindowShowing = false + + private val window by lazy { + EasyWindow>(application).apply { + setContentView(R.layout.window_hint) + setGravity(Gravity.START or Gravity.TOP) + setYOffset(100) + setImageDrawable(android.R.id.icon, R.drawable.ic_chicken) + setOnTouchListener { window, view, event -> + var touch = false + when (event.action) { + MotionEvent.ACTION_DOWN -> { + window.setWindowFlags( + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or + WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + ) + window.update() + touch = true + } + + MotionEvent.ACTION_OUTSIDE -> { + window.setWindowFlags( + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or + WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + ) + window.update() + touch = false + } + + else -> {} + } + touch + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.btnStep1.isEnabled = !Settings.canDrawOverlays(this) + binding.btnStep1.setOnClickListener { + val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) + intent.data = Uri.parse("package:$packageName") + floatingWindowPermission.launch(intent) + } + + binding.btnStep2.isEnabled = Settings.canDrawOverlays(this) + binding.btnStep2.setOnClickListener { + showOverlayWindow() + } + + binding.btnStep3.isEnabled = isFloatWindowShowing + binding.btnStep3.setOnClickListener { + hideOverlayWindow() + } + } + + private fun showOverlayWindow() { + isFloatWindowShowing = true + binding.btnStep2.isEnabled = !isFloatWindowShowing + binding.btnStep3.isEnabled = isFloatWindowShowing + window.show() + } + + private fun hideOverlayWindow() { + isFloatWindowShowing = false + binding.btnStep2.isEnabled = !isFloatWindowShowing + binding.btnStep3.isEnabled = isFloatWindowShowing + window.cancel() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ray650128/easywindowtest/MovingDraggable.kt b/app/src/main/java/com/ray650128/easywindowtest/MovingDraggable.kt new file mode 100644 index 0000000..be123e6 --- /dev/null +++ b/app/src/main/java/com/ray650128/easywindowtest/MovingDraggable.kt @@ -0,0 +1,68 @@ +package com.ray650128.easywindowtest + +import android.annotation.SuppressLint +import android.view.MotionEvent +import android.view.View +import android.view.WindowManager +import com.hjq.window.EasyWindow +import com.hjq.window.draggable.BaseDraggable + +/** + * author : Raymond Yang + * time : 2023/07/03 + * desc : 移動拖曳處理實現類別 + */ +class MovingDraggable(private val window: EasyWindow<*>) : BaseDraggable() { + /** 手指按下的座標 */ + private var mViewDownX = 0f + private var mViewDownY = 0f + + /** 觸控移動標記 */ + private var mMoveTouch = false + @SuppressLint("ClickableViewAccessibility") + override fun onTouch(v: View, event: MotionEvent): Boolean { + when (event.action) { + MotionEvent.ACTION_DOWN -> { + window.setWindowFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or + WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) + window.update() + // 記錄按下的位置(相對 View 的座標) + mViewDownX = event.x + mViewDownY = event.y + mMoveTouch = false + } + + MotionEvent.ACTION_MOVE -> { + // 記錄移動的位置(相對螢幕的座標) + val rawMoveX = event.rawX - windowInvisibleWidth + val rawMoveY = event.rawY - windowInvisibleHeight + var newX = rawMoveX - mViewDownX + var newY = rawMoveY - mViewDownY + if (newX < 0) { + newX = 0f + } + if (newY < 0) { + newY = 0f + } + + // 更新移動的位置 + updateLocation(newX, newY) + if (!mMoveTouch && isFingerMove(mViewDownX, event.x, mViewDownY, event.y)) { + // 如果使用者移動了手指,那麼就攔截本次觸控事件,從而不讓點選事件生效 + mMoveTouch = true + } + } + + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> return mMoveTouch + MotionEvent.ACTION_OUTSIDE -> { + window.setWindowFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or + WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) + window.update() + } + else -> {} + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_window.xml b/app/src/main/res/drawable/bg_window.xml new file mode 100644 index 0000000..767fb89 --- /dev/null +++ b/app/src/main/res/drawable/bg_window.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_chicken.png b/app/src/main/res/drawable/ic_chicken.png new file mode 100644 index 0000000..0fdd5fd Binary files /dev/null and b/app/src/main/res/drawable/ic_chicken.png differ 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/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..6df2c5d --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,39 @@ + + + +