Start removing deprecated kotlin-android-extensions uses

This commit is contained in:
Matthieu 2021-01-13 01:28:08 +01:00
parent 8bfbe2fbb5
commit bebf0233dc
15 changed files with 354 additions and 352 deletions

View File

@ -85,6 +85,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.annotation:annotation:1.1.0"
implementation 'androidx.gridlayout:gridlayout:1.0.0' implementation 'androidx.gridlayout:gridlayout:1.0.0'

View File

@ -9,6 +9,8 @@ import android.os.Bundle
import android.view.View import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.h.pixeldroid.databinding.ActivityLoginBinding
import com.h.pixeldroid.databinding.ActivityPostCreationBinding
import com.h.pixeldroid.utils.BaseActivity import com.h.pixeldroid.utils.BaseActivity
import com.h.pixeldroid.utils.api.PixelfedAPI import com.h.pixeldroid.utils.api.PixelfedAPI
import com.h.pixeldroid.utils.api.objects.* import com.h.pixeldroid.utils.api.objects.*
@ -17,7 +19,6 @@ import com.h.pixeldroid.utils.db.storeInstance
import com.h.pixeldroid.utils.hasInternet import com.h.pixeldroid.utils.hasInternet
import com.h.pixeldroid.utils.normalizeDomain import com.h.pixeldroid.utils.normalizeDomain
import com.h.pixeldroid.utils.openUrl import com.h.pixeldroid.utils.openUrl
import kotlinx.android.synthetic.main.activity_login.*
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -56,9 +57,12 @@ class LoginActivity : BaseActivity() {
private lateinit var pixelfedAPI: PixelfedAPI private lateinit var pixelfedAPI: PixelfedAPI
private var inputVisibility: Int = View.GONE private var inputVisibility: Int = View.GONE
private lateinit var binding: ActivityLoginBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login) binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
loadingAnimation(true) loadingAnimation(true)
appName = getString(R.string.app_name) appName = getString(R.string.app_name)
@ -66,14 +70,14 @@ class LoginActivity : BaseActivity() {
preferences = getSharedPreferences("$PACKAGE_ID.pref", Context.MODE_PRIVATE) preferences = getSharedPreferences("$PACKAGE_ID.pref", Context.MODE_PRIVATE)
if (hasInternet(applicationContext)) { if (hasInternet(applicationContext)) {
connect_instance_button.setOnClickListener { binding.connectInstanceButton.setOnClickListener {
registerAppToServer(normalizeDomain(editText.text.toString())) registerAppToServer(normalizeDomain(binding.editText.text.toString()))
} }
whatsAnInstanceTextView.setOnClickListener{ whatsAnInstance() } binding.whatsAnInstanceTextView.setOnClickListener{ whatsAnInstance() }
inputVisibility = View.VISIBLE inputVisibility = View.VISIBLE
} else { } else {
login_activity_connection_required.visibility = View.VISIBLE binding.loginActivityConnectionRequired.visibility = View.VISIBLE
login_activity_connection_required_button.setOnClickListener { binding.loginActivityConnectionRequiredButton.setOnClickListener {
finish() finish()
startActivity(intent) startActivity(intent)
} }
@ -267,7 +271,7 @@ class LoginActivity : BaseActivity() {
private fun failedRegistration(message: String = getString(R.string.registration_failed)) { private fun failedRegistration(message: String = getString(R.string.registration_failed)) {
loadingAnimation(false) loadingAnimation(false)
editText.error = message binding.editText.error = message
wipeSharedSettings() wipeSharedSettings()
} }
@ -278,12 +282,12 @@ class LoginActivity : BaseActivity() {
private fun loadingAnimation(on: Boolean){ private fun loadingAnimation(on: Boolean){
if(on) { if(on) {
login_activity_instance_input_layout.visibility = View.GONE binding.loginActivityInstanceInputLayout.visibility = View.GONE
progressLayout.visibility = View.VISIBLE binding.progressLayout.visibility = View.VISIBLE
} }
else { else {
login_activity_instance_input_layout.visibility = inputVisibility binding.loginActivityInstanceInputLayout.visibility = inputVisibility
progressLayout.visibility = View.GONE binding.progressLayout.visibility = View.GONE
} }
} }

View File

@ -14,22 +14,21 @@ import androidx.core.view.GravityCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.paging.ExperimentalPagingApi import androidx.paging.ExperimentalPagingApi
import androidx.room.withTransaction
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.h.pixeldroid.utils.db.addUser import com.h.pixeldroid.databinding.ActivityMainBinding
import com.h.pixeldroid.postCreation.camera.CameraFragment import com.h.pixeldroid.postCreation.camera.CameraFragment
import com.h.pixeldroid.utils.db.entities.HomeStatusDatabaseEntity
import com.h.pixeldroid.utils.db.entities.PublicFeedStatusDatabaseEntity
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
import com.h.pixeldroid.posts.feeds.cachedFeeds.notifications.NotificationsFragment import com.h.pixeldroid.posts.feeds.cachedFeeds.notifications.NotificationsFragment
import com.h.pixeldroid.posts.feeds.cachedFeeds.postFeeds.PostFeedFragment import com.h.pixeldroid.posts.feeds.cachedFeeds.postFeeds.PostFeedFragment
import com.h.pixeldroid.utils.api.objects.Account
import com.h.pixeldroid.profile.ProfileActivity import com.h.pixeldroid.profile.ProfileActivity
import com.h.pixeldroid.searchDiscover.SearchDiscoverFragment import com.h.pixeldroid.searchDiscover.SearchDiscoverFragment
import com.h.pixeldroid.settings.SettingsActivity import com.h.pixeldroid.settings.SettingsActivity
import com.h.pixeldroid.utils.BaseActivity import com.h.pixeldroid.utils.BaseActivity
import com.h.pixeldroid.utils.db.addUser
import com.h.pixeldroid.utils.db.entities.HomeStatusDatabaseEntity
import com.h.pixeldroid.utils.db.entities.PublicFeedStatusDatabaseEntity
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
import com.h.pixeldroid.utils.hasInternet import com.h.pixeldroid.utils.hasInternet
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.materialdrawer.iconics.iconicsIcon import com.mikepenz.materialdrawer.iconics.iconicsIcon
@ -40,12 +39,8 @@ import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.widget.AccountHeaderView import com.mikepenz.materialdrawer.widget.AccountHeaderView
import kotlinx.android.synthetic.main.activity_main.*
import org.ligi.tracedroid.sending.TraceDroidEmailSender import org.ligi.tracedroid.sending.TraceDroidEmailSender
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException import retrofit2.HttpException
import retrofit2.Response
import java.io.IOException import java.io.IOException
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
@ -57,11 +52,14 @@ class MainActivity : BaseActivity() {
const val ADD_ACCOUNT_IDENTIFIER: Long = -13 const val ADD_ACCOUNT_IDENTIFIER: Long = -13
} }
private lateinit var binding: ActivityMainBinding
@ExperimentalPagingApi @ExperimentalPagingApi
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme_NoActionBar) setTheme(R.style.AppTheme_NoActionBar)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
TraceDroidEmailSender.sendStackTraces("contact@pixeldroid.org", this) TraceDroidEmailSender.sendStackTraces("contact@pixeldroid.org", this)
@ -96,8 +94,8 @@ class MainActivity : BaseActivity() {
} }
private fun setupDrawer() { private fun setupDrawer() {
main_drawer_button.setOnClickListener{ binding.mainDrawerButton.setOnClickListener{
drawer_layout.open() binding.drawerLayout.open()
} }
header = AccountHeaderView(this).apply { header = AccountHeaderView(this).apply {
@ -112,7 +110,7 @@ class MainActivity : BaseActivity() {
descriptionRes = R.string.add_account_description descriptionRes = R.string.add_account_description
iconicsIcon = GoogleMaterial.Icon.gmd_add iconicsIcon = GoogleMaterial.Icon.gmd_add
}, 0) }, 0)
attachToSliderView(drawer) attachToSliderView(binding.drawer)
dividerBelowHeader = false dividerBelowHeader = false
closeDrawerOnProfileListClick = true closeDrawerOnProfileListClick = true
} }
@ -144,7 +142,7 @@ class MainActivity : BaseActivity() {
//with the received one. This happens asynchronously. //with the received one. This happens asynchronously.
getUpdatedAccount() getUpdatedAccount()
drawer.itemAdapter.add( binding.drawer.itemAdapter.add(
primaryDrawerItem { primaryDrawerItem {
nameRes = R.string.menu_account nameRes = R.string.menu_account
iconicsIcon = GoogleMaterial.Icon.gmd_person iconicsIcon = GoogleMaterial.Icon.gmd_person
@ -157,7 +155,7 @@ class MainActivity : BaseActivity() {
nameRes = R.string.logout nameRes = R.string.logout
iconicsIcon = GoogleMaterial.Icon.gmd_close iconicsIcon = GoogleMaterial.Icon.gmd_close
}) })
drawer.onDrawerItemClickListener = { v, drawerItem, position -> binding.drawer.onDrawerItemClickListener = { v, drawerItem, position ->
when (position){ when (position){
1 -> launchActivity(ProfileActivity()) 1 -> launchActivity(ProfileActivity())
2 -> launchActivity(SettingsActivity()) 2 -> launchActivity(SettingsActivity())
@ -271,7 +269,7 @@ class MainActivity : BaseActivity() {
private fun setupTabs(tab_array: List<() -> Fragment>){ private fun setupTabs(tab_array: List<() -> Fragment>){
view_pager.adapter = object : FragmentStateAdapter(this) { binding.viewPager.adapter = object : FragmentStateAdapter(this) {
override fun createFragment(position: Int): Fragment { override fun createFragment(position: Int): Fragment {
return tab_array[position]() return tab_array[position]()
} }
@ -281,7 +279,7 @@ class MainActivity : BaseActivity() {
} }
} }
TabLayoutMediator(tabs, view_pager) { tab, position -> TabLayoutMediator(binding.tabs, binding.viewPager) { tab, position ->
tab.icon = ContextCompat.getDrawable(applicationContext, tab.icon = ContextCompat.getDrawable(applicationContext,
when(position){ when(position){
0 -> R.drawable.ic_home_white_24dp 0 -> R.drawable.ic_home_white_24dp
@ -312,8 +310,8 @@ class MainActivity : BaseActivity() {
* Closes the drawer if it is open, when we press the back button * Closes the drawer if it is open, when we press the back button
*/ */
override fun onBackPressed() { override fun onBackPressed() {
if(drawer_layout.isDrawerOpen(GravityCompat.START)){ if(binding.drawerLayout.isDrawerOpen(GravityCompat.START)){
drawer_layout.closeDrawer(GravityCompat.START) binding.drawerLayout.closeDrawer(GravityCompat.START)
} else { } else {
super.onBackPressed() super.onBackPressed()
} }

View File

@ -27,6 +27,7 @@ import com.google.android.material.textfield.TextInputLayout
import com.h.pixeldroid.utils.BaseActivity import com.h.pixeldroid.utils.BaseActivity
import com.h.pixeldroid.MainActivity import com.h.pixeldroid.MainActivity
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.databinding.ActivityPostCreationBinding
import com.h.pixeldroid.utils.api.PixelfedAPI import com.h.pixeldroid.utils.api.PixelfedAPI
import com.h.pixeldroid.postCreation.camera.CameraActivity import com.h.pixeldroid.postCreation.camera.CameraActivity
import com.h.pixeldroid.postCreation.carousel.CarouselItem import com.h.pixeldroid.postCreation.carousel.CarouselItem
@ -38,9 +39,6 @@ import com.h.pixeldroid.postCreation.photoEdit.PhotoEditActivity
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_post_creation.*
import kotlinx.android.synthetic.main.activity_post_creation.view.*
import kotlinx.android.synthetic.main.image_album_creation.view.*
import okhttp3.MultipartBody import okhttp3.MultipartBody
import retrofit2.HttpException import retrofit2.HttpException
import java.io.File import java.io.File
@ -69,9 +67,12 @@ class PostCreationActivity : BaseActivity() {
private val photoData: ArrayList<PhotoData> = ArrayList() private val photoData: ArrayList<PhotoData> = ArrayList()
private lateinit var binding: ActivityPostCreationBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_post_creation) binding = ActivityPostCreationBinding.inflate(layoutInflater)
setContentView(binding.root)
// get image URIs // get image URIs
if(intent.clipData != null) { if(intent.clipData != null) {
@ -108,10 +109,10 @@ class PostCreationActivity : BaseActivity() {
//TODO transition instead of at once //TODO transition instead of at once
if(it){ if(it){
// Became a carousel // Became a carousel
toolbar3.visibility = VISIBLE binding.toolbar3.visibility = VISIBLE
} else { } else {
// Became a grid // Became a grid
toolbar3.visibility = INVISIBLE binding.toolbar3.visibility = INVISIBLE
} }
} }
carousel.addPhotoButtonCallback = { carousel.addPhotoButtonCallback = {
@ -125,7 +126,7 @@ class PostCreationActivity : BaseActivity() {
// Button to retry image upload when it fails // Button to retry image upload when it fails
findViewById<Button>(R.id.retry_upload_button).setOnClickListener { findViewById<Button>(R.id.retry_upload_button).setOnClickListener {
upload_error.visibility = View.GONE binding.uploadError.visibility = View.GONE
photoData.forEach { photoData.forEach {
it.uploadId = null it.uploadId = null
it.progress = null it.progress = null
@ -236,23 +237,19 @@ class PostCreationActivity : BaseActivity() {
} }
/** /**
* Uploads the images that are in the [posts] array. * Uploads the images that are in the [photoData] array.
* Keeps track of them in the [progressList] (for the upload progress), and the [muListOfIds] * Keeps track of them in the [PhotoData.progress] (for the upload progress), and the
* (for the list of ids of the uploads). * [PhotoData.uploadId] (for the list of ids of the uploads).
* @param newImagesStartingIndex is the index in the [posts] array we want to start uploading at.
* Indices before this are already uploading, or done uploading, from before.
* @param editedImage contains the index of the image that was edited. If set, other images are
* not uploaded again: they should already be uploading, or be done uploading, from before.
*/ */
private fun upload() { private fun upload() {
enableButton(false) enableButton(false)
uploadProgressBar.visibility = View.VISIBLE binding.uploadProgressBar.visibility = View.VISIBLE
upload_completed_textview.visibility = View.INVISIBLE binding.uploadCompletedTextview.visibility = View.INVISIBLE
removePhotoButton.isEnabled = false binding.removePhotoButton.isEnabled = false
editPhotoButton.isEnabled = false binding.editPhotoButton.isEnabled = false
addPhotoButton.isEnabled = false binding.addPhotoButton.isEnabled = false
for (data in photoData) { for (data: PhotoData in photoData) {
val imageUri = data.imageUri val imageUri = data.imageUri
val imageInputStream = contentResolver.openInputStream(imageUri)!! val imageInputStream = contentResolver.openInputStream(imageUri)!!
@ -282,7 +279,7 @@ class PostCreationActivity : BaseActivity() {
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe { percentage -> .subscribe { percentage ->
data.progress = percentage.toInt() data.progress = percentage.toInt()
uploadProgressBar.progress = binding.uploadProgressBar.progress =
photoData.sumBy { it.progress ?: 0 } / photoData.size photoData.sumBy { it.progress ?: 0 } / photoData.size
} }
@ -298,7 +295,7 @@ class PostCreationActivity : BaseActivity() {
data.uploadId = attachment.id!! data.uploadId = attachment.id!!
}, },
{ e -> { e ->
upload_error.visibility = View.VISIBLE binding.uploadError.visibility = View.VISIBLE
e.printStackTrace() e.printStackTrace()
postSub?.dispose() postSub?.dispose()
sub.dispose() sub.dispose()
@ -306,8 +303,8 @@ class PostCreationActivity : BaseActivity() {
{ {
data.progress = 100 data.progress = 100
if(photoData.all{it.progress == 100}){ if(photoData.all{it.progress == 100}){
uploadProgressBar.visibility = View.GONE binding.uploadProgressBar.visibility = View.GONE
upload_completed_textview.visibility = View.VISIBLE binding.uploadCompletedTextview.visibility = View.VISIBLE
post() post()
} }
postSub?.dispose() postSub?.dispose()
@ -318,7 +315,7 @@ class PostCreationActivity : BaseActivity() {
} }
private fun post() { private fun post() {
val description = new_post_description_input_field.text.toString() val description = binding.newPostDescriptionInputField.text.toString()
enableButton(false) enableButton(false)
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
try { try {
@ -347,13 +344,13 @@ class PostCreationActivity : BaseActivity() {
} }
private fun enableButton(enable: Boolean = true){ private fun enableButton(enable: Boolean = true){
post_creation_send_button.isEnabled = enable binding.postCreationSendButton.isEnabled = enable
if(enable){ if(enable){
posting_progress_bar.visibility = View.GONE binding.postingProgressBar.visibility = View.GONE
post_creation_send_button.visibility = View.VISIBLE binding.postCreationSendButton.visibility = View.VISIBLE
} else { } else {
posting_progress_bar.visibility = View.VISIBLE binding.postingProgressBar.visibility = View.VISIBLE
post_creation_send_button.visibility = View.GONE binding.postCreationSendButton.visibility = View.GONE
} }
} }
@ -373,7 +370,7 @@ class PostCreationActivity : BaseActivity() {
if (resultCode == Activity.RESULT_OK && data != null) { if (resultCode == Activity.RESULT_OK && data != null) {
photoData[positionResult].imageUri = data.getStringExtra("result")!!.toUri() photoData[positionResult].imageUri = data.getStringExtra("result")!!.toUri()
carousel.addData(photoData.map { CarouselItem(it.imageUri.toString()) }) binding.carousel.addData(photoData.map { CarouselItem(it.imageUri.toString()) })
photoData[positionResult].progress = null photoData[positionResult].progress = null
photoData[positionResult].uploadId = null photoData[positionResult].uploadId = null
@ -389,7 +386,7 @@ class PostCreationActivity : BaseActivity() {
photoData.add(PhotoData(imageUri)) photoData.add(PhotoData(imageUri))
} }
carousel.addData(photoData.map { CarouselItem(it.imageUri.toString()) }) binding.carousel.addData(photoData.map { CarouselItem(it.imageUri.toString()) })
} else if(resultCode != Activity.RESULT_CANCELED){ } else if(resultCode != Activity.RESULT_CANCELED){
Toast.makeText(applicationContext, "Error while adding images", Toast.LENGTH_SHORT).show() Toast.makeText(applicationContext, "Error while adding images", Toast.LENGTH_SHORT).show()
} }

View File

@ -22,6 +22,8 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.databinding.ActivityPhotoEditBinding
import com.h.pixeldroid.databinding.ActivityPostCreationBinding
import com.h.pixeldroid.postCreation.PostCreationActivity import com.h.pixeldroid.postCreation.PostCreationActivity
import com.h.pixeldroid.utils.BaseActivity import com.h.pixeldroid.utils.BaseActivity
import com.yalantis.ucrop.UCrop import com.yalantis.ucrop.UCrop
@ -29,7 +31,6 @@ import com.zomato.photofilters.imageprocessors.Filter
import com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubFilter import com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubFilter
import com.zomato.photofilters.imageprocessors.subfilters.ContrastSubFilter import com.zomato.photofilters.imageprocessors.subfilters.ContrastSubFilter
import com.zomato.photofilters.imageprocessors.subfilters.SaturationSubfilter import com.zomato.photofilters.imageprocessors.subfilters.SaturationSubfilter
import kotlinx.android.synthetic.main.activity_photo_edit.*
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
@ -86,9 +87,14 @@ class PhotoEditActivity : BaseActivity() {
internal var imageUri: Uri? = null internal var imageUri: Uri? = null
} }
private lateinit var binding: ActivityPhotoEditBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_photo_edit) binding = ActivityPhotoEditBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.setTitle(R.string.toolbar_title_edit) supportActionBar?.setTitle(R.string.toolbar_title_edit)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
@ -118,7 +124,7 @@ class PhotoEditActivity : BaseActivity() {
compressedImage = resizeImage(originalImage!!) compressedImage = resizeImage(originalImage!!)
compressedOriginalImage = compressedImage!!.copy(BITMAP_CONFIG, true) compressedOriginalImage = compressedImage!!.copy(BITMAP_CONFIG, true)
filteredImage = compressedImage!!.copy(BITMAP_CONFIG, true) filteredImage = compressedImage!!.copy(BITMAP_CONFIG, true)
Glide.with(this).load(compressedImage).into(image_preview) Glide.with(this).load(compressedImage).into(binding.imagePreview)
} }
private fun resizeImage(image: Bitmap): Bitmap { private fun resizeImage(image: Bitmap): Bitmap {
@ -192,7 +198,7 @@ class PhotoEditActivity : BaseActivity() {
fun onFilterSelected(filter: Filter) { fun onFilterSelected(filter: Filter) {
filteredImage = compressedOriginalImage!!.copy(BITMAP_CONFIG, true) filteredImage = compressedOriginalImage!!.copy(BITMAP_CONFIG, true)
image_preview.setImageBitmap(filter.processFilter(filteredImage)) binding.imagePreview.setImageBitmap(filter.processFilter(filteredImage))
compressedImage = filteredImage.copy(BITMAP_CONFIG, true) compressedImage = filteredImage.copy(BITMAP_CONFIG, true)
actualFilter = filter actualFilter = filter
resetControls() resetControls()
@ -211,8 +217,8 @@ class PhotoEditActivity : BaseActivity() {
future?.cancel(true) future?.cancel(true)
future = executor.submit { future = executor.submit {
val bitmap = filter.processFilter(image!!.copy(BITMAP_CONFIG, true)) val bitmap = filter.processFilter(image!!.copy(BITMAP_CONFIG, true))
image_preview.post { binding.imagePreview.post {
image_preview.setImageBitmap(bitmap) binding.imagePreview.setImageBitmap(bitmap)
} }
} }
} }
@ -292,8 +298,8 @@ class PhotoEditActivity : BaseActivity() {
val resultCrop: Uri? = UCrop.getOutput(data!!) val resultCrop: Uri? = UCrop.getOutput(data!!)
if(resultCrop != null) { if(resultCrop != null) {
imageUri = resultCrop imageUri = resultCrop
image_preview.setImageURI(resultCrop) binding.imagePreview.setImageURI(resultCrop)
val bitmap = (image_preview.drawable as BitmapDrawable).bitmap val bitmap = (binding.imagePreview.drawable as BitmapDrawable).bitmap
originalImage = bitmap.copy(Bitmap.Config.ARGB_8888, true) originalImage = bitmap.copy(Bitmap.Config.ARGB_8888, true)
compressedImage = resizeImage(originalImage!!.copy(BITMAP_CONFIG, true)) compressedImage = resizeImage(originalImage!!.copy(BITMAP_CONFIG, true))
compressedOriginalImage = compressedImage!!.copy(BITMAP_CONFIG, true) compressedOriginalImage = compressedImage!!.copy(BITMAP_CONFIG, true)
@ -324,7 +330,7 @@ class PhotoEditActivity : BaseActivity() {
// permission was granted // permission was granted
permissionsGrantedToSave() permissionsGrantedToSave()
} else { } else {
Snackbar.make(coordinator_edit, getString(R.string.permission_denied), Snackbar.make(binding.root, getString(R.string.permission_denied),
Snackbar.LENGTH_LONG).show() Snackbar.LENGTH_LONG).show()
} }
} }
@ -396,7 +402,7 @@ class PhotoEditActivity : BaseActivity() {
return return
} }
saving = true saving = true
progressBarSaveFile.visibility = VISIBLE binding.progressBarSaveFile.visibility = VISIBLE
saveFuture = saveExecutor.submit { saveFuture = saveExecutor.submit {
try { try {
val path: String val path: String
@ -413,17 +419,17 @@ class PhotoEditActivity : BaseActivity() {
if(saving) { if(saving) {
this.runOnUiThread { this.runOnUiThread {
sendBackImage(path) sendBackImage(path)
progressBarSaveFile.visibility = GONE binding.progressBarSaveFile.visibility = GONE
saving = false saving = false
} }
} }
} catch (e: IOException) { } catch (e: IOException) {
this.runOnUiThread { this.runOnUiThread {
Snackbar.make( Snackbar.make(
coordinator_edit, getString(R.string.save_image_failed), binding.root, getString(R.string.save_image_failed),
Snackbar.LENGTH_LONG Snackbar.LENGTH_LONG
).show() ).show()
progressBarSaveFile.visibility = GONE binding.progressBarSaveFile.visibility = GONE
saving = false saving = false
} }
} }

View File

@ -2,15 +2,14 @@ package com.h.pixeldroid.postCreation.photoEdit
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.databinding.ThumbnailListItemBinding
import com.zomato.photofilters.utils.ThumbnailItem import com.zomato.photofilters.utils.ThumbnailItem
import kotlinx.android.synthetic.main.thumbnail_list_item.view.*
class ThumbnailAdapter (private val context: Context, class ThumbnailAdapter (private val context: Context,
private val tbItemList: List<ThumbnailItem>, private val tbItemList: List<ThumbnailItem>,
@ -24,8 +23,8 @@ class ThumbnailAdapter (private val context: Context,
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(context).inflate(R.layout.thumbnail_list_item, parent, false) val itemBinding = ThumbnailListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(itemView) return MyViewHolder(itemBinding)
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
@ -49,8 +48,8 @@ class ThumbnailAdapter (private val context: Context,
holder.filterName.setTextColor(ContextCompat.getColor(context, R.color.filterLabelNormal)) holder.filterName.setTextColor(ContextCompat.getColor(context, R.color.filterLabelNormal))
} }
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { class MyViewHolder(itemBinding: ThumbnailListItemBinding): RecyclerView.ViewHolder(itemBinding.root) {
var thumbnail: ImageView = itemView.thumbnail var thumbnail: ImageView = itemBinding.thumbnail
var filterName: TextView = itemView.filter_name var filterName: TextView = itemBinding.filterName
} }
} }

View File

@ -5,13 +5,13 @@ import android.util.Log
import android.view.View import android.view.View
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.databinding.ActivityPostBinding
import com.h.pixeldroid.utils.api.objects.DiscoverPost import com.h.pixeldroid.utils.api.objects.DiscoverPost
import com.h.pixeldroid.utils.api.objects.Status import com.h.pixeldroid.utils.api.objects.Status
import com.h.pixeldroid.utils.api.objects.Status.Companion.DISCOVER_TAG import com.h.pixeldroid.utils.api.objects.Status.Companion.DISCOVER_TAG
import com.h.pixeldroid.utils.api.objects.Status.Companion.DOMAIN_TAG import com.h.pixeldroid.utils.api.objects.Status.Companion.DOMAIN_TAG
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG
import com.h.pixeldroid.utils.BaseActivity import com.h.pixeldroid.utils.BaseActivity
import kotlinx.android.synthetic.main.activity_post.*
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException import java.io.IOException
@ -20,9 +20,13 @@ class PostActivity : BaseActivity() {
lateinit var domain : String lateinit var domain : String
private lateinit var accessToken : String private lateinit var accessToken : String
private lateinit var binding: ActivityPostBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_post) binding = ActivityPostBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
val status = intent.getSerializableExtra(POST_TAG) as Status? val status = intent.getSerializableExtra(POST_TAG) as Status?
@ -38,7 +42,7 @@ class PostActivity : BaseActivity() {
arguments.putString(DOMAIN_TAG, domain) arguments.putString(DOMAIN_TAG, domain)
if (discoverPost != null) { if (discoverPost != null) {
postProgressBar.visibility = View.VISIBLE binding.postProgressBar.visibility = View.VISIBLE
getDiscoverPost(arguments, discoverPost) getDiscoverPost(arguments, discoverPost)
} else { } else {
initializeFragment(arguments, status) initializeFragment(arguments, status)
@ -59,7 +63,7 @@ class PostActivity : BaseActivity() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
try { try {
val status = api.getStatus("Bearer $accessToken", id) val status = api.getStatus("Bearer $accessToken", id)
postProgressBar.visibility = View.GONE binding.postProgressBar.visibility = View.GONE
initializeFragment(arguments, status) initializeFragment(arguments, status)
} catch (exception: IOException) { } catch (exception: IOException) {
//TODO show error message //TODO show error message
@ -76,6 +80,6 @@ class PostActivity : BaseActivity() {
supportFragmentManager.isStateSaved supportFragmentManager.isStateSaved
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.add(R.id.postFragmentSingle, postFragment).commit() .add(R.id.postFragmentSingle, postFragment).commit()
postFragmentSingle.visibility = View.VISIBLE binding.postFragmentSingle.visibility = View.VISIBLE
} }
} }

View File

@ -5,11 +5,12 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.h.pixeldroid.R import com.h.pixeldroid.databinding.PostFragmentBinding
import com.h.pixeldroid.utils.api.objects.Status import com.h.pixeldroid.utils.api.objects.Status
import com.h.pixeldroid.utils.api.objects.Status.Companion.DOMAIN_TAG import com.h.pixeldroid.utils.api.objects.Status.Companion.DOMAIN_TAG
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG
import com.h.pixeldroid.utils.BaseFragment import com.h.pixeldroid.utils.BaseFragment
import com.h.pixeldroid.utils.bindingLifecycleAware
class PostFragment : BaseFragment() { class PostFragment : BaseFragment() {
@ -17,30 +18,32 @@ class PostFragment : BaseFragment() {
private lateinit var statusDomain: String private lateinit var statusDomain: String
private var currentStatus: Status? = null private var currentStatus: Status? = null
var binding: PostFragmentBinding by bindingLifecycleAware()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
currentStatus = arguments?.getSerializable(POST_TAG) as Status? currentStatus = arguments?.getSerializable(POST_TAG) as Status?
statusDomain = arguments?.getString(DOMAIN_TAG)!! statusDomain = arguments?.getString(DOMAIN_TAG)!!
} }
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
val root: View = inflater.inflate(R.layout.post_fragment, container, false) binding = PostFragmentBinding.inflate(inflater, container, false)
return binding.root
}
val user = db.userDao().getActiveUser()!! override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db) val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
val holder = StatusViewHolder(root) val holder = StatusViewHolder(binding)
holder.bind(currentStatus, api, db, lifecycleScope) holder.bind(currentStatus, api, db, lifecycleScope)
return root
} }
} }

View File

@ -1,25 +1,24 @@
package com.h.pixeldroid.posts package com.h.pixeldroid.posts
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.View import android.view.View
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.utils.api.objects.Report import com.h.pixeldroid.databinding.ActivityReportBinding
import com.h.pixeldroid.utils.api.objects.Status
import com.h.pixeldroid.utils.BaseActivity import com.h.pixeldroid.utils.BaseActivity
import kotlinx.android.synthetic.main.activity_report.* import com.h.pixeldroid.utils.api.objects.Status
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException import retrofit2.HttpException
import retrofit2.Response
import java.io.IOException import java.io.IOException
class ReportActivity : BaseActivity() { class ReportActivity : BaseActivity() {
private lateinit var binding: ActivityReportBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_report) binding = ActivityReportBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setTitle(R.string.report) supportActionBar?.setTitle(R.string.report)
@ -29,21 +28,21 @@ class ReportActivity : BaseActivity() {
val user = db.userDao().getActiveUser() val user = db.userDao().getActiveUser()
report_target_textview.text = getString(R.string.report_target).format(status?.account?.acct) binding.reportTargetTextview.text = getString(R.string.report_target).format(status?.account?.acct)
reportButton.setOnClickListener{ binding.reportButton.setOnClickListener{
reportButton.visibility = View.INVISIBLE binding.reportButton.visibility = View.INVISIBLE
reportProgressBar.visibility = View.VISIBLE binding.reportProgressBar.visibility = View.VISIBLE
textInputLayout.editText?.isEnabled = false binding.textInputLayout.editText?.isEnabled = false
val accessToken = user?.accessToken.orEmpty() val accessToken = user?.accessToken.orEmpty()
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db) val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
try { try {
api.report("Bearer $accessToken", status?.account?.id!!, listOf(status), textInputLayout.editText?.text.toString()) api.report("Bearer $accessToken", status?.account?.id!!, listOf(status), binding.textInputLayout.editText?.text.toString())
reportStatus(true) reportStatus(true)
} catch (exception: IOException) { } catch (exception: IOException) {
@ -57,15 +56,15 @@ class ReportActivity : BaseActivity() {
private fun reportStatus(success: Boolean){ private fun reportStatus(success: Boolean){
if(success){ if(success){
reportProgressBar.visibility = View.GONE binding.reportProgressBar.visibility = View.GONE
reportButton.isEnabled = false binding.reportButton.isEnabled = false
reportButton.text = getString(R.string.reported) binding.reportButton.text = getString(R.string.reported)
reportButton.visibility = View.VISIBLE binding.reportButton.visibility = View.VISIBLE
} else { } else {
textInputLayout.error = getString(R.string.report_error) binding.textInputLayout.error = getString(R.string.report_error)
reportButton.visibility = View.VISIBLE binding.reportButton.visibility = View.VISIBLE
textInputLayout.editText?.isEnabled = true binding.textInputLayout.editText?.isEnabled = true
reportProgressBar.visibility = View.GONE binding.reportProgressBar.visibility = View.GONE
} }
} }

View File

@ -16,60 +16,32 @@ import android.widget.*
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleCoroutineScope
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import at.connyduck.sparkbutton.SparkButton
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder import com.bumptech.glide.RequestBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.databinding.AlbumImageViewBinding
import com.h.pixeldroid.databinding.CommentBinding
import com.h.pixeldroid.databinding.PostFragmentBinding
import com.h.pixeldroid.utils.ImageConverter import com.h.pixeldroid.utils.ImageConverter
import com.h.pixeldroid.utils.api.PixelfedAPI import com.h.pixeldroid.utils.api.PixelfedAPI
import com.h.pixeldroid.utils.api.objects.Attachment import com.h.pixeldroid.utils.api.objects.Attachment
import com.h.pixeldroid.utils.api.objects.Context
import com.h.pixeldroid.utils.api.objects.Status import com.h.pixeldroid.utils.api.objects.Status
import com.h.pixeldroid.utils.db.AppDatabase import com.h.pixeldroid.utils.db.AppDatabase
import com.karumi.dexter.Dexter import com.karumi.dexter.Dexter
import com.karumi.dexter.listener.PermissionDeniedResponse import com.karumi.dexter.listener.PermissionDeniedResponse
import com.karumi.dexter.listener.PermissionGrantedResponse import com.karumi.dexter.listener.PermissionGrantedResponse
import com.karumi.dexter.listener.single.BasePermissionListener import com.karumi.dexter.listener.single.BasePermissionListener
import kotlinx.android.synthetic.main.comment.view.*
import kotlinx.android.synthetic.main.post_fragment.view.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException import retrofit2.HttpException
import retrofit2.Response
import java.io.IOException import java.io.IOException
/** /**
* View Holder for a [Status] RecyclerView list item. * View Holder for a [Status] RecyclerView list item.
*/ */
class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) { class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHolder(binding.root) {
val profilePic : ImageView = view.findViewById(R.id.profilePic)
val postPic : ImageView = view.findViewById(R.id.postPicture)
val username : TextView = view.findViewById(R.id.username)
val usernameDesc: TextView = view.findViewById(R.id.usernameDesc)
val description : TextView = view.findViewById(R.id.description)
val nlikes : TextView = view.findViewById(R.id.nlikes)
val nshares : TextView = view.findViewById(R.id.nshares)
//Spark buttons
val liker : SparkButton = view.findViewById(R.id.liker)
val reblogger : SparkButton = view.findViewById(R.id.reblogger)
val submitCmnt : ImageButton = view.findViewById(R.id.submitComment)
val commenter : ImageView = view.findViewById(R.id.commenter)
val comment : EditText = view.findViewById(R.id.editComment)
val commentCont : LinearLayout = view.findViewById(R.id.commentContainer)
val commentIn : LinearLayout = view.findViewById(R.id.commentIn)
val viewComment : TextView = view.findViewById(R.id.ViewComments)
val postDate : TextView = view.findViewById(R.id.postDate)
val postDomain : TextView = view.findViewById(R.id.postDomain)
val sensitiveW : TextView = view.findViewById(R.id.sensitiveWarning)
val postPager : ViewPager2 = view.findViewById(R.id.postPager)
val more : ImageButton = view.findViewById(R.id.status_more)
private var status: Status? = null private var status: Status? = null
@ -80,7 +52,7 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val metrics = itemView.context.resources.displayMetrics val metrics = itemView.context.resources.displayMetrics
//Limit the height of the different images //Limit the height of the different images
postPic.maxHeight = metrics.heightPixels * 3/4 binding.postPicture.maxHeight = metrics.heightPixels * 3/4
//Setup the post layout //Setup the post layout
val picRequest = Glide.with(itemView) val picRequest = Glide.with(itemView)
@ -89,37 +61,35 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val user = db.userDao().getActiveUser()!! val user = db.userDao().getActiveUser()!!
setupPost(itemView, picRequest, user.instance_uri, false) setupPost(picRequest, user.instance_uri, false)
activateButtons(this, pixelfedAPI, db, lifecycleScope) activateButtons(pixelfedAPI, db, lifecycleScope)
} }
private fun setupPost( private fun setupPost(
rootView: View,
request: RequestBuilder<Drawable>, request: RequestBuilder<Drawable>,
//homeFragment: Fragment,
domain: String, domain: String,
isActivity: Boolean isActivity: Boolean
) { ) {
//Setup username as a button that opens the profile //Setup username as a button that opens the profile
rootView.findViewById<TextView>(R.id.username).apply { binding.username.apply {
text = status?.account?.getDisplayName() ?: "" text = status?.account?.getDisplayName() ?: ""
setTypeface(null, Typeface.BOLD) setTypeface(null, Typeface.BOLD)
setOnClickListener { status?.account?.openProfile(rootView.context) } setOnClickListener { status?.account?.openProfile(rootView.context) }
} }
rootView.findViewById<TextView>(R.id.usernameDesc).apply { binding.usernameDesc.apply {
text = status?.account?.getDisplayName() ?: "" text = status?.account?.getDisplayName() ?: ""
setTypeface(null, Typeface.BOLD) setTypeface(null, Typeface.BOLD)
} }
rootView.findViewById<TextView>(R.id.nlikes).apply { binding.nlikes.apply {
text = status?.getNLikes(rootView.context) text = status?.getNLikes(rootView.context)
setTypeface(null, Typeface.BOLD) setTypeface(null, Typeface.BOLD)
} }
rootView.findViewById<TextView>(R.id.nshares).apply { binding.nshares.apply {
text = status?.getNShares(rootView.context) text = status?.getNShares(rootView.context)
setTypeface(null, Typeface.BOLD) setTypeface(null, Typeface.BOLD)
} }
@ -127,82 +97,81 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
//Convert the date to a readable string //Convert the date to a readable string
setTextViewFromISO8601( setTextViewFromISO8601(
status?.created_at!!, status?.created_at!!,
rootView.postDate, binding.postDate,
isActivity, isActivity,
rootView.context binding.root.context
) )
rootView.postDomain.text = status?.getStatusDomain(domain) binding.postDomain.text = status?.getStatusDomain(domain)
//Setup images //Setup images
ImageConverter.setRoundImageFromURL( ImageConverter.setRoundImageFromURL(
rootView, binding.root,
status?.getProfilePicUrl(), status?.getProfilePicUrl(),
rootView.profilePic binding.profilePic
) )
rootView.profilePic.setOnClickListener { status?.account?.openProfile(rootView.context) } binding.profilePic.setOnClickListener { status?.account?.openProfile(binding.root.context) }
//Setup post pic only if there are media attachments //Setup post pic only if there are media attachments
if(!status?.media_attachments.isNullOrEmpty()) { if(!status?.media_attachments.isNullOrEmpty()) {
setupPostPics(rootView, request) setupPostPics(binding, request)
} else { } else {
rootView.postPicture.visibility = View.GONE binding.postPicture.visibility = View.GONE
rootView.postPager.visibility = View.GONE binding.postPager.visibility = View.GONE
rootView.postTabs.visibility = View.GONE binding.postTabs.visibility = View.GONE
} }
//Set comment initial visibility //Set comment initial visibility
rootView.findViewById<LinearLayout>(R.id.commentIn).visibility = View.GONE binding.commentIn.visibility = View.GONE
rootView.findViewById<LinearLayout>(R.id.commentContainer).visibility = View.GONE binding.commentContainer.visibility = View.GONE
} }
private fun setupPostPics( private fun setupPostPics(
rootView: View, binding: PostFragmentBinding,
request: RequestBuilder<Drawable>, request: RequestBuilder<Drawable>,
//homeFragment: Fragment
) { ) {
// Standard layout // Standard layout
rootView.postPicture.visibility = View.VISIBLE binding.postPicture.visibility = View.VISIBLE
rootView.postPager.visibility = View.GONE binding.postPager.visibility = View.GONE
rootView.postTabs.visibility = View.GONE binding.postTabs.visibility = View.GONE
if(status?.media_attachments?.size == 1) { if(status?.media_attachments?.size == 1) {
request.load(status?.getPostUrl()).into(rootView.postPicture) request.load(status?.getPostUrl()).into(binding.postPicture)
val imgDescription = status?.media_attachments?.get(0)?.description.orEmpty().ifEmpty { rootView.context.getString( val imgDescription = status?.media_attachments?.get(0)?.description.orEmpty().ifEmpty { binding.root.context.getString(
R.string.no_description) } R.string.no_description) }
rootView.postPicture.contentDescription = imgDescription binding.postPicture.contentDescription = imgDescription
rootView.postPicture.setOnLongClickListener { binding.postPicture.setOnLongClickListener {
Snackbar.make(it, imgDescription, Snackbar.LENGTH_SHORT).show() Snackbar.make(it, imgDescription, Snackbar.LENGTH_SHORT).show()
true true
} }
} else if(status?.media_attachments?.size!! > 1) { } else if(status?.media_attachments?.size!! > 1) {
setupTabsLayout(rootView, request) setupTabsLayout(binding, request)
} }
if (status?.sensitive!!) { if (status?.sensitive!!) {
status?.setupSensitiveLayout(rootView) status?.setupSensitiveLayout(binding.root)
} }
} }
private fun setupTabsLayout( private fun setupTabsLayout(
rootView: View, binding: PostFragmentBinding,
request: RequestBuilder<Drawable>, request: RequestBuilder<Drawable>,
) { ) {
//Only show the viewPager and tabs //Only show the viewPager and tabs
rootView.postPicture.visibility = View.GONE binding.postPicture.visibility = View.GONE
rootView.postPager.visibility = View.VISIBLE binding.postPager.visibility = View.VISIBLE
rootView.postTabs.visibility = View.VISIBLE binding.postTabs.visibility = View.VISIBLE
//Attach the given tabs to the view pager //Attach the given tabs to the view pager
rootView.postPager.adapter = AlbumViewPagerAdapter(status?.media_attachments ?: emptyList()) binding.postPager.adapter = AlbumViewPagerAdapter(status?.media_attachments ?: emptyList())
TabLayoutMediator(rootView.postTabs, rootView.postPager) { tab, _ -> TabLayoutMediator(binding.postTabs, binding.postPager) { tab, _ ->
tab.icon = ContextCompat.getDrawable(rootView.context, R.drawable.ic_dot_blue_12dp) tab.icon = ContextCompat.getDrawable(binding.root.context, R.drawable.ic_dot_blue_12dp)
}.attach() }.attach()
} }
@ -229,39 +198,36 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
} }
} }
private fun activateButtons(holder: StatusViewHolder, api: PixelfedAPI, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope){ private fun activateButtons(api: PixelfedAPI, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope){
val user = db.userDao().getActiveUser()!! val user = db.userDao().getActiveUser()!!
val credential = "Bearer ${user.accessToken}" val credential = "Bearer ${user.accessToken}"
//Set the special HTML text //Set the special HTML text
setDescription(holder.view, api, credential, lifecycleScope) setDescription(binding.root, api, credential, lifecycleScope)
//Activate onclickListeners //Activate onclickListeners
activateLiker( activateLiker(
holder, api, credential, api, credential, status?.favourited ?: false,
status?.favourited ?: false,
lifecycleScope lifecycleScope
) )
activateReblogger( activateReblogger(
holder, api, credential, api, credential, status?.reblogged ?: false,
status?.reblogged ?: false,
lifecycleScope lifecycleScope
) )
activateCommenter(holder, api, credential, lifecycleScope) activateCommenter(api, credential, lifecycleScope)
showComments(holder, api, credential, lifecycleScope) showComments(api, credential, lifecycleScope)
activateMoreButton(holder, api, db, lifecycleScope) activateMoreButton(api, db, lifecycleScope)
} }
private fun activateReblogger( private fun activateReblogger(
holder: StatusViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
isReblogged: Boolean, isReblogged: Boolean,
lifecycleScope: LifecycleCoroutineScope lifecycleScope: LifecycleCoroutineScope
) { ) {
holder.reblogger.apply { binding.reblogger.apply {
//Set initial button state //Set initial button state
isChecked = isReblogged isChecked = isReblogged
@ -270,10 +236,10 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
if (buttonState) { if (buttonState) {
// Button is active // Button is active
undoReblogPost(holder, api, credential) undoReblogPost(api, credential)
} else { } else {
// Button is inactive // Button is inactive
reblogPost(holder, api, credential) reblogPost(api, credential)
} }
} }
//show animation or not? //show animation or not?
@ -283,7 +249,6 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
} }
private suspend fun reblogPost( private suspend fun reblogPost(
holder : StatusViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String credential: String
) { ) {
@ -294,20 +259,19 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val resp = api.reblogStatus(credential, it) val resp = api.reblogStatus(credential, it)
//Update shown share count //Update shown share count
holder.nshares.text = resp.getNShares(holder.view.context) binding.nshares.text = resp.getNShares(binding.root.context)
holder.reblogger.isChecked = resp.reblogged!! binding.reblogger.isChecked = resp.reblogged!!
} catch (exception: IOException) { } catch (exception: IOException) {
Log.e("REBLOG ERROR", exception.toString()) Log.e("REBLOG ERROR", exception.toString())
holder.reblogger.isChecked = false binding.reblogger.isChecked = false
} catch (exception: HttpException) { } catch (exception: HttpException) {
Log.e("RESPONSE_CODE", exception.code().toString()) Log.e("RESPONSE_CODE", exception.code().toString())
holder.reblogger.isChecked = false binding.reblogger.isChecked = false
} }
} }
} }
private suspend fun undoReblogPost( private suspend fun undoReblogPost(
holder : StatusViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
) { ) {
@ -317,20 +281,20 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val resp = api.undoReblogStatus(credential, it) val resp = api.undoReblogStatus(credential, it)
//Update shown share count //Update shown share count
holder.nshares.text = resp.getNShares(holder.view.context) binding.nshares.text = resp.getNShares(binding.root.context)
holder.reblogger.isChecked = resp.reblogged!! binding.reblogger.isChecked = resp.reblogged!!
} catch (exception: IOException) { } catch (exception: IOException) {
Log.e("REBLOG ERROR", exception.toString()) Log.e("REBLOG ERROR", exception.toString())
holder.reblogger.isChecked = true binding.reblogger.isChecked = true
} catch (exception: HttpException) { } catch (exception: HttpException) {
Log.e("RESPONSE_CODE", exception.code().toString()) Log.e("RESPONSE_CODE", exception.code().toString())
holder.reblogger.isChecked = true binding.reblogger.isChecked = true
} }
} }
} }
private fun activateMoreButton(holder: StatusViewHolder, api: PixelfedAPI, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope){ private fun activateMoreButton(api: PixelfedAPI, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope){
holder.more.setOnClickListener { binding.statusMore.setOnClickListener {
PopupMenu(it.context, it).apply { PopupMenu(it.context, it).apply {
setOnMenuItemClickListener { item -> setOnMenuItemClickListener { item ->
when (item.itemId) { when (item.itemId) {
@ -354,46 +318,46 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
true true
} }
R.id.post_more_menu_save_to_gallery -> { R.id.post_more_menu_save_to_gallery -> {
Dexter.withContext(holder.view.context) Dexter.withContext(binding.root.context)
.withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) .withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(object : BasePermissionListener() { .withListener(object : BasePermissionListener() {
override fun onPermissionDenied(p0: PermissionDeniedResponse?) { override fun onPermissionDenied(p0: PermissionDeniedResponse?) {
Toast.makeText( Toast.makeText(
holder.view.context, binding.root.context,
holder.view.context.getString(R.string.write_permission_download_pic), binding.root.context.getString(R.string.write_permission_download_pic),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
override fun onPermissionGranted(p0: PermissionGrantedResponse?) { override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
status?.downloadImage( status?.downloadImage(
holder.view.context, binding.root.context,
status?.media_attachments?.get(holder.postPager.currentItem)?.url status?.media_attachments?.get(binding.postPager.currentItem)?.url
?: "", ?: "",
holder.view binding.root
) )
} }
}).check() }).check()
true true
} }
R.id.post_more_menu_share_picture -> { R.id.post_more_menu_share_picture -> {
Dexter.withContext(holder.view.context) Dexter.withContext(binding.root.context)
.withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) .withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(object : BasePermissionListener() { .withListener(object : BasePermissionListener() {
override fun onPermissionDenied(p0: PermissionDeniedResponse?) { override fun onPermissionDenied(p0: PermissionDeniedResponse?) {
Toast.makeText( Toast.makeText(
holder.view.context, binding.root.context,
holder.view.context.getString(R.string.write_permission_share_pic), binding.root.context.getString(R.string.write_permission_share_pic),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
override fun onPermissionGranted(p0: PermissionGrantedResponse?) { override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
status?.downloadImage( status?.downloadImage(
holder.view.context, binding.root.context,
status?.media_attachments?.get(holder.postPager.currentItem)?.url status?.media_attachments?.get(binding.postPager.currentItem)?.url
?: "", ?: "",
holder.view, binding.root,
share = true, share = true,
) )
} }
@ -401,7 +365,7 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
true true
} }
R.id.post_more_menu_delete -> { R.id.post_more_menu_delete -> {
val builder = AlertDialog.Builder(holder.itemView.context) val builder = AlertDialog.Builder(binding.root.context)
builder.apply { builder.apply {
setMessage(R.string.delete_dialog) setMessage(R.string.delete_dialog)
setPositiveButton(android.R.string.ok) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
@ -413,7 +377,7 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
db.publicPostDao().delete(id, user.user_id, user.instance_uri) db.publicPostDao().delete(id, user.user_id, user.instance_uri)
try { try {
api.deleteStatus("Bearer ${user.accessToken}", id) api.deleteStatus("Bearer ${user.accessToken}", id)
holder.itemView.visibility = View.GONE binding.root.visibility = View.GONE
} catch (exception: IOException) { } catch (exception: IOException) {
} catch (exception: HttpException) { } catch (exception: HttpException) {
} }
@ -446,14 +410,13 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
} }
private fun activateLiker( private fun activateLiker(
holder: StatusViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
isLiked: Boolean, isLiked: Boolean,
lifecycleScope: LifecycleCoroutineScope lifecycleScope: LifecycleCoroutineScope
) { ) {
holder.liker.apply { binding.liker.apply {
//Set initial state //Set initial state
isChecked = isLiked isChecked = isLiked
@ -462,10 +425,10 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
if (buttonState) { if (buttonState) {
// Button is active, unlike // Button is active, unlike
unLikePostCall(holder, api, credential) unLikePostCall(api, credential)
} else { } else {
// Button is inactive, like // Button is inactive, like
likePostCall(holder, api, credential) likePostCall(api, credential)
} }
} }
//show animation or not? //show animation or not?
@ -474,38 +437,36 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
} }
//Activate double tap liking //Activate double tap liking
holder.apply {
var clicked = false var clicked = false
postPic.setOnClickListener { binding.postPicture.setOnClickListener {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
//Check that the post isn't hidden //Check that the post isn't hidden
if(sensitiveW.visibility == View.GONE) { if(binding.sensitiveWarning.visibility == View.GONE) {
//Check for double click //Check for double click
if(clicked) { if(clicked) {
if (holder.liker.isChecked) { if (binding.liker.isChecked) {
// Button is active, unlike // Button is active, unlike
holder.liker.isChecked = false binding.liker.isChecked = false
unLikePostCall(holder, api, credential) unLikePostCall(api, credential)
} else { } else {
// Button is inactive, like // Button is inactive, like
holder.liker.playAnimation() binding.liker.playAnimation()
holder.liker.isChecked = true binding.liker.isChecked = true
likePostCall(holder, api, credential) likePostCall(api, credential)
} }
} else { } else {
clicked = true clicked = true
//Reset clicked to false after 500ms //Reset clicked to false after 500ms
postPic.handler.postDelayed(fun() { clicked = false }, 500) binding.postPicture.handler.postDelayed(fun() { clicked = false }, 500)
}
} }
} }
} }
} }
} }
private suspend fun likePostCall( private suspend fun likePostCall(
holder : StatusViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
) { ) {
@ -516,20 +477,19 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val resp = api.likePost(credential, it) val resp = api.likePost(credential, it)
//Update shown like count and internal like toggle //Update shown like count and internal like toggle
holder.nlikes.text = resp.getNLikes(holder.view.context) binding.nlikes.text = resp.getNLikes(binding.root.context)
holder.liker.isChecked = resp.favourited ?: false binding.liker.isChecked = resp.favourited ?: false
} catch (exception: IOException) { } catch (exception: IOException) {
Log.e("LIKE ERROR", exception.toString()) Log.e("LIKE ERROR", exception.toString())
holder.liker.isChecked = false binding.liker.isChecked = false
} catch (exception: HttpException) { } catch (exception: HttpException) {
Log.e("RESPONSE_CODE", exception.code().toString()) Log.e("RESPONSE_CODE", exception.code().toString())
holder.liker.isChecked = false binding.liker.isChecked = false
} }
} }
} }
private suspend fun unLikePostCall( private suspend fun unLikePostCall(
holder : StatusViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
) { ) {
@ -540,37 +500,36 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val resp = api.unlikePost(credential, it) val resp = api.unlikePost(credential, it)
//Update shown like count and internal like toggle //Update shown like count and internal like toggle
holder.nlikes.text = resp.getNLikes(holder.view.context) binding.nlikes.text = resp.getNLikes(binding.root.context)
holder.liker.isChecked = resp.favourited ?: false binding.liker.isChecked = resp.favourited ?: false
} catch (exception: IOException) { } catch (exception: IOException) {
Log.e("UNLIKE ERROR", exception.toString()) Log.e("UNLIKE ERROR", exception.toString())
holder.liker.isChecked = true binding.liker.isChecked = true
} catch (exception: HttpException) { } catch (exception: HttpException) {
Log.e("RESPONSE_CODE", exception.code().toString()) Log.e("RESPONSE_CODE", exception.code().toString())
holder.liker.isChecked = true binding.liker.isChecked = true
} }
} }
} }
private fun showComments( private fun showComments(
holder: StatusViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
lifecycleScope: LifecycleCoroutineScope lifecycleScope: LifecycleCoroutineScope
) { ) {
//Show all comments of a post //Show all comments of a post
if (status?.replies_count == 0) { if (status?.replies_count == 0) {
holder.viewComment.text = holder.view.context.getString(R.string.NoCommentsToShow) binding.viewComments.text = binding.root.context.getString(R.string.NoCommentsToShow)
} else { } else {
holder.viewComment.apply { binding.viewComments.apply {
text = holder.view.context.getString(R.string.number_comments) text = binding.root.context.getString(R.string.number_comments)
.format(status?.replies_count) .format(status?.replies_count)
setOnClickListener { setOnClickListener {
visibility = View.GONE visibility = View.GONE
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
//Retrieve the comments //Retrieve the comments
retrieveComments(holder, api, credential) retrieveComments(api, credential)
} }
} }
} }
@ -578,52 +537,49 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
} }
private fun activateCommenter( private fun activateCommenter(
holder: StatusViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
lifecycleScope: LifecycleCoroutineScope lifecycleScope: LifecycleCoroutineScope
) { ) {
//Toggle comment button //Toggle comment button
toggleCommentInput(holder) toggleCommentInput()
//Activate commenterpostPicture //Activate commenterpostPicture
holder.submitCmnt.setOnClickListener { binding.submitComment.setOnClickListener {
val textIn = holder.comment.text val textIn = binding.editComment.text
//Open text input //Open text input
if(textIn.isNullOrEmpty()) { if(textIn.isNullOrEmpty()) {
Toast.makeText( Toast.makeText(
holder.view.context, binding.root.context,
holder.view.context.getString(R.string.empty_comment), binding.root.context.getString(R.string.empty_comment),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} else { } else {
//Post the comment //Post the comment
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
postComment(holder, api, credential) postComment(api, credential)
} }
} }
} }
} }
private fun toggleCommentInput( private fun toggleCommentInput() {
holder : StatusViewHolder
) {
//Toggle comment button //Toggle comment button
holder.commenter.setOnClickListener { binding.commenter.setOnClickListener {
when(holder.commentIn.visibility) { when(binding.commentIn.visibility) {
View.VISIBLE -> { View.VISIBLE -> {
holder.commentIn.visibility = View.GONE binding.commentIn.visibility = View.GONE
ImageConverter.setImageFromDrawable( ImageConverter.setImageFromDrawable(
holder.view, binding.root,
holder.commenter, binding.commenter,
R.drawable.ic_comment_empty R.drawable.ic_comment_empty
) )
} }
View.GONE -> { View.GONE -> {
holder.commentIn.visibility = View.VISIBLE binding.commentIn.visibility = View.VISIBLE
ImageConverter.setImageFromDrawable( ImageConverter.setImageFromDrawable(
holder.view, binding.root,
holder.commenter, binding.commenter,
R.drawable.ic_comment_blue R.drawable.ic_comment_blue
) )
} }
@ -633,15 +589,16 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun addComment(context: android.content.Context, commentContainer: LinearLayout, commentUsername: String, commentContent: String) { fun addComment(context: android.content.Context, commentContainer: LinearLayout, commentUsername: String, commentContent: String) {
val view = LayoutInflater.from(context)
.inflate(R.layout.comment, commentContainer, true)
view.user.text = commentUsername val itemBinding = CommentBinding.inflate(
view.commentText.text = commentContent LayoutInflater.from(context), commentContainer, false
)
itemBinding.user.text = commentUsername
itemBinding.commentText.text = commentContent
} }
private suspend fun retrieveComments( private suspend fun retrieveComments(
holder: StatusViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
) { ) {
@ -649,15 +606,15 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
try { try {
val statuses = api.statusComments(it, credential).descendants val statuses = api.statusComments(it, credential).descendants
holder.commentCont.removeAllViews() binding.commentContainer.removeAllViews()
//Create the new views for each comment //Create the new views for each comment
for (status in statuses) { for (status in statuses) {
addComment(holder.view.context, holder.commentCont, status.account!!.username!!, addComment(binding.root.context, binding.commentContainer, status.account!!.username!!,
status.content!! status.content!!
) )
} }
holder.commentCont.visibility = View.VISIBLE binding.commentContainer.visibility = View.VISIBLE
} catch (exception: IOException) { } catch (exception: IOException) {
Log.e("COMMENT FETCH ERROR", exception.toString()) Log.e("COMMENT FETCH ERROR", exception.toString())
@ -668,37 +625,36 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
} }
private suspend fun postComment( private suspend fun postComment(
holder : StatusViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
) { ) {
val textIn = holder.comment.text val textIn = binding.editComment.text
val nonNullText = textIn.toString() val nonNullText = textIn.toString()
status?.id?.let { status?.id?.let {
try { try {
val response = api.postStatus(credential, nonNullText, it) val response = api.postStatus(credential, nonNullText, it)
holder.commentIn.visibility = View.GONE binding.commentIn.visibility = View.GONE
//Add the comment to the comment section //Add the comment to the comment section
addComment( addComment(
holder.view.context, holder.commentCont, response.account!!.username!!, binding.root.context, binding.commentContainer, response.account!!.username!!,
response.content!! response.content!!
) )
Toast.makeText( Toast.makeText(
holder.view.context, binding.root.context,
holder.view.context.getString(R.string.comment_posted).format(textIn), binding.root.context.getString(R.string.comment_posted).format(textIn),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} catch (exception: IOException) { } catch (exception: IOException) {
Log.e("COMMENT ERROR", exception.toString()) Log.e("COMMENT ERROR", exception.toString())
Toast.makeText( Toast.makeText(
holder.view.context, holder.view.context.getString(R.string.comment_error), binding.root.context, binding.root.context.getString(R.string.comment_error),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} catch (exception: HttpException) { } catch (exception: HttpException) {
Toast.makeText( Toast.makeText(
holder.view.context, holder.view.context.getString(R.string.comment_error), binding.root.context, binding.root.context.getString(R.string.comment_error),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
Log.e("ERROR_CODE", exception.code().toString()) Log.e("ERROR_CODE", exception.code().toString())
@ -709,26 +665,32 @@ class StatusViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
companion object { companion object {
fun create(parent: ViewGroup): StatusViewHolder { fun create(parent: ViewGroup): StatusViewHolder {
val view = LayoutInflater.from(parent.context) val itemBinding = PostFragmentBinding.inflate(
.inflate(R.layout.post_fragment, parent, false) LayoutInflater.from(parent.context), parent, false
return StatusViewHolder(view) )
return StatusViewHolder(itemBinding)
} }
} }
} }
class AlbumViewPagerAdapter(private val media_attachments: List<Attachment>) : RecyclerView.Adapter<AlbumViewPagerAdapter.ViewHolder>() { class AlbumViewPagerAdapter(private val media_attachments: List<Attachment>) :
RecyclerView.Adapter<AlbumViewPagerAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.album_image_view, parent, false)) val itemBinding = AlbumImageViewBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return ViewHolder(itemBinding)
}
override fun getItemCount() = media_attachments.size override fun getItemCount() = media_attachments.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Glide.with(holder.view) Glide.with(holder.binding.root)
.asDrawable().fitCenter().placeholder(ColorDrawable(Color.GRAY)) .asDrawable().fitCenter().placeholder(ColorDrawable(Color.GRAY))
.load(media_attachments[position].url).into(holder.image) .load(media_attachments[position].url).into(holder.image)
val description = media_attachments[position].description val description = media_attachments[position].description
.orEmpty().ifEmpty{ holder.view.context.getString(R.string.no_description)} .orEmpty().ifEmpty{ holder.binding.root.context.getString(R.string.no_description)}
holder.image.setOnLongClickListener { holder.image.setOnLongClickListener {
Snackbar.make(it, description, Snackbar.LENGTH_SHORT).show() Snackbar.make(it, description, Snackbar.LENGTH_SHORT).show()
@ -738,7 +700,7 @@ class AlbumViewPagerAdapter(private val media_attachments: List<Attachment>) : R
holder.image.contentDescription = description holder.image.contentDescription = description
} }
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view){ class ViewHolder(val binding: AlbumImageViewBinding) : RecyclerView.ViewHolder(binding.root){
val image: ImageView = view.findViewById(R.id.imageImageView) val image: ImageView = binding.imageImageView
} }
} }

View File

@ -18,22 +18,21 @@ import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.h.pixeldroid.posts.PostActivity
import com.h.pixeldroid.profile.ProfileActivity
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.utils.api.PixelfedAPI import com.h.pixeldroid.databinding.FragmentNotificationsBinding
import com.h.pixeldroid.utils.db.AppDatabase import com.h.pixeldroid.posts.PostActivity
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
import com.h.pixeldroid.utils.api.objects.Account
import com.h.pixeldroid.utils.api.objects.Notification
import com.h.pixeldroid.utils.api.objects.Status
import kotlinx.android.synthetic.main.fragment_notifications.view.*
import com.h.pixeldroid.posts.feeds.cachedFeeds.CachedFeedFragment import com.h.pixeldroid.posts.feeds.cachedFeeds.CachedFeedFragment
import com.h.pixeldroid.posts.feeds.cachedFeeds.FeedViewModel import com.h.pixeldroid.posts.feeds.cachedFeeds.FeedViewModel
import com.h.pixeldroid.posts.feeds.cachedFeeds.ViewModelFactory import com.h.pixeldroid.posts.feeds.cachedFeeds.ViewModelFactory
import com.h.pixeldroid.posts.parseHTMLText import com.h.pixeldroid.posts.parseHTMLText
import com.h.pixeldroid.posts.setTextViewFromISO8601 import com.h.pixeldroid.posts.setTextViewFromISO8601
import com.h.pixeldroid.profile.ProfileActivity
import com.h.pixeldroid.utils.api.PixelfedAPI
import com.h.pixeldroid.utils.api.objects.Account
import com.h.pixeldroid.utils.api.objects.Notification
import com.h.pixeldroid.utils.api.objects.Status
import com.h.pixeldroid.utils.db.AppDatabase
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
/** /**
@ -71,12 +70,12 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
/** /**
* View Holder for a [Notification] RecyclerView list item. * View Holder for a [Notification] RecyclerView list item.
*/ */
class NotificationViewHolder(view: View) : RecyclerView.ViewHolder(view) { class NotificationViewHolder(binding: FragmentNotificationsBinding) : RecyclerView.ViewHolder(binding.root) {
private val notificationType: TextView = view.notification_type private val notificationType: TextView = binding.notificationType
private val notificationTime: TextView = view.notification_time private val notificationTime: TextView = binding.notificationTime
private val postDescription: TextView = view.notification_post_description private val postDescription: TextView = binding.notificationPostDescription
private val avatar: ImageView = view.notification_avatar private val avatar: ImageView = binding.notificationAvatar
private val photoThumbnail: ImageView = view.notification_photo_thumbnail private val photoThumbnail: ImageView = binding.notificationPhotoThumbnail
private var notification: Notification? = null private var notification: Notification? = null
@ -216,9 +215,10 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
companion object { companion object {
fun create(parent: ViewGroup): NotificationViewHolder { fun create(parent: ViewGroup): NotificationViewHolder {
val view = LayoutInflater.from(parent.context) val itemBinding = FragmentNotificationsBinding.inflate(
.inflate(R.layout.fragment_notifications, parent, false) LayoutInflater.from(parent.context), parent, false
return NotificationViewHolder(view) )
return NotificationViewHolder(itemBinding)
} }
} }
} }

View File

@ -14,13 +14,13 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.databinding.AccountListEntryBinding
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedFeedFragment import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedFeedFragment
import com.h.pixeldroid.posts.feeds.uncachedFeeds.ViewModelFactory import com.h.pixeldroid.posts.feeds.uncachedFeeds.ViewModelFactory
import com.h.pixeldroid.utils.api.objects.Account import com.h.pixeldroid.utils.api.objects.Account
import com.h.pixeldroid.utils.api.objects.Account.Companion.ACCOUNT_ID_TAG import com.h.pixeldroid.utils.api.objects.Account.Companion.ACCOUNT_ID_TAG
import com.h.pixeldroid.utils.api.objects.Account.Companion.FOLLOWERS_TAG import com.h.pixeldroid.utils.api.objects.Account.Companion.FOLLOWERS_TAG
import kotlinx.android.synthetic.main.account_list_entry.view.*
/** /**
@ -75,10 +75,10 @@ class AccountListFragment : UncachedFeedFragment<Account>() {
/** /**
* View Holder for an [Account] RecyclerView list item. * View Holder for an [Account] RecyclerView list item.
*/ */
class AccountViewHolder(view: View) : RecyclerView.ViewHolder(view) { class AccountViewHolder(binding: AccountListEntryBinding) : RecyclerView.ViewHolder(binding.root) {
private val avatar : ImageView = view.account_entry_avatar private val avatar : ImageView = binding.accountEntryAvatar
private val username : TextView = view.account_entry_username private val username : TextView = binding.accountEntryUsername
private val acct: TextView = view.account_entry_acct private val acct: TextView = binding.accountEntryAcct
private var account: Account? = null private var account: Account? = null
@ -104,9 +104,10 @@ class AccountViewHolder(view: View) : RecyclerView.ViewHolder(view) {
companion object { companion object {
fun create(parent: ViewGroup): AccountViewHolder { fun create(parent: ViewGroup): AccountViewHolder {
val view = LayoutInflater.from(parent.context) val itemBinding = AccountListEntryBinding.inflate(
.inflate(R.layout.account_list_entry, parent, false) LayoutInflater.from(parent.context), parent, false
return AccountViewHolder(view) )
return AccountViewHolder(itemBinding)
} }
} }
} }

View File

@ -12,12 +12,12 @@ import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedFeedFragment import com.h.pixeldroid.databinding.FragmentTagsBinding
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedFeedFragment
import com.h.pixeldroid.posts.feeds.uncachedFeeds.ViewModelFactory import com.h.pixeldroid.posts.feeds.uncachedFeeds.ViewModelFactory
import com.h.pixeldroid.utils.api.objects.Results import com.h.pixeldroid.utils.api.objects.Results
import com.h.pixeldroid.utils.api.objects.Tag import com.h.pixeldroid.utils.api.objects.Tag
import kotlinx.android.synthetic.main.fragment_tags.view.*
/** /**
* Fragment to show a list of [hashtag][Tag]s, as a result of a search. * Fragment to show a list of [hashtag][Tag]s, as a result of a search.
@ -100,9 +100,9 @@ class HashTagAdapter : PagingDataAdapter<Tag, RecyclerView.ViewHolder>(
/** /**
* View Holder for a [Tag] RecyclerView list item. * View Holder for a [Tag] RecyclerView list item.
*/ */
class HashTagViewHolder(view: View) : RecyclerView.ViewHolder(view) { class HashTagViewHolder(binding: FragmentTagsBinding) : RecyclerView.ViewHolder(binding.root) {
private val name : TextView = view.tag_name private val name : TextView = binding.tagName
private var tag: Tag? = null private var tag: Tag? = null
@ -124,9 +124,10 @@ class HashTagViewHolder(view: View) : RecyclerView.ViewHolder(view) {
companion object { companion object {
fun create(parent: ViewGroup): HashTagViewHolder { fun create(parent: ViewGroup): HashTagViewHolder {
val view = LayoutInflater.from(parent.context) val itemBinding = FragmentTagsBinding.inflate(
.inflate(R.layout.fragment_tags, parent, false) LayoutInflater.from(parent.context), parent, false
return HashTagViewHolder(view) )
return HashTagViewHolder(itemBinding)
} }
} }
} }

View File

@ -10,7 +10,12 @@ import android.net.Uri
import android.os.Build import android.os.Build
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.h.pixeldroid.R import com.h.pixeldroid.R
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
fun hasInternet(context: Context): Boolean { fun hasInternet(context: Context): Boolean {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
@ -66,3 +71,25 @@ fun setThemeFromPreferences(preferences: SharedPreferences, resources : Resource
} }
} }
} }
/**
* Delegated property to use in fragments to prevent memory leaks of bindings.
* This makes it unnecessary to set binding to null in onDestroyView.
* The value should be assigned in the Fragment's onCreateView()
*/
fun <T> Fragment.bindingLifecycleAware(): ReadWriteProperty<Fragment, T> =
object : ReadWriteProperty<Fragment, T>, DefaultLifecycleObserver {
private var binding: T? = null
override fun onDestroy(owner: LifecycleOwner) {
binding = null
}
override fun getValue(thisRef: Fragment, property: KProperty<*>): T = binding!!
override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) {
binding = value
this@bindingLifecycleAware.viewLifecycleOwner.lifecycle.addObserver(this)
}
}

View File

@ -222,7 +222,7 @@
tools:text="Yesterday" /> tools:text="Yesterday" />
<TextView <TextView
android:id="@+id/ViewComments" android:id="@+id/viewComments"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"