mirror of
https://gitlab.shinice.net/pixeldroid/PixelDroid
synced 2025-01-27 05:54:50 +01:00
Edit photos (#114)
* Beginning of edit photos activity * First batch for edition of photos * EditActivity working properly except flow & save * Added tests * Changed name of tabLayouts back to tabs * Resolved 2 errors from last build * Truly resolved the 2 issues with requireContext/Activity * Made test work with API23 emulator * added 2 tests * Corrected test @Before to have the right button to click on * Added flow to newPost and few tests * Added a test and refactor PhotoEditActivity * Added flow from upload picture, tests doesn't work * Added CropImageActivity from ucrop library, crashes for now * Modified test FiltersIsSwipeableAndClickeable but still doesn't work * Merge with master * rectified test SaveButtonLaunchNewPostActivity * FiltersIsSwipeableAndClickeable test completed * Ready to merge to master * resolved error in merge * Added button save and upload, removed BitmapUtils * Removed unnecessary libraries and imports * Remove dependency on library for permissions Co-authored-by: Joachim Dunant <joachim.dunant@epfl.ch> Co-authored-by: Matthieu De Beule <61561059+Wv5twkFEKh54vo4tta9yu7dHa3@users.noreply.github.com>
This commit is contained in:
parent
b8b3112f1b
commit
c4946dd61c
@ -30,6 +30,8 @@ android {
|
||||
androidTest.java.srcDirs += 'src/androidTest/java'
|
||||
}
|
||||
|
||||
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
testCoverageEnabled true
|
||||
@ -70,9 +72,13 @@ dependencies {
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
|
||||
implementation 'info.androidhive:imagefilters:1.0.7'
|
||||
|
||||
implementation("com.github.bumptech.glide:glide:4.11.0") {
|
||||
exclude group: "com.android.support"
|
||||
}
|
||||
|
156
app/src/androidTest/java/com/h/pixeldroid/EditPhotoTest.kt
Normal file
156
app/src/androidTest/java/com/h/pixeldroid/EditPhotoTest.kt
Normal file
@ -0,0 +1,156 @@
|
||||
package com.h.pixeldroid
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import android.widget.SeekBar
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.espresso.Espresso
|
||||
import androidx.test.espresso.PerformException
|
||||
import androidx.test.espresso.UiController
|
||||
import androidx.test.espresso.ViewAction
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition
|
||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
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 com.h.pixeldroid.testUtility.MockServer
|
||||
import kotlinx.android.synthetic.main.fragment_edit_image.*
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.Timeout
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class EditPhotoTest {
|
||||
|
||||
private val mockServer = MockServer()
|
||||
private lateinit var activity: PhotoEditActivity
|
||||
private lateinit var activityScenario: ActivityScenario<PhotoEditActivity>
|
||||
|
||||
@get:Rule
|
||||
var globalTimeout: Timeout = Timeout.seconds(100)
|
||||
|
||||
@get:Rule
|
||||
var mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
mockServer.start()
|
||||
val baseUrl = mockServer.getUrl()
|
||||
val preferences = context.getSharedPreferences("com.h.pixeldroid.pref", Context.MODE_PRIVATE)
|
||||
preferences.edit().putString("accessToken", "azerty").apply()
|
||||
preferences.edit().putString("domain", baseUrl.toString()).apply()
|
||||
|
||||
// Launch PhotoEditActivity
|
||||
val uri: Uri = Uri.parse("android.resource://com.h.pixeldroid/drawable/index")
|
||||
val intent = Intent(context, PhotoEditActivity::class.java).putExtra("uri", uri)
|
||||
|
||||
activityScenario = ActivityScenario.launch<PhotoEditActivity>(intent).onActivity{a -> activity = a}
|
||||
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
|
||||
private fun selectTabAtPosition(tabIndex: Int): ViewAction {
|
||||
return object : ViewAction {
|
||||
override fun getDescription() = "with tab at index $tabIndex"
|
||||
|
||||
override fun getConstraints() =
|
||||
allOf(isDisplayed(), isAssignableFrom(TabLayout::class.java))
|
||||
|
||||
override fun perform(uiController: UiController, view: View) {
|
||||
val tabLayout = view as TabLayout
|
||||
val tabAtIndex: TabLayout.Tab = tabLayout.getTabAt(tabIndex)
|
||||
?: throw PerformException.Builder()
|
||||
.withCause(Throwable("No tab at index $tabIndex"))
|
||||
.build()
|
||||
|
||||
tabAtIndex.select()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setProgress(progress: Int): ViewAction? {
|
||||
return object : ViewAction {
|
||||
override fun getDescription() = "Set the progress on a SeekBar"
|
||||
|
||||
override fun getConstraints() = isAssignableFrom(SeekBar::class.java)
|
||||
|
||||
override fun perform(uiController: UiController, view: View) {
|
||||
val seekBar = view as SeekBar
|
||||
seekBar.progress = progress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun PhotoEditActivityHasAnImagePreview() {
|
||||
Espresso.onView(withId(R.id.image_preview)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun FiltersIsSwipeableAndClickeable() {
|
||||
Espresso.onView(withId(R.id.recycler_view))
|
||||
.perform(actionOnItemAtPosition<ThumbnailAdapter.MyViewHolder>(1, CustomMatchers.clickChildViewWithId(R.id.thumbnail)))
|
||||
Thread.sleep(1000)
|
||||
Espresso.onView(withId(R.id.recycler_view))
|
||||
.perform(actionOnItemAtPosition<ThumbnailAdapter.MyViewHolder>(1, CustomMatchers.slowSwipeLeft(false)))
|
||||
Thread.sleep(1000)
|
||||
Espresso.onView(withId(R.id.recycler_view))
|
||||
.perform(actionOnItemAtPosition<ThumbnailAdapter.MyViewHolder>(5, CustomMatchers.clickChildViewWithId(R.id.thumbnail)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun BirghtnessSaturationContrastTest() {
|
||||
Espresso.onView(withId(R.id.tabs)).perform(selectTabAtPosition(1))
|
||||
|
||||
Thread.sleep(1000)
|
||||
|
||||
var change = 5
|
||||
Espresso.onView(withId(R.id.seekbar_brightness)).perform(setProgress(change))
|
||||
Espresso.onView(withId(R.id.seekbar_contrast)).perform(setProgress(change))
|
||||
Espresso.onView(withId(R.id.seekbar_saturation)).perform(setProgress(change))
|
||||
|
||||
Assert.assertEquals(change, activity.seekbar_brightness.progress)
|
||||
Assert.assertEquals(change, activity.seekbar_contrast.progress)
|
||||
Assert.assertEquals(change, activity.seekbar_saturation.progress)
|
||||
|
||||
Thread.sleep(1000)
|
||||
|
||||
change = 15
|
||||
Espresso.onView(withId(R.id.seekbar_brightness)).perform(setProgress(change))
|
||||
Espresso.onView(withId(R.id.seekbar_contrast)).perform(setProgress(change))
|
||||
Espresso.onView(withId(R.id.seekbar_saturation)).perform(setProgress(change))
|
||||
|
||||
Assert.assertEquals(change, activity.seekbar_brightness.progress)
|
||||
Assert.assertEquals(change, activity.seekbar_contrast.progress)
|
||||
Assert.assertEquals(change, activity.seekbar_saturation.progress)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun SaveButton() {
|
||||
Espresso.onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
|
||||
Espresso.onView(withId(R.id.action_save)).perform(click())
|
||||
Espresso.onView(withId(com.google.android.material.R.id.snackbar_text))
|
||||
.check(matches(withText("Image succesfully saved")))
|
||||
|
||||
}
|
||||
|
||||
@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()))
|
||||
}
|
||||
|
||||
}
|
@ -46,6 +46,21 @@ abstract class CustomMatchers {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param percent can be 1 or 0
|
||||
* 1: swipes all the way left
|
||||
* 0: swipes half way left
|
||||
*/
|
||||
fun slowSwipeLeft(percent: Boolean) : ViewAction {
|
||||
return ViewActions.actionWithAssertions(
|
||||
GeneralSwipeAction(
|
||||
Swipe.SLOW,
|
||||
GeneralLocation.CENTER_RIGHT,
|
||||
if(percent) GeneralLocation.CENTER_LEFT else GeneralLocation.CENTER,
|
||||
Press.FINGER)
|
||||
)
|
||||
}
|
||||
|
||||
fun getText(matcher: Matcher<View?>?): String? {
|
||||
val stringHolder = arrayOf<String?>(null)
|
||||
Espresso.onView(matcher).perform(object : ViewAction {
|
||||
|
@ -4,8 +4,10 @@
|
||||
package="com.h.pixeldroid">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera.any"
|
||||
@ -20,6 +22,11 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity
|
||||
android:name=".PhotoEditActivity"
|
||||
android:theme="@style/AppTheme.NoActionBar"/>
|
||||
|
||||
<activity android:name=".PostCreationActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
tools:ignore="LockedOrientationActivity" />
|
||||
@ -68,6 +75,10 @@
|
||||
android:scheme="@string/auth_scheme" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.yalantis.ucrop.UCropActivity"
|
||||
android:screenOrientation="fullSensor"
|
||||
android:theme="@style/AppTheme.NoActionBar"/>
|
||||
|
||||
<activity
|
||||
android:name=".SearchActivity"
|
||||
android:launchMode="singleTop"
|
||||
|
270
app/src/main/java/com/h/pixeldroid/PhotoEditActivity.kt
Normal file
270
app/src/main/java/com/h/pixeldroid/PhotoEditActivity.kt
Normal file
@ -0,0 +1,270 @@
|
||||
package com.h.pixeldroid
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.h.pixeldroid.adapters.EditPhotoViewPagerAdapter
|
||||
import com.h.pixeldroid.fragments.EditImageFragment
|
||||
import com.h.pixeldroid.fragments.FilterListFragment
|
||||
import com.h.pixeldroid.interfaces.EditImageFragmentListener
|
||||
import com.h.pixeldroid.interfaces.FilterListFragmentListener
|
||||
import com.h.pixeldroid.utils.NonSwipeableViewPager
|
||||
import com.zomato.photofilters.imageprocessors.Filter
|
||||
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.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
// 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)
|
||||
|
||||
class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditImageFragmentListener {
|
||||
|
||||
val BITMAP_CONFIG = Bitmap.Config.ARGB_8888
|
||||
|
||||
private var originalImage: Bitmap? = null
|
||||
private lateinit var filteredImage: Bitmap
|
||||
private lateinit var finalImage: Bitmap
|
||||
|
||||
private lateinit var filterListFragment: FilterListFragment
|
||||
private lateinit var editImageFragment: EditImageFragment
|
||||
|
||||
private lateinit var outputDirectory: File
|
||||
|
||||
lateinit var viewPager: NonSwipeableViewPager
|
||||
lateinit var tabLayout: TabLayout
|
||||
|
||||
private var brightnessFinal = 0
|
||||
private var saturationFinal = 1.0f
|
||||
private var contrastFinal = 1.0f
|
||||
|
||||
private var resultUri: Uri? = null
|
||||
|
||||
object URI {var picture_uri: Uri? = null}
|
||||
|
||||
init {
|
||||
System.loadLibrary("NativeImageProcessor")
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_photo_edit)
|
||||
|
||||
URI.picture_uri = intent.getParcelableExtra("uri")
|
||||
|
||||
resultUri = URI.picture_uri
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar!!.title = "Edit"
|
||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar!!.setHomeButtonEnabled(true)
|
||||
|
||||
loadImage()
|
||||
|
||||
viewPager = findViewById(R.id.viewPager)
|
||||
tabLayout = findViewById(R.id.tabs)
|
||||
setupViewPager(viewPager)
|
||||
tabLayout.setupWithViewPager(viewPager)
|
||||
outputDirectory = getOutputDirectory()
|
||||
}
|
||||
|
||||
/** 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 loadImage() {
|
||||
originalImage = MediaStore.Images.Media.getBitmap(contentResolver, URI.picture_uri)
|
||||
|
||||
filteredImage = originalImage!!.copy(BITMAP_CONFIG, true)
|
||||
finalImage = originalImage!!.copy(BITMAP_CONFIG, true)
|
||||
image_preview.setImageBitmap(originalImage)
|
||||
}
|
||||
|
||||
private fun setupViewPager(viewPager: NonSwipeableViewPager?) {
|
||||
val adapter = EditPhotoViewPagerAdapter(supportFragmentManager)
|
||||
|
||||
filterListFragment = FilterListFragment()
|
||||
filterListFragment.setListener(this)
|
||||
|
||||
editImageFragment = EditImageFragment()
|
||||
editImageFragment.setListener(this)
|
||||
adapter.addFragment(filterListFragment, "FILTERS")
|
||||
adapter.addFragment(editImageFragment, "EDIT")
|
||||
|
||||
viewPager!!.adapter = adapter
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
menuInflater.inflate(R.menu.edit_photo_menu, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
||||
when(item.itemId) {
|
||||
android.R.id.home -> {
|
||||
super.onBackPressed()
|
||||
}
|
||||
R.id.action_upload -> {
|
||||
saveImageToGallery(false)
|
||||
}
|
||||
R.id.action_save -> {
|
||||
saveImageToGallery(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
if(grantResults.size > 1
|
||||
&& 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)
|
||||
}
|
||||
} else {
|
||||
Snackbar.make(coordinator_edit, "Permission denied", Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun uploadImage(file: File) {
|
||||
val intent = Intent (applicationContext, PostCreationActivity::class.java)
|
||||
intent.putExtra("picture_uri", Uri.fromFile(file))
|
||||
//file.delete()
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
applicationContext!!.startActivity(intent)
|
||||
}
|
||||
|
||||
private fun saveImageToGallery(save: Boolean) {
|
||||
// runtime permission and process
|
||||
if (!allPermissionsGranted()) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
REQUIRED_PERMISSIONS,
|
||||
if(save) REQUEST_CODE_PERMISSIONS_SAVE_PHOTO else REQUEST_CODE_PERMISSIONS_SEND_PHOTO
|
||||
)
|
||||
} else {
|
||||
permissionsGrantedToSave(save)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all permission specified in the manifest have been granted
|
||||
*/
|
||||
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
|
||||
ContextCompat.checkSelfPermission(
|
||||
applicationContext, it) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
private fun File.writeBitmap(bitmap: Bitmap) {
|
||||
outputStream().use { out ->
|
||||
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_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")
|
||||
}
|
||||
try {
|
||||
file.writeBitmap(finalImage)
|
||||
} catch (e: IOException) {
|
||||
Snackbar.make(coordinator_edit, "Unable to save image", Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
if (!save) {
|
||||
uploadImage(file)
|
||||
} else {
|
||||
Snackbar.make(coordinator_edit, "Image succesfully saved", Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFilterSelected(filter: Filter) {
|
||||
resetControls()
|
||||
filteredImage = originalImage!!.copy(BITMAP_CONFIG, true)
|
||||
image_preview.setImageBitmap(filter.processFilter(filteredImage))
|
||||
finalImage = filteredImage.copy(BITMAP_CONFIG, true)
|
||||
}
|
||||
|
||||
private fun resetControls() {
|
||||
editImageFragment.resetControl()
|
||||
|
||||
brightnessFinal = 0
|
||||
saturationFinal = 1.0f
|
||||
contrastFinal = 1.0f
|
||||
}
|
||||
|
||||
override fun onBrightnessChange(brightness: Int) {
|
||||
brightnessFinal = brightness
|
||||
val myFilter = Filter()
|
||||
myFilter.addSubFilter(BrightnessSubFilter(brightness))
|
||||
image_preview.setImageBitmap(myFilter.processFilter(finalImage.copy(BITMAP_CONFIG, true)))
|
||||
}
|
||||
|
||||
override fun onSaturationChange(saturation: Float) {
|
||||
saturationFinal = saturation
|
||||
val myFilter = Filter()
|
||||
myFilter.addSubFilter(SaturationSubfilter(saturation))
|
||||
image_preview.setImageBitmap(myFilter.processFilter(finalImage.copy(BITMAP_CONFIG, true)))
|
||||
}
|
||||
|
||||
override fun onContrastChange(contrast: Float) {
|
||||
contrastFinal = contrast
|
||||
val myFilter = Filter()
|
||||
myFilter.addSubFilter(ContrastSubFilter(contrast))
|
||||
image_preview.setImageBitmap(myFilter.processFilter(finalImage.copy(BITMAP_CONFIG, true)))
|
||||
}
|
||||
|
||||
override fun onEditStarted() {
|
||||
|
||||
}
|
||||
|
||||
override fun onEditCompleted() {
|
||||
val bitmap = filteredImage.copy(BITMAP_CONFIG, true)
|
||||
val myFilter = Filter()
|
||||
myFilter.addSubFilter(ContrastSubFilter(contrastFinal))
|
||||
myFilter.addSubFilter(SaturationSubfilter(saturationFinal))
|
||||
myFilter.addSubFilter(BrightnessSubFilter(brightnessFinal))
|
||||
|
||||
finalImage = myFilter.processFilter(bitmap)
|
||||
}
|
||||
}
|
@ -47,9 +47,7 @@ class PostCreationActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_post_creation)
|
||||
|
||||
val imageUri: Uri = intent.getParcelableExtra<Uri>("picture_uri")!!
|
||||
|
||||
saveImage(imageUri)
|
||||
|
||||
pictureFrame = findViewById<ImageView>(R.id.post_creation_picture_frame)
|
||||
|
@ -17,7 +17,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.fragments.ProfilePostsRecyclerViewAdapter
|
||||
import com.h.pixeldroid.adapters.ProfilePostsRecyclerViewAdapter
|
||||
import com.h.pixeldroid.objects.Account
|
||||
import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_TAG
|
||||
import com.h.pixeldroid.objects.Relationship
|
||||
|
@ -0,0 +1,25 @@
|
||||
package com.h.pixeldroid.adapters
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
|
||||
class EditPhotoViewPagerAdapter (manager: FragmentManager):
|
||||
FragmentPagerAdapter(manager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
|
||||
private val fragmentList = ArrayList<Fragment>()
|
||||
private val fragmentTitleList = ArrayList<String>()
|
||||
|
||||
override fun getItem(position: Int) = fragmentList[position]
|
||||
|
||||
override fun getCount() = fragmentList.size
|
||||
|
||||
fun addFragment(fragment: Fragment, title: String) {
|
||||
fragmentList.add(fragment)
|
||||
fragmentTitleList.add(title)
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? {
|
||||
return fragmentTitleList[position]
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.fragments
|
||||
package com.h.pixeldroid.adapters
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.recyclerview.widget.RecyclerView
|
@ -0,0 +1,57 @@
|
||||
package com.h.pixeldroid.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.interfaces.FilterListFragmentListener
|
||||
import com.zomato.photofilters.utils.ThumbnailItem
|
||||
import kotlinx.android.synthetic.main.thumbnail_list_item.view.*
|
||||
|
||||
class ThumbnailAdapter (private val context: Context,
|
||||
private val tbItemList: List<ThumbnailItem>,
|
||||
private val listener: FilterListFragmentListener): RecyclerView.Adapter<ThumbnailAdapter.MyViewHolder>() {
|
||||
|
||||
private var selectedIndex = 0
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||
val itemView = LayoutInflater.from(context).inflate(R.layout.thumbnail_list_item, parent, false)
|
||||
return MyViewHolder(itemView)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return tbItemList.size
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
|
||||
val tbItem = tbItemList[position]
|
||||
holder.thumbnail.setImageBitmap(tbItem.image)
|
||||
holder.thumbnail.setOnClickListener {
|
||||
listener.onFilterSelected(tbItem.filter)
|
||||
selectedIndex = position
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
holder.filterName.text = tbItem.filterName
|
||||
|
||||
if(selectedIndex == position)
|
||||
holder.filterName.setTextColor(ContextCompat.getColor(context, R.color.filterLabelSelected))
|
||||
else
|
||||
holder.filterName.setTextColor(ContextCompat.getColor(context, R.color.filterLabelNormal))
|
||||
}
|
||||
|
||||
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
|
||||
var thumbnail: ImageView
|
||||
var filterName: TextView
|
||||
|
||||
init {
|
||||
thumbnail = itemView.thumbnail
|
||||
filterName = itemView.filter_name
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package com.h.pixeldroid.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.SeekBar
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.interfaces.EditImageFragmentListener
|
||||
|
||||
class EditImageFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||
|
||||
private var listener: EditImageFragmentListener? = null
|
||||
|
||||
private lateinit var seekbarBrightness: SeekBar
|
||||
private lateinit var seekbarSaturation: SeekBar
|
||||
private lateinit var seekbarContrast: SeekBar
|
||||
|
||||
private var BRIGHTNESS_START = 100
|
||||
private var SATURATION_START = 0
|
||||
private var CONTRAST_START = 10
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
val view = inflater.inflate(R.layout.fragment_edit_image, container, false)
|
||||
|
||||
seekbarBrightness = view.findViewById(R.id.seekbar_brightness)
|
||||
seekbarSaturation = view.findViewById(R.id.seekbar_saturation)
|
||||
seekbarContrast = view.findViewById(R.id.seekbar_contrast)
|
||||
|
||||
seekbarBrightness.max = 200
|
||||
seekbarBrightness.progress = BRIGHTNESS_START
|
||||
|
||||
seekbarContrast.max = 20
|
||||
seekbarContrast.progress = CONTRAST_START
|
||||
|
||||
seekbarSaturation.max = 30
|
||||
seekbarSaturation.progress = SATURATION_START
|
||||
|
||||
seekbarBrightness.setOnSeekBarChangeListener(this)
|
||||
seekbarContrast.setOnSeekBarChangeListener(this)
|
||||
seekbarSaturation.setOnSeekBarChangeListener(this)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
var prog = progress
|
||||
|
||||
if(listener != null) {
|
||||
when(seekBar!!.id) {
|
||||
R.id.seekbar_brightness -> listener!!.onBrightnessChange(progress - 100)
|
||||
R.id.seekbar_saturation -> {
|
||||
prog += 10
|
||||
val tempProgress = .10f * prog
|
||||
listener!!.onSaturationChange(tempProgress)
|
||||
}
|
||||
R.id.seekbar_contrast -> {
|
||||
val tempProgress = .10f * prog
|
||||
listener!!.onSaturationChange(tempProgress)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resetControl() {
|
||||
seekbarBrightness.progress = BRIGHTNESS_START
|
||||
seekbarContrast.progress = CONTRAST_START
|
||||
seekbarSaturation.progress = SATURATION_START
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {
|
||||
if(listener != null)
|
||||
listener!!.onEditStarted()
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {
|
||||
if(listener != null)
|
||||
listener!!.onEditCompleted()
|
||||
}
|
||||
|
||||
fun setListener(listener: EditImageFragmentListener) {
|
||||
this.listener = listener
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package com.h.pixeldroid.fragments
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.util.TypedValue
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.PhotoEditActivity
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.adapters.ThumbnailAdapter
|
||||
import com.h.pixeldroid.interfaces.FilterListFragmentListener
|
||||
import com.h.pixeldroid.utils.SpaceItemDecoration
|
||||
import com.zomato.photofilters.FilterPack
|
||||
import com.zomato.photofilters.imageprocessors.Filter
|
||||
import com.zomato.photofilters.utils.ThumbnailItem
|
||||
import com.zomato.photofilters.utils.ThumbnailsManager
|
||||
|
||||
class FilterListFragment : Fragment(), FilterListFragmentListener {
|
||||
|
||||
internal lateinit var recyclerView: RecyclerView
|
||||
internal var listener : FilterListFragmentListener? = null
|
||||
internal lateinit var adapter: ThumbnailAdapter
|
||||
internal lateinit var tbItemList: MutableList<ThumbnailItem>
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this 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)
|
||||
recyclerView.itemAnimator = DefaultItemAnimator()
|
||||
|
||||
val space = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics).toInt()
|
||||
recyclerView.addItemDecoration(SpaceItemDecoration(space))
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
displayImage(null)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
fun displayImage(bitmap: Bitmap?) {
|
||||
val r = Runnable {
|
||||
val tbImage: Bitmap?
|
||||
if (bitmap == null) {
|
||||
tbImage = MediaStore.Images.Media.getBitmap(requireActivity().contentResolver, PhotoEditActivity.URI.picture_uri)
|
||||
} else {
|
||||
tbImage = Bitmap.createScaledBitmap(bitmap, 100, 100, false)
|
||||
}
|
||||
|
||||
if (tbImage == null)
|
||||
return@Runnable
|
||||
|
||||
setupFilter(tbImage)
|
||||
|
||||
tbItemList.addAll(ThumbnailsManager.processThumbs(activity))
|
||||
requireActivity().runOnUiThread{ adapter.notifyDataSetChanged() }
|
||||
}
|
||||
|
||||
Thread(r).start()
|
||||
}
|
||||
|
||||
private fun setupFilter(tbImage: Bitmap?) {
|
||||
ThumbnailsManager.clearThumbs()
|
||||
tbItemList.clear()
|
||||
|
||||
val tbItem = ThumbnailItem()
|
||||
tbItem.image = tbImage
|
||||
tbItem.filterName = "Normal"
|
||||
ThumbnailsManager.addThumb(tbItem)
|
||||
|
||||
val filters = FilterPack.getFilterPack(requireActivity())
|
||||
|
||||
for (filter in filters) {
|
||||
val item = ThumbnailItem()
|
||||
item.image = tbImage
|
||||
item.filter = filter
|
||||
item.filterName = filter.name
|
||||
ThumbnailsManager.addThumb(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFilterSelected(filter: Filter) {
|
||||
if(listener != null ){
|
||||
listener!!.onFilterSelected(filter)
|
||||
}
|
||||
}
|
||||
|
||||
fun setListener(listFragmentListener: FilterListFragmentListener) {
|
||||
this.listener = listFragmentListener
|
||||
}
|
||||
}
|
@ -2,13 +2,14 @@ package com.h.pixeldroid.fragments
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.h.pixeldroid.PostCreationActivity
|
||||
import com.h.pixeldroid.PhotoEditActivity
|
||||
import com.h.pixeldroid.R
|
||||
|
||||
/**
|
||||
@ -34,14 +35,21 @@ class NewPostFragment : Fragment() {
|
||||
uploadPicture()
|
||||
}
|
||||
|
||||
val takePictureButton: Button = view.findViewById(R.id.takePictureButton)
|
||||
takePictureButton.setOnClickListener{
|
||||
val uri: Uri = Uri.parse("android.resource://com.h.pixeldroid/drawable/index")
|
||||
val intent = Intent(context, PhotoEditActivity::class.java).putExtra("uri", uri)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null
|
||||
&& requestCode == PICK_IMAGE_REQUEST && data.data != null)
|
||||
startActivity(Intent(activity, PostCreationActivity::class.java)
|
||||
.putExtra("picture_uri", data.data)
|
||||
startActivity(Intent(activity, PhotoEditActivity::class.java)
|
||||
.putExtra("uri", data.data)
|
||||
)
|
||||
}
|
||||
|
||||
@ -56,4 +64,4 @@ class NewPostFragment : Fragment() {
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.BuildConfig
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.adapters.ProfilePostsRecyclerViewAdapter
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.h.pixeldroid.interfaces
|
||||
|
||||
interface EditImageFragmentListener {
|
||||
fun onBrightnessChange(brightness: Int)
|
||||
|
||||
fun onSaturationChange(saturation: Float)
|
||||
|
||||
fun onContrastChange(contrast: Float)
|
||||
|
||||
fun onEditStarted()
|
||||
|
||||
fun onEditCompleted()
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.h.pixeldroid.interfaces
|
||||
|
||||
import com.zomato.photofilters.imageprocessors.Filter
|
||||
|
||||
interface FilterListFragmentListener {
|
||||
fun onFilterSelected(filter: Filter)
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.h.pixeldroid.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.widget.Scroller
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
|
||||
class NonSwipeableViewPager: ViewPager {
|
||||
constructor(context: Context):super(context) {
|
||||
setMyScroller()
|
||||
}
|
||||
|
||||
constructor(context: Context,attributeSet: AttributeSet): super(context, attributeSet) {
|
||||
setMyScroller()
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onTouchEvent(ev: MotionEvent?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun setMyScroller() {
|
||||
try {
|
||||
val viewPager = ViewPager::class.java
|
||||
val scroller = viewPager.getDeclaredField("mScroller")
|
||||
scroller.isAccessible = true
|
||||
scroller.set(this, FilterScroller(context))
|
||||
} catch (e:Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FilterScroller(context: Context): Scroller(context, DecelerateInterpolator()) {
|
||||
override fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int, duration: Int) {
|
||||
super.startScroll(startX, startY, dx, dy, 400)
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.h.pixeldroid.utils
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class SpaceItemDecoration(private val space: Int): RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
if (parent.getChildAdapterPosition(view) == state.itemCount - 1) {
|
||||
outRect.left = space
|
||||
outRect.right = 0
|
||||
} else {
|
||||
outRect.left = 0
|
||||
outRect.right = space
|
||||
}
|
||||
}
|
||||
}
|
9
app/src/main/res/drawable/ic_crop_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_crop_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M17,15h2V7c0,-1.1 -0.9,-2 -2,-2H9v2h8v8zM7,17V1H5v4H1v2h4v10c0,1.1 0.9,2 2,2h10v4h2v-4h4v-2H7z"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_file_upload_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_file_upload_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9,16h6v-6h4l-7,-7 -7,7h4zM5,18h14v2L5,20z"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_save_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_save_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/>
|
||||
</vector>
|
26
app/src/main/res/layout/activity_photo_edit.xml
Normal file
26
app/src/main/res/layout/activity_photo_edit.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".PhotoEditActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<include layout="@layout/content_photo_edit" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
37
app/src/main/res/layout/content_photo_edit.xml
Normal file
37
app/src/main/res/layout/content_photo_edit.xml
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
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:background="@android:color/white"
|
||||
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"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_preview"
|
||||
android:scaleType="centerInside"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="400dp"/>
|
||||
|
||||
<com.h.pixeldroid.utils.NonSwipeableViewPager
|
||||
android:id="@+id/viewPager"
|
||||
android:layout_above="@+id/tabs"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_below="@+id/image_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
app:tabGravity="fill"
|
||||
app:tabMode="fixed"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</RelativeLayout>
|
39
app/src/main/res/layout/fragment_camera.xml
Normal file
39
app/src/main/res/layout/fragment_camera.xml
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/camera_fragment_main_linear_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_horizontal"
|
||||
tools:context=".fragments.CameraFragment"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/upload_picture_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="15dp"
|
||||
android:layout_margin="15dp"
|
||||
android:text="@string/upload_a_picture"
|
||||
android:background="@color/colorPrimary"
|
||||
android:textColor="@color/cardview_light_background"/>
|
||||
<Button
|
||||
android:id="@+id/edit_picture_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="15dp"
|
||||
android:layout_margin="15dp"
|
||||
android:text="Edit a picture"
|
||||
android:background="@color/colorPrimary"
|
||||
android:textColor="@color/cardview_light_background"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/uploaded_picture_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="15dp"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
</LinearLayout>
|
72
app/src/main/res/layout/fragment_edit_image.xml
Normal file
72
app/src/main/res/layout/fragment_edit_image.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
tools:context=".fragments.EditImageFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:text="@string/lbl_brightness"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekbar_brightness"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:text="@string/lbl_contrast"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekbar_contrast"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:text="@string/lbl_saturation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekbar_saturation"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
17
app/src/main/res/layout/fragment_filter_list.xml
Normal file
17
app/src/main/res/layout/fragment_filter_list.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".fragments.FilterListFragment">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:clipChildren="false"
|
||||
android:padding="4dp"
|
||||
android:scrollbars="none"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</FrameLayout>
|
24
app/src/main/res/layout/thumbnail_list_item.xml
Normal file
24
app/src/main/res/layout/thumbnail_list_item.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/filter_name"
|
||||
android:text="FILTER_NAME"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thumbnail"
|
||||
android:scaleType="centerCrop"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:contentDescription="thumbnail of filter" />
|
||||
</LinearLayout>
|
20
app/src/main/res/menu/edit_photo_menu.xml
Normal file
20
app/src/main/res/menu/edit_photo_menu.xml
Normal file
@ -0,0 +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"
|
||||
>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_upload"
|
||||
android:orderInCategory="100"
|
||||
android:title="CREATE POST"
|
||||
android:icon="@drawable/ic_file_upload_black_24dp"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_save"
|
||||
android:orderInCategory="101"
|
||||
android:title="SAVE"
|
||||
android:icon="@drawable/ic_save_black_24dp"
|
||||
app:showAsAction="always"/>
|
||||
</menu>
|
@ -3,4 +3,8 @@
|
||||
<color name="colorPrimary">#6200EE</color>
|
||||
<color name="colorPrimaryDark">#3700B3</color>
|
||||
<color name="colorAccent">#03DAC5</color>
|
||||
|
||||
<color name="filterLabelNormal">#8A8889</color>
|
||||
<color name="filterLabelSelected">#221F20</color>
|
||||
<color name="colorOptionMenu">#FF3990</color>
|
||||
</resources>
|
||||
|
@ -27,6 +27,7 @@
|
||||
<string name="mention_notification">%1$s mentioned you</string>
|
||||
<string name="shared_notification">%1$s shared your post</string>
|
||||
<string name="liked_notification">%1$s liked your post</string>
|
||||
|
||||
<string name="create_a_new_post">Create a new post!</string>
|
||||
<string name="take_a_picture">Take a picture</string>
|
||||
<string name="upload_a_picture">Upload a picture</string>
|
||||
@ -35,9 +36,17 @@
|
||||
<string name="send">send</string>
|
||||
<string name="whats_an_instance">"What's an instance?"</string>
|
||||
<string name="logout">Logout</string>
|
||||
|
||||
<string name="lbl_brightness">BRIGHTNESS</string>
|
||||
<string name="lbl_contrast">CONTRAST</string>
|
||||
<string name="lbl_saturation">SATURATION</string>
|
||||
<string name="tab_filters">FILTERS</string>
|
||||
<string name="tab_edit">EDIT</string>
|
||||
|
||||
<string name="save_to_gallery">Save to Gallery…</string>
|
||||
<string name="image_download_failed">Download has been failed, please try again</string>
|
||||
<string name="image_download_downloading">Downloading…</string>
|
||||
<string name="image_download_success">Image downloaded successfully</string>
|
||||
<string name="share_picture">Share picture…</string>
|
||||
|
||||
</resources>
|
||||
|
@ -15,4 +15,21 @@
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar.FullScreen">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
<style name="posts_title">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_marginBottom">8dp</item>
|
||||
<item name="android:paddingLeft">8dp</item>
|
||||
<item name="android:background">@android:color/holo_orange_light</item>
|
||||
<item name="android:textAppearance">@android:style/TextAppearance.Large</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user