enable creating albums (#229)
* Moved the crop button so that it doesn't take space in the activity * Semi transparent in the middle, same position than the image * First draft of the album creation * choose multiple images in gallery * Added functionalities to Album creation * merge with master * Gallery of images selected for the album creation * to merge with master * Images editable individually * Creation of album is now possible * Added tests * Added test to edit picture selected * merge PostCreation and AlbumCreation * Merged completely PostCreation and AlbumCreation * removed albumCreation in Manifest * Refactored slightly * Don't re-upload all images at each edit, only re-upload one * Make sure all images are uploaded, correctly calculate progress * comment assert, sorry * fix test * fix merge Co-authored-by: Joachim Dunant <joachim.dunant@epfl.ch> Co-authored-by: Matthieu <61561059+Wv5twkFEKh54vo4tta9yu7dHa3@users.noreply.github.com>
This commit is contained in:
parent
a409d69c77
commit
7981ae8643
@ -3,11 +3,9 @@ package com.h.pixeldroid
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.view.View
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.SeekBar
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@ -28,7 +26,6 @@ import com.h.pixeldroid.adapters.ThumbnailAdapter
|
||||
import com.h.pixeldroid.testUtility.CustomMatchers
|
||||
import junit.framework.Assert.assertTrue
|
||||
import kotlinx.android.synthetic.main.fragment_edit_image.*
|
||||
import org.hamcrest.CoreMatchers
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
@ -37,7 +34,6 @@ import org.junit.Test
|
||||
import org.junit.rules.Timeout
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class EditPhotoTest {
|
||||
@ -170,33 +166,9 @@ class EditPhotoTest {
|
||||
@Test
|
||||
fun backButton() {
|
||||
Espresso.onView(withId(R.id.toolbar)).check(matches(isDisplayed()))
|
||||
Espresso.onView(withContentDescription(R.string.abc_action_bar_up_description)).perform(click());
|
||||
Espresso.onView(withContentDescription(R.string.abc_action_bar_up_description)).perform(click())
|
||||
assertTrue(activityScenario.state == Lifecycle.State.DESTROYED) }
|
||||
|
||||
@Test
|
||||
fun buttonUploadLaunchNewPostActivity() {
|
||||
Espresso.onView(withId(R.id.action_upload)).perform(click())
|
||||
Thread.sleep(1000)
|
||||
Espresso.onView(withId(R.id.post_creation_picture_frame)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun modifiedUploadLaunchesNewPostActivity() {
|
||||
Espresso.onView(withId(R.id.recycler_view))
|
||||
.perform(actionOnItemAtPosition<ThumbnailAdapter.MyViewHolder>(2, CustomMatchers.clickChildViewWithId(R.id.thumbnail)))
|
||||
Thread.sleep(1000)
|
||||
|
||||
Espresso.onView(withId(R.id.tabs)).perform(selectTabAtPosition(1))
|
||||
Espresso.onView(withId(R.id.seekbar_brightness)).perform(setProgress(5))
|
||||
Thread.sleep(1000)
|
||||
|
||||
Espresso.onView(withId(R.id.action_upload)).perform(click())
|
||||
Thread.sleep(1000)
|
||||
|
||||
|
||||
Espresso.onView(withId(R.id.post_creation_picture_frame)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun croppingIsPossible() {
|
||||
Espresso.onView(withId(R.id.cropImageButton)).perform(click())
|
||||
|
@ -1,25 +1,28 @@
|
||||
package com.h.pixeldroid
|
||||
|
||||
import android.content.Context
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.core.net.toUri
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.h.pixeldroid.adapters.ThumbnailAdapter
|
||||
import com.h.pixeldroid.db.AppDatabase
|
||||
import com.h.pixeldroid.db.InstanceDatabaseEntity
|
||||
import com.h.pixeldroid.db.UserDatabaseEntity
|
||||
import com.h.pixeldroid.testUtility.CustomMatchers
|
||||
import com.h.pixeldroid.testUtility.MockServer
|
||||
import com.h.pixeldroid.utils.DBUtils
|
||||
import kotlinx.android.synthetic.main.activity_post_creation.*
|
||||
@ -42,6 +45,9 @@ class PostCreationActivityTest {
|
||||
@get:Rule
|
||||
val globalTimeout: Timeout = Timeout.seconds(30)
|
||||
|
||||
@get:Rule
|
||||
val mRuntimePermissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
|
||||
private fun File.writeBitmap(bitmap: Bitmap) {
|
||||
outputStream().use { out ->
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 85, out)
|
||||
@ -76,21 +82,28 @@ class PostCreationActivityTest {
|
||||
)
|
||||
db.close()
|
||||
|
||||
var uri: Uri = "".toUri()
|
||||
val scenario = ActivityScenario.launch(ProfileActivity::class.java)
|
||||
var uri1: String = ""
|
||||
var uri2: String = ""
|
||||
val scenario = ActivityScenario.launch(MainActivity::class.java)
|
||||
scenario.onActivity {
|
||||
val image = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888)
|
||||
image.eraseColor(Color.GREEN)
|
||||
val image1 = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888)
|
||||
image1.eraseColor(Color.GREEN)
|
||||
val image2 = Bitmap.createBitmap(270, 270, Bitmap.Config.ARGB_8888)
|
||||
image2.eraseColor(Color.RED)
|
||||
val folder =
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
|
||||
if (!folder.exists()) {
|
||||
folder.mkdir()
|
||||
}
|
||||
val file = File.createTempFile("temp_img", ".png", folder)
|
||||
file.writeBitmap(image)
|
||||
uri = file.toUri()
|
||||
val file1 = File.createTempFile("temp_img1", ".png", folder)
|
||||
val file2 = File.createTempFile("temp_img2", ".png", folder)
|
||||
file1.writeBitmap(image1)
|
||||
uri1 = file1.toUri().toString()
|
||||
file2.writeBitmap(image2)
|
||||
uri2 = file2.toUri().toString()
|
||||
Log.d("test", uri1+"\n"+uri2)
|
||||
}
|
||||
val intent = Intent(context, PostCreationActivity::class.java).putExtra("picture_uri", uri)
|
||||
val intent = Intent(context, PostCreationActivity::class.java).putExtra("pictures_uri", arrayListOf(uri1, uri2))
|
||||
testScenario = ActivityScenario.launch(intent)
|
||||
}
|
||||
|
||||
@ -108,4 +121,38 @@ class PostCreationActivityTest {
|
||||
// should send on main activity
|
||||
onView(withId(R.id.retry_upload_button)).check(matches(not(isDisplayed())))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun editImage() {
|
||||
onView(withId(R.id.image_grid)).perform(
|
||||
RecyclerViewActions.actionOnItemAtPosition<PostCreationActivity.PostCreationAdapter.ViewHolder>(
|
||||
0,
|
||||
CustomMatchers.clickChildViewWithId(R.id.galleryImage)
|
||||
)
|
||||
)
|
||||
Thread.sleep(1000)
|
||||
|
||||
onView(withId(R.id.recycler_view))
|
||||
.perform(
|
||||
RecyclerViewActions.actionOnItemAtPosition<ThumbnailAdapter.MyViewHolder>(
|
||||
2,
|
||||
CustomMatchers.clickChildViewWithId(R.id.thumbnail)
|
||||
)
|
||||
)
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.action_upload)).perform(click())
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.image_grid)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cancelEdit() {
|
||||
onView(withId(R.id.image_grid)).perform(
|
||||
RecyclerViewActions.actionOnItemAtPosition<PostCreationActivity.PostCreationAdapter.ViewHolder>(
|
||||
0,
|
||||
CustomMatchers.clickChildViewWithId(R.id.galleryImage)
|
||||
)
|
||||
)
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
}
|
@ -27,7 +27,6 @@
|
||||
<activity
|
||||
android:name=".PhotoEditActivity"
|
||||
android:theme="@style/AppTheme.NoActionBar"/>
|
||||
|
||||
<activity android:name=".PostCreationActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
tools:ignore="LockedOrientationActivity"
|
||||
|
Binary file not shown.
@ -88,7 +88,6 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
System.loadLibrary("NativeImageProcessor")
|
||||
}
|
||||
|
||||
|
||||
companion object{
|
||||
private var executor: ExecutorService = newSingleThreadExecutor()
|
||||
private var future: Future<*>? = null
|
||||
@ -114,7 +113,7 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
|
||||
initialUri = intent.getParcelableExtra("picture_uri")
|
||||
imageUri = initialUri
|
||||
|
||||
|
||||
// set on-click listener
|
||||
cropButton.setOnClickListener {
|
||||
startCrop()
|
||||
@ -187,12 +186,12 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
//</editor-fold>
|
||||
//<editor-fold desc="FILTERS">
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
//</editor-fold>
|
||||
override fun onFilterSelected(filter: Filter) {
|
||||
resetControls()
|
||||
filteredImage = compressedOriginalImage!!.copy(BITMAP_CONFIG, true)
|
||||
@ -346,11 +345,15 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
return finalImage
|
||||
}
|
||||
|
||||
private fun uploadImage(file: String) {
|
||||
val intent = Intent (applicationContext, PostCreationActivity::class.java)
|
||||
intent.putExtra("picture_uri", Uri.parse(file))
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
applicationContext!!.startActivity(intent)
|
||||
private fun sendBackImage(file: String) {
|
||||
val intent = Intent(this, PostCreationActivity::class.java)
|
||||
.apply {
|
||||
putExtra("result", file)
|
||||
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
||||
}
|
||||
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun saveImageToGallery(save: Boolean) {
|
||||
@ -464,8 +467,8 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
}
|
||||
if(saving) {
|
||||
this.runOnUiThread {
|
||||
if (!save) {
|
||||
uploadImage(path)
|
||||
if(!save) {
|
||||
sendBackImage(path)
|
||||
} else {
|
||||
MediaScannerConnection.scanFile(
|
||||
this,
|
||||
|
@ -1,56 +1,59 @@
|
||||
package com.h.pixeldroid
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.db.UserDatabaseEntity
|
||||
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.DBUtils
|
||||
import com.mikepenz.iconics.Iconics
|
||||
import io.reactivex.Observable
|
||||
import com.h.pixeldroid.utils.ProgressRequestBody
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import kotlinx.android.synthetic.main.activity_post_creation.*
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import kotlinx.android.synthetic.main.image_album_creation.view.*
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import okio.BufferedSink
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.io.*
|
||||
|
||||
|
||||
class PostCreationActivity : AppCompatActivity(){
|
||||
class PostCreationActivity : AppCompatActivity(), PostCreationListener {
|
||||
|
||||
private val TAG = "Post Creation Activity"
|
||||
|
||||
private lateinit var recycler : RecyclerView
|
||||
private lateinit var adapter : PostCreationAdapter
|
||||
|
||||
private lateinit var accessToken: String
|
||||
private lateinit var pixelfedAPI: PixelfedAPI
|
||||
private lateinit var pictureFrame: ImageView
|
||||
private lateinit var imageUri: Uri
|
||||
|
||||
private var muListOfIds: MutableList<String> = mutableListOf()
|
||||
private var progressList: ArrayList<Int> = arrayListOf()
|
||||
|
||||
|
||||
private var positionResult = 0
|
||||
private var user: UserDatabaseEntity? = null
|
||||
|
||||
private var listOfIds: List<String> = emptyList()
|
||||
private var posts: ArrayList<String> = ArrayList()
|
||||
|
||||
private var maxLength: Int = Instance.DEFAULT_MAX_TOOT_CHARS
|
||||
|
||||
@ -58,14 +61,13 @@ class PostCreationActivity : AppCompatActivity(){
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Iconics.init(this)
|
||||
setContentView(R.layout.activity_post_creation)
|
||||
|
||||
imageUri = intent.getParcelableExtra("picture_uri")!!
|
||||
|
||||
pictureFrame = findViewById(R.id.post_creation_picture_frame)
|
||||
Glide.with(this).load(imageUri).into(pictureFrame)
|
||||
// load images
|
||||
posts = intent.getStringArrayListExtra("pictures_uri")!!
|
||||
|
||||
progressList = posts.map { 0 } as ArrayList<Int>
|
||||
muListOfIds = posts.map { "" }.toMutableList()
|
||||
|
||||
val db = DBUtils.initDB(applicationContext)
|
||||
user = db.userDao().getActiveUser()
|
||||
@ -86,34 +88,38 @@ class PostCreationActivity : AppCompatActivity(){
|
||||
accessToken = user?.accessToken.orEmpty()
|
||||
pixelfedAPI = PixelfedAPI.create(domain)
|
||||
|
||||
// check if the pictures are alright
|
||||
// TODO
|
||||
|
||||
//upload the picture and display progress while doing so
|
||||
upload()
|
||||
|
||||
adapter = PostCreationAdapter(posts)
|
||||
adapter.listener = this
|
||||
recycler = findViewById(R.id.image_grid)
|
||||
recycler.layoutManager = GridLayoutManager(this, if (posts.size > 2) 2 else 1)
|
||||
recycler.adapter = adapter
|
||||
|
||||
// get the description and send the post
|
||||
findViewById<Button>(R.id.post_creation_send_button).setOnClickListener {
|
||||
if (setDescription() && listOfIds.isNotEmpty()) post()
|
||||
if (setDescription() && muListOfIds.isNotEmpty()) post()
|
||||
}
|
||||
|
||||
|
||||
// Button to retry image upload when it fails
|
||||
findViewById<Button>(R.id.retry_upload_button).setOnClickListener {
|
||||
upload_error.visibility = GONE
|
||||
upload_error.visibility = View.GONE
|
||||
muListOfIds = posts.map { "" }.toMutableList()
|
||||
progressList = posts.map { 0 } as ArrayList<Int>
|
||||
upload()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
//delete the temporary image
|
||||
//image.delete()
|
||||
}
|
||||
|
||||
private fun setDescription(): Boolean {
|
||||
val textField = findViewById<TextInputEditText>(R.id.new_post_description_input_field)
|
||||
val content = textField.text.toString()
|
||||
if (content.length > maxLength) {
|
||||
// error, too many characters
|
||||
textField.error = getString(R.string.description_max_characters).format(maxLength)
|
||||
|
||||
// error, too much characters
|
||||
textField.error = "Description must contain $maxLength characters at most."
|
||||
return false
|
||||
}
|
||||
// store the description
|
||||
@ -122,57 +128,69 @@ class PostCreationActivity : AppCompatActivity(){
|
||||
}
|
||||
|
||||
private fun upload() {
|
||||
val imageInputStream = contentResolver.openInputStream(imageUri)!!
|
||||
for ((index, post) in posts.withIndex()) {
|
||||
val imageUri = Uri.parse(post)
|
||||
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 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 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 ->
|
||||
uploadProgressBar.progress = percentage.toInt()
|
||||
}
|
||||
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])
|
||||
var postSub: Disposable? = null
|
||||
val inter = pixelfedAPI.mediaUpload("Bearer $accessToken", requestBody.parts[0])
|
||||
|
||||
postSub = inter
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ attachment ->
|
||||
listOfIds = listOf(attachment.id!!)
|
||||
},{e->
|
||||
upload_error.visibility = VISIBLE
|
||||
e.printStackTrace()
|
||||
postSub?.dispose()
|
||||
sub.dispose()
|
||||
}, {
|
||||
uploadProgressBar.visibility = GONE
|
||||
upload_completed_textview.visibility = VISIBLE
|
||||
enableButton(true)
|
||||
postSub?.dispose()
|
||||
sub.dispose()
|
||||
})
|
||||
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() {
|
||||
@ -180,7 +198,7 @@ class PostCreationActivity : AppCompatActivity(){
|
||||
pixelfedAPI.postStatus(
|
||||
authorization = "Bearer $accessToken",
|
||||
statusText = description,
|
||||
media_ids = listOfIds
|
||||
media_ids = muListOfIds.toList()
|
||||
).enqueue(object: Callback<Status> {
|
||||
override fun onFailure(call: Call<Status>, t: Throwable) {
|
||||
enableButton(true)
|
||||
@ -205,67 +223,79 @@ class PostCreationActivity : AppCompatActivity(){
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun enableButton(enable: Boolean = true){
|
||||
post_creation_send_button.isEnabled = enable
|
||||
if(enable){
|
||||
posting_progress_bar.visibility = GONE
|
||||
post_creation_send_button.visibility = VISIBLE
|
||||
posting_progress_bar.visibility = View.GONE
|
||||
post_creation_send_button.visibility = View.VISIBLE
|
||||
} else{
|
||||
posting_progress_bar.visibility = VISIBLE
|
||||
post_creation_send_button.visibility = GONE
|
||||
posting_progress_bar.visibility = View.VISIBLE
|
||||
post_creation_send_button.visibility = View.GONE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
override fun onClick(position: Int) {
|
||||
positionResult = position
|
||||
|
||||
class ProgressRequestBody(private val mFile: InputStream, private val length: Long) : RequestBody() {
|
||||
|
||||
private val getProgressSubject: PublishSubject<Float> = PublishSubject.create()
|
||||
|
||||
val progressSubject: Observable<Float>
|
||||
get() {
|
||||
return getProgressSubject
|
||||
}
|
||||
|
||||
|
||||
override fun contentType(): MediaType? {
|
||||
return "image/png".toMediaTypeOrNull()
|
||||
val intent = Intent(this, PhotoEditActivity::class.java)
|
||||
.putExtra("picture_uri", Uri.parse(posts[position]))
|
||||
.putExtra("no upload", false)
|
||||
startActivityForResult(intent, positionResult)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun contentLength(): Long {
|
||||
return length
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
val fileLength = contentLength()
|
||||
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||
var uploaded: Long = 0
|
||||
|
||||
mFile.use {
|
||||
var read: Int
|
||||
var lastProgressPercentUpdate = 0.0f
|
||||
read = it.read(buffer)
|
||||
while (read != -1) {
|
||||
|
||||
uploaded += read.toLong()
|
||||
sink.write(buffer, 0, read)
|
||||
read = it.read(buffer)
|
||||
|
||||
val progress = (uploaded.toFloat() / fileLength.toFloat()) * 100f
|
||||
//prevent publishing too many updates, which slows upload, by checking if the upload has progressed by at least 1 percent
|
||||
if (progress - lastProgressPercentUpdate > 1 || progress == 100f) {
|
||||
// publish progress
|
||||
getProgressSubject.onNext(progress)
|
||||
lastProgressPercentUpdate = progress
|
||||
}
|
||||
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.clear()
|
||||
upload()
|
||||
}
|
||||
else if(resultCode == Activity.RESULT_CANCELED){
|
||||
Toast.makeText(applicationContext, "Edition cancelled", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_BUFFER_SIZE = 2048
|
||||
class PostCreationAdapter(private val posts: ArrayList<String>): RecyclerView.Adapter<PostCreationAdapter.ViewHolder>() {
|
||||
private var context: Context? = null
|
||||
var listener: PostCreationListener? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
context = parent.context
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.image_album_creation, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
Log.d("test", "binded")
|
||||
holder.bind()
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = posts.size
|
||||
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
fun bind() {
|
||||
val image = Uri.parse(posts[adapterPosition])
|
||||
// load image
|
||||
Glide.with(context!!)
|
||||
.load(image)
|
||||
.centerCrop()
|
||||
.into(itemView.galleryImage)
|
||||
|
||||
// adding click or tap handler for the image layout
|
||||
itemView.galleryImage.setOnClickListener {
|
||||
Log.d("test", "clicked")
|
||||
listener?.onClick(adapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package com.h.pixeldroid.fragments
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
@ -30,6 +29,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.h.pixeldroid.PhotoEditActivity
|
||||
import com.h.pixeldroid.PostCreationActivity
|
||||
import com.h.pixeldroid.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@ -282,6 +282,7 @@ class CameraFragment : Fragment() {
|
||||
action = Intent.ACTION_GET_CONTENT
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_LOCAL_ONLY, true)
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
startActivityForResult(
|
||||
Intent.createChooser(this, "Select a Picture"), PICK_IMAGE_REQUEST
|
||||
)
|
||||
@ -343,7 +344,9 @@ class CameraFragment : Fragment() {
|
||||
|
||||
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
|
||||
val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
|
||||
startPostCreation(savedUri)
|
||||
val uri: ArrayList<String> = ArrayList()
|
||||
uri.add(savedUri.toString())
|
||||
startAlbumCreation(uri)
|
||||
}
|
||||
})
|
||||
|
||||
@ -359,19 +362,30 @@ class CameraFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK && data != null
|
||||
&& (requestCode == PICK_IMAGE_REQUEST || requestCode == CAPTURE_IMAGE_REQUEST)
|
||||
&& data.data != null) {
|
||||
&& (requestCode == PICK_IMAGE_REQUEST || requestCode == CAPTURE_IMAGE_REQUEST)) {
|
||||
|
||||
startPostCreation(data.data!!)
|
||||
val images: ArrayList<String> = ArrayList()
|
||||
if (data.clipData != null) {
|
||||
val count = data.clipData!!.itemCount
|
||||
for (i in 0 until count) {
|
||||
val imageUri: String = data.clipData!!.getItemAt(i).uri.toString()
|
||||
images.add(imageUri)
|
||||
}
|
||||
startAlbumCreation(images)
|
||||
} else if (data.data != null) {
|
||||
images.add(data.data!!.toString())
|
||||
startAlbumCreation(images)
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun startPostCreation(uri: Uri) {
|
||||
startActivity(
|
||||
Intent(activity, PhotoEditActivity::class.java)
|
||||
.putExtra("picture_uri", uri)
|
||||
)
|
||||
|
||||
private fun startAlbumCreation(uris: ArrayList<String>) {
|
||||
startActivity(
|
||||
Intent(activity, PostCreationActivity::class.java)
|
||||
.putExtra("pictures_uri", uris)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -0,0 +1,5 @@
|
||||
package com.h.pixeldroid.interfaces
|
||||
|
||||
interface PostCreationListener {
|
||||
fun onClick(position: Int)
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.h.pixeldroid.utils
|
||||
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.RequestBody
|
||||
import okio.BufferedSink
|
||||
import java.io.*
|
||||
|
||||
class ProgressRequestBody(private val mFile: InputStream, private val length: Long) : RequestBody() {
|
||||
|
||||
private val getProgressSubject: PublishSubject<Float> = PublishSubject.create()
|
||||
|
||||
val progressSubject: Observable<Float>
|
||||
get() {
|
||||
return getProgressSubject
|
||||
}
|
||||
|
||||
override fun contentType(): MediaType? {
|
||||
return "image/png".toMediaTypeOrNull()
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun contentLength(): Long {
|
||||
return length
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
val fileLength = contentLength()
|
||||
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||
var uploaded: Long = 0
|
||||
|
||||
mFile.use {
|
||||
var read: Int
|
||||
var lastProgressPercentUpdate = 0.0f
|
||||
read = it.read(buffer)
|
||||
while (read != -1) {
|
||||
|
||||
uploaded += read.toLong()
|
||||
sink.write(buffer, 0, read)
|
||||
read = it.read(buffer)
|
||||
|
||||
val progress = (uploaded.toFloat() / fileLength.toFloat()) * 100f
|
||||
//prevent publishing too many updates, which slows upload, by checking if the upload has progressed by at least 1 percent
|
||||
if (progress - lastProgressPercentUpdate > 1 || progress == 100f) {
|
||||
// publish progress
|
||||
getProgressSubject.onNext(progress)
|
||||
lastProgressPercentUpdate = progress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_BUFFER_SIZE = 2048
|
||||
}
|
||||
}
|
15
app/src/main/java/com/h/pixeldroid/utils/SquareLayout.kt
Normal file
15
app/src/main/java/com/h/pixeldroid/utils/SquareLayout.kt
Normal file
@ -0,0 +1,15 @@
|
||||
package com.h.pixeldroid.utils
|
||||
|
||||
import android.widget.RelativeLayout
|
||||
import android.os.Build
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
|
||||
internal class SquareLayout(context: Context, attrs: AttributeSet) :
|
||||
RelativeLayout(context, attrs) {
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, widthMeasureSpec)
|
||||
}
|
||||
}
|
10
app/src/main/res/drawable/ic_baseline_edit_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_edit_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||
</vector>
|
@ -42,17 +42,17 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/post_creation_picture_frame"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/image_grid"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:contentDescription="@string/posting_image_accessibility_hint"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0" />
|
||||
>
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/buttonConstraints"
|
||||
@ -88,10 +88,6 @@
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/uploadProgressBar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
|
23
app/src/main/res/layout/image_album_creation.xml
Normal file
23
app/src/main/res/layout/image_album_creation.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.h.pixeldroid.utils.SquareLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:id="@+id/galleryImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"/>
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:src="@drawable/ic_baseline_edit_24"/>
|
||||
</RelativeLayout>
|
||||
</com.h.pixeldroid.utils.SquareLayout>
|
Loading…
x
Reference in New Issue
Block a user