Add upload progress bars and error handling to PostCreationActivity (#191)

* Add upload bar to PostCreationActivity

* Add upload error handling

* Fix test, remove duplicate api endpoint

* try to trigger ci

* don't show error all the time

* remove unused strings
This commit is contained in:
Wv5twkFEKh54vo4tta9yu7dHa3 2020-05-21 19:31:41 +02:00 committed by GitHub
parent 5b0a344236
commit d942c30898
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 287 additions and 121 deletions

View File

@ -123,6 +123,7 @@ dependencies {
//iconics //iconics
implementation "com.mikepenz:materialdrawer-iconics:8.0.3" implementation "com.mikepenz:materialdrawer-iconics:8.0.3"
implementation "com.mikepenz:iconics-views:5.0.2"
implementation 'com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar' implementation 'com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar'

View File

@ -1,10 +1,10 @@
package com.h.pixeldroid package com.h.pixeldroid
import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.* import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.swipeLeft
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.Intents.intending import androidx.test.espresso.intent.Intents.intending
@ -12,7 +12,6 @@ import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction
import androidx.test.espresso.intent.rule.IntentsTestRule import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule import androidx.test.rule.GrantPermissionRule
@ -22,7 +21,6 @@ import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.testUtility.MockServer import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.utils.DBUtils import com.h.pixeldroid.utils.DBUtils
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.camera_ui_container.*
import org.hamcrest.Matcher import org.hamcrest.Matcher
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
@ -67,9 +65,6 @@ class PostFragmentUITests {
@get:Rule @get:Rule
var globalTimeout: Timeout = Timeout.seconds(30) var globalTimeout: Timeout = Timeout.seconds(30)
@get:Rule
var rule = ActivityScenarioRule(MainActivity::class.java)
private lateinit var db: AppDatabase private lateinit var db: AppDatabase

View File

@ -1,5 +1,8 @@
package com.h.pixeldroid.testUtility package com.h.pixeldroid.testUtility
import com.google.gson.Gson
import com.h.pixeldroid.objects.Application
class JsonValues { class JsonValues {
companion object { companion object {
const val likedJson = """{"id":"156491373246287872","created_at":"2020-04-16T20:00:50.000000Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","url":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","replies_count":1,"reblogs_count":13,"favourites_count":3,"reblogged":false,"favourited":true,"muted":false,"bookmarked":false,"pinned":false,"content":"<a class=\"u-url mention\" href=\"https:\/\/pixelfed.de\/Dobios\" rel=\"external nofollow noopener\">@Dobios<\/a> <a class=\"u-url mention\" href=\"https:\/\/pixelfed.de\/Dante\" rel=\"external nofollow noopener\">@Dante<\/a>","reblog":null,"application":{"name":"web","website":null},"mentions":[{"id":"136800034732773376","url":"https:\/\/pixelfed.de\/Dobios","username":"Dobios","acct":"Dobios"},{"id":"136453537340198912","url":"https:\/\/pixelfed.de\/dante","username":"dante","acct":"dante"}],"tags":[{"name":"mushroom","url":"https:\/\/pixelfed.de\/discover\/tags\/mushroom"},{"name":"commentsstillbroken","url":"https:\/\/pixelfed.de\/discover\/tags\/commentsstillbroken"},{"name":"fixyourapi","url":"https:\/\/pixelfed.de\/discover\/tags\/fixyourapi"},{"name":"pls","url":"https:\/\/pixelfed.de\/discover\/tags\/pls"}],"emojis":[],"card":null,"poll":null,"account":{"id":"145183325781364736","username":"machintuck","acct":"machintuck","display_name":"Arthur","locked":false,"created_at":"2020-03-16T15:06:42.000000Z","followers_count":4,"following_count":4,"statuses_count":5,"note":"","url":"https:\/\/pixelfed.de\/machintuck","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"19228","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP_thumb.jpeg","text_url":null,"meta":null,"description":null}]}""" const val likedJson = """{"id":"156491373246287872","created_at":"2020-04-16T20:00:50.000000Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","url":"https:\/\/pixelfed.de\/p\/machintuck\/156491373246287872","replies_count":1,"reblogs_count":13,"favourites_count":3,"reblogged":false,"favourited":true,"muted":false,"bookmarked":false,"pinned":false,"content":"<a class=\"u-url mention\" href=\"https:\/\/pixelfed.de\/Dobios\" rel=\"external nofollow noopener\">@Dobios<\/a> <a class=\"u-url mention\" href=\"https:\/\/pixelfed.de\/Dante\" rel=\"external nofollow noopener\">@Dante<\/a>","reblog":null,"application":{"name":"web","website":null},"mentions":[{"id":"136800034732773376","url":"https:\/\/pixelfed.de\/Dobios","username":"Dobios","acct":"Dobios"},{"id":"136453537340198912","url":"https:\/\/pixelfed.de\/dante","username":"dante","acct":"dante"}],"tags":[{"name":"mushroom","url":"https:\/\/pixelfed.de\/discover\/tags\/mushroom"},{"name":"commentsstillbroken","url":"https:\/\/pixelfed.de\/discover\/tags\/commentsstillbroken"},{"name":"fixyourapi","url":"https:\/\/pixelfed.de\/discover\/tags\/fixyourapi"},{"name":"pls","url":"https:\/\/pixelfed.de\/discover\/tags\/pls"}],"emojis":[],"card":null,"poll":null,"account":{"id":"145183325781364736","username":"machintuck","acct":"machintuck","display_name":"Arthur","locked":false,"created_at":"2020-03-16T15:06:42.000000Z","followers_count":4,"following_count":4,"statuses_count":5,"note":"","url":"https:\/\/pixelfed.de\/machintuck","avatar":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","avatar_static":"https:\/\/pixelfed.de\/storage\/avatars\/014\/518\/332\/578\/136\/473\/6\/gbdKtKOhTkNA5UxCzeAQ_avatar.jpeg?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35","header":"","header_static":"","emojis":[],"moved":null,"fields":null,"bot":false,"software":"pixelfed","is_admin":false},"media_attachments":[{"id":"19228","type":"image","url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP.jpeg","remote_url":null,"preview_url":"https:\/\/pixelfed.de\/storage\/m\/d0931bf747b992a1c83e055753526516f2706111\/9b4393bfd32c643a265bd1c557b981f167d60969\/lbOqQOMeHLGmhYgehhZUBJ4JvjtKulh83BA97LoP_thumb.jpeg","text_url":null,"meta":null,"description":null}]}"""
@ -175,5 +178,8 @@ class JsonValues {
"version": "69.420", "version": "69.420",
"registrations": true "registrations": true
}""" }"""
var applicationJson = Gson().toJson(Application(name="PixelDroid",
website=null, vapid_key=null, client_id="286",
client_secret="2q3dHY29U8GNZ2eY6cbcw010cWk3qVGmWXxAJzn7"))
} }
} }

View File

@ -45,6 +45,11 @@ class MockServer {
.setResponseCode(200).setBody(JsonValues.tokenJson) .setResponseCode(200).setBody(JsonValues.tokenJson)
} }
when { when {
request.path?.startsWith("/api/v1/apps") == true -> {
return MockResponse()
.addHeader("Content-Type", "application/json; charset=utf-8")
.setResponseCode(200).setBody(JsonValues.applicationJson)
}
request.path?.startsWith("/api/v1/notifications") == true -> { request.path?.startsWith("/api/v1/notifications") == true -> {
return MockResponse() return MockResponse()
.addHeader("Content-Type", "application/json; charset=utf-8") .addHeader("Content-Type", "application/json; charset=utf-8")

View File

@ -30,7 +30,8 @@
<activity android:name=".PostCreationActivity" <activity android:name=".PostCreationActivity"
android:screenOrientation="sensorPortrait" android:screenOrientation="sensorPortrait"
tools:ignore="LockedOrientationActivity" /> tools:ignore="LockedOrientationActivity"
android:theme="@style/AppTheme.NoActionBar"/>
<activity android:name=".FollowsActivity" <activity android:name=".FollowsActivity"
android:screenOrientation="sensorPortrait" android:screenOrientation="sensorPortrait"
tools:ignore="LockedOrientationActivity"/> tools:ignore="LockedOrientationActivity"/>

View File

@ -5,8 +5,9 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.util.Log import android.util.Log
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.Button import android.widget.Button
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast import android.widget.Toast
@ -15,23 +16,25 @@ import androidx.core.net.toUri
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.h.pixeldroid.api.PixelfedAPI import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.UserDatabaseEntity import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.objects.Attachment
import com.h.pixeldroid.objects.Instance import com.h.pixeldroid.objects.Instance
import com.h.pixeldroid.objects.Status import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.DBUtils import com.h.pixeldroid.utils.DBUtils
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.PublishSubject
import kotlinx.android.synthetic.main.activity_post_creation.*
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody import okio.BufferedSink
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.io.ByteArrayOutputStream import java.io.*
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Date
class PostCreationActivity : AppCompatActivity(){ class PostCreationActivity : AppCompatActivity(){
@ -43,6 +46,8 @@ class PostCreationActivity : AppCompatActivity() {
private lateinit var image: File private lateinit var image: File
private var user: UserDatabaseEntity? = null private var user: UserDatabaseEntity? = null
private var listOfIds: List<String> = emptyList()
private var maxLength: Int = Instance.DEFAULT_MAX_TOOT_CHARS private var maxLength: Int = Instance.DEFAULT_MAX_TOOT_CHARS
private var description: String = "" private var description: String = ""
@ -77,21 +82,28 @@ class PostCreationActivity : AppCompatActivity() {
accessToken = user?.accessToken.orEmpty() accessToken = user?.accessToken.orEmpty()
pixelfedAPI = PixelfedAPI.create(domain) pixelfedAPI = PixelfedAPI.create(domain)
// check if the picture is alright //upload the picture and display progress while doing so
// TODO upload()
// edit the picture // get the description and send the post
// TODO
// get the description and send the post to PixelFed
findViewById<Button>(R.id.post_creation_send_button).setOnClickListener { findViewById<Button>(R.id.post_creation_send_button).setOnClickListener {
if (setDescription()) upload() if (setDescription() && listOfIds.isNotEmpty()) post()
} }
// Button to retry image upload when it fails
findViewById<Button>(R.id.retry_upload_button).setOnClickListener {
upload_error.visibility = GONE
upload()
}
}
override fun onDestroy() {
super.onDestroy()
//delete the temporary image
image.delete()
} }
private fun saveImage(imageUri: Uri) { private fun saveImage(imageUri: Uri) {
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val fileName = "PixelDroid_$timeStamp.png"
try { try {
val stream = applicationContext.contentResolver val stream = applicationContext.contentResolver
.openAssetFileDescriptor(imageUri, "r")!! .openAssetFileDescriptor(imageUri, "r")!!
@ -99,9 +111,8 @@ class PostCreationActivity : AppCompatActivity() {
val bm = BitmapFactory.decodeStream(stream) val bm = BitmapFactory.decodeStream(stream)
val bos = ByteArrayOutputStream() val bos = ByteArrayOutputStream()
bm.compress(Bitmap.CompressFormat.PNG, 0, bos) bm.compress(Bitmap.CompressFormat.PNG, 0, bos)
image = File( image = File.createTempFile("temp_compressed_img", ".png", cacheDir)
applicationContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES),
fileName)
val fos = FileOutputStream(image) val fos = FileOutputStream(image)
fos.write(bos.toByteArray()) fos.write(bos.toByteArray())
fos.flush() fos.flush()
@ -116,7 +127,7 @@ class PostCreationActivity : AppCompatActivity() {
val textField = findViewById<TextInputEditText>(R.id.new_post_description_input_field) val textField = findViewById<TextInputEditText>(R.id.new_post_description_input_field)
val content = textField.text.toString() val content = textField.text.toString()
if (content.length > maxLength) { if (content.length > maxLength) {
// error, too much characters // error, too many characters
textField.error = getString(R.string.description_max_characters).format(maxLength) textField.error = getString(R.string.description_max_characters).format(maxLength)
return false return false
@ -127,43 +138,49 @@ class PostCreationActivity : AppCompatActivity() {
} }
private fun upload(){ private fun upload(){
val rBody: RequestBody = image.asRequestBody("image/*".toMediaTypeOrNull()) val imagePart = ProgressRequestBody(image)
val part = MultipartBody.Part.createFormData("file", image.name, rBody) val requestBody = MultipartBody.Builder()
pixelfedAPI.mediaUpload("Bearer $accessToken", part).enqueue(object: .setType(MultipartBody.FORM)
Callback<Attachment> { .addFormDataPart("file", image.name, imagePart)
override fun onFailure(call: Call<Attachment>, t: Throwable) { .build()
Log.e(TAG, t.toString() + call.request())
Toast.makeText(applicationContext,getString(R.string.upload_picture_failed), val sub = imagePart.progressSubject
Toast.LENGTH_SHORT).show() .subscribeOn(Schedulers.io())
.subscribe { percentage ->
uploadProgressBar.progress = percentage.toInt()
} }
override fun onResponse(call: Call<Attachment>, response: Response<Attachment>) { var postSub : Disposable?= null
if (response.code() == 200) { val inter = pixelfedAPI.mediaUpload("Bearer $accessToken", requestBody.parts[0])
val body = response.body()!!
if (body.type.name == "image") { postSub = inter
post(body.id) .subscribeOn(Schedulers.io())
} else .observeOn(AndroidSchedulers.mainThread())
Toast.makeText(applicationContext, getString(R.string.picture_format_error), .subscribe({ attachment ->
Toast.LENGTH_SHORT).show() listOfIds = listOf(attachment.id)
} else { },{e->
Log.e(TAG, upload_error.visibility = VISIBLE
"Server responded: $response${call.request()}${call.request().body}" e.printStackTrace()
) postSub?.dispose()
Toast.makeText(applicationContext,getString(R.string.request_format_error), sub.dispose()
Toast.LENGTH_SHORT).show() }, {
} uploadProgressBar.visibility = GONE
} upload_completed_textview.visibility = VISIBLE
enableButton(true)
postSub?.dispose()
sub.dispose()
}) })
} }
private fun post(id: String) { private fun post() {
if (id.isEmpty()) return enableButton(false)
pixelfedAPI.postStatus( pixelfedAPI.postStatus(
authorization = "Bearer $accessToken", authorization = "Bearer $accessToken",
statusText = description, statusText = description,
media_ids = listOf(id) media_ids = listOfIds
).enqueue(object: Callback<Status> { ).enqueue(object: Callback<Status> {
override fun onFailure(call: Call<Status>, t: Throwable) { override fun onFailure(call: Call<Status>, t: Throwable) {
enableButton(true)
Toast.makeText(applicationContext,getString(R.string.upload_post_failed), Toast.makeText(applicationContext,getString(R.string.upload_post_failed),
Toast.LENGTH_SHORT).show() Toast.LENGTH_SHORT).show()
Log.e(TAG, t.message + call.request()) Log.e(TAG, t.message + call.request())
@ -173,14 +190,80 @@ class PostCreationActivity : AppCompatActivity() {
if (response.code() == 200) { if (response.code() == 200) {
Toast.makeText(applicationContext,getString(R.string.upload_post_success), Toast.makeText(applicationContext,getString(R.string.upload_post_success),
Toast.LENGTH_SHORT).show() Toast.LENGTH_SHORT).show()
startActivity(Intent(applicationContext, MainActivity::class.java)) val intent = Intent(this@PostCreationActivity, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
} else { } else {
Toast.makeText(applicationContext,getString(R.string.upload_post_error), Toast.makeText(applicationContext,getString(R.string.upload_post_error),
Toast.LENGTH_SHORT).show() Toast.LENGTH_SHORT).show()
Log.e(TAG, call.request().toString() + response.raw().toString()) Log.e(TAG, call.request().toString() + response.raw().toString())
enableButton(true)
} }
} }
}) })
} }
private fun enableButton(enable: Boolean = true){
post_creation_send_button.isEnabled = enable
if(enable){
posting_progress_bar.visibility = GONE
post_creation_send_button.visibility = VISIBLE
} else{
posting_progress_bar.visibility = VISIBLE
post_creation_send_button.visibility = GONE
}
} }
}
class ProgressRequestBody(private val mFile: File) : RequestBody() {
private val getProgressSubject: PublishSubject<Float> = PublishSubject.create()
val progressSubject: Observable<Float>
get() {
return getProgressSubject
}
override fun contentType(): MediaType? {
return "image/png".toMediaTypeOrNull()
}
@Throws(IOException::class)
override fun contentLength(): Long {
return mFile.length()
}
@Throws(IOException::class)
override fun writeTo(sink: BufferedSink) {
val fileLength = contentLength()
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
val `in` = FileInputStream(mFile)
var uploaded: Long = 0
`in`.use {
var read: Int
var lastProgressPercentUpdate = 0.0f
read = it.read(buffer)
while (read != -1) {
uploaded += read.toLong()
sink.write(buffer, 0, read)
read = it.read(buffer)
val progress = (uploaded.toFloat() / fileLength.toFloat()) * 100f
//prevent publishing too many updates, which slows upload, by checking if the upload has progressed by at least 1 percent
if (progress - lastProgressPercentUpdate > 1 || progress == 100f) {
// publish progress
getProgressSubject.onNext(progress)
lastProgressPercentUpdate = progress
}
}
}
}
companion object {
private const val DEFAULT_BUFFER_SIZE = 2048
}
}

View File

@ -1,6 +1,7 @@
package com.h.pixeldroid.api package com.h.pixeldroid.api
import com.h.pixeldroid.objects.* import com.h.pixeldroid.objects.*
import io.reactivex.Observable
import okhttp3.MultipartBody import okhttp3.MultipartBody
import retrofit2.Call import retrofit2.Call
import retrofit2.Retrofit import retrofit2.Retrofit
@ -9,6 +10,7 @@ import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.* import retrofit2.http.*
import retrofit2.http.Field import retrofit2.http.Field
/* /*
Implements the Pixelfed API Implements the Pixelfed API
https://docs.pixelfed.org/technical-documentation/api-v1.html https://docs.pixelfed.org/technical-documentation/api-v1.html
@ -233,7 +235,7 @@ interface PixelfedAPI {
//The authorization header needs to be of the form "Bearer <token>" //The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String, @Header("Authorization") authorization: String,
@Part file: MultipartBody.Part @Part file: MultipartBody.Part
): Call<Attachment> ): Observable<Attachment>
// get instance configuration // get instance configuration
@GET("/api/v1/instance") @GET("/api/v1/instance")
@ -245,4 +247,3 @@ interface PixelfedAPI {
@Header("Authorization") authorization: String @Header("Authorization") authorization: String
) : Call<DiscoverPosts> ) : Call<DiscoverPosts>
} }

View File

@ -15,7 +15,6 @@ class ThemeUtils {
fun setThemeFromPreferences(preferences: SharedPreferences, resources : Resources) { fun setThemeFromPreferences(preferences: SharedPreferences, resources : Resources) {
val themes = resources.getStringArray(R.array.theme_values) val themes = resources.getStringArray(R.array.theme_values)
val theme = preferences.getString("theme", "") val theme = preferences.getString("theme", "")
Log.e("themePref", theme!!)
//Set the theme //Set the theme
when(theme) { when(theme) {
//Light //Light

View File

@ -1,56 +1,140 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merge xmlns:tools="http://schemas.android.com/tools" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/upload_error"
android:visibility="gone"
android:layout_width="match_parent"
android:elevation="2dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.mikepenz.iconics.view.IconicsTextView
android:id="@+id/upload_error_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#90000000"
android:text="@string/media_upload_failed"
android:textColor="@color/colorPrimaryError"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/retry_upload_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/retry"
app:layout_constraintEnd_toEndOf="@id/upload_error_text_view"
app:layout_constraintStart_toStartOf="@id/upload_error_text_view"
app:layout_constraintTop_toBottomOf="@id/upload_error_text_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView <ImageView
android:id="@+id/post_creation_picture_frame" android:id="@+id/post_creation_picture_frame"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@string/posting_image_accessibility_hint"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/buttonConstraints"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/textInputLayout2">
<Button
android:id="@+id/post_creation_send_button"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:contentDescription="TODO" /> android:backgroundTint="@color/colorButtonBg"
android:enabled="false"
android:visibility="gone"
android:text="@string/send"
android:textColor="@color/colorButtonText"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout <ProgressBar
android:layout_width="match_parent" android:id="@+id/posting_progress_bar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" app:layout_constraintStart_toStartOf="parent"
android:layout_gravity="bottom" app:layout_constraintBottom_toBottomOf="parent"
android:background="#88000000"> app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<ProgressBar
android:id="@+id/uploadProgressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/textInputLayout2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout2"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/description" android:hint="@string/description"
app:errorEnabled="true"
android:layout_gravity="fill_horizontal"
android:paddingStart="15dp" android:paddingStart="15dp"
android:paddingEnd="15dp"
android:textColorHint="@color/colorPrimaryTab" android:textColorHint="@color/colorPrimaryTab"
app:errorTextColor="@color/colorPrimaryError"> app:errorEnabled="true"
app:errorTextColor="@color/colorPrimaryError"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/buttonConstraints"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/new_post_description_input_field" android:id="@+id/new_post_description_input_field"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:ems="10" android:ems="10"
android:inputType="textMultiLine" android:inputType="textMultiLine"
android:textColor="@color/colorPrimary" /> android:textColor="@color/colorPrimary" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<Button <com.mikepenz.iconics.view.IconicsTextView
android:id="@+id/post_creation_send_button" android:id="@+id/upload_completed_textview"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="0dp"
android:text="@string/send" android:layout_marginTop="8dp"
android:backgroundTint="@color/colorButtonBg" android:layout_marginEnd="8dp"
android:textColor="@color/colorButtonText" android:text="@string/media_upload_completed"
android:layout_margin="15dp" android:textColor="@android:color/holo_green_light"
android:layout_gravity="center_vertical" android:textSize="16sp"
tools:ignore="PrivateResource"/> android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
</LinearLayout> app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</merge>

View File

@ -35,7 +35,6 @@
<string name="domain_of_your_instance">اسم نطاق مثيل خادمك</string> <string name="domain_of_your_instance">اسم نطاق مثيل خادمك</string>
<string name="login_connection_required_once">يجب أن تكون متصلا بالأنترنت لتتمكن مِن إضافة أول حساب واستخدام PixelDroid :(</string> <string name="login_connection_required_once">يجب أن تكون متصلا بالأنترنت لتتمكن مِن إضافة أول حساب واستخدام PixelDroid :(</string>
<string name="capture_button_alt">لقطة شاشة</string> <string name="capture_button_alt">لقطة شاشة</string>
<string name="you_are_in_offline_mode">إنك غير متصل الآن ولكنه لا يزال بإمكانك عرض بعض المحتوى!</string>
<string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / وسائط مخفية <string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / وسائط مخفية
\n (اضعط للعرض)</string> \n (اضعط للعرض)</string>
<string name="lbl_contrast">تباين عالٍ</string> <string name="lbl_contrast">تباين عالٍ</string>

View File

@ -37,7 +37,6 @@
<string name="CommentDisplay">mostrar…</string> <string name="CommentDisplay">mostrar…</string>
<string name="domain_of_your_instance">Domini de la teva instància</string> <string name="domain_of_your_instance">Domini de la teva instància</string>
<string name="connect_to_pixelfed">Connectat a Pixelfed</string> <string name="connect_to_pixelfed">Connectat a Pixelfed</string>
<string name="you_are_in_offline_mode">Estàs en mode fora de línia, però encara pots veure algun contingut!</string>
<string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Imatges ocultes <string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Imatges ocultes
\n(fes clic per mostrar)</string> \n(fes clic per mostrar)</string>
<string name="add_account_description">Afegeix un altre compte</string> <string name="add_account_description">Afegeix un altre compte</string>

View File

@ -37,7 +37,6 @@
<string name="domain_of_your_instance">Domain Ihrer Instanz</string> <string name="domain_of_your_instance">Domain Ihrer Instanz</string>
<string name="connect_to_pixelfed">Mit Pixelfed verbinden</string> <string name="connect_to_pixelfed">Mit Pixelfed verbinden</string>
<string name="login_connection_required_once">Sie müssen online sein, um das erste Konto hinzuzufügen und um PixelDroid verwenden zu können :(</string> <string name="login_connection_required_once">Sie müssen online sein, um das erste Konto hinzuzufügen und um PixelDroid verwenden zu können :(</string>
<string name="you_are_in_offline_mode">Sie befinden sich im Offline-Modus, aber Sie können immer noch einige Inhalte ansehen!</string>
<string name="switch_camera_button_alt">Kamera wechseln</string> <string name="switch_camera_button_alt">Kamera wechseln</string>
<string name="registration_failed">Konnte die App nicht mit diesem Server verbinden</string> <string name="registration_failed">Konnte die App nicht mit diesem Server verbinden</string>
<string name="instance_error">Konnte die Informationen der Instanz nicht abrufen</string> <string name="instance_error">Konnte die Informationen der Instanz nicht abrufen</string>

View File

@ -36,7 +36,6 @@
<string name="CommentDisplay">para mostrar…</string> <string name="CommentDisplay">para mostrar…</string>
<string name="domain_of_your_instance">Dominio de tu nodo</string> <string name="domain_of_your_instance">Dominio de tu nodo</string>
<string name="connect_to_pixelfed">Conectar con Pixelfed</string> <string name="connect_to_pixelfed">Conectar con Pixelfed</string>
<string name="you_are_in_offline_mode">Estás en modo sin conexión, ¡pero todavía puedes ver algo de contenido!</string>
<string name="login_connection_required_once">Necesitas estar conectado para poder añadir la primera cuenta y utilizar PixelDroid :(</string> <string name="login_connection_required_once">Necesitas estar conectado para poder añadir la primera cuenta y utilizar PixelDroid :(</string>
<string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Contenido Oculto <string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Contenido Oculto
\n (haz clic para mostrar)</string> \n (haz clic para mostrar)</string>

View File

@ -24,7 +24,6 @@
<string name="app_name">PixelDroid</string> <string name="app_name">PixelDroid</string>
<string name="gallery_button_alt">Galeria</string> <string name="gallery_button_alt">Galeria</string>
<string name="NoCommentsToShow">Iruzkinik gabeko argitalpena…</string> <string name="NoCommentsToShow">Iruzkinik gabeko argitalpena…</string>
<string name="you_are_in_offline_mode">Lineaz kanpo editatzeko moduan zaude, baina hala ere eduki batzuk ikus ditzakezu!</string>
<string name="theme_title">Aplikazioaren gaia</string> <string name="theme_title">Aplikazioaren gaia</string>
<string name="tab_edit">EDITATU</string> <string name="tab_edit">EDITATU</string>
<string name="switch_camera_button_alt">Aldatu kamera</string> <string name="switch_camera_button_alt">Aldatu kamera</string>

View File

@ -36,7 +36,6 @@
<string name="CommentDisplay">برای نمایش…</string> <string name="CommentDisplay">برای نمایش…</string>
<string name="domain_of_your_instance">دامنهٔ نمونهٔ شما</string> <string name="domain_of_your_instance">دامنهٔ نمونهٔ شما</string>
<string name="connect_to_pixelfed">اتصال به پیکسل‌فد</string> <string name="connect_to_pixelfed">اتصال به پیکسل‌فد</string>
<string name="you_are_in_offline_mode">شما در حالت برخط نیستید، اما همچنان می‌توانید برخی محتواها را ببینید!</string>
<string name="app_name">پیکسل‌دروید</string> <string name="app_name">پیکسل‌دروید</string>
<string name="menu_account">نمایهٔ من</string> <string name="menu_account">نمایهٔ من</string>
<string name="registration_failed">نتوانستیم برنامه را روی این کارساز ثبت کنیم</string> <string name="registration_failed">نتوانستیم برنامه را روی این کارساز ثبت کنیم</string>

View File

@ -35,7 +35,6 @@
<string name="gallery_button_alt">Galerie</string> <string name="gallery_button_alt">Galerie</string>
<string name="NoCommentsToShow">Aucun commentaire dans cette publication…</string> <string name="NoCommentsToShow">Aucun commentaire dans cette publication…</string>
<string name="domain_of_your_instance">Domaine de votre instance</string> <string name="domain_of_your_instance">Domaine de votre instance</string>
<string name="you_are_in_offline_mode">Vous êtes en mode hors ligne mais vous pouvez continuer accéder à un certain contenu !</string>
<string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Média caché <string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Média caché
\n (appuyez pour afficher)</string> \n (appuyez pour afficher)</string>
<string name="CommentDisplay">pour afficher…</string> <string name="CommentDisplay">pour afficher…</string>

View File

@ -35,7 +35,6 @@
<string name="domain_of_your_instance">Dominio da túa instancia</string> <string name="domain_of_your_instance">Dominio da túa instancia</string>
<string name="connect_to_pixelfed">Conectar con Pixelfed</string> <string name="connect_to_pixelfed">Conectar con Pixelfed</string>
<string name="login_connection_required_once">Debes ter conexión a internet para poder engadir a conta e usar PixelDroid :(</string> <string name="login_connection_required_once">Debes ter conexión a internet para poder engadir a conta e usar PixelDroid :(</string>
<string name="you_are_in_offline_mode">Non tes conexión, pero aínda así podes ver algún contido!</string>
<string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Agochado <string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Agochado
\n(preme para amosar)</string> \n(preme para amosar)</string>
<string name="app_name">PixelDroid</string> <string name="app_name">PixelDroid</string>

View File

@ -37,7 +37,6 @@
<string name="gallery_button_alt">Galleria</string> <string name="gallery_button_alt">Galleria</string>
<string name="domain_of_your_instance">Dominio della tua istanza</string> <string name="domain_of_your_instance">Dominio della tua istanza</string>
<string name="login_connection_required_once">È necessario connettersi a Internet almeno una volta per utilizzare PixelDroid :(</string> <string name="login_connection_required_once">È necessario connettersi a Internet almeno una volta per utilizzare PixelDroid :(</string>
<string name="you_are_in_offline_mode">Sei in modalità offline, ma puoi comunque visualizzare alcuni contenuti!</string>
<string name="invalid_domain">Dominio non valido</string> <string name="invalid_domain">Dominio non valido</string>
<string name="tab_edit">MODIFICA</string> <string name="tab_edit">MODIFICA</string>
</resources> </resources>

View File

@ -39,7 +39,6 @@
<string name="tab_edit">編集</string> <string name="tab_edit">編集</string>
<string name="image_download_failed">ダウンロードに失敗しました、もう一度実行してください</string> <string name="image_download_failed">ダウンロードに失敗しました、もう一度実行してください</string>
<string name="NoCommentsToShow">この投稿にはコメントがありません…</string> <string name="NoCommentsToShow">この投稿にはコメントがありません…</string>
<string name="you_are_in_offline_mode">オフラインモードです、一部のコンテンツは引き続き表示できます</string>
<string name="add_account_description">他のPixelfedアカウントを追加</string> <string name="add_account_description">他のPixelfedアカウントを追加</string>
<string name="add_account_name">アカウントを追加</string> <string name="add_account_name">アカウントを追加</string>
<string name="instance_error">インスタンス情報が取得できませんでした</string> <string name="instance_error">インスタンス情報が取得できませんでした</string>

View File

@ -39,7 +39,6 @@
<string name="domain_of_your_instance">Domein van je instance</string> <string name="domain_of_your_instance">Domein van je instance</string>
<string name="connect_to_pixelfed">Met Pixelfed verbinden</string> <string name="connect_to_pixelfed">Met Pixelfed verbinden</string>
<string name="login_connection_required_once">Je moet met het internet verbonden zijn om je eerste account toe te voegen en PixelDroid te kunnen gebruiken :(</string> <string name="login_connection_required_once">Je moet met het internet verbonden zijn om je eerste account toe te voegen en PixelDroid te kunnen gebruiken :(</string>
<string name="you_are_in_offline_mode">Je bent in de offline modus, maar je kan nog steeds bepaalde inhoud zien!</string>
<string name="add_account_description">Andere Pixelfed account toevoegen</string> <string name="add_account_description">Andere Pixelfed account toevoegen</string>
<string name="add_account_name">Account toevoegen</string> <string name="add_account_name">Account toevoegen</string>
</resources> </resources>

View File

@ -12,7 +12,6 @@
<string name="invalid_domain">Domínio inválido</string> <string name="invalid_domain">Domínio inválido</string>
<string name="add_account_description">Adicionar outra conta Pixelfed</string> <string name="add_account_description">Adicionar outra conta Pixelfed</string>
<string name="add_account_name">Adicionar Conta</string> <string name="add_account_name">Adicionar Conta</string>
<string name="you_are_in_offline_mode">Você está em modo offline, mas ainda pode ver alguns conteúdos!</string>
<string name="login_connection_required_once">Você precisa estar conectado para poder adicionar a primeira conta e usar o PixelDroid :(</string> <string name="login_connection_required_once">Você precisa estar conectado para poder adicionar a primeira conta e usar o PixelDroid :(</string>
<string name="connect_to_pixelfed">Conectar-se à Pixelfed</string> <string name="connect_to_pixelfed">Conectar-se à Pixelfed</string>
<string name="domain_of_your_instance">Domínio da sua instância</string> <string name="domain_of_your_instance">Domínio da sua instância</string>

View File

@ -35,7 +35,6 @@
<string name="image_download_failed">Скачивание не удалось, попробуйте ещё раз</string> <string name="image_download_failed">Скачивание не удалось, попробуйте ещё раз</string>
<string name="share_picture">Поделиться изображение…</string> <string name="share_picture">Поделиться изображение…</string>
<string name="login_connection_required_once">Вам необходимо быть в сети что бы добавить аккаунт и использовать PixelDroid :(</string> <string name="login_connection_required_once">Вам необходимо быть в сети что бы добавить аккаунт и использовать PixelDroid :(</string>
<string name="you_are_in_offline_mode">Вы в оффлайн режиме, но вы можете видеть некоторый контент!</string>
<string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Скрытое медиа <string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Скрытое медиа
\n (кликните что бы показать)</string> \n (кликните что бы показать)</string>
<string name="registration_failed">Не удалось зарегистрировать приложение на этом инстансе</string> <string name="registration_failed">Не удалось зарегистрировать приложение на этом инстансе</string>

View File

@ -37,7 +37,6 @@
<string name="NoCommentsToShow">Inga kommentarer på detta inlägg…</string> <string name="NoCommentsToShow">Inga kommentarer på detta inlägg…</string>
<string name="CommentDisplay">att visa…</string> <string name="CommentDisplay">att visa…</string>
<string name="connect_to_pixelfed">Anslut till Pixelfed</string> <string name="connect_to_pixelfed">Anslut till Pixelfed</string>
<string name="you_are_in_offline_mode">Du är i nedkopplad läge, men du kan ändå se lite innehåll!</string>
<string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Dold media <string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Dold media
\n(Tryck för att visa)</string> \n(Tryck för att visa)</string>
<string name="add_account_description">Lägg till ett annat Pixelfed-konto</string> <string name="add_account_description">Lägg till ett annat Pixelfed-konto</string>

View File

@ -34,7 +34,6 @@
<string name="NoCommentsToShow">这条帖文下没有评论……</string> <string name="NoCommentsToShow">这条帖文下没有评论……</string>
<string name="CommentDisplay">显示……</string> <string name="CommentDisplay">显示……</string>
<string name="domain_of_your_instance">实例的域名</string> <string name="domain_of_your_instance">实例的域名</string>
<string name="you_are_in_offline_mode">您处于离线模式,但仍可以查看已缓存内容!</string>
<string name="connect_to_pixelfed">连接至 Pixelfed</string> <string name="connect_to_pixelfed">连接至 Pixelfed</string>
<string name="share_picture">分享图片……</string> <string name="share_picture">分享图片……</string>
<string name="login_connection_required_once">您需要联网才能添加第一个帐户并使用 PixelDroid :(</string> <string name="login_connection_required_once">您需要联网才能添加第一个帐户并使用 PixelDroid :(</string>

View File

@ -102,6 +102,12 @@
<!-- Sensitive media --> <!-- Sensitive media -->
<string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Hidden Media \n (click to show)</string> <string name="cw_nsfw_hidden_media_n_click_to_show">CW / NSFW / Hidden Media \n (click to show)</string>
<!-- Shown when image has finished uploading. {gmd_cloud_done} is an icon, position it as is appropriate in target language -->
<string name="media_upload_completed">{gmd_cloud_done} Media upload completed</string>
<!-- Shown when image uploading has failed. {gmd_cloud_off} is an icon, position it as is appropriate in target language -->
<string name="media_upload_failed">{gmd_cloud_off} Media upload failed, try again or check network conditions</string>
<string name="posting_image_accessibility_hint">Image being posted</string>
<string name="retry">Retry</string>
</resources> </resources>