Refactor working
This commit is contained in:
parent
851d95bf0f
commit
5644a22d38
@ -13,16 +13,13 @@ import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.View.GONE
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
@ -33,7 +30,6 @@ import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityPostCreationBinding
|
||||
import org.pixeldroid.app.postCreation.camera.CameraActivity
|
||||
import org.pixeldroid.app.postCreation.carousel.CarouselItem
|
||||
import org.pixeldroid.app.searchDiscover.TrendingActivity
|
||||
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
||||
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||
@ -110,10 +106,6 @@ class PostCreationActivity : BaseThemedWithoutBarActivity() {
|
||||
model.userMessageShown()
|
||||
}
|
||||
binding.addPhotoButton.isEnabled = uiState.addPhotoButtonEnabled
|
||||
enableButton(uiState.postCreationSendButtonEnabled)
|
||||
binding.uploadProgressBar.visibility =
|
||||
if (uiState.uploadProgressBarVisible) VISIBLE else INVISIBLE
|
||||
binding.uploadProgressBar.progress = uiState.uploadProgress
|
||||
binding.removePhotoButton.isEnabled = uiState.removePhotoButtonEnabled
|
||||
binding.editPhotoButton.isEnabled = uiState.editPhotoButtonEnabled
|
||||
binding.toolbarPostCreation.visibility =
|
||||
@ -136,7 +128,7 @@ class PostCreationActivity : BaseThemedWithoutBarActivity() {
|
||||
// get the description and send the post
|
||||
binding.postCreationSendButton.setOnClickListener {
|
||||
if (validatePost() && model.isNotEmpty()) {
|
||||
model.upload(it.context, binding.root.context)
|
||||
model.nextStep(binding.root.context)
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,7 +266,7 @@ class PostCreationActivity : BaseThemedWithoutBarActivity() {
|
||||
|
||||
|
||||
private fun validatePost(): Boolean {
|
||||
if(model.getPhotoData().value?.all { it.videoEncodeProgress == null } == false){
|
||||
if(model.getPhotoData().value?.all { !it.video || it.videoEncodeComplete } == false){
|
||||
AlertDialog.Builder(this).apply {
|
||||
setMessage(R.string.still_encoding)
|
||||
setNegativeButton(android.R.string.ok) { _, _ -> }
|
||||
@ -284,18 +276,6 @@ class PostCreationActivity : BaseThemedWithoutBarActivity() {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun enableButton(enable: Boolean = true){
|
||||
binding.postCreationSendButton.isEnabled = enable
|
||||
if(enable){
|
||||
binding.submittingProgressBar.visibility = GONE
|
||||
binding.postCreationSendButton.visibility = VISIBLE
|
||||
} else {
|
||||
binding.submittingProgressBar.visibility = VISIBLE
|
||||
binding.postCreationSendButton.visibility = GONE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private val editResultContract: ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
|
||||
result: ActivityResult? ->
|
||||
if (result?.resultCode == Activity.RESULT_OK && result.data != null) {
|
||||
|
@ -7,43 +7,43 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import android.provider.OpenableColumns
|
||||
import android.text.Editable
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import androidx.lifecycle.*
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.jarsilio.android.scrambler.exceptions.UnsupportedFileFormatException
|
||||
import com.jarsilio.android.scrambler.stripMetadata
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import okhttp3.MultipartBody
|
||||
import org.pixeldroid.app.MainActivity
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.media_editor.photoEdit.VideoEditActivity
|
||||
import org.pixeldroid.media_editor.photoEdit.VideoEditActivity.RelativeCropPosition
|
||||
import org.pixeldroid.app.utils.PixelDroidApplication
|
||||
import org.pixeldroid.app.utils.api.objects.Attachment
|
||||
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import org.pixeldroid.app.utils.fileExtension
|
||||
import org.pixeldroid.app.utils.getMimeType
|
||||
import retrofit2.HttpException
|
||||
import org.pixeldroid.media_editor.photoEdit.VideoEditActivity
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.Serializable
|
||||
import java.net.URI
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.MutableList
|
||||
import kotlin.collections.MutableMap
|
||||
import kotlin.collections.arrayListOf
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.get
|
||||
import kotlin.collections.getOrNull
|
||||
import kotlin.collections.indexOfFirst
|
||||
import kotlin.collections.isNotEmpty
|
||||
import kotlin.collections.mutableListOf
|
||||
import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.plus
|
||||
import kotlin.collections.set
|
||||
import kotlin.collections.toMutableList
|
||||
import kotlin.math.ceil
|
||||
|
||||
|
||||
@ -54,18 +54,10 @@ data class PostCreationActivityUiState(
|
||||
val addPhotoButtonEnabled: Boolean = true,
|
||||
val editPhotoButtonEnabled: Boolean = true,
|
||||
val removePhotoButtonEnabled: Boolean = true,
|
||||
val postCreationSendButtonEnabled: Boolean = true,
|
||||
|
||||
val isCarousel: Boolean = true,
|
||||
|
||||
val newPostDescriptionText: String = "",
|
||||
|
||||
val uploadProgressBarVisible: Boolean = false,
|
||||
val uploadProgress: Int = 0,
|
||||
val uploadCompletedTextviewVisible: Boolean = false,
|
||||
val uploadErrorVisible: Boolean = false,
|
||||
val uploadErrorExplanationText: String = "",
|
||||
val uploadErrorExplanationVisible: Boolean = false,
|
||||
)
|
||||
|
||||
@Parcelize
|
||||
@ -201,33 +193,6 @@ class PostCreationViewModel(application: Application, clipdata: ClipData? = null
|
||||
photoData.value = photoData.value
|
||||
}
|
||||
|
||||
fun resetUploadStatus() {
|
||||
photoData.value = photoData.value?.map { it.copy(uploadId = null, progress = null) }?.toMutableList()
|
||||
}
|
||||
|
||||
fun setVideoEncodeAtPosition(uri: Uri, progress: Int?, stabilizationFirstPass: Boolean = false, error: Boolean = false) {
|
||||
photoData.value?.indexOfFirst { it.imageUri == uri }?.let { position ->
|
||||
photoData.value?.set(position,
|
||||
photoData.value!![position].copy(
|
||||
videoEncodeProgress = progress,
|
||||
videoEncodeStabilizationFirstPass = stabilizationFirstPass,
|
||||
videoEncodeError = error,
|
||||
)
|
||||
)
|
||||
photoData.value = photoData.value
|
||||
}
|
||||
}
|
||||
|
||||
fun setUriAtPosition(uri: Uri, position: Int) {
|
||||
photoData.value?.set(position, photoData.value!![position].copy(imageUri = uri))
|
||||
photoData.value = photoData.value
|
||||
}
|
||||
|
||||
fun setSizeAtPosition(imageSize: Long, position: Int) {
|
||||
photoData.value?.set(position, photoData.value!![position].copy(size = imageSize))
|
||||
photoData.value = photoData.value
|
||||
}
|
||||
|
||||
fun removeAt(currentPosition: Int) {
|
||||
photoData.value?.removeAt(currentPosition)
|
||||
_uiState.update {
|
||||
@ -239,183 +204,12 @@ class PostCreationViewModel(application: Application, clipdata: ClipData? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the images that are in the [photoData] array.
|
||||
* Keeps track of them in the [PhotoData.progress] (for the upload progress), and the
|
||||
* [PhotoData.uploadId] (for the list of ids of the uploads).
|
||||
* Next step
|
||||
*/
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
fun upload(context: Context, bindingContext: Context) {
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
postCreationSendButtonEnabled = false,
|
||||
addPhotoButtonEnabled = false,
|
||||
editPhotoButtonEnabled = false,
|
||||
removePhotoButtonEnabled = false,
|
||||
uploadCompletedTextviewVisible = false,
|
||||
uploadErrorVisible = false,
|
||||
uploadProgressBarVisible = true
|
||||
)
|
||||
}
|
||||
|
||||
fun nextStep(context: Context) {
|
||||
val intent = Intent(context, PostSubmissionActivity::class.java)
|
||||
intent.putExtra(PostSubmissionActivity.PHOTO_DATA, getPhotoData().value?.let { ArrayList(it) })
|
||||
ContextCompat.startActivity(bindingContext, intent, null)
|
||||
|
||||
for (data: PhotoData in getPhotoData().value ?: emptyList()) {
|
||||
val extension = data.imageUri.fileExtension(getApplication<PixelDroidApplication>().contentResolver)
|
||||
|
||||
val strippedImage = File.createTempFile("temp_img", ".$extension", getApplication<PixelDroidApplication>().cacheDir)
|
||||
|
||||
val imageUri = data.imageUri
|
||||
|
||||
val (strippedOrNot, size) = try {
|
||||
val orientation = ExifInterface(getApplication<PixelDroidApplication>().contentResolver.openInputStream(imageUri)!!).getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
||||
|
||||
stripMetadata(imageUri, strippedImage, getApplication<PixelDroidApplication>().contentResolver)
|
||||
|
||||
// Restore EXIF orientation
|
||||
val exifInterface = ExifInterface(strippedImage)
|
||||
exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, orientation.toString())
|
||||
exifInterface.saveAttributes()
|
||||
|
||||
Pair(strippedImage.inputStream(), strippedImage.length())
|
||||
} catch (e: UnsupportedFileFormatException){
|
||||
strippedImage.delete()
|
||||
if(imageUri != data.imageUri) File(URI(imageUri.toString())).delete()
|
||||
val imageInputStream = try {
|
||||
getApplication<PixelDroidApplication>().contentResolver.openInputStream(imageUri)!!
|
||||
} catch (e: FileNotFoundException){
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
userMessage = getApplication<PixelDroidApplication>().getString(R.string.file_not_found,
|
||||
data.imageUri)
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
Pair(imageInputStream, data.size)
|
||||
} catch (e: IOException){
|
||||
strippedImage.delete()
|
||||
if(imageUri != data.imageUri) File(URI(imageUri.toString())).delete()
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
userMessage = getApplication<PixelDroidApplication>().getString(R.string.file_not_found,
|
||||
data.imageUri)
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val type = data.imageUri.getMimeType(getApplication<PixelDroidApplication>().contentResolver)
|
||||
val imagePart = ProgressRequestBody(strippedOrNot, size, type)
|
||||
val requestBody = MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart("file", System.currentTimeMillis().toString(), imagePart)
|
||||
.build()
|
||||
|
||||
val sub = imagePart.progressSubject
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe { percentage ->
|
||||
data.progress = percentage.toInt()
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
uploadProgress = getPhotoData().value!!.sumOf { it.progress ?: 0 } / getPhotoData().value!!.size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var postSub: Disposable? = null
|
||||
|
||||
val description = data.imageDescription?.let { MultipartBody.Part.createFormData("description", it) }
|
||||
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
val inter = api.mediaUpload(description, requestBody.parts[0])
|
||||
|
||||
postSub = inter
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ attachment: Attachment ->
|
||||
data.progress = 0
|
||||
data.uploadId = attachment.id!!
|
||||
},
|
||||
{ e: Throwable ->
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
uploadErrorVisible = true,
|
||||
uploadErrorExplanationText = if(e is HttpException){
|
||||
getApplication<PixelDroidApplication>().getString(R.string.upload_error, e.code())
|
||||
} else "",
|
||||
uploadErrorExplanationVisible = e is HttpException,
|
||||
)
|
||||
}
|
||||
strippedImage.delete()
|
||||
if(imageUri != data.imageUri) File(URI(imageUri.toString())).delete()
|
||||
e.printStackTrace()
|
||||
postSub?.dispose()
|
||||
sub.dispose()
|
||||
},
|
||||
{
|
||||
strippedImage.delete()
|
||||
if(imageUri != data.imageUri) File(URI(imageUri.toString())).delete()
|
||||
data.progress = 100
|
||||
if (getPhotoData().value!!.all { it.progress == 100 && it.uploadId != null }) {
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
uploadProgressBarVisible = false,
|
||||
uploadCompletedTextviewVisible = true
|
||||
)
|
||||
}
|
||||
post()
|
||||
}
|
||||
postSub?.dispose()
|
||||
sub.dispose()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun post() {
|
||||
val description = uiState.value.newPostDescriptionText
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
postCreationSendButtonEnabled = false
|
||||
)
|
||||
}
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
|
||||
api.postStatus(
|
||||
statusText = description,
|
||||
media_ids = getPhotoData().value!!.mapNotNull { it.uploadId }.toList()
|
||||
)
|
||||
Toast.makeText(getApplication(), getApplication<PixelDroidApplication>().getString(R.string.upload_post_success),
|
||||
Toast.LENGTH_SHORT).show()
|
||||
val intent = Intent(getApplication(), MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
//TODO make the activity launch this instead (and surrounding toasts too)
|
||||
getApplication<PixelDroidApplication>().startActivity(intent)
|
||||
} catch (exception: IOException) {
|
||||
Toast.makeText(getApplication(), getApplication<PixelDroidApplication>().getString(R.string.upload_post_error),
|
||||
Toast.LENGTH_SHORT).show()
|
||||
Log.e(TAG, exception.toString())
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
postCreationSendButtonEnabled = true
|
||||
)
|
||||
}
|
||||
} catch (exception: HttpException) {
|
||||
Toast.makeText(getApplication(), getApplication<PixelDroidApplication>().getString(R.string.upload_post_failed),
|
||||
Toast.LENGTH_SHORT).show()
|
||||
Log.e(TAG, exception.response().toString() + exception.message().toString())
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
postCreationSendButtonEnabled = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
ContextCompat.startActivity(context, intent, null)
|
||||
}
|
||||
|
||||
fun modifyAt(position: Int, data: Intent): Unit? {
|
||||
@ -512,7 +306,6 @@ class PostCreationViewModel(application: Application, clipdata: ClipData? = null
|
||||
tempFiles.forEach {
|
||||
it.delete()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun registerNewFFmpegSession(position: Uri, sessionId: Long) {
|
||||
|
@ -1,60 +1,39 @@
|
||||
package org.pixeldroid.app.postCreation
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentValues
|
||||
import android.content.Intent
|
||||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
import android.os.*
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.View.GONE
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.launch
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityPostCreationBinding
|
||||
import org.pixeldroid.app.databinding.ActivityPostSubmissionBinding
|
||||
import org.pixeldroid.app.postCreation.camera.CameraActivity
|
||||
import org.pixeldroid.app.postCreation.carousel.CarouselItem
|
||||
import org.pixeldroid.app.postCreation.PostCreationActivity.Companion.TEMP_FILES
|
||||
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
||||
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||
import org.pixeldroid.app.utils.fileExtension
|
||||
import org.pixeldroid.app.utils.getMimeType
|
||||
import org.pixeldroid.app.utils.setSquareImageFromURL
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
|
||||
class PostSubmissionActivity : BaseThemedWithoutBarActivity() {
|
||||
|
||||
companion object {
|
||||
internal const val PICTURE_DESCRIPTION = "picture_description"
|
||||
internal const val TEMP_FILES = "temp_files"
|
||||
internal const val POST_REDRAFT = "post_redraft"
|
||||
internal const val PHOTO_DATA = "photo_data"
|
||||
}
|
||||
|
||||
private lateinit var accounts: List<UserDatabaseEntity>
|
||||
private var selectedAccount: Int = -1
|
||||
private lateinit var menu: Menu
|
||||
private var user: UserDatabaseEntity? = null
|
||||
private lateinit var instance: InstanceDatabaseEntity
|
||||
|
||||
@ -69,8 +48,10 @@ class PostSubmissionActivity : BaseThemedWithoutBarActivity() {
|
||||
setContentView(binding.root)
|
||||
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setTitle(R.string.add_details)
|
||||
|
||||
user = db.userDao().getActiveUser()
|
||||
accounts = db.userDao().getAll()
|
||||
|
||||
instance = user?.run {
|
||||
db.instanceDao().getAll().first { instanceDatabaseEntity ->
|
||||
@ -83,8 +64,7 @@ class PostSubmissionActivity : BaseThemedWithoutBarActivity() {
|
||||
val _model: PostSubmissionViewModel by viewModels {
|
||||
PostSubmissionViewModelFactory(
|
||||
application,
|
||||
photoData!!,
|
||||
instance
|
||||
photoData!!
|
||||
)
|
||||
}
|
||||
model = _model
|
||||
@ -112,6 +92,8 @@ class PostSubmissionActivity : BaseThemedWithoutBarActivity() {
|
||||
binding.uploadErrorTextExplanation.visibility =
|
||||
if (uiState.uploadErrorExplanationVisible) VISIBLE else INVISIBLE
|
||||
|
||||
selectedAccount = accounts.indexOf(uiState.chosenAccount)
|
||||
|
||||
binding.uploadErrorTextExplanation.text = uiState.uploadErrorExplanationText
|
||||
}
|
||||
}
|
||||
@ -120,6 +102,10 @@ class PostSubmissionActivity : BaseThemedWithoutBarActivity() {
|
||||
model.newPostDescriptionChanged(binding.newPostDescriptionInputField.text)
|
||||
}
|
||||
|
||||
binding.nsfwSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
model.updateNSFW(isChecked)
|
||||
}
|
||||
|
||||
val existingDescription: String? = intent.getStringExtra(PICTURE_DESCRIPTION)
|
||||
|
||||
binding.newPostDescriptionInputField.setText(
|
||||
@ -130,8 +116,7 @@ class PostSubmissionActivity : BaseThemedWithoutBarActivity() {
|
||||
|
||||
binding.postTextInputLayout.counterMaxLength = instance.maxStatusChars
|
||||
|
||||
val firstPostImage = photoData!![0]
|
||||
setSquareImageFromURL(View(applicationContext), firstPostImage.imageUri.toString(), binding.postPreview)
|
||||
setSquareImageFromURL(View(applicationContext), photoData!![0].imageUri.toString(), binding.postPreview)
|
||||
// get the description and send the post
|
||||
binding.postCreationSendButton.setOnClickListener {
|
||||
if (validatePost()) model.upload()
|
||||
@ -151,21 +136,30 @@ class PostSubmissionActivity : BaseThemedWithoutBarActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
val redraft = intent.getBooleanExtra(POST_REDRAFT, false)
|
||||
if (redraft) {
|
||||
val builder = AlertDialog.Builder(binding.root.context)
|
||||
builder.apply {
|
||||
setMessage(R.string.redraft_dialog_cancel)
|
||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
super.onBackPressed()
|
||||
}
|
||||
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
show()
|
||||
override fun onCreateOptionsMenu(newMenu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.post_submission_account_menu, newMenu)
|
||||
menu = newMenu
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId){
|
||||
R.id.action_switch_accounts -> {
|
||||
AlertDialog.Builder(this).apply {
|
||||
setIcon(R.drawable.material_drawer_ico_account)
|
||||
setTitle(R.string.switch_accounts)
|
||||
setSingleChoiceItems(accounts.map { it.username + " (${it.fullHandle})" }.toTypedArray(), selectedAccount) { dialog, which ->
|
||||
if(selectedAccount != which){
|
||||
model.chooseAccount(accounts[which])
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
}.show()
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun validatePost(): Boolean {
|
||||
|
@ -1,15 +1,11 @@
|
||||
package org.pixeldroid.app.postCreation
|
||||
|
||||
import android.app.Application
|
||||
import android.content.ClipData
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.text.Editable
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import androidx.lifecycle.*
|
||||
import androidx.preference.PreferenceManager
|
||||
@ -25,11 +21,9 @@ import kotlinx.coroutines.launch
|
||||
import okhttp3.MultipartBody
|
||||
import org.pixeldroid.app.MainActivity
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.media_editor.photoEdit.VideoEditActivity
|
||||
import org.pixeldroid.media_editor.photoEdit.VideoEditActivity.RelativeCropPosition
|
||||
import org.pixeldroid.app.utils.PixelDroidApplication
|
||||
import org.pixeldroid.app.utils.api.objects.Attachment
|
||||
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import org.pixeldroid.app.utils.fileExtension
|
||||
import org.pixeldroid.app.utils.getMimeType
|
||||
@ -39,7 +33,6 @@ import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.ceil
|
||||
|
||||
|
||||
// Models the UI state for the PostCreationActivity
|
||||
@ -49,6 +42,9 @@ data class PostSubmissionActivityUiState(
|
||||
val postCreationSendButtonEnabled: Boolean = true,
|
||||
|
||||
val newPostDescriptionText: String = "",
|
||||
val nsfw: Boolean = false,
|
||||
|
||||
val chosenAccount: UserDatabaseEntity? = null,
|
||||
|
||||
val uploadProgressBarVisible: Boolean = false,
|
||||
val uploadProgress: Int = 0,
|
||||
@ -56,19 +52,21 @@ data class PostSubmissionActivityUiState(
|
||||
val uploadErrorVisible: Boolean = false,
|
||||
val uploadErrorExplanationText: String = "",
|
||||
val uploadErrorExplanationVisible: Boolean = false,
|
||||
)
|
||||
)
|
||||
|
||||
class PostSubmissionViewModel(application: Application, clipdata: ClipData? = null, val instance: InstanceDatabaseEntity? = null) : AndroidViewModel(application) {
|
||||
class PostSubmissionViewModel(application: Application, photodata: ArrayList<PhotoData>? = null) : AndroidViewModel(application) {
|
||||
private val photoData: MutableLiveData<MutableList<PhotoData>> by lazy {
|
||||
MutableLiveData<MutableList<PhotoData>>().also {
|
||||
it.value = clipdata?.let { it1 -> addPossibleImages(it1, mutableListOf()) }
|
||||
if (photodata != null) {
|
||||
it.value = photodata.toMutableList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var apiHolder: PixelfedAPIHolder
|
||||
|
||||
private val _uiState: MutableStateFlow<PostCreationActivityUiState>
|
||||
private val _uiState: MutableStateFlow<PostSubmissionActivityUiState>
|
||||
|
||||
init {
|
||||
(application as PixelDroidApplication).getAppComponent().inject(this)
|
||||
@ -76,10 +74,10 @@ class PostSubmissionViewModel(application: Application, clipdata: ClipData? = nu
|
||||
PreferenceManager.getDefaultSharedPreferences(application)
|
||||
val initialDescription = sharedPreferences.getString("prefill_description", "") ?: ""
|
||||
|
||||
_uiState = MutableStateFlow(PostCreationActivityUiState(newPostDescriptionText = initialDescription))
|
||||
_uiState = MutableStateFlow(PostSubmissionActivityUiState(newPostDescriptionText = initialDescription))
|
||||
}
|
||||
|
||||
val uiState: StateFlow<PostCreationActivityUiState> = _uiState
|
||||
val uiState: StateFlow<PostSubmissionActivityUiState> = _uiState
|
||||
|
||||
// Map photoData indexes to FFmpeg Session IDs
|
||||
private val sessionMap: MutableMap<Uri, Long> = mutableMapOf()
|
||||
@ -94,126 +92,10 @@ class PostSubmissionViewModel(application: Application, clipdata: ClipData? = nu
|
||||
|
||||
fun getPhotoData(): LiveData<MutableList<PhotoData>> = photoData
|
||||
|
||||
/**
|
||||
* Will add as many images as possible to [photoData], from the [clipData], and if
|
||||
* ([photoData].size + [clipData].itemCount) > [InstanceDatabaseEntity.albumLimit] then it will only add as many images
|
||||
* as are legal (if any) and a dialog will be shown to the user alerting them of this fact.
|
||||
*/
|
||||
fun addPossibleImages(clipData: ClipData, previousList: MutableList<PhotoData>? = photoData.value): MutableList<PhotoData> {
|
||||
val dataToAdd: ArrayList<PhotoData> = arrayListOf()
|
||||
var count = clipData.itemCount
|
||||
if(count + (previousList?.size ?: 0) > instance!!.albumLimit){
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(userMessage = getApplication<PixelDroidApplication>().getString(R.string.total_exceeds_album_limit).format(instance.albumLimit))
|
||||
}
|
||||
count = count.coerceAtMost(instance.albumLimit - (previousList?.size ?: 0))
|
||||
}
|
||||
if (count + (previousList?.size ?: 0) >= instance.albumLimit) {
|
||||
// Disable buttons to add more images
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(addPhotoButtonEnabled = false)
|
||||
}
|
||||
}
|
||||
for (i in 0 until count) {
|
||||
clipData.getItemAt(i).let {
|
||||
val sizeAndVideoPair: Pair<Long, Boolean> =
|
||||
getSizeAndVideoValidate(it.uri, (previousList?.size ?: 0) + dataToAdd.size + 1)
|
||||
dataToAdd.add(PhotoData(imageUri = it.uri, size = sizeAndVideoPair.first, video = sizeAndVideoPair.second, imageDescription = it.text?.toString()))
|
||||
}
|
||||
}
|
||||
return previousList?.plus(dataToAdd)?.toMutableList() ?: mutableListOf()
|
||||
}
|
||||
|
||||
fun setImages(addPossibleImages: MutableList<PhotoData>) {
|
||||
photoData.value = addPossibleImages
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the file of the Uri, and whether it is a video,
|
||||
* and opens a dialog in case it is too big or in case the file is unsupported.
|
||||
*/
|
||||
fun getSizeAndVideoValidate(uri: Uri, editPosition: Int): Pair<Long, Boolean> {
|
||||
val size: Long =
|
||||
if (uri.scheme =="content") {
|
||||
getApplication<PixelDroidApplication>().contentResolver.query(uri, null, null, null, null)
|
||||
?.use { cursor ->
|
||||
/* Get the column indexes of the data in the Cursor,
|
||||
* move to the first row in the Cursor, get the data,
|
||||
* and display it.
|
||||
*/
|
||||
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||
cursor.moveToFirst()
|
||||
cursor.getLong(sizeIndex)
|
||||
} ?: 0
|
||||
} else {
|
||||
uri.toFile().length()
|
||||
}
|
||||
|
||||
val sizeInkBytes = ceil(size.toDouble() / 1000).toLong()
|
||||
val type = uri.getMimeType(getApplication<PixelDroidApplication>().contentResolver)
|
||||
val isVideo = type.startsWith("video/")
|
||||
|
||||
if(isVideo && !instance!!.videoEnabled){
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(userMessage = getApplication<PixelDroidApplication>().getString(R.string.video_not_supported))
|
||||
}
|
||||
}
|
||||
|
||||
if (sizeInkBytes > instance!!.maxPhotoSize || sizeInkBytes > instance.maxVideoSize) {
|
||||
val maxSize = if (isVideo) instance.maxVideoSize else instance.maxPhotoSize
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
userMessage = getApplication<PixelDroidApplication>().getString(R.string.size_exceeds_instance_limit, editPosition, sizeInkBytes, maxSize)
|
||||
)
|
||||
}
|
||||
}
|
||||
return Pair(size, isVideo)
|
||||
}
|
||||
|
||||
fun isNotEmpty(): Boolean = photoData.value?.isNotEmpty() ?: false
|
||||
|
||||
fun updateDescription(position: Int, description: String) {
|
||||
photoData.value?.getOrNull(position)?.imageDescription = description
|
||||
photoData.value = photoData.value
|
||||
}
|
||||
|
||||
fun resetUploadStatus() {
|
||||
photoData.value = photoData.value?.map { it.copy(uploadId = null, progress = null) }?.toMutableList()
|
||||
}
|
||||
|
||||
fun setVideoEncodeAtPosition(uri: Uri, progress: Int?, stabilizationFirstPass: Boolean = false, error: Boolean = false) {
|
||||
photoData.value?.indexOfFirst { it.imageUri == uri }?.let { position ->
|
||||
photoData.value?.set(position,
|
||||
photoData.value!![position].copy(
|
||||
videoEncodeProgress = progress,
|
||||
videoEncodeStabilizationFirstPass = stabilizationFirstPass,
|
||||
videoEncodeError = error,
|
||||
)
|
||||
)
|
||||
photoData.value = photoData.value
|
||||
}
|
||||
}
|
||||
|
||||
fun setUriAtPosition(uri: Uri, position: Int) {
|
||||
photoData.value?.set(position, photoData.value!![position].copy(imageUri = uri))
|
||||
photoData.value = photoData.value
|
||||
}
|
||||
|
||||
fun setSizeAtPosition(imageSize: Long, position: Int) {
|
||||
photoData.value?.set(position, photoData.value!![position].copy(size = imageSize))
|
||||
photoData.value = photoData.value
|
||||
}
|
||||
|
||||
fun removeAt(currentPosition: Int) {
|
||||
photoData.value?.removeAt(currentPosition)
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
addPhotoButtonEnabled = true
|
||||
)
|
||||
}
|
||||
photoData.value = photoData.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the images that are in the [photoData] array.
|
||||
* Keeps track of them in the [PhotoData.progress] (for the upload progress), and the
|
||||
@ -224,9 +106,6 @@ class PostSubmissionViewModel(application: Application, clipdata: ClipData? = nu
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
postCreationSendButtonEnabled = false,
|
||||
addPhotoButtonEnabled = false,
|
||||
editPhotoButtonEnabled = false,
|
||||
removePhotoButtonEnabled = false,
|
||||
uploadCompletedTextviewVisible = false,
|
||||
uploadErrorVisible = false,
|
||||
uploadProgressBarVisible = true
|
||||
@ -300,9 +179,14 @@ class PostSubmissionViewModel(application: Application, clipdata: ClipData? = nu
|
||||
|
||||
val description = data.imageDescription?.let { MultipartBody.Part.createFormData("description", it) }
|
||||
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
//Ugly temporary account switching, but it works well enough for now
|
||||
val api = uiState.value.chosenAccount?.let {
|
||||
apiHolder.setToCurrentUser(it)
|
||||
} ?: apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
|
||||
val inter = api.mediaUpload(description, requestBody.parts[0])
|
||||
|
||||
apiHolder.api = null
|
||||
postSub = inter
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
@ -349,6 +233,10 @@ class PostSubmissionViewModel(application: Application, clipdata: ClipData? = nu
|
||||
|
||||
private fun post() {
|
||||
val description = uiState.value.newPostDescriptionText
|
||||
|
||||
//TODO investigate why this works but booleans don't
|
||||
val nsfw = if(uiState.value.nsfw) 1 else 0
|
||||
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
postCreationSendButtonEnabled = false
|
||||
@ -356,11 +244,15 @@ class PostSubmissionViewModel(application: Application, clipdata: ClipData? = nu
|
||||
}
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
//Ugly temporary account switching, but it works well enough for now
|
||||
val api = uiState.value.chosenAccount?.let {
|
||||
apiHolder.setToCurrentUser(it)
|
||||
} ?: apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
|
||||
api.postStatus(
|
||||
statusText = description,
|
||||
media_ids = getPhotoData().value!!.mapNotNull { it.uploadId }.toList()
|
||||
media_ids = getPhotoData().value!!.mapNotNull { it.uploadId }.toList(),
|
||||
sensitive = nsfw
|
||||
)
|
||||
Toast.makeText(getApplication(), getApplication<PixelDroidApplication>().getString(R.string.upload_post_success),
|
||||
Toast.LENGTH_SHORT).show()
|
||||
@ -386,6 +278,8 @@ class PostSubmissionViewModel(application: Application, clipdata: ClipData? = nu
|
||||
postCreationSendButtonEnabled = true
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
apiHolder.api = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -400,16 +294,21 @@ class PostSubmissionViewModel(application: Application, clipdata: ClipData? = nu
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
VideoEditActivity.cancelEncoding()
|
||||
tempFiles.forEach {
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateNSFW(checked: Boolean) { _uiState.update { it.copy(nsfw = checked) } }
|
||||
|
||||
fun chooseAccount(which: UserDatabaseEntity) {
|
||||
_uiState.update { it.copy(chosenAccount = which) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PostSubmissionViewModelFactory(val application: Application, val photoData: ArrayList<PhotoData>, val instance: InstanceDatabaseEntity) : ViewModelProvider.Factory {
|
||||
class PostSubmissionViewModelFactory(val application: Application, val photoData: ArrayList<PhotoData>) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return modelClass.getConstructor(Application::class.java, ArrayList::class.java, InstanceDatabaseEntity::class.java).newInstance(application, photoData, instance)
|
||||
return modelClass.getConstructor(Application::class.java, ArrayList::class.java).newInstance(application, photoData)
|
||||
}
|
||||
}
|
@ -153,18 +153,18 @@ interface PixelfedAPI {
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/statuses")
|
||||
suspend fun postStatus(
|
||||
@Field("status") statusText : String,
|
||||
@Field("in_reply_to_id") in_reply_to_id : String? = null,
|
||||
@Field("media_ids[]") media_ids : List<String> = emptyList(),
|
||||
@Field("poll[options][]") poll_options : List<String>? = null,
|
||||
@Field("poll[expires_in]") poll_expires : List<String>? = null,
|
||||
@Field("poll[multiple]") poll_multiple : List<String>? = null,
|
||||
@Field("poll[hide_totals]") poll_hideTotals : List<String>? = null,
|
||||
@Field("sensitive") sensitive : Boolean? = null,
|
||||
@Field("spoiler_text") spoiler_text : String? = null,
|
||||
@Field("visibility") visibility : String = "public",
|
||||
@Field("scheduled_at") scheduled_at : String? = null,
|
||||
@Field("language") language : String? = null
|
||||
@Field("status") statusText: String,
|
||||
@Field("in_reply_to_id") in_reply_to_id: String? = null,
|
||||
@Field("media_ids[]") media_ids: List<String> = emptyList(),
|
||||
@Field("poll[options][]") poll_options: List<String>? = null,
|
||||
@Field("poll[expires_in]") poll_expires: List<String>? = null,
|
||||
@Field("poll[multiple]") poll_multiple: List<String>? = null,
|
||||
@Field("poll[hide_totals]") poll_hideTotals: List<String>? = null,
|
||||
@Field("sensitive") sensitive: Int? = null,
|
||||
@Field("spoiler_text") spoiler_text: String? = null,
|
||||
@Field("visibility") visibility: String = "public",
|
||||
@Field("scheduled_at") scheduled_at: String? = null,
|
||||
@Field("language") language: String? = null
|
||||
) : Status
|
||||
|
||||
@DELETE("/api/v1/statuses/{id}")
|
||||
|
@ -11,27 +11,16 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:showCaption="true"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/uploadProgressBar"
|
||||
app:layout_constraintBottom_toTopOf="@+id/buttonConstraints"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/uploadProgressBar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toTopOf="@id/buttonConstraints"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/buttonConstraints"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/carousel">
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/post_creation_send_button"
|
||||
@ -44,17 +33,6 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/submitting_progress_bar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
@ -7,15 +7,13 @@
|
||||
tools:context=".postCreation.PostSubmissionActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/profilePictureImageView"
|
||||
android:id="@+id/post_preview"
|
||||
android:layout_margin="20dp"
|
||||
android:layout_width="88dp"
|
||||
android:layout_height="88dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:contentDescription="@string/profile_picture"
|
||||
android:contentDescription="@string/post_preview"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/upload_error"
|
||||
@ -71,14 +69,6 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/post_preview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/buttonConstraints"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/upload_completed_textview"
|
||||
@ -91,7 +81,7 @@
|
||||
android:visibility="invisible"
|
||||
app:drawableStartCompat="@drawable/cloud_done_24"
|
||||
app:drawableTint="@android:color/holo_green_light"
|
||||
app:layout_constraintBottom_toTopOf="@id/postTextInputLayout"
|
||||
app:layout_constraintBottom_toTopOf="@+id/buttonConstraints"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
@ -99,10 +89,9 @@
|
||||
android:id="@+id/buttonConstraints"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/postTextInputLayout">
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/post_creation_send_button"
|
||||
@ -134,7 +123,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toTopOf="@id/postTextInputLayout"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/upload_completed_textview"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
@ -143,13 +132,14 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/description"
|
||||
app:counterEnabled="true"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="15dp"
|
||||
app:counterEnabled="true"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/buttonConstraints"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/post_preview">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/new_post_description_input_field"
|
||||
@ -160,4 +150,27 @@
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/nsfwSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/post_preview"
|
||||
app:layout_constraintStart_toEndOf="@+id/post_preview"
|
||||
app:layout_constraintTop_toTopOf="@+id/post_preview" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/privateTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/contains_nsfw"
|
||||
android:textStyle="bold"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/nsfwSwitch"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/nsfwSwitch"
|
||||
app:layout_constraintTop_toTopOf="@+id/nsfwSwitch" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
11
app/src/main/res/menu/post_submission_account_menu.xml
Normal file
11
app/src/main/res/menu/post_submission_account_menu.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_switch_accounts"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/switch_accounts"
|
||||
android:icon="@drawable/material_drawer_ico_account"
|
||||
app:showAsAction="ifRoom"/>
|
||||
</menu>
|
@ -103,7 +103,8 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
|
||||
<string name="upload_post_error">Post upload error</string>
|
||||
<string name="description">Description…</string>
|
||||
<string name="post">Post</string>
|
||||
<string name="upload_next_step">Upload and go to next step</string>
|
||||
<string name="upload_next_step">Next step</string>
|
||||
<string name="add_details">Add some details</string>
|
||||
<string name="add_photo">Add a photo</string>
|
||||
<string name="post_image">One of the images in the post</string>
|
||||
<string name="switch_to_grid">Switch to grid view</string>
|
||||
@ -323,4 +324,6 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
|
||||
<string name="profile_saved">Changes saved!</string>
|
||||
<string name="error_profile">Something went wrong. Tap to retry</string>
|
||||
<string name="change_profile_picture">Change your profile picture</string>
|
||||
<string name="contains_nsfw">Contains NSFW media</string>
|
||||
<string name="switch_accounts">Switch accounts</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user