Unverified Commit 7981ae86 authored by Sanimys's avatar Sanimys Committed by GitHub

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: default avatarJoachim Dunant <joachim.dunant@epfl.ch>
Co-authored-by: default avatarMatthieu <61561059+Wv5twkFEKh54vo4tta9yu7dHa3@users.noreply.github.com>
parent a409d69c
......@@ -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())
......
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)
}
}
\ No newline at end of file
......@@ -27,7 +27,6 @@
<activity
android:name=".PhotoEditActivity"
android:theme="@style/AppTheme.NoActionBar"/>
<activity android:name=".PostCreationActivity"
android:screenOrientation="sensorPortrait"
tools:ignore="LockedOrientationActivity"
......
No preview for this file type
......@@ -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,
......
......@@ -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) {
startPostCreation(data.data!!)
&& (requestCode == PICK_IMAGE_REQUEST || requestCode == CAPTURE_IMAGE_REQUEST)) {
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) {
private fun startAlbumCreation(uris: ArrayList<String>) {
startActivity(
Intent(activity, PhotoEditActivity::class.java)
.putExtra("picture_uri", uri)
Intent(activity, PostCreationActivity::class.java)
.putExtra("pictures_uri", uris)
)
}
companion object {
......
package com.h.pixeldroid.interfaces
interface PostCreationListener {
fun onClick(position: Int)
}
\ No newline at end of file
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
}
}
\ No newline at end of file
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)
}
}
\ No newline at end of file
<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"
......
<?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>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment