364 lines
14 KiB
Kotlin
364 lines
14 KiB
Kotlin
package com.h.pixeldroid
|
|
|
|
import android.app.Activity
|
|
import android.content.Intent
|
|
import android.net.Uri
|
|
import android.os.Bundle
|
|
import android.provider.OpenableColumns
|
|
import android.util.Log
|
|
import android.view.LayoutInflater
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import android.widget.Button
|
|
import android.widget.Toast
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
import androidx.core.net.toFile
|
|
import androidx.recyclerview.widget.GridLayoutManager
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import com.bumptech.glide.Glide
|
|
import com.google.android.material.textfield.TextInputLayout
|
|
import com.h.pixeldroid.api.PixelfedAPI
|
|
import com.h.pixeldroid.db.AppDatabase
|
|
import com.h.pixeldroid.db.entities.UserDatabaseEntity
|
|
import com.h.pixeldroid.di.PixelfedAPIHolder
|
|
import com.h.pixeldroid.interfaces.PostCreationListener
|
|
import com.h.pixeldroid.objects.Attachment
|
|
import com.h.pixeldroid.objects.Instance
|
|
import com.h.pixeldroid.objects.Status
|
|
import com.h.pixeldroid.utils.ProgressRequestBody
|
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
|
import io.reactivex.disposables.Disposable
|
|
import io.reactivex.schedulers.Schedulers
|
|
import kotlinx.android.synthetic.main.activity_post_creation.*
|
|
import kotlinx.android.synthetic.main.image_album_creation.view.*
|
|
import okhttp3.MultipartBody
|
|
import retrofit2.Call
|
|
import retrofit2.Callback
|
|
import retrofit2.Response
|
|
import javax.inject.Inject
|
|
|
|
private val TAG = "Post Creation Activity"
|
|
private val MORE_PICTURES_REQUEST_CODE = 0xffff
|
|
|
|
|
|
class PostCreationActivity : AppCompatActivity(), PostCreationListener {
|
|
|
|
private lateinit var recycler : RecyclerView
|
|
private lateinit var adapter : PostCreationAdapter
|
|
|
|
private lateinit var accessToken: String
|
|
private lateinit var pixelfedAPI: PixelfedAPI
|
|
|
|
private var muListOfIds: MutableList<String> = mutableListOf()
|
|
private var progressList: ArrayList<Int> = arrayListOf()
|
|
|
|
|
|
private var positionResult = 0
|
|
private var user: UserDatabaseEntity? = null
|
|
|
|
private var posts: ArrayList<String> = ArrayList()
|
|
|
|
@Inject
|
|
lateinit var db: AppDatabase
|
|
|
|
@Inject
|
|
lateinit var apiHolder: PixelfedAPIHolder
|
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(R.layout.activity_post_creation)
|
|
|
|
(this.application as Pixeldroid).getAppComponent().inject(this)
|
|
|
|
// get image URIs
|
|
if(intent.clipData != null) {
|
|
val count = intent.clipData!!.itemCount
|
|
for (i in 0 until count) {
|
|
val imageUri: String = intent.clipData!!.getItemAt(i).uri.toString()
|
|
posts.add(imageUri)
|
|
}
|
|
}
|
|
|
|
user = db.userDao().getActiveUser()
|
|
|
|
val instances = db.instanceDao().getAll()
|
|
|
|
val textField = findViewById<TextInputLayout>(R.id.postTextInputLayout)
|
|
|
|
textField.counterMaxLength = if (user != null){
|
|
val thisInstances =
|
|
instances.filter { instanceDatabaseEntity ->
|
|
instanceDatabaseEntity.uri.contains(user!!.instance_uri)
|
|
}
|
|
thisInstances.first().max_toot_chars
|
|
} else {
|
|
Instance.DEFAULT_MAX_TOOT_CHARS
|
|
}
|
|
|
|
accessToken = user?.accessToken.orEmpty()
|
|
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
|
|
|
// check if the pictures are alright
|
|
// TODO
|
|
|
|
//upload the picture and display progress while doing so
|
|
muListOfIds = posts.map { "" }.toMutableList()
|
|
progressList = posts.map { 0 } as ArrayList<Int>
|
|
upload()
|
|
|
|
adapter = PostCreationAdapter(posts)
|
|
adapter.listener = this
|
|
recycler = findViewById(R.id.image_grid)
|
|
recycler.layoutManager = GridLayoutManager(this, 3)
|
|
recycler.adapter = adapter
|
|
|
|
// get the description and send the post
|
|
findViewById<Button>(R.id.post_creation_send_button).setOnClickListener {
|
|
if (validateDescription() && muListOfIds.isNotEmpty()) post()
|
|
}
|
|
|
|
// Button to retry image upload when it fails
|
|
findViewById<Button>(R.id.retry_upload_button).setOnClickListener {
|
|
upload_error.visibility = View.GONE
|
|
muListOfIds = posts.map { "" }.toMutableList()
|
|
progressList = posts.map { 0 } as ArrayList<Int>
|
|
upload()
|
|
}
|
|
}
|
|
|
|
private fun validateDescription(): Boolean {
|
|
val textField = findViewById<TextInputLayout>(R.id.postTextInputLayout)
|
|
val content = textField.editText?.length() ?: 0
|
|
if (content > textField.counterMaxLength) {
|
|
// error, too many characters
|
|
textField.error = getString(R.string.description_max_characters).format(textField.counterMaxLength)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* Uploads the images that are in the [posts] array.
|
|
* Keeps track of them in the [progressList] (for the upload progress), and the [muListOfIds]
|
|
* (for the list of ids of the uploads).
|
|
* @param newImagesStartingIndex is the index in the [posts] array we want to start uploading at.
|
|
* Indices before this are already uploading, or done uploading, from before.
|
|
* @param editedImage contains the index of the image that was edited. If set, other images are
|
|
* not uploaded again: they should already be uploading, or be done uploading, from before.
|
|
*/
|
|
private fun upload(newImagesStartingIndex: Int = 0, editedImage: Int? = null) {
|
|
enableButton(false)
|
|
uploadProgressBar.visibility = View.VISIBLE
|
|
upload_completed_textview.visibility = View.INVISIBLE
|
|
|
|
val range: IntRange = if(editedImage == null){
|
|
newImagesStartingIndex until posts.size
|
|
} else IntRange(editedImage, editedImage)
|
|
|
|
for (index in range) {
|
|
val imageUri = Uri.parse(posts[index])
|
|
val imageInputStream = contentResolver.openInputStream(imageUri)!!
|
|
|
|
val size =
|
|
if (imageUri.toString().startsWith("content")) {
|
|
contentResolver.query(imageUri, 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 {
|
|
imageUri.toFile().length()
|
|
}
|
|
|
|
val imagePart = ProgressRequestBody(imageInputStream, size)
|
|
val requestBody = MultipartBody.Builder()
|
|
.setType(MultipartBody.FORM)
|
|
.addFormDataPart("file", System.currentTimeMillis().toString(), imagePart)
|
|
.build()
|
|
|
|
val sub = imagePart.progressSubject
|
|
.subscribeOn(Schedulers.io())
|
|
.subscribe { percentage ->
|
|
progressList[index] = percentage.toInt()
|
|
uploadProgressBar.progress =
|
|
progressList.sum() / progressList.size
|
|
}
|
|
|
|
var postSub: Disposable? = null
|
|
val inter = pixelfedAPI.mediaUpload("Bearer $accessToken", requestBody.parts[0])
|
|
|
|
postSub = inter
|
|
.subscribeOn(Schedulers.io())
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
.subscribe(
|
|
{ attachment: Attachment ->
|
|
progressList[index] = 0
|
|
muListOfIds[index] = attachment.id!!
|
|
},
|
|
{ e ->
|
|
upload_error.visibility = View.VISIBLE
|
|
e.printStackTrace()
|
|
postSub?.dispose()
|
|
sub.dispose()
|
|
},
|
|
{
|
|
progressList[index] = 100
|
|
if(progressList.all{it == 100}){
|
|
enableButton(true)
|
|
uploadProgressBar.visibility = View.GONE
|
|
upload_completed_textview.visibility = View.VISIBLE
|
|
}
|
|
postSub?.dispose()
|
|
sub.dispose()
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun post() {
|
|
val description = new_post_description_input_field.text.toString()
|
|
enableButton(false)
|
|
pixelfedAPI.postStatus(
|
|
authorization = "Bearer $accessToken",
|
|
statusText = description,
|
|
media_ids = muListOfIds.toList()
|
|
).enqueue(object: Callback<Status> {
|
|
override fun onFailure(call: Call<Status>, t: Throwable) {
|
|
enableButton(true)
|
|
Toast.makeText(applicationContext,getString(R.string.upload_post_failed),
|
|
Toast.LENGTH_SHORT).show()
|
|
Log.e(TAG, t.message + call.request())
|
|
}
|
|
|
|
override fun onResponse(call: Call<Status>, response: Response<Status>) {
|
|
if (response.code() == 200) {
|
|
Toast.makeText(applicationContext,getString(R.string.upload_post_success),
|
|
Toast.LENGTH_SHORT).show()
|
|
val intent = Intent(this@PostCreationActivity, MainActivity::class.java)
|
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
startActivity(intent)
|
|
} else {
|
|
Toast.makeText(applicationContext,getString(R.string.upload_post_error),
|
|
Toast.LENGTH_SHORT).show()
|
|
Log.e(TAG, call.request().toString() + response.raw().toString())
|
|
enableButton(true)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
private fun enableButton(enable: Boolean = true){
|
|
post_creation_send_button.isEnabled = enable
|
|
if(enable){
|
|
posting_progress_bar.visibility = View.GONE
|
|
post_creation_send_button.visibility = View.VISIBLE
|
|
} else {
|
|
posting_progress_bar.visibility = View.VISIBLE
|
|
post_creation_send_button.visibility = View.GONE
|
|
}
|
|
|
|
}
|
|
|
|
override fun onClick(position: Int) {
|
|
positionResult = position
|
|
|
|
val intent = Intent(this, PhotoEditActivity::class.java)
|
|
.putExtra("picture_uri", Uri.parse(posts[position]))
|
|
.putExtra("no upload", false)
|
|
startActivityForResult(intent, positionResult)
|
|
}
|
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
super.onActivityResult(requestCode, resultCode, data)
|
|
if (requestCode == positionResult) {
|
|
if (resultCode == Activity.RESULT_OK && data != null) {
|
|
posts[positionResult] = data.getStringExtra("result")!!
|
|
adapter.notifyItemChanged(positionResult)
|
|
muListOfIds[positionResult] = ""
|
|
progressList[positionResult] = 0
|
|
upload(editedImage = positionResult)
|
|
} else if(resultCode == Activity.RESULT_CANCELED){
|
|
Toast.makeText(applicationContext, "Editing canceled", Toast.LENGTH_SHORT).show()
|
|
} else {
|
|
Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show()
|
|
}
|
|
} else if (requestCode == MORE_PICTURES_REQUEST_CODE) {
|
|
if (resultCode == Activity.RESULT_OK && data?.clipData != null) {
|
|
|
|
val count = data.clipData!!.itemCount
|
|
for (i in 0 until count) {
|
|
val imageUri: String = data.clipData!!.getItemAt(i).uri.toString()
|
|
posts.add(imageUri)
|
|
progressList.add(0)
|
|
muListOfIds.add("")
|
|
}
|
|
adapter.notifyDataSetChanged()
|
|
upload(newImagesStartingIndex = posts.size - count)
|
|
} else if(resultCode == Activity.RESULT_CANCELED){
|
|
Toast.makeText(applicationContext, "Adding images canceled", Toast.LENGTH_SHORT).show()
|
|
} else {
|
|
Toast.makeText(applicationContext, "Error while adding images", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
|
|
inner class PostCreationAdapter(private val posts: ArrayList<String>): RecyclerView.Adapter<PostCreationAdapter.ViewHolder>() {
|
|
var listener: PostCreationListener? = null
|
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
|
val view =
|
|
if(viewType == 0) LayoutInflater.from(parent.context)
|
|
.inflate(R.layout.image_album_creation, parent, false)
|
|
else LayoutInflater.from(parent.context)
|
|
.inflate(R.layout.add_more_album_creation, parent, false)
|
|
return ViewHolder(view)
|
|
}
|
|
|
|
override fun getItemViewType(position: Int): Int {
|
|
if(position == posts.size) return 1
|
|
return 0
|
|
}
|
|
|
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
|
if(position != posts.size) {
|
|
holder.bindImage()
|
|
} else{
|
|
holder.bindPlusButton()
|
|
}
|
|
}
|
|
|
|
override fun getItemCount(): Int = posts.size + 1
|
|
|
|
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
|
|
|
fun bindImage() {
|
|
val image = Uri.parse(
|
|
posts[adapterPosition]
|
|
)
|
|
// load image
|
|
Glide.with(itemView.context)
|
|
.load(image)
|
|
.centerCrop()
|
|
.into(itemView.galleryImage)
|
|
// adding click or tap handler for the image layout
|
|
itemView.setOnClickListener {
|
|
listener?.onClick(adapterPosition)
|
|
}
|
|
|
|
}
|
|
|
|
fun bindPlusButton() {
|
|
itemView.setOnClickListener {
|
|
val intent = Intent(itemView.context, CameraActivity::class.java)
|
|
this@PostCreationActivity.startActivityForResult(intent, MORE_PICTURES_REQUEST_CODE)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |