Camera tab (#149)

* added CameraX depedencies

* added basis of camera activity layout

* basis of camera layout

* declare activity and request camera permissions

* request camera permissions

* implemented basic viewFinder aka stream preview

* added drawing of layout

* linked camera activity to new post fragment

* texture view now fills the entire screen

* added button, call to camera activity FOR RESULT and return to New Post Fragment

* can now take a photo a upload it

* locked camera activity in portrait orientation

* added basic test to CameraActivity

* added test with device orientation

* deleted orientation test as it broke the whole app

* deleted occurence of orientation tool for tests

* added buttons for flipping cameras and picture upload

* replaced NewPostFragment and took its job

* deleted cameraActivity

* replaced newPostFragment by Camera Activity

* revamped NewPostFragment

* perform basic test on buttons

* corrected retrieval of buttons

* robust camera fragment

* fix thumbnail permission requests

* refactor, finish fragment

* try to fix build not working on CI

* Add dependency

* Fix camera test

* update tests, delete obsolete xml

* Try to fix test

* Stop nullpointer that happens sometimes, when it shouldn't

* remove unused strings

* edit the photo instead of posting directly

* fix test after changing putextra name

* remove useless analyzer

* add test?

* better tests?

* add dependencies

* update dependencies

* try to fix build

* undo change to build.gradle

* try to fix tests

* try to fix tests

* remove useless listener

* save image so that ci can go through gallery

* remove extraneous permission

* Any app that declares the WRITE_EXTERNAL_STORAGE permission is implicitly granted this permission.

Co-authored-by: Andrea Clement <samuel.dietz@epfl.ch>
This commit is contained in:
Wv5twkFEKh54vo4tta9yu7dHa3 2020-05-15 12:10:28 +02:00 committed by GitHub
parent 5ac3967400
commit 252a192ff3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1014 additions and 168 deletions

View File

@ -65,11 +65,11 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.8.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.16'
implementation 'io.reactivex.rxjava2:rxjava:2.2.17'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "androidx.browser:browser:1.2.0"
implementation 'com.google.android.material:material:1.1.0'
implementation 'com.github.connyduck:sparkbutton:3.0.0'
implementation 'com.github.connyduck:sparkbutton:4.0.0'
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
@ -94,7 +94,7 @@ dependencies {
// Excludes the support library because it's already included by Glide.
transitive = false
}
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation "com.github.tomakehurst:wiremock-jre8:2.26.3"
@ -110,6 +110,8 @@ dependencies {
androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.2'
implementation 'androidx.paging:paging-runtime-ktx:2.1.2'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
@ -117,7 +119,18 @@ dependencies {
def fragment_version = '1.2.4'
debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
implementation 'com.karumi:dexter:6.1.2'
// Use the most recent version of CameraX
def camerax_version = '1.0.0-beta03'
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
// CameraX Lifecycle library
implementation "androidx.camera:camera-lifecycle:$camerax_version"
// CameraX View class
implementation 'androidx.camera:camera-view:1.0.0-alpha10'
implementation 'com.karumi:dexter:6.1.0'
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'

View File

@ -0,0 +1,116 @@
package com.h.pixeldroid
import android.Manifest
import android.content.ContentValues
import android.content.Intent
import android.content.Intent.ACTION_CHOOSER
import android.graphics.Bitmap
import android.graphics.Color
import android.media.MediaScannerConnection
import android.os.Environment
import android.provider.MediaStore
import android.webkit.MimeTypeMap
import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import com.h.pixeldroid.fragments.CameraFragment
import kotlinx.android.synthetic.main.camera_ui_container.*
import org.hamcrest.CoreMatchers
import org.hamcrest.Matcher
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import java.io.File
import java.io.OutputStream
class CameraTest {
@get:Rule
val mRuntimePermissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.CAMERA, 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(){
Intents.init()
}
@After
fun after(){
Intents.release()
}
@Test
fun takePictureButton() {
var scenario = launchFragmentInContainer<CameraFragment>()
scenario.onFragment { _ ->
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)
val context = InstrumentationRegistry.getInstrumentation().targetContext
val mimeType = MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(file.extension)
MediaScannerConnection.scanFile(
context,
arrayOf(file.absolutePath),
arrayOf(mimeType)){_, _ ->
}
}
scenario = launchFragmentInContainer<CameraFragment>()
Thread.sleep(2000)
scenario.onFragment { fragment ->
fragment.camera_capture_button.performClick()
}
scenario.onFragment { fragment ->
assert(fragment.isHidden)
}
}
@Test
fun uploadButton() {
val expectedIntent: Matcher<Intent> = CoreMatchers.allOf(
IntentMatchers.hasAction(ACTION_CHOOSER)
)
val scenario = launchFragmentInContainer<CameraFragment>()
scenario.onFragment { fragment ->
fragment.photo_view_button.performClick()
}
Thread.sleep(1000)
Intents.intended(expectedIntent)
}
@Test
fun switchButton() {
val scenario = launchFragmentInContainer<CameraFragment>()
scenario.onFragment { fragment ->
fragment.camera_switch_button.performClick()
}
Thread.sleep(1000)
scenario.onFragment { fragment ->
assert(!fragment.isHidden)
}
}
}

View File

@ -54,7 +54,7 @@ class EditPhotoTest {
// Launch PhotoEditActivity
val uri: Uri = Uri.parse("android.resource://com.h.pixeldroid/drawable/index")
val intent = Intent(context, PhotoEditActivity::class.java).putExtra("uri", uri)
val intent = Intent(context, PhotoEditActivity::class.java).putExtra("picture_uri", uri)
activityScenario = ActivityScenario.launch<PhotoEditActivity>(intent).onActivity{a -> activity = a}
@ -140,7 +140,7 @@ class EditPhotoTest {
@Test
fun SaveButton() {
Espresso.onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
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")))

View File

@ -18,6 +18,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import com.h.pixeldroid.testUtility.MockServer
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.camera_ui_container.*
import org.hamcrest.Matcher
import org.junit.Before
import org.junit.Rule
@ -50,7 +51,7 @@ class PostCreationFragmentTest {
fun uploadButtonLaunchesGalleryIntent() {
val expectedIntent: Matcher<Intent> = hasAction(Intent.ACTION_CHOOSER)
intending(expectedIntent)
onView(withId(R.id.uploadPictureButton)).perform(click())
onView(withId(R.id.photo_view_button)).perform(click())
Thread.sleep(1000)
intended(expectedIntent)
}
@ -73,16 +74,16 @@ class PostFragmentUITests {
.targetContext.getSharedPreferences("com.h.pixeldroid.pref", Context.MODE_PRIVATE)
preferences.edit().putString("accessToken", "azerty").apply()
preferences.edit().putString("domain", baseUrl.toString()).apply()
ActivityScenario.launch(MainActivity::class.java).onActivity {
a -> a.tabs.getTabAt(2)!!.select()
}
Thread.sleep(300)
}
@Test
fun newPostUiTest() {
onView(withId(R.id.uploadPictureButton)).check(matches(isDisplayed()))
onView(withId(R.id.takePictureButton)).check(matches(isDisplayed()))
ActivityScenario.launch(MainActivity::class.java).onActivity {
a -> a.tabs.getTabAt(2)!!.select()
}
Thread.sleep(1500)
onView(withId(R.id.photo_view_button)).check(matches(isDisplayed()))
onView(withId(R.id.camera_capture_button)).check(matches(isDisplayed()))
}
}

View File

@ -5,10 +5,9 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<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-permission android:name="android.permission.CAMERA"/>
<uses-feature
android:name="android.hardware.camera.any"

View File

@ -15,9 +15,9 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.navigation.NavigationView
import com.google.android.material.tabs.TabLayoutMediator
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.fragments.CameraFragment
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.fragments.NewPostFragment
import com.h.pixeldroid.fragments.SearchDiscoverFragment
import com.h.pixeldroid.fragments.feeds.PostsFeedFragment
import com.h.pixeldroid.fragments.feeds.NotificationsFragment
@ -60,7 +60,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
if (hasInternet(applicationContext)) PostsFeedFragment()
else OfflineFeedFragment(),
searchDiscoverFragment,
NewPostFragment(),
CameraFragment(),
NotificationsFragment(),
PublicTimelineFragment()
)

View File

@ -97,7 +97,7 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
supportActionBar!!.setHomeButtonEnabled(true)
cropUri = intent.getParcelableExtra("uri")
cropUri = intent.getParcelableExtra("picture_uri")
loadImage()
val file = File.createTempFile("temp_compressed_img", ".png", cacheDir)

View File

@ -0,0 +1,407 @@
package com.h.pixeldroid.fragments
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.hardware.display.DisplayManager
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.util.DisplayMetrics
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import androidx.camera.core.*
import androidx.camera.core.ImageCapture.Metadata
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.setPadding
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.h.pixeldroid.PhotoEditActivity
import com.h.pixeldroid.PostCreationActivity
import com.h.pixeldroid.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
// 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 = 10
private const val ANIMATION_FAST_MILLIS = 50L
private const val ANIMATION_SLOW_MILLIS = 100L
/**
* Camera fragment
*/
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
private var displayId: Int = -1
private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
private var preview: Preview? = null
private var imageCapture: ImageCapture? = null
private var camera: Camera? = null
private val displayManager by lazy {
requireContext().getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
}
/** Blocking camera operations are performed using this executor */
private lateinit var cameraExecutor: ExecutorService
override fun onResume() {
super.onResume()
// Make sure that all permissions are still present on resume,
// since they could have been removed while away.
if (!allPermissionsGranted()) {
ActivityCompat.requestPermissions(
requireActivity(),
REQUIRED_PERMISSIONS,
REQUEST_CODE_PERMISSIONS
)
} else {
//Bind the viewfinder here, since when leaving the fragment it gets unbound
bindCameraUseCases()
// Build UI controls
updateCameraUi()
}
}
/**
* Check if all permission specified in the manifest have been granted
*/
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
requireContext(), it) == PackageManager.PERMISSION_GRANTED
}
override fun onDestroyView() {
super.onDestroyView()
// Shut down our background executor
cameraExecutor.shutdown()
// Unregister the broadcast receivers and listeners
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.fragment_camera, container, false)
private fun setGalleryThumbnail(uri: String) {
// Reference of the view that holds the gallery thumbnail
val thumbnail = container.findViewById<ImageButton>(R.id.photo_view_button)
// Run the operations in the view's thread
thumbnail.post {
// Remove thumbnail padding
thumbnail.setPadding(10)
// Load thumbnail into circular button using Glide
Glide.with(thumbnail)
.load(uri)
.apply(RequestOptions.circleCropTransform())
.into(thumbnail)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
container = view as ConstraintLayout
viewFinder = container.findViewById(R.id.view_finder)
// Initialize our background executor
cameraExecutor = Executors.newSingleThreadExecutor()
// 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 {
// Keep track of the display in which this view is attached
displayId = viewFinder.display?.displayId ?: -1
}
}
/**
* Inflate camera controls and update the UI manually upon config changes to avoid removing
* and re-adding the view finder from the view hierarchy; this provides a seamless rotation
* transition on devices that support it.
*
* NOTE: The flag is supported starting in Android 8 but there still is a small flash on the
* screen for devices that run Android 9 or below.
*/
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
updateCameraUi()
}
/** Declare and bind preview, capture and analysis use cases */
private fun bindCameraUseCases() {
// Get screen metrics used to setup camera for full screen resolution
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
// Bind the CameraProvider to the LifeCycleOwner
val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
cameraProviderFuture.addListener(Runnable {
// CameraProvider
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
preview = Preview.Builder()
// We request aspect ratio but no resolution
.setTargetAspectRatio(screenAspectRatio)
// Set initial target rotation
.setTargetRotation(rotation)
.build()
// ImageCapture
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
// We request aspect ratio but no resolution to match preview config, but letting
// CameraX optimize for whatever specific resolution best fits our use cases
.setTargetAspectRatio(screenAspectRatio)
// Set initial target rotation, we will have to call this again if rotation changes
// during the lifecycle of this use case
.setTargetRotation(rotation)
.build()
// Must unbind the use-cases before rebinding them
cameraProvider.unbindAll()
try {
// A variable number of use-cases can be passed here -
// camera provides access to CameraControl & CameraInfo
camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture)
// Attach the viewfinder's surface provider to preview use case
preview?.setSurfaceProvider(viewFinder.createSurfaceProvider(camera?.cameraInfo))
} catch(exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(requireContext()))
}
/**
* setTargetAspectRatio requires enum value of
* [androidx.camera.core.AspectRatio]. Currently it has values of 4:3 & 16:9.
*
* Detecting the most suitable ratio for dimensions provided in @params by counting absolute
* of preview ratio to one of the provided values.
*
* @param width - preview width
* @param height - preview height
* @return suitable aspect ratio
*/
private fun aspectRatio(width: Int, height: Int): Int {
val previewRatio = max(width, height).toDouble() / min(width, height)
if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) {
return AspectRatio.RATIO_4_3
}
return AspectRatio.RATIO_16_9
}
/** Method used to re-draw the camera UI controls, called every time configuration changes. */
private fun updateCameraUi() {
// Remove previous UI if any
container.findViewById<ConstraintLayout>(R.id.camera_ui_container)?.let {
container.removeView(it)
}
// Inflate a new view containing all UI for controlling the camera
val controls = View.inflate(requireContext(), R.layout.camera_ui_container, container)
// In the background, load latest photo taken (if any) for gallery thumbnail
lifecycleScope.launch(Dispatchers.IO) {
// Find the last picture
// Find the last picture
val projection = arrayOf(
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE
)
val cursor = requireContext().contentResolver
.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null,
null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC"
)
if (cursor != null && cursor.moveToFirst()) {
val uri = Uri.parse(cursor.getString(1)).path ?: ""
setGalleryThumbnail(uri)
cursor.close();
}
}
setupImageCapture(controls)
setupFlipCameras(controls)
setupUploadImage(controls)
}
private fun setupUploadImage(controls: View) {
// Listener for button used to view the most recent photo
controls.findViewById<ImageButton>(R.id.photo_view_button).setOnClickListener {
Intent().apply {
type = "image/*"
action = Intent.ACTION_GET_CONTENT
addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_LOCAL_ONLY, true)
startActivityForResult(
Intent.createChooser(this, "Select a Picture"), PICK_IMAGE_REQUEST
)
}
}
}
private fun setupFlipCameras(controls: View) {
// Listener for button used to switch cameras
controls.findViewById<ImageButton>(R.id.camera_switch_button).setOnClickListener {
lensFacing = if (CameraSelector.LENS_FACING_FRONT == lensFacing) {
CameraSelector.LENS_FACING_BACK
} else {
CameraSelector.LENS_FACING_FRONT
}
// Re-bind use cases to update selected camera, being careful about permissions.
if (!allPermissionsGranted()) {
ActivityCompat.requestPermissions(
requireActivity(),
REQUIRED_PERMISSIONS,
REQUEST_CODE_PERMISSIONS
)
} else {
bindCameraUseCases()
}
}
}
private fun setupImageCapture(controls: View) {
// Listener for button used to capture photo
controls.findViewById<ImageButton>(R.id.camera_capture_button).setOnClickListener {
// Get a stable reference of the modifiable image capture use case
imageCapture?.let { imageCapture ->
// Create output file to hold the image
val photoFile = File.createTempFile(
"${System.currentTimeMillis()}.jpg", null, context?.cacheDir
)
// Setup image capture metadata
val metadata = Metadata().apply {
// Mirror image when using the front camera
isReversedHorizontal = lensFacing == CameraSelector.LENS_FACING_FRONT
}
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile)
.setMetadata(metadata)
.build()
// Setup image capture listener which is triggered after photo has been taken
imageCapture.takePicture(
outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
startPostCreation(savedUri)
}
})
// Display flash animation to indicate that photo was captured
container.postDelayed({
container.foreground = ColorDrawable(Color.WHITE)
container.postDelayed(
{ container.foreground = null }, ANIMATION_FAST_MILLIS
)
}, ANIMATION_SLOW_MILLIS)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK && data != null
&& (requestCode == PICK_IMAGE_REQUEST || requestCode == CAPTURE_IMAGE_REQUEST)
&& data.data != null) {
startPostCreation(data.data!!)
}
}
private fun startPostCreation(uri: Uri) {
startActivity(
Intent(activity, PhotoEditActivity::class.java)
.putExtra("picture_uri", uri)
)
}
companion object {
private const val TAG = "CameraFragment"
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
}
}
}

View File

@ -1,67 +0,0 @@
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.PhotoEditActivity
import com.h.pixeldroid.R
/**
* This fragment is the entry point to create a post.
* You can either upload an existing picture or take a new one.
* once the URI of the picture to be posted is set, it will send
* it to the post creation activity where you can modify it,
* add a description and more.
*/
class NewPostFragment : Fragment() {
private val PICK_IMAGE_REQUEST = 1
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_new_post, container, false)
val uploadPictureButton: Button = view.findViewById(R.id.uploadPictureButton)
uploadPictureButton.setOnClickListener{
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, PhotoEditActivity::class.java)
.putExtra("uri", data.data)
)
}
private fun uploadPicture() {
Intent().apply {
type = "image/*"
action = Intent.ACTION_GET_CONTENT
addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_LOCAL_ONLY, true)
startActivityForResult(
Intent.createChooser(this, "Select a Picture"), PICK_IMAGE_REQUEST
)
}
}
}

View File

@ -285,6 +285,8 @@ data class Status(
// Button is inactive
undoReblogPost(holder, api, credential, this@Status)
}
//show animation or not?
true
}
}
}
@ -309,7 +311,9 @@ data class Status(
// Button is inactive
unLikePostCall(holder, api, credential, this@Status)
}
}
//show animation or not?
true
}
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="#000000" />
<item android:state_enabled="false" android:color="#000000" />
<item android:state_focused="true" android:color="#000000" />
<item android:color="#FFFFFF" />
</selector>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@color/icPressed" />
<item android:state_focused="true" android:color="@color/icFocused" />
<item android:color="@color/icActive" />
</selector>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="oval">
<stroke
android:width="@dimen/stroke_small"
android:color="@color/icPressed" />
<size
android:width="@dimen/round_button_medium"
android:height="@dimen/round_button_medium" />
</shape>
</item>
<item android:state_focused="true">
<shape android:shape="oval">
<stroke
android:width="@dimen/stroke_small"
android:color="@color/icFocused" />
<size
android:width="@dimen/round_button_medium"
android:height="@dimen/round_button_medium" />
</shape>
</item>
<item>
<shape android:shape="oval">
<stroke
android:width="@dimen/stroke_small"
android:color="@color/icActive" />
<size
android:width="@dimen/round_button_medium"
android:height="@dimen/round_button_medium" />
</shape>
</item>
</selector>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="@color/selector_ic"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#000000"
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
</vector>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/ic_shutter_pressed" />
<item android:state_focused="true" android:drawable="@drawable/ic_shutter_focused" />
<item android:drawable="@drawable/ic_shutter_normal" />
</selector>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="74"
android:viewportHeight="74">
<path android:fillColor="#FFFFFF" android:fillType="evenOdd"
android:pathData="M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37"
android:strokeColor="#00000000" android:strokeWidth="1"/>
<path android:fillColor="#58A0C4" android:fillType="evenOdd"
android:pathData="M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37"
android:strokeColor="#00000000" android:strokeWidth="1"/>
</vector>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="74"
android:viewportHeight="74">
<path android:fillColor="#FFFFFF" android:fillType="evenOdd"
android:pathData="M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37"
android:strokeColor="#00000000" android:strokeWidth="1"/>
<path android:fillColor="#CFD7DB" android:fillType="evenOdd"
android:pathData="M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37"
android:strokeColor="#00000000" android:strokeWidth="1"/>
</vector>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="74"
android:viewportHeight="74">
<path android:fillColor="#FFFFFF" android:fillType="evenOdd"
android:pathData="M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37"
android:strokeColor="#00000000" android:strokeWidth="1"/>
<path android:fillColor="#58A0C4" android:fillType="evenOdd"
android:pathData="M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37"
android:strokeColor="#00000000" android:strokeWidth="1"/>
</vector>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:tint="@color/selector_ic"
android:width="24dp"
android:height="24dp"
android:viewportWidth="46.0"
android:viewportHeight="46.0"
tools:ignore="VectorRaster">
<path android:fillAlpha="0" android:fillColor="#D8D8D8"
android:fillType="evenOdd" android:pathData="M-4,-4h54v54h-54z"
android:strokeColor="#00000000" android:strokeWidth="1"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M23,23m-6.6,0a6.6,6.6 0,1 1,13.2 0a6.6,6.6 0,1 1,-13.2 0"
android:strokeColor="#FFFFFF" android:strokeWidth="2.8"/>
<path android:fillColor="#FEFEFE" android:fillType="evenOdd"
android:pathData="M42.923,34.5C38.9462,41.3747 31.5132,46 23,46C10.2975,46 0,35.7025 0,23L2.8,23C2.8,34.1562 11.8438,43.2 23,43.2C29.8831,43.2 35.9621,39.7574 39.6091,34.5L42.923,34.5ZM46,23L43.2,23C43.2,11.8438 34.1562,2.8 23,2.8C16.1169,2.8 10.0379,6.2426 6.3909,11.5L3.077,11.5C7.0538,4.6253 14.4868,0 23,0C35.7025,0 46,10.2975 46,23Z"
android:strokeColor="#00000000" android:strokeWidth="1"/>
<path android:fillColor="#FEFEFE" android:fillType="evenOdd"
android:pathData="M3,9.2h10v2.8h-10z"
android:strokeColor="#00000000" android:strokeWidth="1"/>
<path android:fillColor="#FEFEFE" android:fillType="evenOdd"
android:pathData="M33,34h10v2.8h-10z"
android:strokeColor="#00000000" android:strokeWidth="1"/>
<path android:fillColor="#FEFEFE" android:fillType="evenOdd"
android:pathData="M5.8,2l-0,10l-2.8,0l-0,-10z"
android:strokeColor="#00000000" android:strokeWidth="1"/>
<path android:fillColor="#FEFEFE" android:fillType="evenOdd"
android:pathData="M43,34l-0,10l-2.8,0l-0,-10z"
android:strokeColor="#00000000" android:strokeWidth="1"/>
</vector>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/camera_ui_container"
android:layoutDirection="ltr"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Camera control and gallery buttons -->
<ImageButton
android:id="@id/camera_switch_button"
android:layout_width="@dimen/round_button_medium"
android:layout_height="@dimen/round_button_medium"
android:layout_marginEnd="@dimen/margin_xlarge"
android:layout_marginBottom="@dimen/margin_small"
android:padding="@dimen/spacing_small"
android:scaleType="fitXY"
android:background="@android:color/transparent"
app:srcCompat="@drawable/ic_switch"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:contentDescription="@string/gallery_button_alt" />
<ImageButton
android:id="@id/camera_capture_button"
android:layout_width="@dimen/round_button_large"
android:layout_height="@dimen/round_button_large"
android:layout_marginEnd="@dimen/shutter_button_margin"
android:background="@drawable/ic_shutter"
android:contentDescription="@string/capture_button_alt"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@id/photo_view_button"
android:layout_width="@dimen/round_button_medium"
android:layout_height="@dimen/round_button_medium"
android:layout_marginEnd="@dimen/margin_xlarge"
android:layout_marginTop="@dimen/margin_small"
android:padding="@dimen/spacing_large"
android:scaleType="fitXY"
android:background="@drawable/ic_outer_circle"
app:srcCompat="@drawable/ic_photo"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/switch_camera_button_alt" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/camera_ui_container"
android:layoutDirection="ltr"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Camera control and gallery buttons -->
<ImageButton
android:id="@+id/camera_switch_button"
android:layout_width="@dimen/round_button_medium"
android:layout_height="@dimen/round_button_medium"
android:layout_marginBottom="@dimen/margin_xlarge"
android:layout_marginStart="@dimen/margin_small"
android:padding="@dimen/spacing_small"
android:scaleType="fitCenter"
android:background="@android:color/transparent"
app:srcCompat="@drawable/ic_switch"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:contentDescription="@string/switch_camera_button_alt" />
<ImageButton
android:id="@+id/camera_capture_button"
android:layout_width="@dimen/round_button_large"
android:layout_height="@dimen/round_button_large"
android:layout_marginBottom="@dimen/shutter_button_margin"
android:scaleType="fitCenter"
android:background="@drawable/ic_shutter"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:contentDescription="@string/capture_button_alt" />
<ImageButton
android:id="@+id/photo_view_button"
android:layout_width="@dimen/round_button_medium"
android:layout_height="@dimen/round_button_medium"
android:layout_marginBottom="@dimen/margin_xlarge"
android:layout_marginEnd="@dimen/margin_small"
android:padding="@dimen/spacing_large"
android:scaleType="fitCenter"
android:background="@drawable/ic_outer_circle"
app:srcCompat="@drawable/ic_photo"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:contentDescription="@string/gallery_button_alt" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,39 +1,29 @@
<?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"
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/camera_container"
android:background="@android:color/black"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
tools:context=".fragments.CameraFragment"
android:orientation="vertical">
android:layout_height="match_parent">
<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:backgroundTint="@color/colorButtonBg"
android:textColor="@color/colorButtonText"/>
<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:backgroundTint="@color/colorButtonBg"
android:textColor="@color/colorButtonText"/>
<ImageView
android:id="@+id/uploaded_picture_view"
<androidx.camera.view.PreviewView
android:id="@+id/view_finder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="15dp"
tools:srcCompat="@tools:sample/avatars" />
android:layout_height="match_parent" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,47 +0,0 @@
<?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_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/create_a_new_post"
android:textSize="25sp"
android:layout_marginBottom="50dp"/>
<Button
android:id="@+id/uploadPictureButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:gravity="center"
android:padding="15dp"
android:text="@string/upload_a_picture"
android:backgroundTint="@color/colorButtonBg"
android:textColor="@color/colorButtonText"
tools:ignore="PrivateResource" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/or"/>
<Button
android:id="@+id/takePictureButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:gravity="center"
android:padding="15dp"
android:text="@string/take_a_picture"
android:backgroundTint="@color/colorButtonBg"
android:textColor="@color/colorButtonText"
tools:ignore="PrivateResource" />
</LinearLayout>

View File

@ -4,9 +4,11 @@
<color name="colorPrimaryTab">#FFFFFF</color>
<color name="colorPrimaryDark">#3700B3</color>
<color name="colorAccent">#03DAC5</color>
<color name="icActive">#FFFFFFFF</color>
<color name="icFocused">#DDFFFFFF</color>
<color name="icPressed">#AAFFFFFF</color>
<color name="colorButtonBg">#6200EE</color>
<color name="colorButtonText">#FFFFFF</color>
<color name="filterLabelNormal">#8A8889</color>
<color name="filterLabelSelected">#221F20</color>
<color name="colorOptionMenu">#FF3990</color>

View File

@ -5,4 +5,27 @@
<dimen name="nav_header_vertical_spacing">8dp</dimen>
<dimen name="nav_header_height">176dp</dimen>
<dimen name="margin_xsmall">16dp</dimen>
<dimen name="margin_small">32dp</dimen>
<dimen name="margin_medium">48dp</dimen>
<dimen name="margin_large">64dp</dimen>
<dimen name="margin_xlarge">92dp</dimen>
<dimen name="spacing_small">4dp</dimen>
<dimen name="spacing_medium">8dp</dimen>
<dimen name="spacing_large">16dp</dimen>
<dimen name="stroke_small">4dp</dimen>
<dimen name="stroke_medium">8dp</dimen>
<dimen name="stroke_large">12dp</dimen>
<dimen name="round_button_small">32dp</dimen>
<dimen name="round_button_medium">64dp</dimen>
<dimen name="round_button_large">92dp</dimen>
<dimen name="rect_button_margin">12dp</dimen>
<dimen name="rect_button_width">80dp</dimen>
<dimen name="rect_button_height">24dp</dimen>
<dimen name="shutter_button_margin">80dp</dimen>
</resources>

View File

@ -40,6 +40,11 @@
<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="capture_button_alt">Capture</string>
<string name="switch_camera_button_alt">Switch camera</string>
<string name="gallery_button_alt">Gallery</string>
<string name="capture_mode_camera">Camera</string>
<string name="delete_title">Confirm</string>
<string name="share_picture">Share picture…</string>
<string name="NoCommentsToShow">No comments on this post…</string>
<string name="CommentDisplay"> to show…</string>