update Android Image Cropper and get rid of deprecated onActivityResult (#2351)
* update Android Image Cropper and get rid of deprecated onActivityResult * add comment why skipping caches is necessary * inject application into EditProfileViewModel instead of passing it everytime
This commit is contained in:
parent
4dee5c2774
commit
a6335e6bcd
4 changed files with 104 additions and 323 deletions
|
@ -168,7 +168,7 @@ dependencies {
|
|||
implementation "com.mikepenz:materialdrawer-iconics:$materialdrawerVersion"
|
||||
implementation 'com.mikepenz:google-material-typeface:4.0.0.2-kotlin@aar'
|
||||
|
||||
implementation "com.github.CanHub:Android-Image-Cropper:3.1.0"
|
||||
implementation "com.github.CanHub:Android-Image-Cropper:4.1.0"
|
||||
|
||||
implementation "de.c1710:filemojicompat:1.0.18"
|
||||
|
||||
|
|
|
@ -15,28 +15,26 @@
|
|||
|
||||
package com.keylesspalace.tusky
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.resource.bitmap.FitCenter
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.canhub.cropper.CropImage
|
||||
import com.canhub.cropper.CropImageContract
|
||||
import com.canhub.cropper.options
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.adapter.AccountFieldEditAdapter
|
||||
import com.keylesspalace.tusky.databinding.ActivityEditProfileBinding
|
||||
|
@ -44,9 +42,7 @@ import com.keylesspalace.tusky.di.Injectable
|
|||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.util.Error
|
||||
import com.keylesspalace.tusky.util.Loading
|
||||
import com.keylesspalace.tusky.util.Resource
|
||||
import com.keylesspalace.tusky.util.Success
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.viewmodel.EditProfileViewModel
|
||||
|
@ -63,12 +59,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
const val HEADER_WIDTH = 1500
|
||||
const val HEADER_HEIGHT = 500
|
||||
|
||||
private const val AVATAR_PICK_RESULT = 1
|
||||
private const val HEADER_PICK_RESULT = 2
|
||||
private const val PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1
|
||||
private const val MAX_ACCOUNT_FIELDS = 4
|
||||
|
||||
private const val BUNDLE_CURRENTLY_PICKING = "BUNDLE_CURRENTLY_PICKING"
|
||||
}
|
||||
|
||||
@Inject
|
||||
|
@ -78,23 +69,28 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
|
||||
private val binding by viewBinding(ActivityEditProfileBinding::inflate)
|
||||
|
||||
private var currentlyPicking: PickType = PickType.NOTHING
|
||||
|
||||
private val accountFieldEditAdapter = AccountFieldEditAdapter()
|
||||
|
||||
private enum class PickType {
|
||||
NOTHING,
|
||||
AVATAR,
|
||||
HEADER
|
||||
}
|
||||
|
||||
private val cropImage = registerForActivityResult(CropImageContract()) { result ->
|
||||
if (result.isSuccessful) {
|
||||
if (result.uriContent == viewModel.getAvatarUri()) {
|
||||
viewModel.newAvatarPicked()
|
||||
} else {
|
||||
viewModel.newHeaderPicked()
|
||||
}
|
||||
} else {
|
||||
onPickFailure(result.error)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
savedInstanceState?.getString(BUNDLE_CURRENTLY_PICKING)?.let {
|
||||
currentlyPicking = PickType.valueOf(it)
|
||||
}
|
||||
|
||||
setContentView(binding.root)
|
||||
|
||||
setSupportActionBar(binding.includedToolbar.toolbar)
|
||||
|
@ -104,8 +100,8 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
setDisplayShowHomeEnabled(true)
|
||||
}
|
||||
|
||||
binding.avatarButton.setOnClickListener { onMediaPick(PickType.AVATAR) }
|
||||
binding.headerButton.setOnClickListener { onMediaPick(PickType.HEADER) }
|
||||
binding.avatarButton.setOnClickListener { pickMedia(PickType.AVATAR) }
|
||||
binding.headerButton.setOnClickListener { pickMedia(PickType.HEADER) }
|
||||
|
||||
binding.fieldList.layoutManager = LinearLayoutManager(this)
|
||||
binding.fieldList.adapter = accountFieldEditAdapter
|
||||
|
@ -159,11 +155,11 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
}
|
||||
}
|
||||
is Error -> {
|
||||
val snackbar = Snackbar.make(binding.avatarButton, R.string.error_generic, Snackbar.LENGTH_LONG)
|
||||
snackbar.setAction(R.string.action_retry) {
|
||||
viewModel.obtainProfile()
|
||||
}
|
||||
snackbar.show()
|
||||
Snackbar.make(binding.avatarButton, R.string.error_generic, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_retry) {
|
||||
viewModel.obtainProfile()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
is Loading -> { }
|
||||
}
|
||||
|
@ -179,30 +175,24 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
observeImage(viewModel.avatarData, binding.avatarPreview, binding.avatarProgressBar, true)
|
||||
observeImage(viewModel.headerData, binding.headerPreview, binding.headerProgressBar, false)
|
||||
observeImage(viewModel.avatarData, binding.avatarPreview, true)
|
||||
observeImage(viewModel.headerData, binding.headerPreview, false)
|
||||
|
||||
viewModel.saveData.observe(
|
||||
this,
|
||||
{
|
||||
when (it) {
|
||||
is Success -> {
|
||||
finish()
|
||||
}
|
||||
is Loading -> {
|
||||
binding.saveProgressBar.visibility = View.VISIBLE
|
||||
}
|
||||
is Error -> {
|
||||
onSaveFailure(it.errorMessage)
|
||||
}
|
||||
this
|
||||
) {
|
||||
when (it) {
|
||||
is Success -> {
|
||||
finish()
|
||||
}
|
||||
is Loading -> {
|
||||
binding.saveProgressBar.visibility = View.VISIBLE
|
||||
}
|
||||
is Error -> {
|
||||
onSaveFailure(it.errorMessage)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(BUNDLE_CURRENTLY_PICKING, currentlyPicking.toString())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
@ -218,90 +208,60 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
}
|
||||
|
||||
private fun observeImage(
|
||||
liveData: LiveData<Resource<Bitmap>>,
|
||||
liveData: LiveData<Uri>,
|
||||
imageView: ImageView,
|
||||
progressBar: View,
|
||||
roundedCorners: Boolean
|
||||
) {
|
||||
liveData.observe(
|
||||
this,
|
||||
{
|
||||
this
|
||||
) { imageUri ->
|
||||
|
||||
when (it) {
|
||||
is Success -> {
|
||||
val glide = Glide.with(imageView)
|
||||
.load(it.data)
|
||||
// skipping all caches so we can always reuse the same uri
|
||||
val glide = Glide.with(imageView)
|
||||
.load(imageUri)
|
||||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
|
||||
if (roundedCorners) {
|
||||
glide.transform(
|
||||
FitCenter(),
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp))
|
||||
)
|
||||
}
|
||||
|
||||
glide.into(imageView)
|
||||
|
||||
imageView.show()
|
||||
progressBar.hide()
|
||||
}
|
||||
is Loading -> {
|
||||
progressBar.show()
|
||||
}
|
||||
is Error -> {
|
||||
progressBar.hide()
|
||||
if (!it.consumed) {
|
||||
onResizeFailure()
|
||||
it.consumed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (roundedCorners) {
|
||||
glide.transform(
|
||||
FitCenter(),
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp))
|
||||
).into(imageView)
|
||||
} else {
|
||||
glide.into(imageView)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun onMediaPick(pickType: PickType) {
|
||||
if (currentlyPicking != PickType.NOTHING) {
|
||||
// Ignore inputs if another pick operation is still occurring.
|
||||
return
|
||||
}
|
||||
currentlyPicking = pickType
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE)
|
||||
} else {
|
||||
initiateMediaPicking()
|
||||
imageView.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
when (requestCode) {
|
||||
PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE -> {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
initiateMediaPicking()
|
||||
} else {
|
||||
endMediaPicking()
|
||||
Snackbar.make(binding.avatarButton, R.string.error_media_upload_permission, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initiateMediaPicking() {
|
||||
private fun pickMedia(pickType: PickType) {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.type = "image/*"
|
||||
when (currentlyPicking) {
|
||||
when (pickType) {
|
||||
PickType.AVATAR -> {
|
||||
startActivityForResult(intent, AVATAR_PICK_RESULT)
|
||||
cropImage.launch(
|
||||
options {
|
||||
setRequestedSize(AVATAR_SIZE, AVATAR_SIZE)
|
||||
setAspectRatio(AVATAR_SIZE, AVATAR_SIZE)
|
||||
setImageSource(includeGallery = true, includeCamera = false)
|
||||
setOutputUri(viewModel.getAvatarUri())
|
||||
setOutputCompressFormat(Bitmap.CompressFormat.PNG)
|
||||
}
|
||||
)
|
||||
}
|
||||
PickType.HEADER -> {
|
||||
startActivityForResult(intent, HEADER_PICK_RESULT)
|
||||
cropImage.launch(
|
||||
options {
|
||||
setRequestedSize(HEADER_WIDTH, HEADER_HEIGHT)
|
||||
setAspectRatio(HEADER_WIDTH, HEADER_HEIGHT)
|
||||
setImageSource(includeGallery = true, includeCamera = false)
|
||||
setOutputUri(viewModel.getHeaderUri())
|
||||
setOutputCompressFormat(Bitmap.CompressFormat.PNG)
|
||||
}
|
||||
)
|
||||
}
|
||||
PickType.NOTHING -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,16 +281,11 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
}
|
||||
|
||||
private fun save() {
|
||||
if (currentlyPicking != PickType.NOTHING) {
|
||||
return
|
||||
}
|
||||
|
||||
viewModel.save(
|
||||
binding.displayNameEditText.text.toString(),
|
||||
binding.noteEditText.text.toString(),
|
||||
binding.lockedCheckBox.isChecked,
|
||||
accountFieldEditAdapter.getFieldData(),
|
||||
this
|
||||
accountFieldEditAdapter.getFieldData()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -340,90 +295,8 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
binding.saveProgressBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun beginMediaPicking() {
|
||||
when (currentlyPicking) {
|
||||
PickType.AVATAR -> {
|
||||
binding.avatarProgressBar.visibility = View.VISIBLE
|
||||
binding.avatarPreview.visibility = View.INVISIBLE
|
||||
binding.avatarButton.setImageDrawable(null)
|
||||
}
|
||||
PickType.HEADER -> {
|
||||
binding.headerProgressBar.visibility = View.VISIBLE
|
||||
binding.headerPreview.visibility = View.INVISIBLE
|
||||
binding.headerButton.setImageDrawable(null)
|
||||
}
|
||||
PickType.NOTHING -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
private fun endMediaPicking() {
|
||||
binding.avatarProgressBar.visibility = View.GONE
|
||||
binding.headerProgressBar.visibility = View.GONE
|
||||
|
||||
currentlyPicking = PickType.NOTHING
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
AVATAR_PICK_RESULT -> {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
CropImage.activity(data.data)
|
||||
.setInitialCropWindowPaddingRatio(0f)
|
||||
.setOutputCompressFormat(Bitmap.CompressFormat.PNG)
|
||||
.setAspectRatio(AVATAR_SIZE, AVATAR_SIZE)
|
||||
.start(this)
|
||||
} else {
|
||||
endMediaPicking()
|
||||
}
|
||||
}
|
||||
HEADER_PICK_RESULT -> {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
CropImage.activity(data.data)
|
||||
.setInitialCropWindowPaddingRatio(0f)
|
||||
.setOutputCompressFormat(Bitmap.CompressFormat.PNG)
|
||||
.setAspectRatio(HEADER_WIDTH, HEADER_HEIGHT)
|
||||
.start(this)
|
||||
} else {
|
||||
endMediaPicking()
|
||||
}
|
||||
}
|
||||
CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE -> {
|
||||
val result = CropImage.getActivityResult(data)
|
||||
when (resultCode) {
|
||||
Activity.RESULT_OK -> beginResize(result?.uriContent)
|
||||
CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE -> onResizeFailure()
|
||||
else -> endMediaPicking()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun beginResize(uri: Uri?) {
|
||||
if (uri == null) {
|
||||
currentlyPicking = PickType.NOTHING
|
||||
return
|
||||
}
|
||||
|
||||
beginMediaPicking()
|
||||
|
||||
when (currentlyPicking) {
|
||||
PickType.AVATAR -> {
|
||||
viewModel.newAvatar(uri, this)
|
||||
}
|
||||
PickType.HEADER -> {
|
||||
viewModel.newHeader(uri, this)
|
||||
}
|
||||
else -> {
|
||||
throw AssertionError("PickType not set.")
|
||||
}
|
||||
}
|
||||
|
||||
currentlyPicking = PickType.NOTHING
|
||||
}
|
||||
|
||||
private fun onResizeFailure() {
|
||||
private fun onPickFailure(throwable: Throwable?) {
|
||||
Log.w("EditProfileActivity", "failed to pick media", throwable)
|
||||
Snackbar.make(binding.avatarButton, R.string.error_media_upload_sending, Snackbar.LENGTH_LONG).show()
|
||||
endMediaPicking()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,15 +15,11 @@
|
|||
|
||||
package com.keylesspalace.tusky.viewmodel
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.keylesspalace.tusky.EditProfileActivity.Companion.AVATAR_SIZE
|
||||
import com.keylesspalace.tusky.EditProfileActivity.Companion.HEADER_HEIGHT
|
||||
import com.keylesspalace.tusky.EditProfileActivity.Companion.HEADER_WIDTH
|
||||
import com.keylesspalace.tusky.appstore.EventHub
|
||||
import com.keylesspalace.tusky.appstore.ProfileEditedEvent
|
||||
import com.keylesspalace.tusky.entity.Account
|
||||
|
@ -31,16 +27,12 @@ import com.keylesspalace.tusky.entity.Instance
|
|||
import com.keylesspalace.tusky.entity.StringField
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.Error
|
||||
import com.keylesspalace.tusky.util.IOUtils
|
||||
import com.keylesspalace.tusky.util.Loading
|
||||
import com.keylesspalace.tusky.util.Resource
|
||||
import com.keylesspalace.tusky.util.Success
|
||||
import com.keylesspalace.tusky.util.getSampledBitmap
|
||||
import com.keylesspalace.tusky.util.randomAlphanumericString
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.addTo
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
|
@ -52,30 +44,26 @@ import retrofit2.Call
|
|||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.FileOutputStream
|
||||
import java.io.OutputStream
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val HEADER_FILE_NAME = "header.png"
|
||||
private const val AVATAR_FILE_NAME = "avatar.png"
|
||||
|
||||
private const val TAG = "EditProfileViewModel"
|
||||
|
||||
class EditProfileViewModel @Inject constructor(
|
||||
private val mastodonApi: MastodonApi,
|
||||
private val eventHub: EventHub
|
||||
private val eventHub: EventHub,
|
||||
private val application: Application
|
||||
) : ViewModel() {
|
||||
|
||||
val profileData = MutableLiveData<Resource<Account>>()
|
||||
val avatarData = MutableLiveData<Resource<Bitmap>>()
|
||||
val headerData = MutableLiveData<Resource<Bitmap>>()
|
||||
val avatarData = MutableLiveData<Uri>()
|
||||
val headerData = MutableLiveData<Uri>()
|
||||
val saveData = MutableLiveData<Resource<Nothing>>()
|
||||
val instanceData = MutableLiveData<Resource<Instance>>()
|
||||
|
||||
private var oldProfileData: Account? = null
|
||||
|
||||
private val disposeables = CompositeDisposable()
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
fun obtainProfile() {
|
||||
if (profileData.value == null || profileData.value is Error) {
|
||||
|
@ -92,70 +80,30 @@ class EditProfileViewModel @Inject constructor(
|
|||
profileData.postValue(Error())
|
||||
}
|
||||
)
|
||||
.addTo(disposeables)
|
||||
.addTo(disposables)
|
||||
}
|
||||
}
|
||||
|
||||
fun newAvatar(uri: Uri, context: Context) {
|
||||
val cacheFile = getCacheFileForName(context, AVATAR_FILE_NAME)
|
||||
fun getAvatarUri() = getCacheFileForName(AVATAR_FILE_NAME).toUri()
|
||||
|
||||
resizeImage(uri, context, AVATAR_SIZE, AVATAR_SIZE, cacheFile, avatarData)
|
||||
fun getHeaderUri() = getCacheFileForName(HEADER_FILE_NAME).toUri()
|
||||
|
||||
fun newAvatarPicked() {
|
||||
avatarData.value = getAvatarUri()
|
||||
}
|
||||
|
||||
fun newHeader(uri: Uri, context: Context) {
|
||||
val cacheFile = getCacheFileForName(context, HEADER_FILE_NAME)
|
||||
|
||||
resizeImage(uri, context, HEADER_WIDTH, HEADER_HEIGHT, cacheFile, headerData)
|
||||
fun newHeaderPicked() {
|
||||
headerData.value = getHeaderUri()
|
||||
}
|
||||
|
||||
private fun resizeImage(
|
||||
uri: Uri,
|
||||
context: Context,
|
||||
resizeWidth: Int,
|
||||
resizeHeight: Int,
|
||||
cacheFile: File,
|
||||
imageLiveData: MutableLiveData<Resource<Bitmap>>
|
||||
) {
|
||||
|
||||
Single.fromCallable {
|
||||
val contentResolver = context.contentResolver
|
||||
val sourceBitmap = getSampledBitmap(contentResolver, uri, resizeWidth, resizeHeight)
|
||||
|
||||
if (sourceBitmap == null) {
|
||||
throw Exception()
|
||||
}
|
||||
|
||||
// dont upscale image if its smaller than the desired size
|
||||
val bitmap =
|
||||
if (sourceBitmap.width <= resizeWidth && sourceBitmap.height <= resizeHeight) {
|
||||
sourceBitmap
|
||||
} else {
|
||||
Bitmap.createScaledBitmap(sourceBitmap, resizeWidth, resizeHeight, true)
|
||||
}
|
||||
|
||||
if (!saveBitmapToFile(bitmap, cacheFile)) {
|
||||
throw Exception()
|
||||
}
|
||||
|
||||
bitmap
|
||||
}.subscribeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
{
|
||||
imageLiveData.postValue(Success(it))
|
||||
},
|
||||
{
|
||||
imageLiveData.postValue(Error())
|
||||
}
|
||||
)
|
||||
.addTo(disposeables)
|
||||
}
|
||||
|
||||
fun save(newDisplayName: String, newNote: String, newLocked: Boolean, newFields: List<StringField>, context: Context) {
|
||||
fun save(newDisplayName: String, newNote: String, newLocked: Boolean, newFields: List<StringField>) {
|
||||
|
||||
if (saveData.value is Loading || profileData.value !is Success) {
|
||||
return
|
||||
}
|
||||
|
||||
saveData.value = Loading()
|
||||
|
||||
val displayName = if (oldProfileData?.displayName == newDisplayName) {
|
||||
null
|
||||
} else {
|
||||
|
@ -174,15 +122,15 @@ class EditProfileViewModel @Inject constructor(
|
|||
newLocked.toString().toRequestBody(MultipartBody.FORM)
|
||||
}
|
||||
|
||||
val avatar = if (avatarData.value is Success && avatarData.value?.data != null) {
|
||||
val avatarBody = getCacheFileForName(context, AVATAR_FILE_NAME).asRequestBody("image/png".toMediaTypeOrNull())
|
||||
val avatar = if (avatarData.value != null) {
|
||||
val avatarBody = getCacheFileForName(AVATAR_FILE_NAME).asRequestBody("image/png".toMediaTypeOrNull())
|
||||
MultipartBody.Part.createFormData("avatar", randomAlphanumericString(12), avatarBody)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val header = if (headerData.value is Success && headerData.value?.data != null) {
|
||||
val headerBody = getCacheFileForName(context, HEADER_FILE_NAME).asRequestBody("image/png".toMediaTypeOrNull())
|
||||
val header = if (headerData.value != null) {
|
||||
val headerBody = getCacheFileForName(HEADER_FILE_NAME).asRequestBody("image/png".toMediaTypeOrNull())
|
||||
MultipartBody.Part.createFormData("header", randomAlphanumericString(12), headerBody)
|
||||
} else {
|
||||
null
|
||||
|
@ -256,29 +204,12 @@ class EditProfileViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun getCacheFileForName(context: Context, filename: String): File {
|
||||
return File(context.cacheDir, filename)
|
||||
}
|
||||
|
||||
private fun saveBitmapToFile(bitmap: Bitmap, file: File): Boolean {
|
||||
|
||||
val outputStream: OutputStream
|
||||
|
||||
try {
|
||||
outputStream = FileOutputStream(file)
|
||||
} catch (e: FileNotFoundException) {
|
||||
Log.w(TAG, Log.getStackTraceString(e))
|
||||
return false
|
||||
}
|
||||
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
|
||||
IOUtils.closeQuietly(outputStream)
|
||||
|
||||
return true
|
||||
private fun getCacheFileForName(filename: String): File {
|
||||
return File(application.cacheDir, filename)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
disposeables.dispose()
|
||||
disposables.dispose()
|
||||
}
|
||||
|
||||
fun obtainInstance() {
|
||||
|
@ -293,7 +224,7 @@ class EditProfileViewModel @Inject constructor(
|
|||
instanceData.postValue(Error())
|
||||
}
|
||||
)
|
||||
.addTo(disposeables)
|
||||
.addTo(disposables)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.keylesspalace.tusky.EditProfileActivity">
|
||||
tools:context=".EditProfileActivity">
|
||||
|
||||
<include
|
||||
android:id="@+id/includedToolbar"
|
||||
|
@ -37,17 +37,6 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_add_a_photo_32dp" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/headerProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/headerPreview"
|
||||
app:layout_constraintEnd_toEndOf="@id/headerPreview"
|
||||
app:layout_constraintStart_toStartOf="@id/headerPreview"
|
||||
app:layout_constraintTop_toTopOf="@id/headerPreview" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatarPreview"
|
||||
android:layout_width="80dp"
|
||||
|
@ -71,18 +60,6 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/headerPreview"
|
||||
app:srcCompat="@drawable/ic_add_a_photo_32dp" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/avatarProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatarPreview"
|
||||
app:layout_constraintEnd_toEndOf="@id/avatarPreview"
|
||||
app:layout_constraintStart_toStartOf="@id/avatarPreview"
|
||||
app:layout_constraintTop_toTopOf="@id/avatarPreview" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/contentContainer"
|
||||
android:layout_width="@dimen/timeline_width"
|
||||
|
|
Loading…
Reference in a new issue