LAFBackgroundImageActivity.kt raw
1 package io.github.domi04151309.alwayson.activities
2
3 import android.Manifest
4 import android.content.Intent
5 import android.content.pm.PackageManager
6 import android.graphics.Bitmap
7 import android.graphics.BitmapFactory
8 import android.os.Build
9 import android.os.Bundle
10 import android.provider.MediaStore
11 import android.util.Base64
12 import android.widget.ImageView
13 import android.widget.Toast
14 import androidx.activity.result.ActivityResult
15 import androidx.activity.result.ActivityResultCallback
16 import androidx.activity.result.ActivityResultLauncher
17 import androidx.activity.result.contract.ActivityResultContracts
18 import androidx.core.app.ActivityCompat
19 import androidx.core.content.edit
20 import androidx.core.graphics.scale
21 import androidx.recyclerview.widget.LinearLayoutManager
22 import androidx.recyclerview.widget.RecyclerView
23 import io.github.domi04151309.alwayson.R
24 import io.github.domi04151309.alwayson.actions.alwayson.AlwaysOn
25 import io.github.domi04151309.alwayson.adapters.LayoutListAdapter
26 import io.github.domi04151309.alwayson.helpers.P
27 import java.io.ByteArrayOutputStream
28 import java.io.InputStream
29 import java.lang.Integer.min
30
31 class LAFBackgroundImageActivity :
32 BaseActivity(),
33 ActivityResultCallback<ActivityResult>,
34 LayoutListAdapter.OnItemClickListener {
35 internal var value: String = P.BACKGROUND_IMAGE_DEFAULT
36 private lateinit var preview: ImageView
37 private lateinit var layoutList: RecyclerView
38 private lateinit var customImageResult: ActivityResultLauncher<Intent>
39
40 private val drawables =
41 arrayOf(
42 R.drawable.ic_close,
43 R.drawable.unsplash_daniel_olah_1,
44 R.drawable.unsplash_daniel_olah_2,
45 R.drawable.unsplash_daniel_olah_3,
46 R.drawable.unsplash_daniel_olah_4,
47 R.drawable.unsplash_daniel_olah_5,
48 R.drawable.unsplash_daniel_olah_6,
49 R.drawable.unsplash_daniel_olah_7,
50 R.drawable.unsplash_daniel_olah_8,
51 R.drawable.unsplash_filip_baotic_1,
52 R.drawable.unsplash_tyler_lastovich_1,
53 R.drawable.unsplash_tyler_lastovich_2,
54 R.drawable.unsplash_tyler_lastovich_3,
55 R.drawable.ic_color_draw_over_other_apps,
56 )
57
58 @Suppress("CyclomaticComplexMethod")
59 private fun positionToString(position: Int): String =
60 when (position) {
61 ITEM_NONE -> P.BACKGROUND_IMAGE_NONE
62 ITEM_DANIEL_OLAH_1 -> P.BACKGROUND_IMAGE_DANIEL_OLAH_1
63 ITEM_DANIEL_OLAH_2 -> P.BACKGROUND_IMAGE_DANIEL_OLAH_2
64 ITEM_DANIEL_OLAH_3 -> P.BACKGROUND_IMAGE_DANIEL_OLAH_3
65 ITEM_DANIEL_OLAH_4 -> P.BACKGROUND_IMAGE_DANIEL_OLAH_4
66 ITEM_DANIEL_OLAH_5 -> P.BACKGROUND_IMAGE_DANIEL_OLAH_5
67 ITEM_DANIEL_OLAH_6 -> P.BACKGROUND_IMAGE_DANIEL_OLAH_6
68 ITEM_DANIEL_OLAH_7 -> P.BACKGROUND_IMAGE_DANIEL_OLAH_7
69 ITEM_DANIEL_OLAH_8 -> P.BACKGROUND_IMAGE_DANIEL_OLAH_8
70 ITEM_FILIP_BAOTIC_1 -> P.BACKGROUND_IMAGE_FILIP_BAOTIC_1
71 ITEM_TYLER_LASTOVICH_1 -> P.BACKGROUND_IMAGE_TYLER_LASTOVICH_1
72 ITEM_TYLER_LASTOVICH_2 -> P.BACKGROUND_IMAGE_TYLER_LASTOVICH_2
73 ITEM_TYLER_LASTOVICH_3 -> P.BACKGROUND_IMAGE_TYLER_LASTOVICH_3
74 ITEM_CUSTOM -> P.BACKGROUND_IMAGE_CUSTOM
75 else -> P.BACKGROUND_IMAGE_DEFAULT
76 }
77
78 @Suppress("CyclomaticComplexMethod")
79 private fun stringToPosition(string: String): Int =
80 when (string) {
81 P.BACKGROUND_IMAGE_NONE -> ITEM_NONE
82 P.BACKGROUND_IMAGE_DANIEL_OLAH_1 -> ITEM_DANIEL_OLAH_1
83 P.BACKGROUND_IMAGE_DANIEL_OLAH_2 -> ITEM_DANIEL_OLAH_2
84 P.BACKGROUND_IMAGE_DANIEL_OLAH_3 -> ITEM_DANIEL_OLAH_3
85 P.BACKGROUND_IMAGE_DANIEL_OLAH_4 -> ITEM_DANIEL_OLAH_4
86 P.BACKGROUND_IMAGE_DANIEL_OLAH_5 -> ITEM_DANIEL_OLAH_5
87 P.BACKGROUND_IMAGE_DANIEL_OLAH_6 -> ITEM_DANIEL_OLAH_6
88 P.BACKGROUND_IMAGE_DANIEL_OLAH_7 -> ITEM_DANIEL_OLAH_7
89 P.BACKGROUND_IMAGE_DANIEL_OLAH_8 -> ITEM_DANIEL_OLAH_8
90 P.BACKGROUND_IMAGE_FILIP_BAOTIC_1 -> ITEM_FILIP_BAOTIC_1
91 P.BACKGROUND_IMAGE_TYLER_LASTOVICH_1 -> ITEM_TYLER_LASTOVICH_1
92 P.BACKGROUND_IMAGE_TYLER_LASTOVICH_2 -> ITEM_TYLER_LASTOVICH_2
93 P.BACKGROUND_IMAGE_TYLER_LASTOVICH_3 -> ITEM_TYLER_LASTOVICH_3
94 P.BACKGROUND_IMAGE_CUSTOM -> ITEM_CUSTOM
95 else -> ITEM_NONE
96 }
97
98 override fun onCreate(savedInstanceState: Bundle?) {
99 super.onCreate(savedInstanceState)
100 setContentView(R.layout.activity_layout_list)
101
102 preview = findViewById(R.id.preview)
103 layoutList = findViewById(R.id.layout_list)
104
105 customImageResult =
106 registerForActivityResult(ActivityResultContracts.StartActivityForResult(), this)
107
108 layoutList.layoutManager =
109 LinearLayoutManager(this).apply {
110 orientation = LinearLayoutManager.HORIZONTAL
111 }
112 layoutList.adapter =
113 LayoutListAdapter(
114 drawables,
115 resources.getStringArray(R.array.pref_ao_background_image_array_display),
116 this,
117 )
118 }
119
120 override fun onActivityResult(result: ActivityResult) {
121 if (result.resultCode == RESULT_OK) {
122 if (result.data == null) return
123 @Suppress("LabeledExpression")
124 Thread {
125 val inputStream: InputStream? =
126 contentResolver.openInputStream(
127 result.data?.data
128 ?: return@Thread,
129 )
130
131 var bitmap = BitmapFactory.decodeStream(inputStream) ?: return@Thread
132 val size = min(bitmap.width, bitmap.height)
133 bitmap =
134 Bitmap.createBitmap(
135 bitmap,
136 (bitmap.width - size) / 2,
137 (bitmap.height - size) / 2,
138 size,
139 size,
140 )
141 if (bitmap.width > MAXIMUM_BACKGROUND_RESOLUTION) {
142 bitmap =
143 bitmap.scale(MAXIMUM_BACKGROUND_RESOLUTION, MAXIMUM_BACKGROUND_RESOLUTION)
144 }
145
146 val os = ByteArrayOutputStream()
147 bitmap.compress(Bitmap.CompressFormat.JPEG, BACKGROUND_QUALITY, os)
148 val encoded: String =
149 Base64.encodeToString(
150 os.toByteArray(),
151 Base64.DEFAULT,
152 )
153 runOnUiThread { preview.setImageBitmap(bitmap) }
154 P.getPreferences(this).edit {
155 putString(P.CUSTOM_BACKGROUND, encoded)
156 }
157 }.start()
158 }
159 }
160
161 override fun onItemClick(position: Int) {
162 if (position == ITEM_CUSTOM) {
163 showCustomImage()
164 if (hasPermission()) {
165 customImageResult.launch(
166 Intent(Intent.ACTION_PICK).apply {
167 setDataAndType(
168 MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
169 "image/*",
170 )
171 },
172 )
173 } else {
174 Toast.makeText(
175 this@LAFBackgroundImageActivity,
176 R.string.missing_permissions,
177 Toast.LENGTH_LONG,
178 ).show()
179 ActivityCompat.requestPermissions(
180 this@LAFBackgroundImageActivity,
181 arrayOf(
182 if (
183 Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
184 ) {
185 Manifest.permission.READ_MEDIA_IMAGES
186 } else {
187 Manifest.permission.READ_EXTERNAL_STORAGE
188 },
189 ),
190 0,
191 )
192 }
193 } else {
194 preview.setImageResource(drawables[position])
195 }
196 value = positionToString(position)
197 AlwaysOn.finish()
198 }
199
200 override fun onStart() {
201 super.onStart()
202 value = P.getPreferences(this).getString(P.BACKGROUND_IMAGE, P.BACKGROUND_IMAGE_DEFAULT)
203 ?: P.BACKGROUND_IMAGE_DEFAULT
204 setSelectedItem(layoutList.adapter as LayoutListAdapter, stringToPosition(value))
205 }
206
207 override fun onStop() {
208 super.onStop()
209 P.getPreferences(this).edit { putString(P.BACKGROUND_IMAGE, value) }
210 }
211
212 private fun showCustomImage() {
213 val decoded = Base64.decode(P.getPreferences(this).getString(P.CUSTOM_BACKGROUND, ""), 0)
214 preview.setImageBitmap(BitmapFactory.decodeByteArray(decoded, 0, decoded.size))
215 }
216
217 private fun setSelectedItem(
218 adapter: LayoutListAdapter,
219 position: Int,
220 ) {
221 if (position == ITEM_CUSTOM) {
222 showCustomImage()
223 } else {
224 preview.setImageResource(drawables[position])
225 }
226 adapter.setSelectedItem(position)
227 }
228
229 private fun hasPermission(): Boolean =
230 applicationContext.checkSelfPermission(
231 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
232 Manifest.permission.READ_MEDIA_IMAGES
233 } else {
234 Manifest.permission.READ_EXTERNAL_STORAGE
235 },
236 ) == PackageManager.PERMISSION_GRANTED
237
238 companion object {
239 private const val ITEM_NONE = 0
240 private const val ITEM_DANIEL_OLAH_1 = 1
241 private const val ITEM_DANIEL_OLAH_2 = 2
242 private const val ITEM_DANIEL_OLAH_3 = 3
243 private const val ITEM_DANIEL_OLAH_4 = 4
244 private const val ITEM_DANIEL_OLAH_5 = 5
245 private const val ITEM_DANIEL_OLAH_6 = 6
246 private const val ITEM_DANIEL_OLAH_7 = 7
247 private const val ITEM_DANIEL_OLAH_8 = 8
248 private const val ITEM_FILIP_BAOTIC_1 = 9
249 private const val ITEM_TYLER_LASTOVICH_1 = 10
250 private const val ITEM_TYLER_LASTOVICH_2 = 11
251 private const val ITEM_TYLER_LASTOVICH_3 = 12
252 private const val ITEM_CUSTOM = 13
253 private const val MAXIMUM_BACKGROUND_RESOLUTION = 1080
254 private const val BACKGROUND_QUALITY = 60
255 }
256 }
257