Compare commits

..

17 Commits

19 changed files with 635 additions and 169 deletions
+8 -7
View File
@@ -5,14 +5,14 @@ plugins {
android {
namespace = "com.ray650128.floatingwindow"
compileSdk = 33
compileSdk = 34
defaultConfig {
applicationId = "com.ray650128.floatingwindow"
minSdk = 26
targetSdk = 33
versionCode = 2
versionName = "1.0.1"
minSdk = 24
targetSdk = 34
versionCode = 4
versionName = "1.0.4"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -40,13 +40,14 @@ android {
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.9.0")
implementation("com.google.android.material:material:1.11.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")
// EasyWindow
implementation("com.github.getActivity:EasyWindow:10.2")
}
+8 -2
View File
@@ -5,6 +5,12 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.TYPE_APPLICATION_OVERLAY" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<application
android:name=".MyApp"
android:allowBackup="true"
@@ -17,11 +23,11 @@
android:theme="@style/Theme.EasyWindowTest"
tools:targetApi="31">
<activity
android:name=".SettingActivity"
android:name=".ui.SettingActivity"
android:exported="false"
android:screenOrientation="landscape" />
<activity
android:name=".MainActivity"
android:name=".ui.MainActivity"
android:exported="true"
android:screenOrientation="landscape">
<intent-filter>
@@ -0,0 +1,9 @@
package com.ray650128.floatingwindow
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()
@@ -1,91 +0,0 @@
package com.ray650128.floatingwindow
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import androidx.core.widget.addTextChangedListener
import com.ray650128.floatingwindow.databinding.ActivitySettingBinding
class SettingActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingBinding
private var xOffset = 0
private var yOffset = 0
private var windowGravity = FloatingWindowHelper.GravityType.TOP_LEFT
private var windowIcon = FloatingWindowHelper.IconType.CHICKEN
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySettingBinding.inflate(layoutInflater)
setContentView(binding.root)
xOffset = PreferenceUtil.xOffset
yOffset = PreferenceUtil.yOffset
windowGravity = FloatingWindowHelper.GravityType.fromInt(PreferenceUtil.gravity)
windowIcon = FloatingWindowHelper.IconType.fromInt(PreferenceUtil.icon)
binding.apply {
spGravity.apply {
setSelection(PreferenceUtil.gravity)
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
windowGravity = FloatingWindowHelper.GravityType.fromInt(position)
FloatingWindowHelper.setGravity(windowGravity)
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
spIcon.apply {
setSelection(FloatingWindowHelper.IconType.getIndex(PreferenceUtil.icon) )
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
windowIcon = FloatingWindowHelper.IconType.fromIndex(position)
FloatingWindowHelper.setIcon(windowIcon)
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
etXOffset.apply {
setText("$xOffset")
addTextChangedListener {
if (it.isNullOrEmpty()) return@addTextChangedListener
xOffset = it.toString().toIntOrNull() ?: return@addTextChangedListener
FloatingWindowHelper.setXOffset(xOffset)
}
}
etYOffset.apply {
setText("$yOffset")
addTextChangedListener {
if (it.isNullOrEmpty()) return@addTextChangedListener
yOffset = it.toString().toIntOrNull() ?: return@addTextChangedListener
FloatingWindowHelper.setYOffset(yOffset)
}
}
btnOk.setOnClickListener {
PreferenceUtil.xOffset = xOffset
PreferenceUtil.yOffset = yOffset
PreferenceUtil.gravity = windowGravity.type
PreferenceUtil.icon = windowIcon.value
finish()
}
btnCancel.setOnClickListener {
FloatingWindowHelper.setXOffset(PreferenceUtil.xOffset)
FloatingWindowHelper.setYOffset(PreferenceUtil.yOffset)
FloatingWindowHelper.setGravity(FloatingWindowHelper.GravityType.fromInt(PreferenceUtil.gravity))
FloatingWindowHelper.setIcon(FloatingWindowHelper.IconType.fromInt(PreferenceUtil.icon))
finish()
}
}
}
}
@@ -1,4 +1,4 @@
package com.ray650128.floatingwindow
package com.ray650128.floatingwindow.ui
import android.app.Activity
import android.content.Context
@@ -7,7 +7,10 @@ import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.Settings
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import com.ray650128.floatingwindow.BuildConfig
import com.ray650128.floatingwindow.utils.FloatingWindowHelperUtils
import com.ray650128.floatingwindow.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
@@ -23,8 +26,8 @@ class MainActivity : AppCompatActivity() {
if (!Settings.canDrawOverlays(mContext)) {
binding.btnStep1.isEnabled = !Settings.canDrawOverlays(mContext)
binding.btnStep2.isEnabled = Settings.canDrawOverlays(mContext)
binding.btnStep3.isEnabled = FloatingWindowHelper.isShowing
binding.btnSetting.isEnabled = FloatingWindowHelper.isShowing
binding.btnStep3.isEnabled = FloatingWindowHelperUtils.isShowing
binding.btnSetting.isEnabled = FloatingWindowHelperUtils.isShowing
return@registerForActivityResult
}
showOverlayWindow()
@@ -33,6 +36,7 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -55,6 +59,8 @@ class MainActivity : AppCompatActivity() {
val intent = Intent(mContext, SettingActivity::class.java)
startActivity(intent)
}
textVersion.text = BuildConfig.VERSION_NAME
}
}
@@ -63,23 +69,23 @@ class MainActivity : AppCompatActivity() {
binding.apply {
btnStep1.isEnabled = !Settings.canDrawOverlays(mContext)
btnStep2.isEnabled = Settings.canDrawOverlays(mContext) && !FloatingWindowHelper.isShowing
btnStep3.isEnabled = FloatingWindowHelper.isShowing
btnSetting.isEnabled = FloatingWindowHelper.isShowing
btnStep2.isEnabled = Settings.canDrawOverlays(mContext) && !FloatingWindowHelperUtils.isShowing
btnStep3.isEnabled = FloatingWindowHelperUtils.isShowing
btnSetting.isEnabled = FloatingWindowHelperUtils.isShowing
}
}
private fun showOverlayWindow() {
FloatingWindowHelper.show()
binding.btnStep2.isEnabled = !FloatingWindowHelper.isShowing
binding.btnStep3.isEnabled = FloatingWindowHelper.isShowing
binding.btnSetting.isEnabled = FloatingWindowHelper.isShowing
FloatingWindowHelperUtils.show()
binding.btnStep2.isEnabled = !FloatingWindowHelperUtils.isShowing
binding.btnStep3.isEnabled = FloatingWindowHelperUtils.isShowing
binding.btnSetting.isEnabled = FloatingWindowHelperUtils.isShowing
}
private fun hideOverlayWindow() {
FloatingWindowHelper.hide()
binding.btnStep2.isEnabled = !FloatingWindowHelper.isShowing
binding.btnStep3.isEnabled = FloatingWindowHelper.isShowing
binding.btnSetting.isEnabled = FloatingWindowHelper.isShowing
FloatingWindowHelperUtils.hide()
binding.btnStep2.isEnabled = !FloatingWindowHelperUtils.isShowing
binding.btnStep3.isEnabled = FloatingWindowHelperUtils.isShowing
binding.btnSetting.isEnabled = FloatingWindowHelperUtils.isShowing
}
}
@@ -0,0 +1,265 @@
package com.ray650128.floatingwindow.ui
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.View
import android.webkit.MimeTypeMap
import android.widget.AdapterView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.ray650128.floatingwindow.databinding.ActivitySettingBinding
import com.ray650128.floatingwindow.dp
import com.ray650128.floatingwindow.utils.DensityUtil
import com.ray650128.floatingwindow.utils.FloatingWindowHelperUtils
import com.ray650128.floatingwindow.utils.PreferenceUtil
import com.ray650128.floatingwindow.view.ValueEditor
import java.io.File
class SettingActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingBinding
private var xOffset = 0
private var yOffset = 0
private var windowGravity = FloatingWindowHelperUtils.GravityType.TOP_LEFT
private var windowIcon = FloatingWindowHelperUtils.IconType.CHICKEN
private val requestPermission =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
permissions.entries.forEach {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
if (it.key == Manifest.permission.READ_EXTERNAL_STORAGE && it.value) {
pickImage()
}
} else {
if (it.key == Manifest.permission.READ_MEDIA_IMAGES && it.value) {
pickImage()
}
}
Log.e("DEBUG", "${it.key} = ${it.value}")
}
}
private val galleryLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data = result.data
val returnUri = data?.data ?: return@registerForActivityResult
imageUri = saveImageToCache(returnUri)
Log.d(TAG, "imageUri=$imageUri")
val bitmap = BitmapFactory.decodeFile(imageUri)
binding.imgPreview.setImageBitmap(bitmap)
FloatingWindowHelperUtils.setIcon(windowIcon, imageUri)
//workingCheckItemAdapter.setImageUri(currentPosition, currentUri)
//checkPassButton()
}
}
private var imageUri: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
binding = ActivitySettingBinding.inflate(layoutInflater)
setContentView(binding.root)
xOffset = PreferenceUtil.xOffset
yOffset = PreferenceUtil.yOffset
windowGravity = FloatingWindowHelperUtils.GravityType.fromInt(PreferenceUtil.gravity)
windowIcon = FloatingWindowHelperUtils.IconType.fromInt(PreferenceUtil.icon)
if (!PreferenceUtil.iconPath.isNullOrEmpty()) {
imageUri = PreferenceUtil.iconPath
}
binding.apply {
spGravity.apply {
setSelection(PreferenceUtil.gravity)
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
windowGravity = FloatingWindowHelperUtils.GravityType.fromInt(position)
FloatingWindowHelperUtils.setGravity(windowGravity)
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
spIcon.apply {
setSelection(FloatingWindowHelperUtils.IconType.getIndex(PreferenceUtil.icon))
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
windowIcon = FloatingWindowHelperUtils.IconType.fromIndex(position)
if (imageUri != null) {
FloatingWindowHelperUtils.setIcon(windowIcon, imageUri)
}
if (windowIcon == FloatingWindowHelperUtils.IconType.CUSTOM) {
imgPreview.isVisible = true
btnPickPhoto.isVisible = true
} else {
imgPreview.isVisible = false
btnPickPhoto.isVisible = false
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
edXOffset.apply {
value = xOffset
setOnValueChangeListener(object : ValueEditor.OnValueChangeListener {
override fun onValueAdd() {
if (xOffset in 0 .. (DensityUtil.getScreenWidth() - 52.dp)) {
xOffset += 1
}
value = xOffset
FloatingWindowHelperUtils.setXOffset(xOffset)
}
override fun onValueMinus() {
if (xOffset in 1 .. (DensityUtil.getScreenWidth() - 52.dp)) {
xOffset -= 1
}
value = xOffset
FloatingWindowHelperUtils.setXOffset(xOffset)
}
override fun onValueClick(result: Int?) {
if (result == null) return
if (xOffset in 0 .. (DensityUtil.getScreenWidth() - 52.dp)) {
xOffset = result
value = result
}
FloatingWindowHelperUtils.setXOffset(xOffset)
}
})
}
edYOffset.apply {
value = yOffset
setOnValueChangeListener(object : ValueEditor.OnValueChangeListener {
override fun onValueAdd() {
if (yOffset in 0 .. (DensityUtil.getScreenHeight() - 36.dp)) {
yOffset += 1
}
value = yOffset
FloatingWindowHelperUtils.setYOffset(yOffset)
}
override fun onValueMinus() {
if (yOffset in 1 .. (DensityUtil.getScreenHeight() - 36.dp)) {
yOffset -= 1
}
value = yOffset
FloatingWindowHelperUtils.setYOffset(yOffset)
}
override fun onValueClick(result: Int?) {
if (result == null) return
if (yOffset in 0 .. (DensityUtil.getScreenHeight() - 36.dp)) {
yOffset = result
value = result
}
FloatingWindowHelperUtils.setYOffset(yOffset)
}
})
}
/*etXOffset.apply {
setText("$xOffset")
addTextChangedListener {
if (it.isNullOrEmpty()) return@addTextChangedListener
xOffset = it.toString().toIntOrNull() ?: return@addTextChangedListener
FloatingWindowHelperUtils.setXOffset(xOffset)
}
}
etYOffset.apply {
setText("$yOffset")
addTextChangedListener {
if (it.isNullOrEmpty()) return@addTextChangedListener
yOffset = it.toString().toIntOrNull() ?: return@addTextChangedListener
FloatingWindowHelperUtils.setYOffset(yOffset)
}
}*/
if (imageUri != null) {
val bitmap = BitmapFactory.decodeFile(imageUri)
imgPreview.setImageBitmap(bitmap)
}
btnPickPhoto.setOnClickListener {
requestPermission.launch(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.READ_MEDIA_IMAGES
)
)
}
btnOk.setOnClickListener {
PreferenceUtil.xOffset = xOffset
PreferenceUtil.yOffset = yOffset
PreferenceUtil.gravity = windowGravity.type
PreferenceUtil.icon = windowIcon.value
PreferenceUtil.iconPath = imageUri
finish()
}
btnCancel.setOnClickListener {
FloatingWindowHelperUtils.setXOffset(PreferenceUtil.xOffset)
FloatingWindowHelperUtils.setYOffset(PreferenceUtil.yOffset)
FloatingWindowHelperUtils.setGravity(
FloatingWindowHelperUtils.GravityType.fromInt(
PreferenceUtil.gravity
)
)
FloatingWindowHelperUtils.setIcon(
FloatingWindowHelperUtils.IconType.fromInt(PreferenceUtil.icon),
PreferenceUtil.iconPath
)
finish()
}
}
}
private fun pickImage() {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
galleryLauncher.launch(intent)
}
private fun saveImageToCache(uri: Uri?): String? {
if (uri == null) return null
val inputStream = contentResolver.openInputStream(uri)
inputStream?.use { input ->
val extension = getFileExtension(uri.toString())
val outputFile = File(cacheDir, "custom.$extension")
outputFile.outputStream().use { output ->
input.copyTo(output)
}
return outputFile.absolutePath
}
return null
}
private fun getFileExtension(uri: String): String {
return MimeTypeMap.getSingleton().getExtensionFromMimeType(contentResolver.getType(Uri.parse(uri)))
?: "jpg" // Default extension
}
companion object {
private val TAG = SettingActivity::class.java.simpleName
}
}
@@ -0,0 +1,77 @@
package com.ray650128.floatingwindow.utils
import android.content.Context
import android.os.Build
import android.util.DisplayMetrics
import android.view.WindowManager
import com.ray650128.floatingwindow.MyApp
/**
* 畫面尺寸相關工具類別。
*/
object DensityUtil {
/**
* 取得螢幕寬度
* @return 螢幕寬度(px)
*/
fun getScreenWidth(): Int {
val windowManager = MyApp.appContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowMetrics = windowManager.currentWindowMetrics
windowMetrics.bounds.width()
} else {
val metric = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(metric)
metric.widthPixels
}
}
/**
* 取得螢幕高度
* @return 螢幕高度(px)
*/
fun getScreenHeight(): Int {
val windowManager = MyApp.appContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowMetrics = windowManager.currentWindowMetrics
windowMetrics.bounds.height()
} else {
val metric = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(metric)
metric.heightPixels
}
}
/**
* 取得狀態列高度
* @return 狀態列高度(px)
*/
fun getStatusBarHeight(): Int {
var result = 0
val resourceId: Int = MyApp.appContext.resources.getIdentifier(
"status_bar_height",
"dimen",
"android"
)
if (resourceId > 0) {
result = MyApp.appContext.resources.getDimensionPixelSize(resourceId)
}
return result
}
/**
* 取得導航列高度
* @return 導航列高度(px)
*/
fun getNavigationBarHeight(): Int {
val resources = MyApp.appContext.resources
val resourceId: Int = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return if (resourceId > 0) {
resources.getDimensionPixelSize(resourceId)
} else 0
}
}
@@ -1,11 +1,19 @@
package com.ray650128.floatingwindow
package com.ray650128.floatingwindow.utils
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.provider.MediaStore
import android.view.Gravity
import android.view.MotionEvent
import android.view.WindowManager
import com.hjq.window.EasyWindow
import com.ray650128.floatingwindow.MyApp
import com.ray650128.floatingwindow.R
import java.io.File
object FloatingWindowHelper {
object FloatingWindowHelperUtils {
private var _isShowing: Boolean = false
val isShowing: Boolean
@@ -28,7 +36,25 @@ object FloatingWindowHelper {
})
setXOffset(xOffset)
setYOffset(yOffset)
setImageDrawable(android.R.id.icon, windowIcon.value)
if (windowIcon == IconType.CUSTOM) {
if (PreferenceUtil.iconPath.isNullOrEmpty()) {
setImageDrawable(android.R.id.icon, windowIcon.value)
} else {
val iconPath = PreferenceUtil.iconPath
if (iconPath?.contains("/data/user/0/com.ray650128.floatingwindow/cache/") == true) {
val bitmap = BitmapFactory.decodeFile(iconPath)
val drawable = BitmapDrawable(MyApp.appContext.resources, bitmap)
setImageDrawable(android.R.id.icon, drawable)
} else {
val uri = Uri.parse(PreferenceUtil.iconPath)
val bitmap = BitmapFactory.decodeStream(MyApp.appContext.contentResolver.openInputStream(uri))
val drawable = BitmapDrawable(MyApp.appContext.resources, bitmap)
setImageDrawable(android.R.id.icon, drawable)
}
}
} else {
setImageDrawable(android.R.id.icon, windowIcon.value)
}
setOnTouchListener { window, view, event ->
var touch = false
when (event.action) {
@@ -97,8 +123,33 @@ object FloatingWindowHelper {
window.update()
}
fun setIcon(value: IconType) {
window.setImageDrawable(android.R.id.icon, value.value)
fun setIcon(value: IconType, inputUri: Uri? = null) {
if (value == IconType.CUSTOM) {
if (inputUri == null) {
window.setImageDrawable(android.R.id.icon, value.value)
} else {
val bitmap = BitmapFactory.decodeStream(MyApp.appContext.contentResolver.openInputStream(inputUri))
val drawable = BitmapDrawable(MyApp.appContext.resources, bitmap)
window.setImageDrawable(android.R.id.icon, drawable)
}
} else {
window.setImageDrawable(android.R.id.icon, value.value)
}
window.update()
}
fun setIcon(value: IconType, inputUri: String? = null) {
if (value == IconType.CUSTOM) {
if (inputUri == null) {
window.setImageDrawable(android.R.id.icon, value.value)
} else {
val bitmap = BitmapFactory.decodeFile(inputUri)
val drawable = BitmapDrawable(MyApp.appContext.resources, bitmap)
window.setImageDrawable(android.R.id.icon, drawable)
}
} else {
window.setImageDrawable(android.R.id.icon, value.value)
}
window.update()
}
@@ -124,7 +175,8 @@ object FloatingWindowHelper {
GURA(R.drawable.ic_gura, 7),
RUSHIA2(R.drawable.ic_uruha_rushia2, 8),
RUSHIA3(R.drawable.ic_uruha_rushia3, 9),
HOSHIMACHI_SUISEI(R.drawable.ic_hoshimachii_suisei, 10);
HOSHIMACHI_SUISEI(R.drawable.ic_hoshimachii_suisei, 10),
CUSTOM(R.mipmap.ic_launcher, 11);
companion object {
fun fromInt(value: Int) = IconType.values().first { it.value == value }
@@ -1,6 +1,8 @@
package com.ray650128.floatingwindow
package com.ray650128.floatingwindow.utils
import android.content.Context
import com.ray650128.floatingwindow.MyApp
import com.ray650128.floatingwindow.R
/**
* Shared Preferences 工具類別
@@ -12,6 +14,7 @@ object PreferenceUtil {
private const val Y_AXIS_OFFSET = "Y_AXIS_OFFSET"
private const val GRAVITY = "GRAVITY"
private const val ICON = "ICON"
private const val ICON_PATH = "ICON_PATH"
private val sharedPreferences = MyApp.appContext.getSharedPreferences(MAIN_KEY, Context.MODE_PRIVATE)
@@ -30,4 +33,8 @@ object PreferenceUtil {
var icon: Int
get() = sharedPreferences.getInt(ICON, R.drawable.ic_chicken)
set(value) = sharedPreferences.edit().putInt(ICON, value).apply()
var iconPath: String?
get() = sharedPreferences.getString(ICON_PATH, "")
set(value) = sharedPreferences.edit().putString(ICON_PATH, value).apply()
}
@@ -0,0 +1,113 @@
package com.ray650128.floatingwindow.view
import android.content.Context
import android.graphics.Color
import android.text.InputType
import android.util.AttributeSet
import android.widget.Button
import android.widget.EditText
import android.widget.ImageButton
import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
import com.ray650128.floatingwindow.R
import com.ray650128.floatingwindow.dp
class ValueEditor: LinearLayout {
constructor(context: Context): super(context) {
this.context = context
initContentView()
}
constructor(context: Context, attrs: AttributeSet): super(context, attrs) {
this.context = context
initContentView()
}
constructor(context: Context, attrs: AttributeSet, intRes: Int): super(context, attrs, intRes) {
this.context = context
initContentView()
}
private lateinit var context: Context
private var onValueChangeListener: OnValueChangeListener? = null
private val btnAdd by lazy {
ImageButton(context).apply {
setImageResource(R.drawable.ic_value_add)
setBackgroundColor(Color.TRANSPARENT)
layoutParams = LayoutParams(48.dp, 48.dp)
}
}
private val btnValue by lazy {
Button(context).apply {
text = "$value"
setBackgroundColor(Color.TRANSPARENT)
layoutParams = LayoutParams(0, 48.dp).apply {
weight = 1.0f
}
}
}
private val btnMinus by lazy {
ImageButton(context).apply {
setImageResource(R.drawable.ic_value_minus)
setBackgroundColor(Color.TRANSPARENT)
layoutParams = LayoutParams(48.dp, 48.dp)
}
}
var value: Int = 0
set(value) {
field = value
btnValue.text = "$value"
}
private fun initContentView() {
this.orientation = HORIZONTAL
this.addView(btnAdd)
this.addView(btnValue)
this.addView(btnMinus)
btnAdd.setOnClickListener {
onValueChangeListener?.onValueAdd()
}
btnValue.setOnClickListener {
AlertDialog.Builder(context).apply {
val editText = EditText(context).apply {
inputType = InputType.TYPE_CLASS_NUMBER
}
setTitle("請輸入想要的值")
setView(editText)
setPositiveButton("確定") { dialog, _ ->
val inputText = editText.text?.toString()?.toIntOrNull()
onValueChangeListener?.onValueClick(inputText)
dialog.dismiss()
}
setNegativeButton("取消") { dialog, _ ->
dialog.dismiss()
}
}.show()
}
btnMinus.setOnClickListener {
onValueChangeListener?.onValueMinus()
}
}
fun setOnValueChangeListener(listener: OnValueChangeListener) {
this.onValueChangeListener = listener
}
interface OnValueChangeListener {
fun onValueAdd()
fun onValueMinus()
fun onValueClick(value: Int?)
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,13H5v-2h14v2z"/>
</vector>
+18 -10
View File
@@ -4,16 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.125"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/bg_winnie_the_pooh" />
tools:context=".ui.MainActivity">
<TextView
android:id="@+id/textView"
@@ -81,4 +72,21 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/textView8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="by 楓小夜"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textVersion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
+35 -34
View File
@@ -4,16 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SettingActivity">
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.125"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/bg_winnie_the_pooh" />
tools:context=".ui.SettingActivity">
<TextView
android:id="@+id/textView6"
@@ -45,22 +36,17 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="水平偏移像素"
app:layout_constraintBottom_toBottomOf="@+id/etXOffset"
app:layout_constraintEnd_toStartOf="@+id/etXOffset"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintBottom_toBottomOf="@+id/edXOffset"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/etXOffset" />
app:layout_constraintTop_toTopOf="@+id/edXOffset" />
<EditText
android:id="@+id/etXOffset"
<com.ray650128.floatingwindow.view.ValueEditor
android:id="@+id/edXOffset"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:ems="10"
android:inputType="number"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView4"
app:layout_constraintTop_toBottomOf="@+id/spGravity" />
@@ -69,25 +55,22 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="14dp"
android:layout_marginBottom="13dp"
android:text="垂直偏移像素"
app:layout_constraintBottom_toBottomOf="@+id/etYOffset"
app:layout_constraintEnd_toStartOf="@+id/etYOffset"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintBottom_toBottomOf="@+id/edYOffset"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/etYOffset" />
app:layout_constraintTop_toTopOf="@+id/edYOffset" />
<EditText
android:id="@+id/etYOffset"
<com.ray650128.floatingwindow.view.ValueEditor
android:id="@+id/edYOffset"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:ems="10"
android:inputType="number"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView5"
app:layout_constraintTop_toBottomOf="@+id/etXOffset" />
app:layout_constraintTop_toBottomOf="@+id/edXOffset" />
<Button
android:id="@+id/btnOk"
@@ -99,8 +82,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btnCancel"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/etYOffset" />
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/btnCancel"
@@ -112,8 +94,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/btnOk"
app:layout_constraintTop_toBottomOf="@+id/etYOffset" />
app:layout_constraintStart_toEndOf="@+id/btnOk" />
<TextView
android:id="@+id/textView7"
@@ -137,5 +118,25 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView7"
app:layout_constraintTop_toBottomOf="@+id/etYOffset" />
app:layout_constraintTop_toBottomOf="@+id/edYOffset" />
<ImageView
android:id="@+id/imgPreview"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/spIcon"
app:srcCompat="@mipmap/ic_launcher" />
<Button
android:id="@+id/btnPickPhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="選擇圖片"
app:layout_constraintBottom_toBottomOf="@+id/imgPreview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/imgPreview" />
</androidx.constraintlayout.widget.ConstraintLayout>
+2 -1
View File
@@ -1,5 +1,5 @@
<resources>
<string name="app_name">懸浮視窗 by 楓小夜</string>
<string name="app_name">卡幀神器</string>
<array name="item_edge_position">
<item>左上角</item>
@@ -20,5 +20,6 @@
<item>露西亞2</item>
<item>露西亞3</item>
<item>星街彗星</item>
<item>自訂圖片</item>
</array>
</resources>
+1 -1
View File
@@ -1,5 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.1.0-rc01" apply false
id("com.android.application") version "8.3.1" apply false
id("org.jetbrains.kotlin.android") version "1.8.0" apply false
}
+1
View File
@@ -21,3 +21,4 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true
+1 -1
View File
@@ -1,6 +1,6 @@
#Mon Jul 03 09:29:45 CST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists