Merge branch 'maintenance' into 'master'

Reduce technical debt and improve code, remove hardcoded strings

See merge request pixeldroid/PixelDroid!454
This commit is contained in:
Matthieu 2022-07-28 19:37:26 +00:00
commit 5158c2b1c0
38 changed files with 231 additions and 378 deletions

View File

@ -303,7 +303,6 @@ class LoginActivity : BaseThemedWithoutBarActivity() {
}
}
@OptIn(ExperimentalPagingApi::class)
private suspend fun storeUser(accessToken: String, refreshToken: String?, clientId: String, clientSecret: String, instance: String) {
try {
val user = pixelfedAPI.verifyCredentials("Bearer $accessToken")

View File

@ -62,7 +62,6 @@ class MainActivity : BaseThemedWithoutBarActivity() {
companion object {
const val ADD_ACCOUNT_IDENTIFIER: Long = -13
const val LOG_OUT_REQUESTED = "LOG_OUT_REQUESTED"
}
private lateinit var binding: ActivityMainBinding

View File

@ -13,6 +13,7 @@ import android.util.Log
import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.View.GONE
import android.widget.Toast
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
@ -195,7 +196,7 @@ class PostCreationActivity : BaseThemedWithoutBarActivity() {
model.setImages(model.addPossibleImages(it))
}
} else if (result.resultCode != Activity.RESULT_CANCELED) {
Toast.makeText(applicationContext, "Error while adding images", Toast.LENGTH_SHORT).show()
Toast.makeText(applicationContext, R.string.add_images_error, Toast.LENGTH_SHORT).show()
}
}
@ -296,11 +297,11 @@ class PostCreationActivity : BaseThemedWithoutBarActivity() {
private fun enableButton(enable: Boolean = true){
binding.postCreationSendButton.isEnabled = enable
if(enable){
binding.postingProgressBar.visibility = View.GONE
binding.postCreationSendButton.visibility = View.VISIBLE
binding.postingProgressBar.visibility = GONE
binding.postCreationSendButton.visibility = VISIBLE
} else {
binding.postingProgressBar.visibility = View.VISIBLE
binding.postCreationSendButton.visibility = View.GONE
binding.postingProgressBar.visibility = VISIBLE
binding.postCreationSendButton.visibility = GONE
}
}
@ -310,9 +311,9 @@ class PostCreationActivity : BaseThemedWithoutBarActivity() {
if (result?.resultCode == Activity.RESULT_OK && result.data != null) {
val position: Int = result.data!!.getIntExtra(PhotoEditActivity.PICTURE_POSITION, 0)
model.modifyAt(position, result.data!!)
?: Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show()
?: Toast.makeText(applicationContext, R.string.error_editing, Toast.LENGTH_SHORT).show()
} else if(result?.resultCode != Activity.RESULT_CANCELED){
Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show()
Toast.makeText(applicationContext, R.string.error_editing, Toast.LENGTH_SHORT).show()
}
}

View File

@ -248,7 +248,8 @@ class PostCreationViewModel(application: Application, clipdata: ClipData? = null
return
}
val imagePart = ProgressRequestBody(imageInputStream, data.size)
val type = imageUri.getMimeType(getApplication<PixelDroidApplication>().contentResolver)
val imagePart = ProgressRequestBody(imageInputStream, data.size, type)
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", System.currentTimeMillis().toString(), imagePart)

View File

@ -8,7 +8,7 @@ import okhttp3.RequestBody
import okio.BufferedSink
import java.io.*
class ProgressRequestBody(private val mFile: InputStream, private val length: Long) : RequestBody() {
class ProgressRequestBody(private val mFile: InputStream, private val length: Long, private val type: String) : RequestBody() {
private val getProgressSubject: PublishSubject<Float> = PublishSubject.create()
@ -18,7 +18,7 @@ class ProgressRequestBody(private val mFile: InputStream, private val length: Lo
}
override fun contentType(): MediaType? {
return "image/png".toMediaTypeOrNull()
return type.toMediaTypeOrNull()
}
@Throws(IOException::class)

View File

@ -3,7 +3,6 @@ package org.pixeldroid.app.postCreation
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
import android.widget.RelativeLayout
internal class SquareLayout(context: Context, attrs: AttributeSet) :
FrameLayout(context, attrs) {

View File

@ -25,14 +25,15 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.pixeldroid.app.R
import org.pixeldroid.app.databinding.FragmentCameraBinding
import org.pixeldroid.app.postCreation.PostCreationActivity
import org.pixeldroid.app.utils.BaseFragment
import java.io.File
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
@ -40,8 +41,6 @@ import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.properties.Delegates
import org.pixeldroid.app.R
import org.pixeldroid.app.utils.BaseFragment
private const val ANIMATION_FAST_MILLIS = 50L
private const val ANIMATION_SLOW_MILLIS = 100L

View File

@ -7,14 +7,12 @@ import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import org.pixeldroid.app.R
import org.pixeldroid.app.databinding.FragmentEditImageBinding
class EditImageFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
private var listener: PhotoEditActivity? = null
private lateinit var seekbarBrightness: SeekBar
private lateinit var seekbarSaturation: SeekBar
private lateinit var seekbarContrast: SeekBar
private lateinit var binding: FragmentEditImageBinding
private var BRIGHTNESS_MAX = 200
private var SATURATION_MAX = 20
@ -26,32 +24,28 @@ class EditImageFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
): View {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_edit_image, container, false)
binding = FragmentEditImageBinding.inflate(inflater, container, false)
seekbarBrightness = view.findViewById(R.id.seekbar_brightness)
seekbarSaturation = view.findViewById(R.id.seekbar_saturation)
seekbarContrast = view.findViewById(R.id.seekbar_contrast)
binding.seekbarBrightness.max = BRIGHTNESS_MAX
binding.seekbarBrightness.progress = BRIGHTNESS_START
seekbarBrightness.max = BRIGHTNESS_MAX
seekbarBrightness.progress = BRIGHTNESS_START
binding.seekbarContrast.max = CONTRAST_MAX
binding.seekbarContrast.progress = CONTRAST_START
seekbarContrast.max = CONTRAST_MAX
seekbarContrast.progress = CONTRAST_START
seekbarSaturation.max = SATURATION_MAX
seekbarSaturation.progress = SATURATION_START
binding.seekbarSaturation.max = SATURATION_MAX
binding.seekbarSaturation.progress = SATURATION_START
setOnSeekBarChangeListeners(this)
return view
return binding.root
}
private fun setOnSeekBarChangeListeners(listener: EditImageFragment?){
seekbarBrightness.setOnSeekBarChangeListener(listener)
seekbarContrast.setOnSeekBarChangeListener(listener)
seekbarSaturation.setOnSeekBarChangeListener(listener)
binding.seekbarBrightness.setOnSeekBarChangeListener(listener)
binding.seekbarContrast.setOnSeekBarChangeListener(listener)
binding.seekbarSaturation.setOnSeekBarChangeListener(listener)
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
@ -75,9 +69,9 @@ class EditImageFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
// Make sure to ignore seekbar change events, since we don't want to have the reset cause
// filter applications due to the onProgressChanged calls
setOnSeekBarChangeListeners(null)
seekbarBrightness.progress = BRIGHTNESS_START
seekbarContrast.progress = CONTRAST_START
seekbarSaturation.progress = SATURATION_START
binding.seekbarBrightness.progress = BRIGHTNESS_START
binding.seekbarContrast.progress = CONTRAST_START
binding.seekbarSaturation.progress = SATURATION_START
setOnSeekBarChangeListeners(this)
}

View File

@ -1,25 +0,0 @@
package org.pixeldroid.app.postCreation.photoEdit
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
class EditPhotoViewPagerAdapter (manager: FragmentManager):
FragmentPagerAdapter(manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val fragmentList = ArrayList<Fragment>()
private val fragmentTitleList = ArrayList<String>()
override fun getItem(position: Int) = fragmentList[position]
override fun getCount() = fragmentList.size
fun addFragment(fragment: Fragment, title: String) {
fragmentList.add(fragment)
fragmentTitleList.add(title)
}
override fun getPageTitle(position: Int): CharSequence {
return fragmentTitleList[position]
}
}

View File

@ -1,75 +1,64 @@
package org.pixeldroid.app.postCreation.photoEdit
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import org.pixeldroid.app.R
import com.zomato.photofilters.FilterPack
import com.zomato.photofilters.imageprocessors.Filter
import com.zomato.photofilters.utils.ThumbnailItem
import com.zomato.photofilters.utils.ThumbnailsManager
import kotlinx.coroutines.launch
import org.pixeldroid.app.R
import org.pixeldroid.app.databinding.FragmentFilterListBinding
import org.pixeldroid.app.utils.bitmapFromUri
class FilterListFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
private var listener : PhotoEditActivity? = null
private lateinit var binding: FragmentFilterListBinding
private var listener : ((Filter) -> Unit)? = null
internal lateinit var adapter: ThumbnailAdapter
private lateinit var tbItemList: MutableList<ThumbnailItem>
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
): View {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_filter_list, container, false)
binding = FragmentFilterListBinding.inflate(inflater, container, false)
tbItemList = ArrayList()
recyclerView = view.findViewById(R.id.recycler_view)
recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
recyclerView.itemAnimator = DefaultItemAnimator()
val space = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics).toInt()
recyclerView.addItemDecoration(SpaceItemDecoration(space))
binding.recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
adapter = ThumbnailAdapter(requireActivity(), tbItemList, this)
recyclerView.adapter = adapter
binding.recyclerView.adapter = adapter
return view
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
displayImage(null)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
displayImage()
}
private fun displayImage(bitmap: Bitmap?) {
val r = Runnable {
val tbImage: Bitmap = (if (bitmap == null) {
bitmapFromUri(requireActivity().contentResolver, PhotoEditActivity.imageUri)
} else {
Bitmap.createScaledBitmap(bitmap, 100, 100, false)
})
?: return@Runnable
private fun displayImage() {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
val tbImage: Bitmap = bitmapFromUri(requireActivity().contentResolver, PhotoEditActivity.imageUri)
setupFilter(tbImage)
if(activity != null) setupFilter(tbImage)
if(context != null) tbItemList.addAll(ThumbnailsManager.processThumbs(context))
activity?.runOnUiThread{ adapter.notifyDataSetChanged() }
tbItemList.addAll(ThumbnailsManager.processThumbs(context))
adapter.notifyDataSetChanged()
}
}
Thread(r).start()
}
private fun setupFilter(tbImage: Bitmap?) {
@ -95,14 +84,13 @@ class FilterListFragment : Fragment() {
fun resetSelectedFilter(){
adapter.resetSelected()
displayImage(null)
}
fun onFilterSelected(filter: Filter) {
listener?.onFilterSelected(filter)
listener?.invoke(filter)
}
fun setListener(listFragmentListener: PhotoEditActivity) {
fun setListener(listFragmentListener: (filter: Filter) -> Unit) {
this.listener = listFragmentListener
}
}

View File

@ -1,43 +0,0 @@
package org.pixeldroid.app.postCreation.photoEdit
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.animation.DecelerateInterpolator
import android.widget.Scroller
import androidx.viewpager.widget.ViewPager
class NonSwipeableViewPager: ViewPager {
constructor(context: Context):super(context) {
setMyScroller()
}
constructor(context: Context,attributeSet: AttributeSet): super(context, attributeSet) {
setMyScroller()
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
return false
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
return false
}
private fun setMyScroller() {
try {
val viewPager = ViewPager::class.java
val scroller = viewPager.getDeclaredField("mScroller")
scroller.isAccessible = true
scroller.set(this, FilterScroller(context))
} catch (e:Exception) {
e.printStackTrace()
}
}
}
class FilterScroller(context: Context): Scroller(context, DecelerateInterpolator()) {
override fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int, duration: Int) {
super.startScroll(startX, startY, dx, dy, 400)
}
}

View File

@ -5,12 +5,10 @@ import android.app.AlertDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Point
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.os.Bundle
import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import android.view.View.GONE
@ -18,9 +16,12 @@ import android.view.View.VISIBLE
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.bumptech.glide.Glide
import com.google.android.material.color.MaterialColors
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayoutMediator
import com.yalantis.ucrop.UCrop
import com.zomato.photofilters.imageprocessors.Filter
import com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubFilter
@ -115,7 +116,6 @@ class PhotoEditActivity : BaseThemedWithBarActivity() {
loadImage()
setupViewPager(binding.viewPager)
binding.tabs.setupWithViewPager(binding.viewPager)
}
private fun loadImage() {
@ -137,18 +137,36 @@ class PhotoEditActivity : BaseThemedWithBarActivity() {
return Bitmap.createScaledBitmap(image, (image.width * scale).toInt(), newY.toInt(), true)
}
private fun setupViewPager(viewPager: NonSwipeableViewPager?) {
val adapter = EditPhotoViewPagerAdapter(supportFragmentManager)
private fun setupViewPager(viewPager: ViewPager2) {
filterListFragment = FilterListFragment()
filterListFragment.setListener(this)
filterListFragment.setListener(::onFilterSelected)
editImageFragment = EditImageFragment()
editImageFragment.setListener(this)
adapter.addFragment(filterListFragment, "FILTERS")
adapter.addFragment(editImageFragment, "EDIT")
viewPager!!.adapter = adapter
val tabs: List<() -> Fragment> = listOf({ filterListFragment }, { editImageFragment })
// Keep both tabs loaded at all times because values are needed there
viewPager.offscreenPageLimit = 1
//Disable swiping in viewpager
viewPager.isUserInputEnabled = false
viewPager.adapter = object : FragmentStateAdapter(this) {
override fun createFragment(position: Int): Fragment {
return tabs[position]()
}
override fun getItemCount(): Int {
return tabs.size
}
}
TabLayoutMediator(binding.tabs, viewPager) { tab, position ->
tab.setText(when(position) {
0 -> R.string.tab_filters
else -> R.string.edit
})
}.attach()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {

View File

@ -1,22 +0,0 @@
package org.pixeldroid.app.postCreation.photoEdit
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class SpaceItemDecoration(private val space: Int): RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
if (parent.getChildAdapterPosition(view) == state.itemCount - 1) {
outRect.left = space
outRect.right = 0
} else {
outRect.left = 0
outRect.right = space
}
}
}

View File

@ -5,11 +5,10 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.zomato.photofilters.utils.ThumbnailItem
import org.pixeldroid.app.R
import org.pixeldroid.app.databinding.ThumbnailListItemBinding
import com.zomato.photofilters.utils.ThumbnailItem
import org.pixeldroid.app.utils.getColorFromAttr
class ThumbnailAdapter (private val context: Context,

View File

@ -36,6 +36,6 @@ class AlbumActivity : BaseActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowTitleEnabled(false)
supportActionBar?.setBackgroundDrawable(null)
window.statusBarColor = ContextCompat.getColor(this,android.R.color.transparent);
window.statusBarColor = ContextCompat.getColor(this,android.R.color.transparent)
}
}

View File

@ -7,7 +7,6 @@ import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.ClickableSpan
import android.text.style.URLSpan
import android.util.Log
import android.view.View
import android.widget.TextView
import androidx.core.text.toSpanned
@ -22,7 +21,6 @@ import java.net.URI
import java.net.URISyntaxException
import java.text.ParseException
import java.time.Instant
import java.time.ZoneOffset
import java.util.*
fun fromHtml(html: String): Spanned {

View File

@ -38,7 +38,7 @@ class NestedScrollableHost(context: Context, attrs: AttributeSet? = null) :
}
var images: ArrayList<Attachment> = ArrayList();
var images: ArrayList<Attachment> = ArrayList()
var doubleTapCallback: (() -> Unit)? = null
private val child: View? get() = if (childCount > 0) getChildAt(0) else null

View File

@ -42,7 +42,6 @@ import org.pixeldroid.app.databinding.OpenedAlbumBinding
import org.pixeldroid.app.databinding.PostFragmentBinding
import org.pixeldroid.app.posts.MediaViewerActivity.Companion.openActivity
import org.pixeldroid.app.utils.BlurHashDecoder
import org.pixeldroid.app.utils.ImageConverter
import org.pixeldroid.app.utils.api.PixelfedAPI
import org.pixeldroid.app.utils.api.objects.Attachment
import org.pixeldroid.app.utils.api.objects.Status
@ -51,6 +50,7 @@ import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_TAG
import org.pixeldroid.app.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
import org.pixeldroid.app.utils.db.AppDatabase
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
import org.pixeldroid.app.utils.setProfileImageFromURL
import retrofit2.HttpException
import java.io.File
import java.io.IOException
@ -132,10 +132,10 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
binding.root.context
)
binding.postDomain.text = status?.getStatusDomain(domain)
binding.postDomain.text = status?.getStatusDomain(domain, binding.postDomain.context)
//Setup images
ImageConverter.setRoundImageFromURL(
setProfileImageFromURL(
binding.root,
status?.getProfilePicUrl(),
binding.profilePic

View File

@ -6,7 +6,6 @@ import android.view.ViewGroup
import android.widget.ProgressBar
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.core.view.isVisible
import androidx.core.view.size
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.paging.LoadState
import androidx.paging.LoadStateAdapter
@ -14,14 +13,14 @@ import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.gson.Gson
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.pixeldroid.app.R
import org.pixeldroid.app.databinding.ErrorLayoutBinding
import org.pixeldroid.app.databinding.LoadStateFooterViewItemBinding
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
import org.pixeldroid.app.utils.api.objects.FeedContent
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import retrofit2.HttpException
/**

View File

@ -7,12 +7,15 @@ import android.view.ViewGroup
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.paging.*
import androidx.paging.LoadState.*
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadState.NotLoading
import androidx.paging.PagingDataAdapter
import androidx.paging.RemoteMediator
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filter
import org.pixeldroid.app.databinding.FragmentFeedBinding
import org.pixeldroid.app.posts.feeds.initAdapter
import org.pixeldroid.app.utils.BaseFragment

View File

@ -7,18 +7,18 @@ import android.view.ViewGroup
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.paging.*
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadState
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.RecyclerView
import org.pixeldroid.app.posts.feeds.launch
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import org.pixeldroid.app.databinding.FragmentFeedBinding
import org.pixeldroid.app.utils.BaseFragment
import org.pixeldroid.app.posts.feeds.initAdapter
import org.pixeldroid.app.posts.feeds.launch
import org.pixeldroid.app.utils.BaseFragment
import org.pixeldroid.app.utils.api.objects.FeedContent

View File

@ -4,11 +4,9 @@ import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import org.pixeldroid.app.utils.api.PixelfedAPI
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
import org.pixeldroid.app.utils.api.objects.FeedContent
import org.pixeldroid.app.utils.api.objects.Results
import kotlinx.coroutines.flow.Flow
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
import org.pixeldroid.app.utils.api.PixelfedAPI
import org.pixeldroid.app.utils.api.objects.Status
import javax.inject.Inject

View File

@ -33,15 +33,12 @@ import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
import org.pixeldroid.app.posts.feeds.uncachedFeeds.profile.ProfileContentRepository
import org.pixeldroid.app.posts.parseHTMLText
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
import org.pixeldroid.app.utils.BlurHashDecoder
import org.pixeldroid.app.utils.ImageConverter
import org.pixeldroid.app.utils.*
import org.pixeldroid.app.utils.api.PixelfedAPI
import org.pixeldroid.app.utils.api.objects.Account
import org.pixeldroid.app.utils.api.objects.Attachment
import org.pixeldroid.app.utils.api.objects.Status
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
import org.pixeldroid.app.utils.openUrl
import retrofit2.HttpException
import java.io.IOException
@ -99,9 +96,9 @@ class ProfileActivity : BaseThemedWithBarActivity() {
}
/**
* Shows or hides the error in the different FeedFragments
* Shows or hides the error in the profile
*/
private fun showError(errorText: String = "Something went wrong while loading", show: Boolean = true){
private fun showError(errorText: String = getString(R.string.profile_error), show: Boolean = true){
if(show){
binding.profileProgressBar.visibility = View.GONE
binding.motionLayout.transitionToEnd()
@ -116,6 +113,7 @@ class ProfileActivity : BaseThemedWithBarActivity() {
if(account != null) {
setViews(account)
} else {
supportActionBar?.setTitle(R.string.menu_account)
lifecycleScope.launchWhenResumed {
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
val myAccount: Account = try {
@ -149,7 +147,7 @@ class ProfileActivity : BaseThemedWithBarActivity() {
*/
private fun setViews(account: Account) {
val profilePicture = binding.profilePictureImageView
ImageConverter.setRoundImageFromURL(
setProfileImageFromURL(
View(applicationContext),
account.anyAvatar(),
profilePicture
@ -368,7 +366,7 @@ class ProfilePostsViewHolder(binding: FragmentProfilePostsBinding) : RecyclerVie
).placeholder(R.drawable.ic_sensitive).apply(RequestOptions().centerCrop())
.into(postPreview)
} else {
ImageConverter.setSquareImageFromURL(postPreview,
setSquareImageFromURL(postPreview,
post.getPostPreviewURL(),
postPreview,
post.media_attachments?.firstOrNull()?.blurhash)

View File

@ -18,9 +18,9 @@ import org.pixeldroid.app.utils.api.PixelfedAPI
import org.pixeldroid.app.utils.api.objects.Status
import org.pixeldroid.app.posts.PostActivity
import org.pixeldroid.app.utils.BaseFragment
import org.pixeldroid.app.utils.ImageConverter
import org.pixeldroid.app.utils.api.objects.Attachment
import org.pixeldroid.app.utils.bindingLifecycleAware
import org.pixeldroid.app.utils.setSquareImageFromURL
import retrofit2.HttpException
import java.io.IOException
@ -69,7 +69,7 @@ class SearchDiscoverFragment : BaseFragment() {
}
}
fun showError(@StringRes errorText: Int = R.string.loading_toast, show: Boolean = true){
private fun showError(@StringRes errorText: Int = R.string.loading_toast, show: Boolean = true){
binding.motionLayout.apply {
if(show){
transitionToEnd()
@ -126,7 +126,7 @@ class SearchDiscoverFragment : BaseFragment() {
} else holder.videoIcon.visibility = View.GONE
}
ImageConverter.setSquareImageFromURL(holder.postView, post?.getPostPreviewURL(), holder.postPreview, post?.media_attachments?.firstOrNull()?.blurhash)
setSquareImageFromURL(holder.postView, post?.getPostPreviewURL(), holder.postPreview, post?.media_attachments?.firstOrNull()?.blurhash)
holder.postPreview.setOnClickListener {
val intent = Intent(holder.postView.context, PostActivity::class.java)
intent.putExtra(Status.POST_TAG, post)

View File

@ -29,7 +29,7 @@ object BlurHashDecoder {
)
}
fun decode(blurHash: String?, width: Int?, height: Int?, punch: Float = 1f): Bitmap? {
private fun decode(blurHash: String?, width: Int?, height: Int?, punch: Float = 1f): Bitmap? {
if (blurHash == null || width == null || height == null || blurHash.length < 6) {
return null
}

View File

@ -1,99 +1,30 @@
package org.pixeldroid.app.utils
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import org.pixeldroid.app.R
class ImageConverter {
companion object {
/**
* @brief Loads a given image (via url) into a given image view
* @param activity, the activity in which this is happening
* @param url, the url of the image that will be loaded
* @param view, the imageView into which we will load the image
*/
fun setImageViewFromURL(activity: AppCompatActivity, url : String?, view : ImageView) {
Glide.with(activity).load(url).into(view)
}
/**
* @brief Loads a given image (via url) as a round image into a given image view
* @param view, the view in which this is happening
* @param url, the url of the image that will be loaded
* @param image, the imageView into which we will load the image
*/
fun setProfileImageFromURL(view : View, url : String?, image : ImageView) {
Glide.with(view).load(url).apply(RequestOptions().circleCrop())
.placeholder(R.drawable.ic_default_user).into(image)
}
/**
* @brief Loads a given image (via url) into a given image view
* @param fragment, the fragment in which this is happening
* @param url, the url of the image that will be loaded
* @param view, the imageView into which we will load the image
*/
fun setImageViewFromURL(fragment: Fragment, url : String?, view : ImageView) {
Glide.with(fragment).load(url).into(view)
}
/**
* @brief Loads a given image (via url) into a given image view
* @param fragmentActivity, the fragmentActivity in which this is happening
* @param url, the url of the image that will be loaded
* @param view, the imageView into which we will load the image
*/
fun setImageViewFromURL(fragmentActivity: FragmentActivity, url : String?, view : ImageView) {
Glide.with(fragmentActivity).load(url).into(view)
}
/**
* @brief Loads a given image (via url) into a given image view
* @param fragView, the view in which this is happening
* @param url, the url of the image that will be loaded
* @param view, the imageView into which we will load the image
*/
fun setImageViewFromURL(fragView: View, url : String?, view : ImageView) {
Glide.with(fragView).load(url).into(view)
}
/**
* @brief Loads a given image (via url) as a round image into a given image view
* @param view, the view in which this is happening
* @param url, the url of the image that will be loaded
* @param image, the imageView into which we will load the image
*/
fun setRoundImageFromURL(view : View, url : String?, image : ImageView) {
Glide.with(view).load(url).apply(RequestOptions().circleCrop())
.placeholder(R.drawable.ic_default_user).into(image)
}
/**
* @brief Loads a given image (via url) as a square image into a given image view
* @param view, the view in which this is happening
* @param url, the url of the image that will be loaded
* @param image, the imageView into which we will load the image
*/
fun setSquareImageFromURL(view : View, url : String?, image : ImageView, blurhash: String? = null) {
Glide.with(view).load(url).placeholder(
blurhash?.let { BlurHashDecoder.blurHashBitmap(view.resources, it, 32,32) }
).apply(RequestOptions().centerCrop()).into(image)
}
/**
* @brief Loads a given image (via url) as a square image into a given image view
* @param view, the view in which this is happening
* @param drawable, the drawable of the image
* @param image, the imageView into which we will load the image
*/
fun setSquareImageFromDrawable(view : View, drawable : Drawable?, image : ImageView) {
Glide.with(view).load(drawable).apply(RequestOptions().centerCrop()).into(image)
}
/**
* @brief Loads a default image into a given image view
* @param view, the view in which this is happening
* @param image, the imageView into which we will load the image
*/
fun setImageFromDrawable(view : View, image : ImageView, drawable : Int) {
Glide.with(view).load(drawable).into(image)
}
}
/**
* @brief Loads a given image (via url) as a square image into a given image view
* @param view, the view in which this is happening
* @param url, the url of the image that will be loaded
* @param image, the imageView into which we will load the image
*/
fun setSquareImageFromURL(view : View, url : String?, image : ImageView, blurhash: String? = null) {
Glide.with(view).load(url).placeholder(
blurhash?.let { BlurHashDecoder.blurHashBitmap(view.resources, it, 32, 32) }
).apply(RequestOptions().centerCrop()).into(image)
}

View File

@ -3,9 +3,8 @@ package org.pixeldroid.app.utils
import android.app.Application
import androidx.preference.PreferenceManager
import com.google.android.material.color.DynamicColors
import org.pixeldroid.app.utils.di.*
import com.mikepenz.iconics.Iconics
import org.ligi.tracedroid.TraceDroid
import org.pixeldroid.app.utils.di.*
class PixelDroidApplication: Application() {

View File

@ -29,6 +29,7 @@ import com.arthenica.ffmpegkit.FFmpegKitConfig
import com.google.android.material.color.MaterialColors
import okhttp3.HttpUrl
import org.pixeldroid.app.R
import java.util.*
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@ -65,7 +66,7 @@ fun Uri.getMimeType(contentResolver: ContentResolver, fallback: String = "image/
contentResolver.getType(this)
} else {
MimeTypeMap.getFileExtensionFromUrl(toString())
?.run { MimeTypeMap.getSingleton().getMimeTypeFromExtension(toLowerCase()) }
?.run { MimeTypeMap.getSingleton().getMimeTypeFromExtension(lowercase(Locale.getDefault())) }
} ?: fallback
}

View File

@ -73,7 +73,7 @@ data class Account(
fun getDisplayName() : String = when {
username.isNullOrBlank() && display_name.isNullOrBlank() -> ""
display_name.isNullOrBlank() -> "@$username"
else -> display_name.orEmpty()
else -> display_name
}
fun anyAvatar(): String? = avatar_static ?: avatar

View File

@ -84,10 +84,10 @@ open class Status(
)
}
fun getStatusDomain(domain: String) : String {
fun getStatusDomain(domain: String, context: Context) : String {
val accountDomain = getDomain(account!!.url)
return if(getDomain(domain) == accountDomain) ""
else " from $accountDomain"
else context.getString(R.string.from_other_domain).format(accountDomain)
}

View File

@ -6,7 +6,6 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
@ -24,6 +23,7 @@ import org.pixeldroid.app.utils.api.objects.Status
import org.pixeldroid.app.utils.db.AppDatabase
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
import org.pixeldroid.app.utils.getColorFromAttr
import retrofit2.HttpException
import java.io.IOException
import java.time.Instant
@ -171,7 +171,6 @@ class NotificationsWorker(
}.putExtra(USER_NOTIFICATION_TAG, user.user_id)
.putExtra(INSTANCE_NOTIFICATION_TAG, user.instance_uri)
val builder = NotificationCompat.Builder(applicationContext, makeChannelId(uniqueUserId, notification.type))
.setSmallIcon(
when (notification.type) {
@ -184,7 +183,7 @@ class NotificationsWorker(
null -> R.drawable.ic_comment_empty
}
)
.setColor(Color.parseColor("#6200EE"))
.setColor(applicationContext.getColorFromAttr(R.attr.colorPrimary))
.setContentTitle(
notification.account?.username?.let { username ->
applicationContext.getString(

View File

@ -26,7 +26,7 @@
android:scaleType="centerInside"
android:contentDescription="@string/image_preview" />
<org.pixeldroid.app.postCreation.photoEdit.NonSwipeableViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"

View File

@ -1,70 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
tools:context=".postCreation.photoEdit.EditImageFragment">
<LinearLayout
android:orientation="horizontal"
android:paddingBottom="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="@string/lbl_brightness"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<SeekBar
android:id="@+id/seekbar_brightness"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingTop="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/label_brightness"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:gravity="center_horizontal"
android:text="@string/lbl_brightness"
app:layout_constraintBottom_toTopOf="@+id/label_contrast"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="spread" />
<TextView
android:text="@string/lbl_contrast"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/label_contrast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:gravity="center_horizontal"
android:text="@string/lbl_contrast"
app:layout_constraintBottom_toTopOf="@+id/label_saturation"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/label_brightness" />
<SeekBar
android:id="@+id/seekbar_contrast"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/label_saturation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:gravity="center_horizontal"
android:text="@string/lbl_saturation"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/label_contrast" />
</LinearLayout>
<SeekBar
android:id="@+id/seekbar_brightness"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/label_brightness"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/label_barrier"
app:layout_constraintTop_toTopOf="@+id/label_brightness" />
<LinearLayout
android:orientation="horizontal"
android:paddingTop="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<SeekBar
android:id="@+id/seekbar_saturation"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/label_saturation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/seekbar_contrast"
app:layout_constraintTop_toTopOf="@+id/label_saturation" />
<TextView
android:text="@string/lbl_saturation"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<SeekBar
android:id="@+id/seekbar_contrast"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/label_contrast"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/seekbar_brightness"
app:layout_constraintTop_toTopOf="@+id/label_contrast" />
<SeekBar
android:id="@+id/seekbar_saturation"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/label_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="label_brightness,label_contrast,label_saturation" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -58,7 +58,7 @@
app:layout_constraintTop_toBottomOf="@id/notification_type"
tools:src="@drawable/ic_default_user"
tools:srcCompat="@tools:sample/backgrounds/scenic"
android:contentDescription="TODO" />
android:contentDescription="@string/notification_thumbnail" />
<TextView
android:id="@+id/notification_post_description"

View File

@ -15,7 +15,7 @@
android:id="@+id/postPreview"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="TODO"
android:contentDescription="@string/post_preview"
android:padding="1dp"
app:layout_columnWeight="1"
app:layout_constraintDimensionRatio="H,1:1"

View File

@ -42,11 +42,12 @@
android:id="@+id/postDomain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="#b3b3b3"
app:layout_constraintBottom_toBottomOf="@+id/profilePic"
app:layout_constraintStart_toEndOf="@+id/username"
app:layout_constraintTop_toTopOf="@+id/profilePic"
tools:text=" from domain.tld" />
tools:text="from domain.tld" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/postConstraint"

View File

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:padding="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

View File

@ -119,10 +119,10 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
<!-- Post editing -->
<string name="lbl_brightness">BRIGHTNESS</string>
<string name="lbl_contrast">CONTRAST</string>
<string name="lbl_saturation">SATURATION</string>
<string name="tab_filters">FILTERS</string>
<string name="lbl_brightness">Brightness</string>
<string name="lbl_contrast">Contrast</string>
<string name="lbl_saturation">Saturation</string>
<string name="tab_filters">Filters</string>
<string name="edit">Edit</string>
<string name="filter_thumbnail">Thumbnail of filter</string>
<string name="normal_filter">Normal</string>
@ -133,6 +133,7 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
<string name="crop_button">Button to crop or rotate the image</string>
<string name="save_before_returning">Save your edits?</string>
<string name="no_cancel_edit">No, cancel edit</string>
<string name="error_editing">Error while editing</string>
<!-- Camera -->
<string name="capture_button_alt">Capture</string>
@ -282,4 +283,12 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
<string name="accentColorSummary">Choose a color accent</string>
<string name="color_choice_button">Choose this color accent</string>
<string name="color_chosen">Chosen color accent</string>
<string name="profile_error">Could not load profile</string>
<!-- Tells user what domain this user is from -->
<string name="from_other_domain">from %1$s</string>
<string name="add_images_error">Error while adding images</string>
<string name="notification_thumbnail">"Thumbnail of image in this notification's post"</string>
<string name="post_preview">Preview of a post</string>
</resources>