Compare commits

...

11 Commits

16 changed files with 568 additions and 144 deletions

View File

@ -9,10 +9,10 @@ android {
defaultConfig { defaultConfig {
applicationId = "com.ray650128.floatingwindow" applicationId = "com.ray650128.floatingwindow"
minSdk = 26 minSdk = 24
targetSdk = 33 targetSdk = 33
versionCode = 3 versionCode = 4
versionName = "1.0.2" versionName = "1.0.3-beta"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@ -48,5 +48,10 @@ dependencies {
androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
// EasyWindow
implementation("com.github.getActivity:EasyWindow:10.2") implementation("com.github.getActivity:EasyWindow:10.2")
// Glide
implementation("com.github.bumptech.glide:glide:4.11.0")
} }

View File

@ -5,6 +5,12 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.TYPE_APPLICATION_OVERLAY" /> <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 <application
android:name=".MyApp" android:name=".MyApp"
android:allowBackup="true" android:allowBackup="true"
@ -17,11 +23,11 @@
android:theme="@style/Theme.EasyWindowTest" android:theme="@style/Theme.EasyWindowTest"
tools:targetApi="31"> tools:targetApi="31">
<activity <activity
android:name=".SettingActivity" android:name=".ui.SettingActivity"
android:exported="false" android:exported="false"
android:screenOrientation="landscape" /> android:screenOrientation="landscape" />
<activity <activity
android:name=".MainActivity" android:name=".ui.MainActivity"
android:exported="true" android:exported="true"
android:screenOrientation="landscape"> android:screenOrientation="landscape">
<intent-filter> <intent-filter>

View File

@ -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()

View File

@ -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()
}
}
}
}

View File

@ -1,4 +1,4 @@
package com.ray650128.floatingwindow package com.ray650128.floatingwindow.ui
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
@ -7,7 +7,10 @@ import android.net.Uri
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import com.ray650128.floatingwindow.BuildConfig
import com.ray650128.floatingwindow.utils.FloatingWindowHelperUtils
import com.ray650128.floatingwindow.databinding.ActivityMainBinding import com.ray650128.floatingwindow.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -23,8 +26,8 @@ class MainActivity : AppCompatActivity() {
if (!Settings.canDrawOverlays(mContext)) { if (!Settings.canDrawOverlays(mContext)) {
binding.btnStep1.isEnabled = !Settings.canDrawOverlays(mContext) binding.btnStep1.isEnabled = !Settings.canDrawOverlays(mContext)
binding.btnStep2.isEnabled = Settings.canDrawOverlays(mContext) binding.btnStep2.isEnabled = Settings.canDrawOverlays(mContext)
binding.btnStep3.isEnabled = FloatingWindowHelper.isShowing binding.btnStep3.isEnabled = FloatingWindowHelperUtils.isShowing
binding.btnSetting.isEnabled = FloatingWindowHelper.isShowing binding.btnSetting.isEnabled = FloatingWindowHelperUtils.isShowing
return@registerForActivityResult return@registerForActivityResult
} }
showOverlayWindow() showOverlayWindow()
@ -33,6 +36,7 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
@ -55,6 +59,8 @@ class MainActivity : AppCompatActivity() {
val intent = Intent(mContext, SettingActivity::class.java) val intent = Intent(mContext, SettingActivity::class.java)
startActivity(intent) startActivity(intent)
} }
textVersion.text = BuildConfig.VERSION_NAME
} }
} }
@ -63,23 +69,23 @@ class MainActivity : AppCompatActivity() {
binding.apply { binding.apply {
btnStep1.isEnabled = !Settings.canDrawOverlays(mContext) btnStep1.isEnabled = !Settings.canDrawOverlays(mContext)
btnStep2.isEnabled = Settings.canDrawOverlays(mContext) && !FloatingWindowHelper.isShowing btnStep2.isEnabled = Settings.canDrawOverlays(mContext) && !FloatingWindowHelperUtils.isShowing
btnStep3.isEnabled = FloatingWindowHelper.isShowing btnStep3.isEnabled = FloatingWindowHelperUtils.isShowing
btnSetting.isEnabled = FloatingWindowHelper.isShowing btnSetting.isEnabled = FloatingWindowHelperUtils.isShowing
} }
} }
private fun showOverlayWindow() { private fun showOverlayWindow() {
FloatingWindowHelper.show() FloatingWindowHelperUtils.show()
binding.btnStep2.isEnabled = !FloatingWindowHelper.isShowing binding.btnStep2.isEnabled = !FloatingWindowHelperUtils.isShowing
binding.btnStep3.isEnabled = FloatingWindowHelper.isShowing binding.btnStep3.isEnabled = FloatingWindowHelperUtils.isShowing
binding.btnSetting.isEnabled = FloatingWindowHelper.isShowing binding.btnSetting.isEnabled = FloatingWindowHelperUtils.isShowing
} }
private fun hideOverlayWindow() { private fun hideOverlayWindow() {
FloatingWindowHelper.hide() FloatingWindowHelperUtils.hide()
binding.btnStep2.isEnabled = !FloatingWindowHelper.isShowing binding.btnStep2.isEnabled = !FloatingWindowHelperUtils.isShowing
binding.btnStep3.isEnabled = FloatingWindowHelper.isShowing binding.btnStep3.isEnabled = FloatingWindowHelperUtils.isShowing
binding.btnSetting.isEnabled = FloatingWindowHelper.isShowing binding.btnSetting.isEnabled = FloatingWindowHelperUtils.isShowing
} }
} }

View File

@ -0,0 +1,233 @@
package com.ray650128.floatingwindow.ui
import android.Manifest
import android.app.Activity
import android.content.Intent
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.widget.AdapterView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.bumptech.glide.Glide
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
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
imageUri = data?.data
Glide.with(this).load(imageUri).into(binding.imgPreview)
FloatingWindowHelperUtils.setIcon(windowIcon, imageUri)
//workingCheckItemAdapter.setImageUri(currentPosition, currentUri)
//checkPassButton()
}
}
private var imageUri: Uri? = 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)
imageUri = if (PreferenceUtil.iconPath.isNullOrEmpty()) null else {
Uri.parse(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)
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) {
Glide.with(this@SettingActivity).load(imageUri).into(imgPreview)
}
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?.toString()
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),
Uri.parse(PreferenceUtil.iconPath)
)
finish()
}
}
}
private fun pickImage() {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
galleryLauncher.launch(intent)
}
}

View File

@ -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
}
}

View File

@ -1,11 +1,17 @@
package com.ray650128.floatingwindow package com.ray650128.floatingwindow.utils
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.provider.MediaStore
import android.view.Gravity import android.view.Gravity
import android.view.MotionEvent import android.view.MotionEvent
import android.view.WindowManager import android.view.WindowManager
import com.hjq.window.EasyWindow import com.hjq.window.EasyWindow
import com.ray650128.floatingwindow.MyApp
import com.ray650128.floatingwindow.R
object FloatingWindowHelper { object FloatingWindowHelperUtils {
private var _isShowing: Boolean = false private var _isShowing: Boolean = false
val isShowing: Boolean val isShowing: Boolean
@ -28,7 +34,18 @@ object FloatingWindowHelper {
}) })
setXOffset(xOffset) setXOffset(xOffset)
setYOffset(yOffset) 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 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 -> setOnTouchListener { window, view, event ->
var touch = false var touch = false
when (event.action) { when (event.action) {
@ -97,8 +114,18 @@ object FloatingWindowHelper {
window.update() window.update()
} }
fun setIcon(value: IconType) { fun setIcon(value: IconType, inputUri: Uri? = null) {
window.setImageDrawable(android.R.id.icon, value.value) 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() window.update()
} }
@ -124,7 +151,8 @@ object FloatingWindowHelper {
GURA(R.drawable.ic_gura, 7), GURA(R.drawable.ic_gura, 7),
RUSHIA2(R.drawable.ic_uruha_rushia2, 8), RUSHIA2(R.drawable.ic_uruha_rushia2, 8),
RUSHIA3(R.drawable.ic_uruha_rushia3, 9), 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 { companion object {
fun fromInt(value: Int) = IconType.values().first { it.value == value } fun fromInt(value: Int) = IconType.values().first { it.value == value }

View File

@ -1,6 +1,8 @@
package com.ray650128.floatingwindow package com.ray650128.floatingwindow.utils
import android.content.Context import android.content.Context
import com.ray650128.floatingwindow.MyApp
import com.ray650128.floatingwindow.R
/** /**
* Shared Preferences 工具類別 * Shared Preferences 工具類別
@ -12,6 +14,7 @@ object PreferenceUtil {
private const val Y_AXIS_OFFSET = "Y_AXIS_OFFSET" private const val Y_AXIS_OFFSET = "Y_AXIS_OFFSET"
private const val GRAVITY = "GRAVITY" private const val GRAVITY = "GRAVITY"
private const val ICON = "ICON" private const val ICON = "ICON"
private const val ICON_PATH = "ICON_PATH"
private val sharedPreferences = MyApp.appContext.getSharedPreferences(MAIN_KEY, Context.MODE_PRIVATE) private val sharedPreferences = MyApp.appContext.getSharedPreferences(MAIN_KEY, Context.MODE_PRIVATE)
@ -30,4 +33,8 @@ object PreferenceUtil {
var icon: Int var icon: Int
get() = sharedPreferences.getInt(ICON, R.drawable.ic_chicken) get() = sharedPreferences.getInt(ICON, R.drawable.ic_chicken)
set(value) = sharedPreferences.edit().putInt(ICON, value).apply() 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()
} }

View File

@ -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?)
}
}

View File

@ -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>

View File

@ -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>

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".MainActivity"> tools:context=".ui.MainActivity">
<ImageView <ImageView
android:id="@+id/imageView" android:id="@+id/imageView"
@ -89,4 +89,13 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="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> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".SettingActivity"> tools:context=".ui.SettingActivity">
<ImageView <ImageView
android:id="@+id/imageView2" android:id="@+id/imageView2"
@ -45,22 +45,17 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:text="水平偏移像素" android:text="水平偏移像素"
app:layout_constraintBottom_toBottomOf="@+id/etXOffset" app:layout_constraintBottom_toBottomOf="@+id/edXOffset"
app:layout_constraintEnd_toStartOf="@+id/etXOffset"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/etXOffset" /> app:layout_constraintTop_toTopOf="@+id/edXOffset" />
<EditText <com.ray650128.floatingwindow.view.ValueEditor
android:id="@+id/etXOffset" android:id="@+id/edXOffset"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:ems="10"
android:inputType="number"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView4" app:layout_constraintStart_toEndOf="@+id/textView4"
app:layout_constraintTop_toBottomOf="@+id/spGravity" /> app:layout_constraintTop_toBottomOf="@+id/spGravity" />
@ -69,25 +64,22 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="14dp"
android:layout_marginBottom="13dp"
android:text="垂直偏移像素" android:text="垂直偏移像素"
app:layout_constraintBottom_toBottomOf="@+id/etYOffset" app:layout_constraintBottom_toBottomOf="@+id/edYOffset"
app:layout_constraintEnd_toStartOf="@+id/etYOffset"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/etYOffset" /> app:layout_constraintTop_toTopOf="@+id/edYOffset" />
<EditText <com.ray650128.floatingwindow.view.ValueEditor
android:id="@+id/etYOffset" android:id="@+id/edYOffset"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:ems="10"
android:inputType="number"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView5" app:layout_constraintStart_toEndOf="@+id/textView5"
app:layout_constraintTop_toBottomOf="@+id/etXOffset" /> app:layout_constraintTop_toBottomOf="@+id/edXOffset" />
<Button <Button
android:id="@+id/btnOk" android:id="@+id/btnOk"
@ -99,8 +91,7 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btnCancel" app:layout_constraintEnd_toStartOf="@+id/btnCancel"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintTop_toBottomOf="@+id/etYOffset" />
<Button <Button
android:id="@+id/btnCancel" android:id="@+id/btnCancel"
@ -112,8 +103,7 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/btnOk" app:layout_constraintStart_toEndOf="@+id/btnOk" />
app:layout_constraintTop_toBottomOf="@+id/etYOffset" />
<TextView <TextView
android:id="@+id/textView7" android:id="@+id/textView7"
@ -137,5 +127,25 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView7" 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> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -20,5 +20,6 @@
<item>露西亞2</item> <item>露西亞2</item>
<item>露西亞3</item> <item>露西亞3</item>
<item>星街彗星</item> <item>星街彗星</item>
<item>自訂圖片</item>
</array> </array>
</resources> </resources>

View File

@ -20,4 +20,5 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the # Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true