comparison app/src/main/java/com/bartsent/simpleresizer/EditImage.kt @ 32:be08576794af

Replace static object with ViewModel.
author David Barts <n5jrn@me.com>
date Fri, 26 Feb 2021 14:26:50 -0800
parents aacf7a856b5f
children bead5d7e8c69
comparison
equal deleted inserted replaced
31:0023e6013dd9 32:be08576794af
19 import android.widget.ProgressBar 19 import android.widget.ProgressBar
20 import android.widget.Toast 20 import android.widget.Toast
21 import androidx.appcompat.app.AlertDialog 21 import androidx.appcompat.app.AlertDialog
22 import androidx.appcompat.app.AppCompatActivity 22 import androidx.appcompat.app.AppCompatActivity
23 import androidx.appcompat.widget.PopupMenu 23 import androidx.appcompat.widget.PopupMenu
24 import androidx.lifecycle.ViewModel
25 import androidx.lifecycle.ViewModelProvider
24 import androidx.preference.PreferenceManager 26 import androidx.preference.PreferenceManager
25 import com.bartsent.simpleresizer.databinding.ActivityEditImageBinding 27 import com.bartsent.simpleresizer.databinding.ActivityEditImageBinding
26 import com.bartsent.simpleresizer.lib.ThreadPools 28 import com.bartsent.simpleresizer.lib.ThreadPools
27 import com.bartsent.simpleresizer.lib.getScaledInstance 29 import com.bartsent.simpleresizer.lib.getScaledInstance
28 import java.io.File 30 import java.io.File
29 import java.io.IOException 31 import java.io.IOException
30 32
31 class EditImage : AppCompatActivity() { 33 class EditImage : AppCompatActivity() {
32 private object State { 34 class State: ViewModel() {
33 var uri: Uri? = null 35 var uri: Uri? = null
34 var bitmap: Bitmap? = null 36 var bitmap: Bitmap? = null
35 } 37 }
38 private lateinit var viewModel: State
36 39
37 private val STDDIMS = arrayOf<Int>(1600, 1280, 1024, 800, 640, 512, 400, 320).apply { 40 private val STDDIMS = arrayOf<Int>(1600, 1280, 1024, 800, 640, 512, 400, 320).apply {
38 sort() 41 sort()
39 } 42 }
40 43
43 override fun onCreate(savedInstanceState: Bundle?) { 46 override fun onCreate(savedInstanceState: Bundle?) {
44 super.onCreate(savedInstanceState) 47 super.onCreate(savedInstanceState)
45 binding = ActivityEditImageBinding.inflate(layoutInflater) 48 binding = ActivityEditImageBinding.inflate(layoutInflater)
46 setContentView(binding.root) 49 setContentView(binding.root)
47 PreferenceManager.setDefaultValues(applicationContext, R.xml.root_preferences, false) 50 PreferenceManager.setDefaultValues(applicationContext, R.xml.root_preferences, false)
51 viewModel = ViewModelProvider(this).get(State::class.java)
48 } 52 }
49 53
50 override fun onCreateOptionsMenu(menu: Menu?): Boolean { 54 override fun onCreateOptionsMenu(menu: Menu?): Boolean {
51 menuInflater.inflate(R.menu.menu_edit, menu) 55 menuInflater.inflate(R.menu.menu_edit, menu)
52 return super.onCreateOptionsMenu(menu) 56 return super.onCreateOptionsMenu(menu)
95 super.onResume() 99 super.onResume()
96 100
97 // Read the URI, die if we can't. 101 // Read the URI, die if we can't.
98 val imageUri = intent?.data ?: intent?.extras?.get(Intent.EXTRA_STREAM) as? Uri 102 val imageUri = intent?.data ?: intent?.extras?.get(Intent.EXTRA_STREAM) as? Uri
99 if (imageUri == null) { 103 if (imageUri == null) {
100 if (State.bitmap == null) 104 if (viewModel.bitmap == null)
101 showFatalError(getString(R.string.error_no_uri)) 105 showFatalError(getString(R.string.error_no_uri))
102 else 106 else
103 setImage(State.bitmap!!) 107 setImage(viewModel.bitmap!!)
104 return 108 return
105 } 109 }
106 110
107 // User has opened a new image. 111 // User has opened a new image.
108 if (imageUri != State.uri) { 112 if (imageUri != viewModel.uri) {
109 State.uri = imageUri 113 viewModel.uri = imageUri
110 binding.progressBar.visibility = ProgressBar.VISIBLE 114 binding.progressBar.visibility = ProgressBar.VISIBLE
111 ThreadPools.WORKERS.execute { 115 ThreadPools.WORKERS.execute {
112 val newBitmap = contentResolver.openInputStream(imageUri).use { 116 val newBitmap = contentResolver.openInputStream(imageUri).use {
113 BitmapFactory.decodeStream(it) 117 BitmapFactory.decodeStream(it)
114 } 118 }
122 } 126 }
123 return 127 return
124 } 128 }
125 129
126 // Rotation (of the phone). 130 // Rotation (of the phone).
127 val oldBitmap = State.bitmap 131 val oldBitmap = viewModel.bitmap
128 if (oldBitmap != null) 132 if (oldBitmap != null)
129 setImage(oldBitmap) 133 setImage(oldBitmap)
130 } 134 }
131 135
132 private fun setImage(image: Bitmap): Unit { 136 private fun setImage(image: Bitmap): Unit {
133 binding.imageSize.text = getString(R.string.image_size_text, image.width, image.height) 137 binding.imageSize.text = getString(R.string.image_size_text, image.width, image.height)
134 binding.image.setImageBitmap(image) 138 binding.image.setImageBitmap(image)
135 State.bitmap = image 139 viewModel.bitmap = image
136 binding.root.invalidate() 140 binding.root.invalidate()
137 } 141 }
138 142
139 private fun unsetImage(): Unit { 143 private fun unsetImage(): Unit {
140 State.uri = null 144 viewModel.uri = null
141 State.bitmap = null 145 viewModel.bitmap = null
142 } 146 }
143 147
144 private val CUSTOM = 999998 148 private val CUSTOM = 999998
145 private val CANCEL = 999999 149 private val CANCEL = 999999
146 150
147 fun scaleClicked(view: View): Unit { 151 fun scaleClicked(view: View): Unit {
148 val (maxSize, horizontal) = State.bitmap!!.run { 152 val (maxSize, horizontal) = viewModel.bitmap!!.run {
149 if (width > height) Pair(width, true) else Pair(height, false) 153 if (width > height) Pair(width, true) else Pair(height, false)
150 } 154 }
151 PopupMenu(this, view).apply { 155 PopupMenu(this, view).apply {
152 menu.run { 156 menu.run {
153 STDDIMS.filter { it < maxSize }.forEach { major -> 157 STDDIMS.filter { it < maxSize }.forEach { major ->
178 new.setColorSpace(oldColorSpace) 182 new.setColorSpace(oldColorSpace)
179 } 183 }
180 } 184 }
181 185
182 private fun doScale(newMax: Int): Unit { 186 private fun doScale(newMax: Int): Unit {
183 val oldBitmap = State.bitmap!! 187 val oldBitmap = viewModel.bitmap!!
184 val factor = newMax.toDouble() / maxOf(oldBitmap.width, oldBitmap.height).toDouble() 188 val factor = newMax.toDouble() / maxOf(oldBitmap.width, oldBitmap.height).toDouble()
185 if (factor >= 1.0) { 189 if (factor >= 1.0) {
186 throw IllegalArgumentException("can only scale down") 190 throw IllegalArgumentException("can only scale down")
187 } 191 }
188 val scaleType = PreferenceManager.getDefaultSharedPreferences(applicationContext).getString( 192 val scaleType = PreferenceManager.getDefaultSharedPreferences(applicationContext).getString(
202 } 206 }
203 } 207 }
204 } 208 }
205 209
206 private fun showCustomScaleDialog(): Unit { 210 private fun showCustomScaleDialog(): Unit {
207 val image = State.bitmap!! 211 val image = viewModel.bitmap!!
208 val curMaxDim = maxOf(image.width, image.height) 212 val curMaxDim = maxOf(image.width, image.height)
209 val dialogView = layoutInflater.inflate(R.layout.dialog_custom_scale, null) 213 val dialogView = layoutInflater.inflate(R.layout.dialog_custom_scale, null)
210 AlertDialog.Builder(this).also { 214 AlertDialog.Builder(this).also {
211 it.setPositiveButton(R.string.ok_text) { dialog, _ -> 215 it.setPositiveButton(R.string.ok_text) { dialog, _ ->
212 val maxDim = dialogView.findViewById<EditText>(R.id.custom_scale)?.text.toString().toIntOrNull() 216 val maxDim = dialogView.findViewById<EditText>(R.id.custom_scale)?.text.toString().toIntOrNull()
249 show() 253 show()
250 } 254 }
251 } 255 }
252 256
253 private fun doRotate(deg: Int): Unit { 257 private fun doRotate(deg: Int): Unit {
254 val oldBitmap = State.bitmap!! 258 val oldBitmap = viewModel.bitmap!!
255 if (deg % 90 != 0) { 259 if (deg % 90 != 0) {
256 throw IllegalArgumentException("$deg not a multiple of 90") 260 throw IllegalArgumentException("$deg not a multiple of 90")
257 } 261 }
258 val (w, h) = if (deg % 180 == 0) Pair(oldBitmap.width, oldBitmap.height) else Pair(oldBitmap.height, oldBitmap.width) 262 val (w, h) = if (deg % 180 == 0) Pair(oldBitmap.width, oldBitmap.height) else Pair(oldBitmap.height, oldBitmap.width)
259 val rotater = Matrix().apply { 263 val rotater = Matrix().apply {
278 unsetImage() 282 unsetImage()
279 finish() 283 finish()
280 } 284 }
281 285
282 fun doneClicked(view: View): Unit { 286 fun doneClicked(view: View): Unit {
283 val image = State.bitmap!! 287 val image = viewModel.bitmap!!
284 binding.progressBar.visibility = ProgressBar.VISIBLE 288 binding.progressBar.visibility = ProgressBar.VISIBLE
285 ThreadPools.WORKERS.execute { 289 ThreadPools.WORKERS.execute {
286 val contentValues = ContentValues().apply { 290 val contentValues = ContentValues().apply {
287 var fileName = getFileName(State.uri!!) 291 var fileName = getFileName(viewModel.uri!!)
288 if (fileName == null) { 292 if (fileName == null) {
289 val d = java.util.Date() 293 val d = java.util.Date()
290 fileName = "IMG_%tY%tm%td_%tH%tM%tS".format(d, d, d, d, d, d) 294 fileName = "IMG_%tY%tm%td_%tH%tM%tS".format(d, d, d, d, d, d)
291 } 295 }
292 val dot = fileName.lastIndexOf('.') 296 val dot = fileName.lastIndexOf('.')