Carousel in the PostCreationActivity, change PhotoEditActivity to be more intuitive

This commit is contained in:
Matthieu 2021-01-09 16:19:26 +01:00
parent 1a2a971de6
commit d6e7d2377f
13 changed files with 265 additions and 178 deletions

View File

@ -157,6 +157,7 @@ dependencies {
implementation 'com.github.ligi.tracedroid:lib:3.0'
implementation 'com.github.ligi.tracedroid:supportemail:3.0'
implementation 'com.github.ImaginativeShohag:Why-Not-Image-Carousel:v1.1.0'
/**
* Not in release, so not mentioned in licenses list

View File

@ -1,9 +1,16 @@
package com.h.pixeldroid.postCreation
import android.app.Activity
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Intent
import android.graphics.Bitmap
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.provider.OpenableColumns
import android.util.Log
import android.view.LayoutInflater
@ -12,10 +19,12 @@ import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.core.net.toFile
import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputLayout
import com.h.pixeldroid.utils.BaseActivity
import com.h.pixeldroid.MainActivity
@ -33,11 +42,18 @@ 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 org.imaginativeworld.whynotimagecarousel.CarouselItem
import org.imaginativeworld.whynotimagecarousel.ImageCarousel
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException
import retrofit2.Response
import java.io.File
import java.io.IOException
import java.io.OutputStream
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
private const val TAG = "Post Creation Activity"
private const val MORE_PICTURES_REQUEST_CODE = 0xffff
@ -45,9 +61,6 @@ private const val MORE_PICTURES_REQUEST_CODE = 0xffff
class PostCreationActivity : BaseActivity() {
private lateinit var recycler : RecyclerView
private lateinit var adapter : PostCreationAdapter
private lateinit var accessToken: String
private lateinit var pixelfedAPI: PixelfedAPI
@ -100,10 +113,9 @@ class PostCreationActivity : BaseActivity() {
progressList = posts.map { 0 } as ArrayList<Int>
upload()
adapter = PostCreationAdapter(posts)
recycler = findViewById(R.id.image_grid)
recycler.layoutManager = GridLayoutManager(this, 3)
recycler.adapter = adapter
val carousel: ImageCarousel = findViewById(R.id.carousel)
carousel.addData(posts.map { CarouselItem(it) })
// get the description and send the post
findViewById<Button>(R.id.post_creation_send_button).setOnClickListener {
@ -117,7 +129,79 @@ class PostCreationActivity : BaseActivity() {
progressList = posts.map { 0 } as ArrayList<Int>
upload()
}
findViewById<Button>(R.id.editPhotoButton).setOnClickListener {
onClick(carousel.currentPosition)
}
findViewById<Button>(R.id.addPhotoButton).setOnClickListener {
val intent = Intent(it.context, CameraActivity::class.java)
this@PostCreationActivity.startActivityForResult(intent, MORE_PICTURES_REQUEST_CODE)
}
findViewById<Button>(R.id.savePhotoButton).setOnClickListener {
val name = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US)
.format(System.currentTimeMillis()) + ".png"
val pair = getOutputFile(name)
val outputStream: OutputStream = pair.first
val path: String = pair.second
contentResolver.openInputStream(posts[carousel.currentPosition].toUri())!!.use { input ->
outputStream.use { output ->
input.copyTo(output)
}
}
if(path.startsWith("file")) {
MediaScannerConnection.scanFile(
this,
arrayOf(path.toUri().toFile().absolutePath),
null
) { path, uri ->
if (uri == null) {
Log.e(
"NEW IMAGE SCAN FAILED",
"Tried to scan $path, but it failed"
)
}
}
}
Snackbar.make(
it, getString(R.string.save_image_success),
Snackbar.LENGTH_LONG
).show()
}
}
private fun getOutputFile(name: String): Pair<OutputStream, String> {
val outputStream: OutputStream
val path: String
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver: ContentResolver = contentResolver
val contentValues = ContentValues()
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name)
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
contentValues.put(
MediaStore.MediaColumns.RELATIVE_PATH,
Environment.DIRECTORY_PICTURES
)
val imageUri: Uri =
resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)!!
path = imageUri.toString()
outputStream = resolver.openOutputStream(Objects.requireNonNull(imageUri))!!
} else {
@Suppress("DEPRECATION") val imagesDir =
Environment.getExternalStoragePublicDirectory(getString(R.string.app_name))
imagesDir.mkdir()
val file = File(imagesDir, name)
path = Uri.fromFile(file).toString()
outputStream = file.outputStream()
}
return Pair(outputStream, path)
}
private fun validateDescription(): Boolean {
val textField = findViewById<TextInputLayout>(R.id.postTextInputLayout)
@ -268,13 +352,13 @@ class PostCreationActivity : BaseActivity() {
if (requestCode == positionResult) {
if (resultCode == Activity.RESULT_OK && data != null) {
posts[positionResult] = data.getStringExtra("result")!!
adapter.notifyItemChanged(positionResult)
carousel.addData(posts.map { CarouselItem(it) })
muListOfIds[positionResult] = ""
progressList[positionResult] = 0
upload(editedImage = positionResult)
} else if(resultCode == Activity.RESULT_CANCELED){
Toast.makeText(applicationContext, "Editing canceled", Toast.LENGTH_SHORT).show()
} else {
} else if(resultCode != Activity.RESULT_CANCELED){
Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show()
}
} else if (requestCode == MORE_PICTURES_REQUEST_CODE) {
@ -287,16 +371,16 @@ class PostCreationActivity : BaseActivity() {
progressList.add(0)
muListOfIds.add("")
}
adapter.notifyDataSetChanged()
carousel.addData(posts.map { CarouselItem(it) })
upload(newImagesStartingIndex = posts.size - count)
} else if(resultCode == Activity.RESULT_CANCELED){
Toast.makeText(applicationContext, "Adding images canceled", Toast.LENGTH_SHORT).show()
} else {
} else if(resultCode != Activity.RESULT_CANCELED){
Toast.makeText(applicationContext, "Error while adding images", Toast.LENGTH_SHORT).show()
}
}
}
/*
inner class PostCreationAdapter(private val posts: ArrayList<String>): RecyclerView.Adapter<PostCreationAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -349,4 +433,5 @@ class PostCreationActivity : BaseActivity() {
}
}
}
*/
}

View File

@ -43,46 +43,50 @@ class EditImageFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
seekbarSaturation.max = SATURATION_MAX
seekbarSaturation.progress = SATURATION_START
seekbarBrightness.setOnSeekBarChangeListener(this)
seekbarContrast.setOnSeekBarChangeListener(this)
seekbarSaturation.setOnSeekBarChangeListener(this)
setOnSeekBarChangeListeners(this)
return view
}
private fun setOnSeekBarChangeListeners(listener: EditImageFragment?){
seekbarBrightness.setOnSeekBarChangeListener(listener)
seekbarContrast.setOnSeekBarChangeListener(listener)
seekbarSaturation.setOnSeekBarChangeListener(listener)
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
var prog = progress
if(listener != null) {
listener?.let {
when(seekBar!!.id) {
R.id.seekbar_brightness -> listener!!.onBrightnessChange(progress - 100)
R.id.seekbar_brightness -> it.onBrightnessChange(progress - 100)
R.id.seekbar_saturation -> {
prog += 10
val tempProgress = .10f * prog
listener!!.onSaturationChange(tempProgress)
it.onSaturationChange(.10f * prog)
}
R.id.seekbar_contrast -> {
val tempProgress = .10f * prog
listener!!.onContrastChange(tempProgress)
it.onContrastChange(.10f * prog)
}
}
}
}
fun resetControl() {
// Make sure to ignore seekbar change events, since we don't want to have the reset cause
// filter applications due to the onProgressChanged calls
setOnSeekBarChangeListeners(null)
seekbarBrightness.progress = BRIGHTNESS_START
seekbarContrast.progress = CONTRAST_START
seekbarSaturation.progress = SATURATION_START
setOnSeekBarChangeListeners(this)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
if(listener != null)
listener!!.onEditStarted()
listener?.onEditStarted()
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
if(listener != null)
listener!!.onEditCompleted()
listener?.onEditCompleted()
}
fun setListener(listener: PhotoEditActivity) {

View File

@ -32,7 +32,6 @@ class FilterListFragment : Fragment() {
val view = inflater.inflate(R.layout.fragment_filter_list, container, false)
tbItemList = ArrayList()
adapter = ThumbnailAdapter(requireActivity(), tbItemList, this)
recyclerView = view.findViewById(R.id.recycler_view)
recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
@ -40,6 +39,8 @@ class FilterListFragment : Fragment() {
val space = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics).toInt()
recyclerView.addItemDecoration(SpaceItemDecoration(space))
adapter = ThumbnailAdapter(requireActivity(), tbItemList, this)
recyclerView.adapter = adapter
return view
@ -99,10 +100,13 @@ class FilterListFragment : Fragment() {
}
}
fun resetSelectedFilter(){
adapter.resetSelected()
displayImage(null)
}
fun onFilterSelected(filter: Filter) {
if(listener != null ){
listener!!.onFilterSelected(filter)
}
listener?.onFilterSelected(filter)
}
fun setListener(listFragmentListener: PhotoEditActivity) {

View File

@ -2,20 +2,14 @@ package com.h.pixeldroid.postCreation.photoEdit
import android.app.Activity
import android.app.AlertDialog
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.drawable.BitmapDrawable
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View.GONE
@ -23,15 +17,13 @@ import android.view.View.VISIBLE
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.net.toFile
import androidx.core.net.toUri
import com.bumptech.glide.Glide
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout
import com.h.pixeldroid.utils.BaseActivity
import com.h.pixeldroid.R
import com.h.pixeldroid.postCreation.PostCreationActivity
import com.h.pixeldroid.utils.BaseActivity
import com.yalantis.ucrop.UCrop
import com.zomato.photofilters.imageprocessors.Filter
import com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubFilter
@ -41,8 +33,6 @@ import kotlinx.android.synthetic.main.activity_photo_edit.*
import java.io.File
import java.io.IOException
import java.io.OutputStream
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors.newSingleThreadExecutor
import java.util.concurrent.Future
@ -50,10 +40,11 @@ import java.util.concurrent.Future
// This is an arbitrary number we are using to keep track of the permission
// request. Where an app has multiple context for requesting permission,
// this can help differentiate the different contexts.
private const val REQUEST_CODE_PERMISSIONS_SAVE_PHOTO = 8
private const val REQUEST_CODE_PERMISSIONS_SEND_PHOTO = 7
private val REQUIRED_PERMISSIONS = arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
private val REQUIRED_PERMISSIONS = arrayOf(
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
)
class PhotoEditActivity : BaseActivity() {
@ -108,7 +99,7 @@ class PhotoEditActivity : BaseActivity() {
initialUri = intent.getParcelableExtra("picture_uri")
imageUri = initialUri
// set on-click listener
// Crop button on-click listener
cropButton.setOnClickListener {
startCrop()
}
@ -122,7 +113,6 @@ class PhotoEditActivity : BaseActivity() {
}
//<editor-fold desc="ON LAUNCH">
private fun loadImage() {
originalImage = MediaStore.Images.Media.getBitmap(contentResolver, imageUri)
compressedImage = resizeImage(originalImage!!)
@ -169,43 +159,54 @@ class PhotoEditActivity : BaseActivity() {
when(item.itemId) {
android.R.id.home -> {
onBackPressed()
}
R.id.action_upload -> {
saveImageToGallery(false)
if (noEdits()) onBackPressed()
else {
val builder = AlertDialog.Builder(this)
builder.apply {
setMessage(R.string.save_before_returning)
setPositiveButton(android.R.string.ok) { _, _ ->
saveImageToGallery()
}
setNegativeButton(R.string.no_cancel_edit) { _, _ ->
onBackPressed()
}
}
// Create the AlertDialog
builder.show()
}
}
R.id.action_save -> {
saveImageToGallery(true)
return true
saveImageToGallery()
}
R.id.action_reset -> {
resetControls()
actualFilter = null
imageUri = initialUri
loadImage()
filterListFragment.resetSelectedFilter()
}
}
//<editor-fold desc="FILTERS">
return super.onOptionsItemSelected(item)
}
//</editor-fold>
fun onFilterSelected(filter: Filter) {
resetControls()
filteredImage = compressedOriginalImage!!.copy(BITMAP_CONFIG, true)
image_preview.setImageBitmap(filter.processFilter(filteredImage))
compressedImage = filteredImage.copy(BITMAP_CONFIG, true)
actualFilter = filter
resetControls()
}
private fun resetControls() {
editImageFragment.resetControl()
brightnessFinal = BRIGHTNESS_START
saturationFinal = SATURATION_START
contrastFinal = CONTRAST_START
editImageFragment.resetControl()
}
//</editor-fold>
//<editor-fold desc="EDITS">
private fun applyFilterAndShowImage(filter: Filter, image: Bitmap?) {
future?.cancel(true)
future = executor.submit {
@ -255,8 +256,6 @@ class PhotoEditActivity : BaseActivity() {
compressedImage = myFilter.processFilter(bitmap)
}
//</editor-fold>
//<editor-fold desc="CROPPING">
private fun startCrop() {
val file = File.createTempFile("temp_crop_img", ".png", cacheDir)
@ -314,8 +313,6 @@ class PhotoEditActivity : BaseActivity() {
}
}
//</editor-fold>
//<editor-fold desc="FLOW">
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
@ -325,10 +322,7 @@ class PhotoEditActivity : BaseActivity() {
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
// permission was granted
when (requestCode) {
REQUEST_CODE_PERMISSIONS_SAVE_PHOTO -> permissionsGrantedToSave(true)
REQUEST_CODE_PERMISSIONS_SEND_PHOTO -> permissionsGrantedToSave(false)
}
permissionsGrantedToSave()
} else {
Snackbar.make(coordinator_edit, getString(R.string.permission_denied),
Snackbar.LENGTH_LONG).show()
@ -354,16 +348,16 @@ class PhotoEditActivity : BaseActivity() {
finish()
}
private fun saveImageToGallery(save: Boolean) {
private fun saveImageToGallery() {
// runtime permission and process
if (!allPermissionsGranted()) {
ActivityCompat.requestPermissions(
this,
REQUIRED_PERMISSIONS,
if(save) REQUEST_CODE_PERMISSIONS_SAVE_PHOTO else REQUEST_CODE_PERMISSIONS_SEND_PHOTO
REQUEST_CODE_PERMISSIONS_SEND_PHOTO
)
} else {
permissionsGrantedToSave(save)
permissionsGrantedToSave()
}
}
@ -375,32 +369,6 @@ class PhotoEditActivity : BaseActivity() {
applicationContext, it) == PackageManager.PERMISSION_GRANTED
}
private fun getOutputFile(name: String): Pair<OutputStream, String> {
val outputStream: OutputStream
val path: String
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver: ContentResolver = contentResolver
val contentValues = ContentValues()
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name)
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
contentValues.put(
MediaStore.MediaColumns.RELATIVE_PATH,
Environment.DIRECTORY_PICTURES
)
val imageUri: Uri =
resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)!!
path = imageUri.toString()
outputStream = resolver.openOutputStream(Objects.requireNonNull(imageUri))!!
} else {
val imagesDir =
Environment.getExternalStoragePublicDirectory(getString(R.string.app_name))
imagesDir.mkdir()
val file = File(imagesDir, name)
path = Uri.fromFile(file).toString()
outputStream = file.outputStream()
}
return Pair(outputStream, path)
}
private fun OutputStream.writeBitmap(bitmap: Bitmap) {
use { out ->
@ -410,7 +378,13 @@ class PhotoEditActivity : BaseActivity() {
}
}
private fun permissionsGrantedToSave(save: Boolean) {
private fun noEdits(): Boolean =
brightnessFinal == BRIGHTNESS_START
&& contrastFinal == CONTRAST_START
&& saturationFinal == SATURATION_START
&& actualFilter?.let { it.name == getString(R.string.normal_filter)} ?: true
private fun permissionsGrantedToSave() {
if (saving) {
val builder = AlertDialog.Builder(this)
builder.apply {
@ -424,36 +398,24 @@ class PhotoEditActivity : BaseActivity() {
saving = true
progressBarSaveFile.visibility = VISIBLE
saveFuture = saveExecutor.submit {
val outputStream: OutputStream
var path: String
if (!save) {
//put picture in cache
try {
val path: String
if(!noEdits()) {
// Save modified image in cache
val tempFile = File.createTempFile("temp_edit_img", ".png", cacheDir)
path = Uri.fromFile(tempFile).toString()
outputStream = tempFile.outputStream()
} else {
// Save the picture to gallery
val name = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US)
.format(System.currentTimeMillis()) + ".png"
val pair = getOutputFile(name)
outputStream = pair.first
path = pair.second
}
try {
if(brightnessFinal != BRIGHTNESS_START || contrastFinal != CONTRAST_START
|| saturationFinal != SATURATION_START
|| (actualFilter != null && actualFilter!!.name != getString(R.string.normal_filter))) {
outputStream.writeBitmap(applyFinalFilters(originalImage))
tempFile.outputStream().writeBitmap(applyFinalFilters(originalImage))
}
else {
if(save) {
contentResolver.openInputStream(imageUri!!)!!.use { input ->
outputStream.use { output ->
input.copyTo(output)
}
}
path = imageUri.toString()
}
if(saving) {
this.runOnUiThread {
sendBackImage(path)
progressBarSaveFile.visibility = GONE
saving = false
}
else path = imageUri.toString()
}
} catch (e: IOException) {
this.runOnUiThread {
@ -461,39 +423,10 @@ class PhotoEditActivity : BaseActivity() {
coordinator_edit, getString(R.string.save_image_failed),
Snackbar.LENGTH_LONG
).show()
}
}
if(saving) {
this.runOnUiThread {
if(!save) {
sendBackImage(path)
} else {
if(path.startsWith("file")) {
MediaScannerConnection.scanFile(
this,
arrayOf(path.toUri().toFile().absolutePath),
null
) { path, uri ->
if (uri == null) {
Log.e(
"NEW IMAGE SCAN FAILED",
"Tried to scan $path, but it failed"
)
}
}
}
Snackbar.make(
coordinator_edit, getString(R.string.save_image_success),
Snackbar.LENGTH_LONG
).show()
}
progressBarSaveFile.visibility = GONE
saving = false
}
}
}
}
//</editor-fold>
}

View File

@ -18,6 +18,11 @@ class ThumbnailAdapter (private val context: Context,
private var selectedIndex = 0
fun resetSelected(){
selectedIndex = 0
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(context).inflate(R.layout.thumbnail_list_item, parent, false)
return MyViewHolder(itemView)

View File

@ -404,7 +404,7 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val builder = AlertDialog.Builder(holder.itemView.context)
builder.apply {
setMessage(R.string.delete_dialog)
setPositiveButton(R.string.OK) { _, _ ->
setPositiveButton(android.R.string.ok) { _, _ ->
lifecycleScope.launch {
val user = db.userDao().getActiveUser()!!
@ -420,7 +420,7 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
}
}
}
setNegativeButton(R.string.cancel) { _, _ -> }
setNegativeButton(android.R.string.cancel) { _, _ -> }
show()
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"
android:fillColor="@color/colorDrawing"/>
</vector>

View File

@ -2,9 +2,8 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:fillColor="@color/colorDrawing"
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>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -46,24 +47,22 @@
android:id="@+id/upload_completed_textview"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="@string/media_upload_completed"
android:textColor="@android:color/holo_green_light"
android:textSize="16sp"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@+id/imageButton" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/image_grid"
android:layout_width="0dp"
<org.imaginativeworld.whynotimagecarousel.ImageCarousel
android:id="@+id/carousel"
android:layout_width="match_parent"
android:layout_height="0dp"
android:padding="16dp"
app:layout_constraintBottom_toTopOf="@id/postTextInputLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/upload_completed_textview" />
app:layout_constraintTop_toBottomOf="@id/upload_completed_textview">
</org.imaginativeworld.whynotimagecarousel.ImageCarousel>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/buttonConstraints"
@ -133,4 +132,43 @@
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/addPhotoButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="TODO"
app:icon="@drawable/add_photo_alternate_black_24dp"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/savePhotoButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="TODO"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:layout_constraintBottom_toBottomOf="@+id/addPhotoButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/addPhotoButton"
app:icon="@drawable/download_file_24dp" />
<Button
android:id="@+id/editPhotoButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:contentDescription="TODO"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:layout_constraintBottom_toBottomOf="@+id/addPhotoButton"
app:layout_constraintEnd_toStartOf="@+id/addPhotoButton"
app:layout_constraintTop_toTopOf="@+id/addPhotoButton"
app:icon="@drawable/ic_baseline_edit_24" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,20 +1,20 @@
<?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"
>
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_upload"
android:id="@+id/action_reset"
android:orderInCategory="100"
android:title="CREATE POST"
android:icon="@drawable/ic_file_upload_24dp"
android:title="RESET"
android:icon="@drawable/restore_24dp"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_save"
android:orderInCategory="101"
android:title="SAVE"
android:icon="@drawable/ic_save_24dp"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -149,12 +149,12 @@
<string name="something_went_wrong">Something went wrong…</string>
<string name="panda_pull_to_refresh_to_try_again">This panda is not happy. Pull to refresh to try again.</string>
<string name="delete">Delete</string>
<string name="OK">OK</string>
<string name="delete_dialog">Delete this post?</string>
<string name="cancel">Cancel</string>
<string name="language">Language</string>
<string name="help_translate">Help translate PixelDroid to your language:</string>
<string name="issues_contribute">Report issues or contribute to the application:</string>
<string name="mascot_description">Image showing a red panda, Pixelfed\'s mascot, using a phone</string>
<string name="save_before_returning">Save your edits?</string>
<string name="no_cancel_edit">No, cancel edit</string>
</resources>