mirror of
https://gitlab.shinice.net/pixeldroid/PixelDroid
synced 2025-02-02 00:26:44 +01:00
Improve upload flow performance & visual feedback (#224)
* Make less copies, detect if no changes are made for a fast path, give some feedback while processing the image * avoid NPE on camera, use more generic inputstream so that file picking works again * stop using resource in test * stop using resource in test * fix uri issue and add test * Test dialog, stringify strings * click error button, for fun * test error button in post creation * check retry of upload works * Remove wrong button click in test * add some tests for followers list * test edit profile button * test back button * try to get all callbacks to be called * Fix typo * Make sure crop is not ignored
This commit is contained in:
parent
9a758ee7bf
commit
0348696f3a
@ -114,13 +114,29 @@ class DrawerMenuTest {
|
||||
onView(withId(R.id.profilePictureImageView)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
/*@Test
|
||||
fun testDrawerAvatarClick() {
|
||||
@Test
|
||||
fun testDrawerOwnProfileFollowers() {
|
||||
// Start the screen of your activity.
|
||||
onView(withText(R.string.menu_account)).perform(click())
|
||||
// Check that profile activity was opened.
|
||||
onView(withId(R.id.profilePictureImageView)).check(matches(isDisplayed()))
|
||||
}*/
|
||||
onView(withId(R.id.editButton)).check(matches(isDisplayed()))
|
||||
val followersText = context.getString(R.string.nb_followers)
|
||||
.format(68)
|
||||
onView(withText(followersText)).perform(click())
|
||||
onView(withText("Dobios")).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDrawerOwnProfileFollowing() {
|
||||
// Start the screen of your activity.
|
||||
onView(withText(R.string.menu_account)).perform(click())
|
||||
// Check that profile activity was opened.
|
||||
onView(withId(R.id.editButton)).check(matches(isDisplayed()))
|
||||
val followingText = context.getString(R.string.nb_following)
|
||||
.format(27)
|
||||
onView(withText(followingText)).perform(click())
|
||||
onView(withText("Dobios")).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
/*@Test
|
||||
fun testDrawerAccountNameClick() {
|
||||
|
@ -1,9 +1,16 @@
|
||||
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
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.espresso.Espresso
|
||||
import androidx.test.espresso.PerformException
|
||||
@ -19,7 +26,9 @@ import androidx.test.rule.GrantPermissionRule
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
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
|
||||
@ -27,6 +36,8 @@ import org.junit.Rule
|
||||
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 {
|
||||
@ -40,12 +51,30 @@ class EditPhotoTest {
|
||||
@get:Rule
|
||||
var mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
|
||||
private fun File.writeBitmap(bitmap: Bitmap) {
|
||||
outputStream().use { out ->
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 85, out)
|
||||
out.flush()
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
|
||||
// Launch PhotoEditActivity
|
||||
val uri: Uri = Uri.parse("android.resource://com.h.pixeldroid/drawable/index")
|
||||
var uri: Uri = "".toUri()
|
||||
val scenario = ActivityScenario.launch(ProfileActivity::class.java)
|
||||
scenario.onActivity {
|
||||
val image = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888)
|
||||
image.eraseColor(Color.GREEN)
|
||||
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 intent = Intent(context, PhotoEditActivity::class.java).putExtra("picture_uri", uri)
|
||||
|
||||
activityScenario = ActivityScenario.launch<PhotoEditActivity>(intent).onActivity{a -> activity = a}
|
||||
@ -138,6 +167,12 @@ class EditPhotoTest {
|
||||
.check(matches(withText(R.string.save_image_success)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun backButton() {
|
||||
Espresso.onView(withId(R.id.toolbar)).check(matches(isDisplayed()))
|
||||
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())
|
||||
@ -145,6 +180,23 @@ class EditPhotoTest {
|
||||
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())
|
||||
@ -152,4 +204,12 @@ class EditPhotoTest {
|
||||
Espresso.onView(withId(R.id.menu_crop)).perform(click())
|
||||
Espresso.onView(withId(R.id.image_preview)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun alreadyUploadingDialog() {
|
||||
activityScenario.onActivity { a -> a.saving = true }
|
||||
Espresso.onView(withId(R.id.action_upload)).perform(click())
|
||||
Thread.sleep(1000)
|
||||
Espresso.onView(withText(R.string.busy_dialog_text)).check(matches(isDisplayed()))
|
||||
}
|
||||
}
|
@ -12,6 +12,10 @@ import androidx.test.espresso.Espresso
|
||||
import androidx.test.espresso.NoMatchingViewException
|
||||
import androidx.test.espresso.UiController
|
||||
import androidx.test.espresso.ViewAction
|
||||
import androidx.test.espresso.action.ViewActions
|
||||
import androidx.test.espresso.assertion.ViewAssertions
|
||||
import androidx.test.espresso.contrib.DrawerActions
|
||||
import androidx.test.espresso.contrib.DrawerMatchers
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.espresso.intent.Intents.intended
|
||||
@ -164,29 +168,28 @@ class IntentTest {
|
||||
}
|
||||
}
|
||||
|
||||
/*@Test
|
||||
fun launchesIntent() {
|
||||
// Open Drawer to click on navigation.
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
Espresso.onView(ViewMatchers.withId(R.id.drawer_layout))
|
||||
.check(ViewAssertions.matches(DrawerMatchers.isClosed(Gravity.LEFT))) // Left Drawer should be closed.
|
||||
.perform(DrawerActions.open()) // Open Drawer
|
||||
|
||||
Espresso.onView(ViewMatchers.withId(R.id.drawer))
|
||||
.perform(NavigationViewActions.navigateTo(R.id.nav_account))
|
||||
@Test
|
||||
fun clickEditProfileMakesIntent() {
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
|
||||
Espresso.onView(ViewMatchers.withId(R.id.drawer_layout))
|
||||
.check(ViewAssertions.matches(DrawerMatchers.isClosed())) // Left Drawer should be closed.
|
||||
.perform(DrawerActions.open()) // Open Drawer
|
||||
|
||||
val expectedIntent: Matcher<Intent> = CoreMatchers.allOf(
|
||||
IntentMatchers.hasAction(Intent.ACTION_VIEW),
|
||||
IntentMatchers.hasDataString(CoreMatchers.containsString("settings/home"))
|
||||
)
|
||||
|
||||
Thread.sleep(1000)
|
||||
|
||||
Espresso.onView(ViewMatchers.withId(R.id.editButton)).perform(ViewActions.click())
|
||||
Thread.sleep(1000)
|
||||
|
||||
// Start the screen of your activity.
|
||||
Espresso.onView(ViewMatchers.withText(R.string.menu_account)).perform(ViewActions.click())
|
||||
// Check that profile activity was opened.
|
||||
Espresso.onView(ViewMatchers.withId(R.id.editButton))
|
||||
.perform(ViewActions.click())
|
||||
intended(expectedIntent)
|
||||
} */
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
fun after() {
|
||||
|
@ -2,7 +2,12 @@ package com.h.pixeldroid
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
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
|
||||
@ -17,15 +22,19 @@ import com.h.pixeldroid.db.InstanceDatabaseEntity
|
||||
import com.h.pixeldroid.db.UserDatabaseEntity
|
||||
import com.h.pixeldroid.testUtility.MockServer
|
||||
import com.h.pixeldroid.utils.DBUtils
|
||||
import kotlinx.android.synthetic.main.activity_post_creation.*
|
||||
import org.hamcrest.Matchers.not
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.Timeout
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PostCreationActivityTest {
|
||||
|
||||
private var testScenario: ActivityScenario<PostCreationActivity>? = null
|
||||
private val mockServer = MockServer()
|
||||
private lateinit var db: AppDatabase
|
||||
|
||||
@ -33,6 +42,13 @@ class PostCreationActivityTest {
|
||||
@get:Rule
|
||||
val globalTimeout: Timeout = Timeout.seconds(30)
|
||||
|
||||
private fun File.writeBitmap(bitmap: Bitmap) {
|
||||
outputStream().use { out ->
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 85, out)
|
||||
out.flush()
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
@ -59,11 +75,23 @@ class PostCreationActivityTest {
|
||||
)
|
||||
)
|
||||
db.close()
|
||||
val uri: Uri = Uri.parse("android.resource://com.h.pixeldroid/drawable/index")
|
||||
val intent = Intent(context, PostCreationActivity::class.java)
|
||||
.putExtra("picture_uri", uri)
|
||||
|
||||
ActivityScenario.launch<PostCreationActivity>(intent)
|
||||
var uri: Uri = "".toUri()
|
||||
val scenario = ActivityScenario.launch(ProfileActivity::class.java)
|
||||
scenario.onActivity {
|
||||
val image = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888)
|
||||
image.eraseColor(Color.GREEN)
|
||||
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 intent = Intent(context, PostCreationActivity::class.java).putExtra("picture_uri", uri)
|
||||
testScenario = ActivityScenario.launch(intent)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -72,4 +100,12 @@ class PostCreationActivityTest {
|
||||
// should send on main activity
|
||||
onView(withId(R.id.main_activity_main_linear_layout)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun errorShown() {
|
||||
testScenario!!.onActivity { a -> a.upload_error.visibility = VISIBLE }
|
||||
onView(withId(R.id.retry_upload_button)).perform(click())
|
||||
// should send on main activity
|
||||
onView(withId(R.id.retry_upload_button)).check(matches(not(isDisplayed())))
|
||||
}
|
||||
}
|
@ -1,20 +1,30 @@
|
||||
package com.h.pixeldroid
|
||||
|
||||
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.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
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
|
||||
@ -30,9 +40,9 @@ import com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubFilter
|
||||
import com.zomato.photofilters.imageprocessors.subfilters.ContrastSubFilter
|
||||
import com.zomato.photofilters.imageprocessors.subfilters.SaturationSubfilter
|
||||
import kotlinx.android.synthetic.main.activity_photo_edit.*
|
||||
import kotlinx.android.synthetic.main.content_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
|
||||
@ -49,6 +59,7 @@ private val REQUIRED_PERMISSIONS = arrayOf(android.Manifest.permission.READ_EXTE
|
||||
|
||||
class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditImageFragmentListener {
|
||||
|
||||
internal var saving: Boolean = false
|
||||
private val BITMAP_CONFIG = Bitmap.Config.ARGB_8888
|
||||
private val BRIGHTNESS_START = 0
|
||||
private val SATURATION_START = 1.0f
|
||||
@ -58,15 +69,12 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
private var compressedImage: Bitmap? = null
|
||||
private var compressedOriginalImage: Bitmap? = null
|
||||
private lateinit var filteredImage: Bitmap
|
||||
private lateinit var finalImage: Bitmap
|
||||
|
||||
private var actualFilter: Filter? = null
|
||||
|
||||
private lateinit var filterListFragment: FilterListFragment
|
||||
private lateinit var editImageFragment: EditImageFragment
|
||||
|
||||
private lateinit var outputDirectory: File
|
||||
|
||||
lateinit var viewPager: NonSwipeableViewPager
|
||||
lateinit var tabLayout: TabLayout
|
||||
|
||||
@ -74,11 +82,6 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
private var saturationFinal = SATURATION_START
|
||||
private var contrastFinal = CONTRAST_START
|
||||
|
||||
private var imageUri: Uri? = null
|
||||
private var cropUri: Uri? = null
|
||||
|
||||
object URI {var picture_uri: Uri? = null}
|
||||
|
||||
init {
|
||||
System.loadLibrary("NativeImageProcessor")
|
||||
}
|
||||
@ -87,6 +90,12 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
companion object{
|
||||
private var executor: ExecutorService = newSingleThreadExecutor()
|
||||
private var future: Future<*>? = null
|
||||
|
||||
private var saveExecutor: ExecutorService = newSingleThreadExecutor()
|
||||
private var saveFuture: Future<*>? = null
|
||||
|
||||
private var initialUri: Uri? = null
|
||||
internal var imageUri: Uri? = null
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -101,7 +110,8 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
|
||||
val cropButton: FloatingActionButton = findViewById(R.id.cropImageButton)
|
||||
|
||||
cropUri = intent.getParcelableExtra("picture_uri")
|
||||
initialUri = intent.getParcelableExtra("picture_uri")
|
||||
imageUri = initialUri
|
||||
|
||||
// set on-click listener
|
||||
cropButton.setOnClickListener {
|
||||
@ -109,25 +119,21 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
}
|
||||
|
||||
loadImage()
|
||||
val file = File.createTempFile("temp_compressed_img", ".png", cacheDir)
|
||||
file.writeBitmap(compressedImage!!)
|
||||
URI.picture_uri = Uri.fromFile(file)
|
||||
|
||||
viewPager = findViewById(R.id.viewPager)
|
||||
tabLayout = findViewById(R.id.tabs)
|
||||
setupViewPager(viewPager)
|
||||
tabLayout.setupWithViewPager(viewPager)
|
||||
outputDirectory = getOutputDirectory()
|
||||
}
|
||||
|
||||
|
||||
//<editor-fold desc="ON LAUNCH">
|
||||
private fun loadImage() {
|
||||
originalImage = MediaStore.Images.Media.getBitmap(contentResolver, cropUri)
|
||||
originalImage = MediaStore.Images.Media.getBitmap(contentResolver, imageUri)
|
||||
compressedImage = resizeImage(originalImage!!.copy(BITMAP_CONFIG, true))
|
||||
compressedOriginalImage = compressedImage!!.copy(BITMAP_CONFIG, true)
|
||||
filteredImage = compressedImage!!.copy(BITMAP_CONFIG, true)
|
||||
image_preview.setImageBitmap(compressedImage)
|
||||
Glide.with(this).load(compressedImage).into(image_preview)
|
||||
}
|
||||
|
||||
private fun resizeImage(image: Bitmap): Bitmap {
|
||||
@ -159,11 +165,16 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
saving = false
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
||||
when(item.itemId) {
|
||||
android.R.id.home -> {
|
||||
super.onBackPressed()
|
||||
onBackPressed()
|
||||
}
|
||||
R.id.action_upload -> {
|
||||
saveImageToGallery(false)
|
||||
@ -253,19 +264,15 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
//<editor-fold desc="CROPPING">
|
||||
|
||||
private fun startCrop() {
|
||||
applyFinalFilters(MediaStore.Images.Media.getBitmap(contentResolver, cropUri))
|
||||
val file = File.createTempFile("temp_crop_img", ".png", cacheDir)
|
||||
file.writeBitmap(finalImage)
|
||||
|
||||
val uCrop: UCrop = UCrop.of(Uri.fromFile(file), URI.picture_uri!!)
|
||||
val uCrop: UCrop = UCrop.of(initialUri!!, Uri.fromFile(file))
|
||||
uCrop.start(this)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if(resultCode == Activity.RESULT_OK) {
|
||||
imageUri = data!!.data
|
||||
|
||||
if (requestCode == UCrop.RESULT_ERROR) {
|
||||
handleCropError(data)
|
||||
} else {
|
||||
@ -286,8 +293,8 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
private fun handleCropResult(data: Intent?) {
|
||||
val resultCrop: Uri? = UCrop.getOutput(data!!)
|
||||
if(resultCrop != null) {
|
||||
imageUri = resultCrop
|
||||
image_preview.setImageURI(resultCrop)
|
||||
|
||||
val bitmap = (image_preview.drawable as BitmapDrawable).bitmap
|
||||
originalImage = bitmap.copy(Bitmap.Config.ARGB_8888, true)
|
||||
compressedImage = resizeImage(originalImage!!.copy(BITMAP_CONFIG, true))
|
||||
@ -295,7 +302,7 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
filteredImage = compressedImage!!.copy(BITMAP_CONFIG, true)
|
||||
resetFilteredImage()
|
||||
} else {
|
||||
Toast.makeText(this, "Cannot retrieve image", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, R.string.crop_result_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,7 +311,7 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
if(resultError != null) {
|
||||
Toast.makeText(this, "" + resultError, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(this, "Unexpected Error", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, R.string.crop_result_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,16 +336,17 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyFinalFilters(image: Bitmap?) {
|
||||
private fun applyFinalFilters(image: Bitmap?): Bitmap {
|
||||
val editFilter = Filter().addEditFilters(brightnessFinal, saturationFinal, contrastFinal)
|
||||
|
||||
finalImage = editFilter.processFilter(image!!.copy(BITMAP_CONFIG, true))
|
||||
var finalImage = editFilter.processFilter(image!!.copy(BITMAP_CONFIG, true))
|
||||
if (actualFilter!=null) finalImage = actualFilter!!.processFilter(finalImage)
|
||||
return finalImage
|
||||
}
|
||||
|
||||
private fun uploadImage(file: File) {
|
||||
private fun uploadImage(file: String) {
|
||||
val intent = Intent (applicationContext, PostCreationActivity::class.java)
|
||||
intent.putExtra("picture_uri", Uri.fromFile(file))
|
||||
intent.putExtra("picture_uri", Uri.parse(file))
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
applicationContext!!.startActivity(intent)
|
||||
}
|
||||
@ -364,45 +372,115 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
||||
applicationContext, it) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
/** Use external media if it is available, our app's file directory otherwise */
|
||||
private fun getOutputDirectory(): File {
|
||||
val appContext = applicationContext
|
||||
val mediaDir = externalMediaDirs.firstOrNull()?.let {
|
||||
File(it, appContext.resources.getString(R.string.app_name)).apply { mkdirs() } }
|
||||
return if (mediaDir != null && mediaDir.exists())
|
||||
mediaDir else appContext.filesDir
|
||||
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 File.writeBitmap(bitmap: Bitmap) {
|
||||
outputStream().use { out ->
|
||||
private fun OutputStream.writeBitmap(bitmap: Bitmap) {
|
||||
use { out ->
|
||||
//(quality is ignored for PNG)
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 85, out)
|
||||
out.flush()
|
||||
}
|
||||
}
|
||||
|
||||
private fun permissionsGrantedToSave(save: Boolean) {
|
||||
val file =
|
||||
if(!save){
|
||||
//put picture in cache
|
||||
File.createTempFile("temp_edit_img", ".png", cacheDir)
|
||||
} else{
|
||||
// Save the picture (quality is ignored for PNG)
|
||||
File(outputDirectory, SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US)
|
||||
.format(System.currentTimeMillis()) + ".png")
|
||||
if (saving) {
|
||||
val builder = AlertDialog.Builder(this)
|
||||
builder.apply {
|
||||
setMessage(R.string.busy_dialog_text)
|
||||
setNegativeButton(R.string.busy_dialog_ok_button) { _, _ -> }
|
||||
}
|
||||
try {
|
||||
applyFinalFilters(originalImage)
|
||||
file.writeBitmap(finalImage)
|
||||
} catch (e: IOException) {
|
||||
Snackbar.make(coordinator_edit, getString(R.string.save_image_failed),
|
||||
Snackbar.LENGTH_LONG).show()
|
||||
// Create the AlertDialog
|
||||
builder.show()
|
||||
return
|
||||
}
|
||||
saving = true
|
||||
progressBarSaveFile.visibility = VISIBLE
|
||||
saveFuture = saveExecutor.submit {
|
||||
val outputStream: OutputStream
|
||||
var path: String
|
||||
if (!save) {
|
||||
//put picture 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))
|
||||
}
|
||||
else {
|
||||
if(save) {
|
||||
contentResolver.openInputStream(imageUri!!)!!.use { input ->
|
||||
outputStream.use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
else path = imageUri.toString()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
this.runOnUiThread {
|
||||
Snackbar.make(
|
||||
coordinator_edit, getString(R.string.save_image_failed),
|
||||
Snackbar.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}
|
||||
if(saving) {
|
||||
this.runOnUiThread {
|
||||
if (!save) {
|
||||
uploadImage(path)
|
||||
} else {
|
||||
val mimeType = MimeTypeMap.getSingleton()
|
||||
.getMimeTypeFromExtension("png")
|
||||
MediaScannerConnection.scanFile(
|
||||
this,
|
||||
arrayOf(path),
|
||||
arrayOf(mimeType), null)
|
||||
|
||||
if (!save) {
|
||||
uploadImage(file)
|
||||
} else {
|
||||
Snackbar.make(coordinator_edit, getString(R.string.save_image_success),
|
||||
Snackbar.LENGTH_LONG).show()
|
||||
Snackbar.make(
|
||||
coordinator_edit, getString(R.string.save_image_success),
|
||||
Snackbar.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
progressBarSaveFile.visibility = GONE
|
||||
saving = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//</editor-fold>
|
||||
|
@ -5,6 +5,7 @@ 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
|
||||
@ -12,13 +13,16 @@ 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 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.objects.Instance
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import com.h.pixeldroid.utils.DBUtils
|
||||
import com.mikepenz.iconics.Iconics
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
@ -43,7 +47,7 @@ class PostCreationActivity : AppCompatActivity(){
|
||||
private lateinit var accessToken: String
|
||||
private lateinit var pixelfedAPI: PixelfedAPI
|
||||
private lateinit var pictureFrame: ImageView
|
||||
private lateinit var image: File
|
||||
private lateinit var imageUri: Uri
|
||||
private var user: UserDatabaseEntity? = null
|
||||
|
||||
private var listOfIds: List<String> = emptyList()
|
||||
@ -54,14 +58,14 @@ class PostCreationActivity : AppCompatActivity(){
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Iconics.init(this)
|
||||
setContentView(R.layout.activity_post_creation)
|
||||
|
||||
val imageUri: Uri = intent.getParcelableExtra("picture_uri")!!
|
||||
|
||||
saveImage(imageUri)
|
||||
imageUri = intent.getParcelableExtra("picture_uri")!!
|
||||
|
||||
pictureFrame = findViewById(R.id.post_creation_picture_frame)
|
||||
pictureFrame.setImageURI(image.toUri())
|
||||
Glide.with(this).load(imageUri).into(pictureFrame)
|
||||
|
||||
|
||||
val db = DBUtils.initDB(applicationContext)
|
||||
user = db.userDao().getActiveUser()
|
||||
@ -100,27 +104,7 @@ class PostCreationActivity : AppCompatActivity(){
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
//delete the temporary image
|
||||
image.delete()
|
||||
}
|
||||
|
||||
private fun saveImage(imageUri: Uri) {
|
||||
try {
|
||||
val stream = applicationContext.contentResolver
|
||||
.openAssetFileDescriptor(imageUri, "r")!!
|
||||
.createInputStream()
|
||||
val bm = BitmapFactory.decodeStream(stream)
|
||||
val bos = ByteArrayOutputStream()
|
||||
bm.compress(Bitmap.CompressFormat.PNG, 0, bos)
|
||||
image = File.createTempFile("temp_compressed_img", ".png", cacheDir)
|
||||
|
||||
val fos = FileOutputStream(image)
|
||||
fos.write(bos.toByteArray())
|
||||
fos.flush()
|
||||
fos.close()
|
||||
} catch (error: IOException) {
|
||||
error.printStackTrace()
|
||||
throw error
|
||||
}
|
||||
//image.delete()
|
||||
}
|
||||
|
||||
private fun setDescription(): Boolean {
|
||||
@ -137,11 +121,30 @@ class PostCreationActivity : AppCompatActivity(){
|
||||
return true
|
||||
}
|
||||
|
||||
private fun upload(){
|
||||
val imagePart = ProgressRequestBody(image)
|
||||
private fun upload() {
|
||||
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", image.name, imagePart)
|
||||
.addFormDataPart("file", System.currentTimeMillis().toString(), imagePart)
|
||||
.build()
|
||||
|
||||
val sub = imagePart.progressSubject
|
||||
@ -216,7 +219,7 @@ class PostCreationActivity : AppCompatActivity(){
|
||||
|
||||
}
|
||||
|
||||
class ProgressRequestBody(private val mFile: File) : RequestBody() {
|
||||
class ProgressRequestBody(private val mFile: InputStream, private val length: Long) : RequestBody() {
|
||||
|
||||
private val getProgressSubject: PublishSubject<Float> = PublishSubject.create()
|
||||
|
||||
@ -232,17 +235,16 @@ class ProgressRequestBody(private val mFile: File) : RequestBody() {
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun contentLength(): Long {
|
||||
return mFile.length()
|
||||
return length
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
val fileLength = contentLength()
|
||||
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||
val `in` = FileInputStream(mFile)
|
||||
var uploaded: Long = 0
|
||||
|
||||
`in`.use {
|
||||
mFile.use {
|
||||
var read: Int
|
||||
var lastProgressPercentUpdate = 0.0f
|
||||
read = it.read(buffer)
|
||||
|
@ -54,8 +54,6 @@ class CameraFragment : Fragment() {
|
||||
|
||||
private lateinit var container: ConstraintLayout
|
||||
private lateinit var viewFinder: PreviewView
|
||||
private lateinit var outputDirectory: File
|
||||
|
||||
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
private val PICK_IMAGE_REQUEST = 1
|
||||
private val CAPTURE_IMAGE_REQUEST = 2
|
||||
@ -138,9 +136,6 @@ class CameraFragment : Fragment() {
|
||||
|
||||
// Every time the orientation of device changes, update rotation for use cases
|
||||
|
||||
// Determine the output directory
|
||||
outputDirectory = getGalleryDirectory(requireContext())
|
||||
|
||||
// Wait for the views to be properly laid out
|
||||
viewFinder.post {
|
||||
|
||||
@ -166,13 +161,13 @@ class CameraFragment : Fragment() {
|
||||
private fun bindCameraUseCases() {
|
||||
|
||||
// Get screen metrics used to setup camera for full screen resolution
|
||||
val metrics = DisplayMetrics().also { viewFinder.display.getRealMetrics(it) }
|
||||
val metrics = DisplayMetrics().also { viewFinder.display?.getRealMetrics(it) }
|
||||
Log.d(TAG, "Screen metrics: ${metrics.widthPixels} x ${metrics.heightPixels}")
|
||||
|
||||
val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels)
|
||||
Log.d(TAG, "Preview aspect ratio: $screenAspectRatio")
|
||||
|
||||
val rotation = viewFinder.display.rotation
|
||||
val rotation = viewFinder.display?.rotation ?: 0
|
||||
|
||||
// Bind the CameraProvider to the LifeCycleOwner
|
||||
val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
|
||||
@ -324,7 +319,7 @@ class CameraFragment : Fragment() {
|
||||
|
||||
// Create output file to hold the image
|
||||
val photoFile = File.createTempFile(
|
||||
"${System.currentTimeMillis()}.jpg", null, context?.cacheDir
|
||||
"cachedPhoto", ".png", context?.cacheDir
|
||||
)
|
||||
|
||||
// Setup image capture metadata
|
||||
@ -385,15 +380,5 @@ class CameraFragment : Fragment() {
|
||||
private const val RATIO_4_3_VALUE = 4.0 / 3.0
|
||||
private const val RATIO_16_9_VALUE = 16.0 / 9.0
|
||||
|
||||
/** Use external media if it is available, our app's file directory otherwise */
|
||||
private fun getGalleryDirectory(context: Context): File {
|
||||
val appContext = context.applicationContext
|
||||
val mediaDir = context.externalMediaDirs.firstOrNull()?.let {
|
||||
File(it, appContext.resources.getString(R.string.app_name)).apply { mkdirs() } }
|
||||
return if (mediaDir != null && mediaDir.exists())
|
||||
mediaDir else appContext.filesDir
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -64,7 +64,7 @@ class EditImageFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||
}
|
||||
R.id.seekbar_contrast -> {
|
||||
val tempProgress = .10f * prog
|
||||
listener!!.onSaturationChange(tempProgress)
|
||||
listener!!.onContrastChange(tempProgress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,17 @@
|
||||
package com.h.pixeldroid.fragments
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.ImageDecoder
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.graphics.decodeBitmap
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -51,10 +55,20 @@ class FilterListFragment : Fragment(), FilterListFragmentListener {
|
||||
return view
|
||||
}
|
||||
|
||||
fun displayImage(bitmap: Bitmap?) {
|
||||
private fun displayImage(bitmap: Bitmap?) {
|
||||
val r = Runnable {
|
||||
val tbImage: Bitmap = (if (bitmap == null) {
|
||||
MediaStore.Images.Media.getBitmap(requireActivity().contentResolver, PhotoEditActivity.URI.picture_uri)
|
||||
// TODO: Shouldn't use deprecated API on newer versions of Android,
|
||||
// but the proper way to do it seems to crash for OpenGL reasons
|
||||
//if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
// ImageDecoder.decodeBitmap(
|
||||
// ImageDecoder.createSource(requireActivity().contentResolver, PhotoEditActivity.imageUri!!))
|
||||
//} else {
|
||||
MediaStore.Images.Media.getBitmap(
|
||||
requireActivity().contentResolver,
|
||||
PhotoEditActivity.imageUri
|
||||
)
|
||||
//}
|
||||
} else {
|
||||
Bitmap.createScaledBitmap(bitmap, 100, 100, false)
|
||||
})
|
||||
@ -75,7 +89,8 @@ class FilterListFragment : Fragment(), FilterListFragmentListener {
|
||||
|
||||
val tbItem = ThumbnailItem()
|
||||
tbItem.image = tbImage
|
||||
tbItem.filterName = getString(R.string.normal_filter)
|
||||
tbItem.filter.name = getString(R.string.normal_filter)
|
||||
tbItem.filterName = tbItem.filter.name
|
||||
ThumbnailsManager.addThumb(tbItem)
|
||||
|
||||
val filters = FilterPack.getFilterPack(requireActivity())
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<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:id="@+id/coordinator_edit"
|
||||
@ -7,19 +7,91 @@
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".PhotoEditActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".PhotoEditActivity"
|
||||
tools:showIn="@layout/activity_photo_edit"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight=".70"
|
||||
android:scaleType="centerInside"/>
|
||||
|
||||
<com.h.pixeldroid.utils.NonSwipeableViewPager
|
||||
android:id="@+id/viewPager"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight=".22" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
app:tabGravity="fill"
|
||||
app:tabMode="fixed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight=".08"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBarSaveFile"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/left_guideline"
|
||||
app:layout_constraintGuide_percent=".15"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
<include layout="@layout/content_photo_edit" />
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/right_guideline"
|
||||
app:layout_constraintGuide_percent=".85"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/bottom_guideline"
|
||||
app:layout_constraintGuide_percent=".7"
|
||||
android:orientation="horizontal"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/cropImageButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="411dp"
|
||||
android:backgroundTint="#FFFFFFFF"
|
||||
android:src="@drawable/ic_crop_black_24dp"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/left_guideline"
|
||||
app:layout_constraintRight_toRightOf="@+id/right_guideline"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" />
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorPrimaryActionBar"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"/>
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,76 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".PhotoEditActivity"
|
||||
tools:showIn="@layout/activity_photo_edit"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight=".70"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<com.h.pixeldroid.utils.NonSwipeableViewPager
|
||||
android:id="@+id/viewPager"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight=".22" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
app:tabGravity="fill"
|
||||
app:tabMode="fixed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight=".08"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/left_guideline"
|
||||
app:layout_constraintGuide_percent=".15"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/right_guideline"
|
||||
app:layout_constraintGuide_percent=".85"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/bottom_guideline"
|
||||
app:layout_constraintGuide_percent=".7"
|
||||
android:orientation="horizontal"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/cropImageButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="411dp"
|
||||
android:backgroundTint="#FFFFFFFF"
|
||||
android:src="@drawable/ic_crop_black_24dp"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/left_guideline"
|
||||
app:layout_constraintRight_toRightOf="@+id/right_guideline"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -9,12 +9,12 @@
|
||||
android:orderInCategory="100"
|
||||
android:title="CREATE POST"
|
||||
android:icon="@drawable/ic_file_upload_24dp"
|
||||
app:showAsAction="always"/>
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_save"
|
||||
android:orderInCategory="101"
|
||||
android:title="SAVE"
|
||||
android:icon="@drawable/ic_save_24dp"
|
||||
app:showAsAction="always"/>
|
||||
app:showAsAction="ifRoom"/>
|
||||
</menu>
|
@ -49,6 +49,9 @@
|
||||
<string name="tab_filters">FILTERS</string>
|
||||
<string name="tab_edit">EDIT</string>
|
||||
<string name="normal_filter">Normal</string>
|
||||
<string name="busy_dialog_text">Still processing image, wait for that to finish first!</string>
|
||||
<string name="busy_dialog_ok_button">OK, wait for that.</string>
|
||||
<string name="crop_result_error">"Couldn't retrieve image after crop"</string>
|
||||
<!-- Camera -->
|
||||
<string name="capture_button_alt">Capture</string>
|
||||
<string name="switch_camera_button_alt">Switch camera</string>
|
||||
|
@ -7,7 +7,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.3'
|
||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
#Fri Feb 28 15:51:25 CET 2020
|
||||
#Mon Jun 01 23:59:23 CEST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||
|
Loading…
x
Reference in New Issue
Block a user