Implement first version of redraft feature

This commit is contained in:
Frédéric Gerber 2022-10-19 23:03:32 +02:00 committed by fgerber
parent 650e7b248b
commit 746242eed8
6 changed files with 160 additions and 21 deletions

View File

@ -87,7 +87,13 @@ class PostCreationActivity : BaseThemedWithoutBarActivity() {
}
} ?: InstanceDatabaseEntity("", "")
val _model: PostCreationViewModel by viewModels { PostCreationViewModelFactory(application, intent.clipData!!, instance) }
val _model: PostCreationViewModel by viewModels {
PostCreationViewModelFactory(
application,
intent.clipData!!,
instance
)
}
model = _model
model.getPhotoData().observe(this) { newPhotoData ->
@ -116,15 +122,20 @@ class PostCreationActivity : BaseThemedWithoutBarActivity() {
}
binding.addPhotoButton.isEnabled = uiState.addPhotoButtonEnabled
enableButton(uiState.postCreationSendButtonEnabled)
binding.uploadProgressBar.visibility = if(uiState.uploadProgressBarVisible) VISIBLE else INVISIBLE
binding.uploadProgressBar.visibility =
if (uiState.uploadProgressBarVisible) VISIBLE else INVISIBLE
binding.uploadProgressBar.progress = uiState.uploadProgress
binding.uploadCompletedTextview.visibility = if(uiState.uploadCompletedTextviewVisible) VISIBLE else INVISIBLE
binding.uploadCompletedTextview.visibility =
if (uiState.uploadCompletedTextviewVisible) VISIBLE else INVISIBLE
binding.removePhotoButton.isEnabled = uiState.removePhotoButtonEnabled
binding.editPhotoButton.isEnabled = uiState.editPhotoButtonEnabled
binding.uploadError.visibility = if(uiState.uploadErrorVisible) VISIBLE else INVISIBLE
binding.uploadErrorTextExplanation.visibility = if(uiState.uploadErrorExplanationVisible) VISIBLE else INVISIBLE
binding.uploadError.visibility =
if (uiState.uploadErrorVisible) VISIBLE else INVISIBLE
binding.uploadErrorTextExplanation.visibility =
if (uiState.uploadErrorExplanationVisible) VISIBLE else INVISIBLE
binding.toolbarPostCreation.visibility = if(uiState.isCarousel) VISIBLE else INVISIBLE
binding.toolbarPostCreation.visibility =
if (uiState.isCarousel) VISIBLE else INVISIBLE
binding.carousel.layoutCarousel = uiState.isCarousel
@ -155,6 +166,11 @@ class PostCreationActivity : BaseThemedWithoutBarActivity() {
binding.newPostDescriptionInputField.doAfterTextChanged {
model.newPostDescriptionChanged(binding.newPostDescriptionInputField.text)
}
// Fetch existing description passed through putExtra (if any)
val existingDescription = intent.getStringExtra(PhotoEditActivity.PICTURE_DESCRIPTION)
binding.newPostDescriptionInputField.setText(existingDescription)
binding.postTextInputLayout.counterMaxLength = instance.maxStatusChars
binding.carousel.apply {

View File

@ -80,6 +80,7 @@ class PhotoEditActivity : BaseThemedWithBarActivity() {
companion object{
internal const val PICTURE_URI = "picture_uri"
internal const val PICTURE_POSITION = "picture_position"
internal const val PICTURE_DESCRIPTION = "picture_description"
private var executor: ExecutorService = newSingleThreadExecutor()
private var future: Future<*>? = null

View File

@ -3,7 +3,9 @@ package org.pixeldroid.app.posts
import android.Manifest
import android.app.Activity
import android.app.AlertDialog
import android.content.ClipData
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Typeface
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
@ -17,6 +19,7 @@ import android.view.ViewGroup
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
@ -37,10 +40,17 @@ import com.karumi.dexter.listener.PermissionDeniedResponse
import com.karumi.dexter.listener.PermissionGrantedResponse
import com.karumi.dexter.listener.single.BasePermissionListener
import kotlinx.coroutines.launch
import okhttp3.*
import okio.BufferedSink
import okio.Okio
import okio.buffer
import okio.sink
import org.pixeldroid.app.R
import org.pixeldroid.app.databinding.AlbumImageViewBinding
import org.pixeldroid.app.databinding.OpenedAlbumBinding
import org.pixeldroid.app.databinding.PostFragmentBinding
import org.pixeldroid.app.postCreation.PostCreationActivity
import org.pixeldroid.app.postCreation.photoEdit.PhotoEditActivity
import org.pixeldroid.app.posts.MediaViewerActivity.Companion.openActivity
import org.pixeldroid.app.utils.BlurHashDecoder
import org.pixeldroid.app.utils.api.PixelfedAPI
@ -49,12 +59,14 @@ import org.pixeldroid.app.utils.api.objects.Status
import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_COMMENT_TAG
import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_TAG
import org.pixeldroid.app.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
import org.pixeldroid.app.posts.fromHtml
import org.pixeldroid.app.utils.db.AppDatabase
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
import org.pixeldroid.app.utils.setProfileImageFromURL
import retrofit2.HttpException
import java.io.File
import java.io.IOException
import java.io.OutputStream
import kotlin.math.roundToInt
@ -322,7 +334,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
//Call the api function
status?.id?.let { id ->
try {
if(bookmarked) {
if (bookmarked) {
api.bookmarkStatus(id)
} else {
api.undoBookmarkStatus(id)
@ -337,7 +349,10 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
} catch (exception: HttpException) {
Toast.makeText(
binding.root.context,
binding.root.context.getString(R.string.bookmark_post_failed_error, exception.code()),
binding.root.context.getString(
R.string.bookmark_post_failed_error,
exception.code()
),
Toast.LENGTH_SHORT
).show()
} catch (exception: IOException) {
@ -351,6 +366,14 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
return null
}
private fun OutputStream.writeBitmap(bitmap: Bitmap) {
use { out ->
//(quality is ignored for PNG)
bitmap.compress(Bitmap.CompressFormat.PNG, 85, out)
out.flush()
}
}
private fun activateMoreButton(apiHolder: PixelfedAPIHolder, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope){
var bookmarked: Boolean? = null
binding.statusMore.setOnClickListener {
@ -439,30 +462,95 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
val builder = AlertDialog.Builder(binding.root.context)
builder.apply {
setMessage(R.string.delete_dialog)
setPositiveButton(android.R.string.ok) { _, _ ->
lifecycleScope.launch {
deletePost(apiHolder.api ?: apiHolder.setToCurrentUser(), db)
}
}
setNegativeButton(android.R.string.cancel) { _, _ -> }
show()
}
true
}
R.id.post_more_menu_redraft -> {
val builder = AlertDialog.Builder(binding.root.context)
builder.apply {
setMessage(R.string.redraft_dialog)
setPositiveButton(android.R.string.ok) { _, _ ->
lifecycleScope.launch {
val user = db.userDao().getActiveUser()!!
status?.id?.let { id ->
db.homePostDao().delete(id, user.user_id, user.instance_uri)
db.publicPostDao().delete(id, user.user_id, user.instance_uri)
try {
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
api.deleteStatus(id)
binding.root.visibility = View.GONE
// Prepare image download
val imageUri = Uri.parse(status?.media_attachments?.getOrNull(binding.postPager.currentItem)?.url?: "")
val request: Request = Request.Builder().url(imageUri.toString()).build()
// TODO: check whether image is in cache directory already (e.g. using Glide)?
OkHttpClient().newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {}
@Throws(IOException::class)
override fun onResponse(call: Call, response: Response) {
// Download existing image
val downloadedFile = File(context.cacheDir, "temp_redraft_img.png")
val sink: BufferedSink =
downloadedFile.sink().buffer()
sink.writeAll(response.body!!.source())
sink.close()
val downloadedUri = Uri.fromFile(downloadedFile)
// Create new post creation activity
val intent = Intent(context, PostCreationActivity::class.java)
// Pass downloaded image to post creation activity
intent.apply {
if (clipData == null) {
clipData = ClipData("", emptyArray(), ClipData.Item(downloadedUri))
} else {
clipData!!.addItem(ClipData.Item(downloadedUri))
}
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
val imageDescription = status?.content
// Pass description of existing image to post creation activity
if (imageDescription != null) {
intent.putExtra(PhotoEditActivity.PICTURE_DESCRIPTION, fromHtml(imageDescription!!).toString())
}
// Launch post creation activity
binding.root.context.startActivity(intent)
// TODO: find better way to clean up temporary file in cacheDir?
downloadedFile.deleteOnExit()
}
})
} catch (exception: HttpException) {
Toast.makeText(
binding.root.context,
binding.root.context.getString(R.string.delete_post_failed_error, exception.code()),
Toast.LENGTH_SHORT
binding.root.context,
binding.root.context.getString(R.string.redraft_post_failed_error, exception.code()),
Toast.LENGTH_SHORT
).show()
} catch (exception: IOException) {
Toast.makeText(
binding.root.context,
binding.root.context.getString(R.string.delete_post_failed_io_except),
Toast.LENGTH_SHORT
binding.root.context,
binding.root.context.getString(R.string.redraft_post_failed_io_except),
Toast.LENGTH_SHORT
).show()
}
// Delete original post
deletePost(apiHolder.api ?: apiHolder.setToCurrentUser(), db)
}
}
}
@ -546,6 +634,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
status?.media_attachments?.let { binding.postPagerHost.images = ArrayList(it) }
}
private fun ImageView.animateView() {
visibility = View.VISIBLE
when (val drawable = drawable) {
@ -627,7 +716,29 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
}
}
private suspend fun deletePost(api: PixelfedAPI, db: AppDatabase) {
val user = db.userDao().getActiveUser()!!
status?.id?.let { id ->
db.homePostDao().delete(id, user.user_id, user.instance_uri)
db.publicPostDao().delete(id, user.user_id, user.instance_uri)
try {
api.deleteStatus(id)
binding.root.visibility = View.GONE
} catch (exception: HttpException) {
Toast.makeText(
binding.root.context,
binding.root.context.getString(R.string.delete_post_failed_error, exception.code()),
Toast.LENGTH_SHORT
).show()
} catch (exception: IOException) {
Toast.makeText(
binding.root.context,
binding.root.context.getString(R.string.delete_post_failed_io_except),
Toast.LENGTH_SHORT
).show()
}
}
}
companion object {
@ -672,7 +783,7 @@ class AlbumViewPagerAdapter(
val imageUrl = if(video) preview_url else url
if(opened){
Glide.with(holder.binding.root)
.download(GlideUrl(imageUrl))
.download( GlideUrl(imageUrl))
.into(object : CustomViewTarget<SubsamplingScaleImageView, File>((holder.image as SubsamplingScaleImageView)) {
override fun onResourceReady(resource: File, t: Transition<in File>?) =
view.setImage(ImageSource.uri(Uri.fromFile(resource)))

View File

@ -32,6 +32,10 @@
android:id="@+id/post_more_menu_group_delete"
android:visible="false">
<item android:id="@+id/post_more_menu_redraft"
android:title="@string/redraft" />
<item android:id="@+id/post_more_menu_delete"
android:title="@string/delete"/>

View File

@ -246,12 +246,16 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
<string name="discover_no_infinite_load">Discover doesn\'t load infinitely. Pull to refresh for other images.</string>
<string name="something_went_wrong">Something went wrong…</string>
<string name="panda_pull_to_refresh_to_try_again">This panda is not happy. Pull to refresh to try again.</string>
<string name="redraft">Redraft</string>
<string name="redraft_dialog">Redrafting this post will allow you to edit the photo and its description, but it will delete all current comments and likes. Continue?</string>
<string name="delete">Delete</string>
<string name="delete_dialog">Delete this post?</string>
<string name="language">Language</string>
<string name="help_translate">Help translate PixelDroid to your language:</string>
<string name="issues_contribute">Report issues or contribute to the application:</string>
<string name="mascot_description">Image showing a red panda, Pixelfed\'s mascot, using a phone</string>
<string name="redraft_post_failed_error">Could not redraft the post, error %1$d</string>
<string name="redraft_post_failed_io_except">Could not redraft the post, check your connection?</string>
<string name="delete_post_failed_error">Could not delete the post, error %1$d</string>
<string name="delete_post_failed_io_except">Could not delete the post, check your connection?</string>
<string name="bookmark_post_failed_error">Could not (un)bookmark the post, error %1$d</string>

View File

@ -3463,6 +3463,9 @@
<artifact name="aapt2-7.3.0-rc01-8691043-linux.jar">
<sha256 value="28b7445d32544169c8c31fba72fb63e067c3608e1c61ea78f2fa89cde38794fa" origin="Generated by Gradle"/>
</artifact>
<artifact name="aapt2-7.3.0-rc01-8691043-osx.jar">
<sha256 value="c1aa071d7284bb771c93037494547f772aefae18da4ad7af1523a5b7ab4d915c" origin="Generated by Gradle"/>
</artifact>
<artifact name="aapt2-7.3.0-rc01-8691043.pom">
<sha256 value="360cf64368acc9f9b1c0d0c9a82488a045e2780a5f7bfc16a57bfcc0d40cf200" origin="Generated by Gradle"/>
</artifact>