Merge branch 'crash_fixes' into 'master'
Crash fixes, various small improvements Closes #255 and #223 See merge request pixeldroid/PixelDroid!254
This commit is contained in:
commit
ee06e0229c
|
@ -7,10 +7,7 @@ Free (as in freedom) Android client for Pixelfed, the federated image sharing pl
|
|||
<a href=https://apt.izzysoft.de/fdroid/index/apk/com.h.pixeldroid><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" width="170"></a>
|
||||
|
||||
## Compiling the code yourself
|
||||
If you want to try out PixelDroid on your own device, you can try to compile the source code yourself. To do that you will need to install [Android Studio](https://developer.android.com/studio/).
|
||||
|
||||
- Open the ___gradle___ project inside of Android Studio. Then you should plug your Android device into your computer (make sure that your device is in [developer mode](https://developer.android.com/studio/debug/dev-options)) and select ___share files___ on it.
|
||||
If you want to try out PixelDroid on your own device, you can compile the source code yourself. To do that you can install [Android Studio](https://developer.android.com/studio/).
|
||||
|
||||
- You should see that Android studio has detected your device and its name should appear next to a small play button on the top right corner of Android Studio. If that is the case, then you can click said play button and, after Android studio will have built the project, you'll be able to use PixelDroid on your device!
|
||||
|
||||
At this point PixelDroid will be installed on your phone, so it won't have to be plugged in anymore!
|
||||
## Art attribution
|
||||
Various works have been used from the pixelfed branding repository ( https://github.com/pixelfed/brand-assets ). In addition, a drawing of a red panda is used for some error messages ( https://thenounproject.com/search/?q=red+panda&i=2877785 )
|
||||
|
|
|
@ -66,15 +66,15 @@ dependencies {
|
|||
*/
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
||||
implementation 'androidx.navigation:navigation-fragment:2.3.0'
|
||||
implementation 'androidx.navigation:navigation-ui:2.3.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
|
||||
implementation "androidx.browser:browser:1.2.0"
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
|
||||
implementation 'androidx.paging:paging-runtime-ktx:2.1.2'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
|
||||
|
@ -83,14 +83,14 @@ dependencies {
|
|||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
|
||||
// Use the most recent version of CameraX
|
||||
def camerax_version = '1.0.0-beta08'
|
||||
def camerax_version = '1.0.0-beta11'
|
||||
implementation "androidx.camera:camera-core:${camerax_version}"
|
||||
implementation "androidx.camera:camera-camera2:${camerax_version}"
|
||||
// CameraX Lifecycle library
|
||||
implementation "androidx.camera:camera-lifecycle:$camerax_version"
|
||||
|
||||
// CameraX View class
|
||||
implementation 'androidx.camera:camera-view:1.0.0-alpha15'
|
||||
implementation 'androidx.camera:camera-view:1.0.0-alpha18'
|
||||
|
||||
def room_version = "2.2.5"
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
|
@ -106,13 +106,13 @@ dependencies {
|
|||
implementation 'com.google.android.material:material:1.2.1'
|
||||
|
||||
//Dagger (dependency injection)
|
||||
implementation 'com.google.dagger:dagger-android:2.28.3'
|
||||
implementation 'com.google.dagger:dagger-android-support:2.28.3'
|
||||
implementation 'com.google.dagger:dagger-android:2.29.1'
|
||||
implementation 'com.google.dagger:dagger-android-support:2.29.1'
|
||||
// if you use the support libraries
|
||||
kapt 'com.google.dagger:dagger-android-processor:2.28.3'
|
||||
kapt 'com.google.dagger:dagger-compiler:2.28.3'
|
||||
kapt 'com.google.dagger:dagger-android-processor:2.29.1'
|
||||
kapt 'com.google.dagger:dagger-compiler:2.29.1'
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
|
||||
|
@ -160,9 +160,9 @@ dependencies {
|
|||
|
||||
debugImplementation "androidx.fragment:fragment-testing:1.2.5"
|
||||
|
||||
testImplementation 'com.github.tomakehurst:wiremock-jre8:2.27.1'
|
||||
testImplementation 'com.github.tomakehurst:wiremock-jre8:2.27.2'
|
||||
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
||||
testImplementation 'junit:junit:4.13'
|
||||
testImplementation 'junit:junit:4.13.1'
|
||||
testImplementation "androidx.room:room-testing:$room_version"
|
||||
|
||||
|
||||
|
@ -175,7 +175,7 @@ dependencies {
|
|||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.3.0'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.2'
|
||||
androidTestImplementation('com.squareup.okhttp3:mockwebserver:4.8.0')
|
||||
androidTestImplementation('com.squareup.okhttp3:mockwebserver:4.9.0')
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,6 @@ class FollowsActivity : AppCompatActivity() {
|
|||
private var followsFragment = AccountListFragment()
|
||||
@Inject
|
||||
lateinit var db: AppDatabase
|
||||
@Inject
|
||||
lateinit var apiHolder: PixelfedAPIHolder
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -35,28 +32,10 @@ class FollowsActivity : AppCompatActivity() {
|
|||
val followers = intent.getSerializableExtra(FOLLOWERS_TAG) as Boolean
|
||||
|
||||
if(account == null) {
|
||||
val user = db.userDao().getActiveUser()
|
||||
|
||||
val accessToken = user?.accessToken.orEmpty()
|
||||
|
||||
val pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
||||
|
||||
pixelfedAPI.verifyCredentials("Bearer $accessToken").enqueue(object :
|
||||
Callback<Account> {
|
||||
override fun onFailure(call: Call<Account>, t: Throwable) {
|
||||
Log.e("Cannot get account id", t.toString())
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call<Account>, response: Response<Account>) {
|
||||
if(response.code() == 200) {
|
||||
val id = response.body()!!.id
|
||||
val displayName = response.body()!!.display_name
|
||||
startFragment(id, displayName, followers)
|
||||
}
|
||||
}
|
||||
})
|
||||
val user = db.userDao().getActiveUser()!!
|
||||
startFragment(user.user_id, user.display_name, followers)
|
||||
} else {
|
||||
startFragment(account.id, account.display_name, followers)
|
||||
startFragment(account.id!!, account.getDisplayName(), followers)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -190,7 +190,7 @@ class MainActivity : AppCompatActivity() {
|
|||
if (response.body() != null && response.isSuccessful) {
|
||||
val account = response.body() as Account
|
||||
DBUtils.addUser(db, account, domain, accessToken = accessToken)
|
||||
fillDrawerAccountInfo(account.id)
|
||||
fillDrawerAccountInfo(account.id!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,17 +248,16 @@ class MainActivity : AppCompatActivity() {
|
|||
iconUrl = user.avatar_static
|
||||
isNameShown = true
|
||||
identifier = user.user_id.toLong()
|
||||
descriptionText = "${user.username}@${user.instance_uri.removePrefix("https://")}"
|
||||
descriptionText = "@${user.username}@${user.instance_uri.removePrefix("https://")}"
|
||||
}
|
||||
}.toMutableList()
|
||||
|
||||
// reuse the already existing "add account" item
|
||||
for (profile in header.profiles.orEmpty()) {
|
||||
if (profile.identifier == ADD_ACCOUNT_IDENTIFIER) {
|
||||
profiles.add(profile)
|
||||
break
|
||||
}
|
||||
}
|
||||
header.profiles.orEmpty()
|
||||
.filter { it.identifier == ADD_ACCOUNT_IDENTIFIER }
|
||||
.take(1)
|
||||
.map { profiles.add(it) }
|
||||
|
||||
header.clear()
|
||||
header.profiles = profiles
|
||||
header.setActiveProfile(account.toLong())
|
||||
|
|
|
@ -129,7 +129,7 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
|
|||
//<editor-fold desc="ON LAUNCH">
|
||||
private fun loadImage() {
|
||||
originalImage = MediaStore.Images.Media.getBitmap(contentResolver, imageUri)
|
||||
compressedImage = resizeImage(originalImage!!.copy(BITMAP_CONFIG, true))
|
||||
compressedImage = resizeImage(originalImage!!)
|
||||
compressedOriginalImage = compressedImage!!.copy(BITMAP_CONFIG, true)
|
||||
filteredImage = compressedImage!!.copy(BITMAP_CONFIG, true)
|
||||
Glide.with(this).load(compressedImage).into(image_preview)
|
||||
|
|
|
@ -86,7 +86,7 @@ class PostActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun initializeFragment(arguments: Bundle, status: Status?){
|
||||
supportActionBar?.title = getString(R.string.post_title).format(status!!.account?.display_name)
|
||||
supportActionBar?.title = getString(R.string.post_title).format(status!!.account?.getDisplayName())
|
||||
arguments.putSerializable(POST_TAG, status)
|
||||
postFragment.arguments = arguments
|
||||
supportFragmentManager.beginTransaction()
|
||||
|
|
|
@ -6,23 +6,25 @@ import android.net.Uri
|
|||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import android.widget.*
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.h.pixeldroid.adapters.ProfilePostsRecyclerViewAdapter
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.db.AppDatabase
|
||||
import com.h.pixeldroid.db.UserDatabaseEntity
|
||||
import com.h.pixeldroid.di.PixelfedAPIHolder
|
||||
import com.h.pixeldroid.objects.Account
|
||||
import com.h.pixeldroid.objects.Relationship
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import kotlinx.android.synthetic.main.fragment_search.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
@ -32,9 +34,11 @@ class ProfileActivity : AppCompatActivity() {
|
|||
private lateinit var pixelfedAPI : PixelfedAPI
|
||||
private lateinit var adapter : ProfilePostsRecyclerViewAdapter
|
||||
private lateinit var recycler : RecyclerView
|
||||
private lateinit var refreshLayout: SwipeRefreshLayout
|
||||
private lateinit var accessToken : String
|
||||
private lateinit var domain : String
|
||||
private var account: Account? = null
|
||||
private var user: UserDatabaseEntity? = null
|
||||
|
||||
@Inject
|
||||
lateinit var db: AppDatabase
|
||||
|
||||
|
@ -49,7 +53,7 @@ class ProfileActivity : AppCompatActivity() {
|
|||
|
||||
(this.application as Pixeldroid).getAppComponent().inject(this)
|
||||
|
||||
val user = db.userDao().getActiveUser()
|
||||
user = db.userDao().getActiveUser()
|
||||
|
||||
domain = user?.instance_uri.orEmpty()
|
||||
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
||||
|
@ -61,7 +65,16 @@ class ProfileActivity : AppCompatActivity() {
|
|||
adapter = ProfilePostsRecyclerViewAdapter()
|
||||
recycler.adapter = adapter
|
||||
|
||||
setContent()
|
||||
// Set profile according to given account
|
||||
val account = intent.getSerializableExtra(Account.ACCOUNT_TAG) as Account?
|
||||
|
||||
setContent(account)
|
||||
|
||||
refreshLayout = findViewById(R.id.profileRefreshLayout)
|
||||
|
||||
refreshLayout.setOnRefreshListener {
|
||||
getAndSetAccount(account?.id ?: user!!.user_id)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
|
@ -69,95 +82,128 @@ class ProfileActivity : AppCompatActivity() {
|
|||
return true
|
||||
}
|
||||
|
||||
private fun setContent() {
|
||||
// Set profile according to given account
|
||||
account = intent.getSerializableExtra(Account.ACCOUNT_TAG) as Account?
|
||||
|
||||
account?.let {
|
||||
setViews()
|
||||
activateFollow()
|
||||
setPosts()
|
||||
} ?: run {
|
||||
private fun setContent(account: Account?) {
|
||||
if(account != null){
|
||||
setViews(account)
|
||||
setPosts(account)
|
||||
} else {
|
||||
pixelfedAPI.verifyCredentials("Bearer $accessToken")
|
||||
.enqueue(object : Callback<Account> {
|
||||
override fun onResponse(call: Call<Account>, response: Response<Account>) {
|
||||
if (response.code() == 200) {
|
||||
account = response.body()!!
|
||||
val myAccount = response.body()!!
|
||||
|
||||
setViews()
|
||||
setViews(myAccount)
|
||||
// Populate profile page with user's posts
|
||||
setPosts()
|
||||
setPosts(myAccount)
|
||||
} else {
|
||||
showError()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Account>, t: Throwable) {
|
||||
Log.e("ProfileActivity:", t.toString())
|
||||
showError()
|
||||
}
|
||||
})
|
||||
|
||||
// Edit button redirects to Pixelfed's "edit account" page
|
||||
val editButton = findViewById<Button>(R.id.editButton)
|
||||
editButton.visibility = View.VISIBLE
|
||||
editButton.setOnClickListener{ onClickEditButton() }
|
||||
}
|
||||
|
||||
//if we aren't viewing our own account, activate follow button
|
||||
if(account != null && account.id != user?.user_id) activateFollow(account)
|
||||
//if we *are* viewing our own account, activate the edit button
|
||||
else activateEditButton()
|
||||
|
||||
|
||||
// On click open followers list
|
||||
findViewById<TextView>(R.id.nbFollowersTextView).setOnClickListener{ onClickFollowers() }
|
||||
findViewById<TextView>(R.id.nbFollowersTextView).setOnClickListener{ onClickFollowers(account) }
|
||||
// On click open followers list
|
||||
findViewById<TextView>(R.id.nbFollowingTextView).setOnClickListener{ onClickFollowing() }
|
||||
findViewById<TextView>(R.id.nbFollowingTextView).setOnClickListener{ onClickFollowing(account) }
|
||||
}
|
||||
|
||||
private fun getAndSetAccount(id: String){
|
||||
pixelfedAPI.getAccount("Bearer $accessToken", id)
|
||||
.enqueue(object : Callback<Account> {
|
||||
override fun onResponse(call: Call<Account>, response: Response<Account>) {
|
||||
if (response.code() == 200) {
|
||||
val account = response.body()!!
|
||||
|
||||
setContent(account)
|
||||
} else {
|
||||
showError()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Account>, t: Throwable) {
|
||||
Log.e("ProfileActivity:", t.toString())
|
||||
showError()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun showError(@StringRes errorText: Int = R.string.loading_toast, show: Boolean = true){
|
||||
val motionLayout = findViewById<MotionLayout>(R.id.motionLayout)
|
||||
if(show){
|
||||
motionLayout?.transitionToEnd()
|
||||
} else {
|
||||
findViewById<ProgressBar>(R.id.profileProgressBar).visibility = View.GONE
|
||||
motionLayout?.transitionToStart()
|
||||
}
|
||||
refreshLayout.isRefreshing = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate myProfile page with user's data
|
||||
* Populate profile page with user's data
|
||||
*/
|
||||
private fun setViews() {
|
||||
private fun setViews(account: Account) {
|
||||
val profilePicture = findViewById<ImageView>(R.id.profilePictureImageView)
|
||||
ImageConverter.setRoundImageFromURL(
|
||||
View(applicationContext),
|
||||
account!!.avatar,
|
||||
account.avatar,
|
||||
profilePicture
|
||||
)
|
||||
|
||||
val description = findViewById<TextView>(R.id.descriptionTextView)
|
||||
description.text = parseHTMLText(
|
||||
account!!.note, emptyList(), pixelfedAPI,
|
||||
account.note ?: "", emptyList(), pixelfedAPI,
|
||||
applicationContext, "Bearer $accessToken"
|
||||
)
|
||||
|
||||
val accountName = findViewById<TextView>(R.id.accountNameTextView)
|
||||
accountName.text = account!!.display_name
|
||||
accountName.text = account.getDisplayName()
|
||||
|
||||
supportActionBar?.title = account!!.display_name
|
||||
if(account!!.display_name != account!!.acct){
|
||||
supportActionBar?.subtitle = "@${account!!.acct}"
|
||||
val displayName = account.getDisplayName()
|
||||
supportActionBar?.title = displayName
|
||||
if(displayName != "@${account.acct}"){
|
||||
supportActionBar?.subtitle = "@${account.acct}"
|
||||
}
|
||||
|
||||
accountName.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
val nbPosts = findViewById<TextView>(R.id.nbPostsTextView)
|
||||
nbPosts.text = applicationContext.getString(R.string.nb_posts)
|
||||
.format(account!!.statuses_count.toString())
|
||||
.format(account.statuses_count.toString())
|
||||
nbPosts.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
val nbFollowers = findViewById<TextView>(R.id.nbFollowersTextView)
|
||||
nbFollowers.text = applicationContext.getString(R.string.nb_followers)
|
||||
.format(account!!.followers_count.toString())
|
||||
.format(account.followers_count.toString())
|
||||
nbFollowers.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
val nbFollowing = findViewById<TextView>(R.id.nbFollowingTextView)
|
||||
nbFollowing.text = applicationContext.getString(R.string.nb_following)
|
||||
.format(account!!.following_count.toString())
|
||||
.format(account.following_count.toString())
|
||||
nbFollowing.setTypeface(null, Typeface.BOLD)
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate profile page with user's posts
|
||||
*/
|
||||
private fun setPosts() {
|
||||
pixelfedAPI.accountPosts("Bearer $accessToken", account_id = account!!.id)
|
||||
private fun setPosts(account: Account) {
|
||||
pixelfedAPI.accountPosts("Bearer $accessToken", account_id = account.id)
|
||||
.enqueue(object : Callback<List<Status>> {
|
||||
|
||||
override fun onFailure(call: Call<List<Status>>, t: Throwable) {
|
||||
showError()
|
||||
Log.e("ProfileActivity.Posts:", t.toString())
|
||||
}
|
||||
|
||||
|
@ -168,6 +214,9 @@ class ProfileActivity : AppCompatActivity() {
|
|||
if (response.code() == 200) {
|
||||
val statuses = response.body()!!
|
||||
adapter.addPosts(statuses)
|
||||
showError(show = false)
|
||||
} else {
|
||||
showError()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -184,15 +233,15 @@ class ProfileActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun onClickFollowers() {
|
||||
private fun onClickFollowers(account: Account?) {
|
||||
val intent = Intent(this, FollowsActivity::class.java)
|
||||
intent.putExtra(Account.FOLLOWERS_TAG, true)
|
||||
intent.putExtra(Account.ACCOUNT_ID_TAG, account?.id)
|
||||
intent.putExtra(Account.ACCOUNT_TAG, account)
|
||||
|
||||
ContextCompat.startActivity(this, intent, null)
|
||||
}
|
||||
|
||||
private fun onClickFollowing() {
|
||||
private fun onClickFollowing(account: Account?) {
|
||||
val intent = Intent(this, FollowsActivity::class.java)
|
||||
intent.putExtra(Account.FOLLOWERS_TAG, false)
|
||||
intent.putExtra(Account.ACCOUNT_TAG, account)
|
||||
|
@ -200,12 +249,19 @@ class ProfileActivity : AppCompatActivity() {
|
|||
ContextCompat.startActivity(this, intent, null)
|
||||
}
|
||||
|
||||
private fun activateEditButton() {
|
||||
// Edit button redirects to Pixelfed's "edit account" page
|
||||
val editButton = findViewById<Button>(R.id.editButton)
|
||||
editButton.visibility = View.VISIBLE
|
||||
editButton.setOnClickListener{ onClickEditButton() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up follow button
|
||||
*/
|
||||
private fun activateFollow() {
|
||||
private fun activateFollow(account: Account) {
|
||||
// Get relationship between the two users (credential and this) and set followButton accordingly
|
||||
pixelfedAPI.checkRelationships("Bearer $accessToken", listOf(account!!.id))
|
||||
pixelfedAPI.checkRelationships("Bearer $accessToken", listOf(account.id.orEmpty()))
|
||||
.enqueue(object : Callback<List<Relationship>> {
|
||||
|
||||
override fun onFailure(call: Call<List<Relationship>>, t: Throwable) {
|
||||
|
@ -225,9 +281,9 @@ class ProfileActivity : AppCompatActivity() {
|
|||
val followButton = findViewById<Button>(R.id.followButton)
|
||||
|
||||
if (response.body()!![0].following) {
|
||||
setOnClickUnfollow()
|
||||
setOnClickUnfollow(account)
|
||||
} else {
|
||||
setOnClickFollow()
|
||||
setOnClickFollow(account)
|
||||
}
|
||||
followButton.visibility = View.VISIBLE
|
||||
}
|
||||
|
@ -241,13 +297,13 @@ class ProfileActivity : AppCompatActivity() {
|
|||
})
|
||||
}
|
||||
|
||||
private fun setOnClickFollow() {
|
||||
private fun setOnClickFollow(account: Account) {
|
||||
val followButton = findViewById<Button>(R.id.followButton)
|
||||
|
||||
followButton.setText(R.string.follow)
|
||||
|
||||
followButton.setOnClickListener {
|
||||
pixelfedAPI.follow(account!!.id, "Bearer $accessToken")
|
||||
pixelfedAPI.follow(account.id.orEmpty(), "Bearer $accessToken")
|
||||
.enqueue(object : Callback<Relationship> {
|
||||
|
||||
override fun onFailure(call: Call<Relationship>, t: Throwable) {
|
||||
|
@ -263,7 +319,7 @@ class ProfileActivity : AppCompatActivity() {
|
|||
response: Response<Relationship>
|
||||
) {
|
||||
if (response.code() == 200) {
|
||||
setOnClickUnfollow()
|
||||
setOnClickUnfollow(account)
|
||||
} else if (response.code() == 403) {
|
||||
Toast.makeText(
|
||||
applicationContext, getString(R.string.action_not_allowed),
|
||||
|
@ -275,13 +331,13 @@ class ProfileActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setOnClickUnfollow() {
|
||||
private fun setOnClickUnfollow(account: Account) {
|
||||
val followButton = findViewById<Button>(R.id.followButton)
|
||||
|
||||
followButton.setText(R.string.unfollow)
|
||||
|
||||
followButton.setOnClickListener {
|
||||
pixelfedAPI.unfollow(account!!.id, "Bearer $accessToken")
|
||||
pixelfedAPI.unfollow(account.id.orEmpty(), "Bearer $accessToken")
|
||||
.enqueue(object : Callback<Relationship> {
|
||||
|
||||
override fun onFailure(call: Call<Relationship>, t: Throwable) {
|
||||
|
@ -297,7 +353,7 @@ class ProfileActivity : AppCompatActivity() {
|
|||
response: Response<Relationship>
|
||||
) {
|
||||
if (response.code() == 200) {
|
||||
setOnClickFollow()
|
||||
setOnClickFollow(account)
|
||||
} else if (response.code() == 401) {
|
||||
Toast.makeText(
|
||||
applicationContext, getString(R.string.access_token_invalid),
|
||||
|
|
|
@ -16,22 +16,22 @@ import com.h.pixeldroid.utils.ImageConverter.Companion.setSquareImageFromURL
|
|||
/**
|
||||
* [RecyclerView.Adapter] that can display a list of [Status]s
|
||||
*/
|
||||
class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostsRecyclerViewAdapter.ViewHolder>() {
|
||||
class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostViewHolder>() {
|
||||
private val posts: ArrayList<Status> = ArrayList()
|
||||
|
||||
fun addPosts(newPosts : List<Status>) {
|
||||
val size = posts.size
|
||||
posts.clear()
|
||||
posts.addAll(newPosts)
|
||||
notifyItemRangeInserted(size, newPosts.size)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfilePostViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.fragment_profile_posts, parent, false)
|
||||
return ViewHolder(view)
|
||||
return ProfilePostViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
override fun onBindViewHolder(holder: ProfilePostViewHolder, position: Int) {
|
||||
val post = posts[position]
|
||||
|
||||
if (post.sensitive!!)
|
||||
|
@ -39,6 +39,12 @@ class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostsRecycler
|
|||
else
|
||||
setSquareImageFromURL(holder.postView, post.getPostPreviewURL(), holder.postPreview)
|
||||
|
||||
if(post.media_attachments?.size ?: 0 > 1){
|
||||
holder.albumIcon.visibility = View.VISIBLE
|
||||
} else {
|
||||
holder.albumIcon.visibility = View.GONE
|
||||
}
|
||||
|
||||
holder.postPreview.setOnClickListener {
|
||||
val intent = Intent(holder.postPreview.context, PostActivity::class.java)
|
||||
intent.putExtra(Status.POST_TAG, post)
|
||||
|
@ -47,8 +53,9 @@ class ProfilePostsRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostsRecycler
|
|||
}
|
||||
|
||||
override fun getItemCount(): Int = posts.size
|
||||
|
||||
inner class ViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
|
||||
val postPreview: ImageView = postView.findViewById(R.id.postPreview)
|
||||
}
|
||||
}
|
||||
|
||||
class ProfilePostViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
|
||||
val postPreview: ImageView = postView.findViewById(R.id.postPreview)
|
||||
val albumIcon: ImageView = postView.findViewById(R.id.albumIcon)
|
||||
}
|
|
@ -184,7 +184,7 @@ interface PixelfedAPI {
|
|||
@Query("q") q: String,
|
||||
@Query("resolve") resolve: Boolean? = null,
|
||||
@Query("limit") limit: String? = null,
|
||||
@Query("offset") offset: Int? = null,
|
||||
@Query("offset") offset: String? = null,
|
||||
@Query("following") following: Boolean? = null
|
||||
): Call<Results>
|
||||
|
||||
|
@ -229,7 +229,8 @@ interface PixelfedAPI {
|
|||
@Header("Authorization") authorization: String,
|
||||
@Query("max_id") max_id: String? = null,
|
||||
@Query("since_id") since_id: String? = null,
|
||||
@Query("limit") limit: Number? = null
|
||||
@Query("limit") limit: Number? = null,
|
||||
@Query("page") page: String? = null
|
||||
) : Call<List<Account>>
|
||||
|
||||
@GET("/api/v1/accounts/{id}/following")
|
||||
|
@ -238,7 +239,8 @@ interface PixelfedAPI {
|
|||
@Header("Authorization") authorization: String,
|
||||
@Query("max_id") max_id: String? = null,
|
||||
@Query("since_id") since_id: String? = null,
|
||||
@Query("limit") limit: Number? = 40
|
||||
@Query("limit") limit: Number? = 40,
|
||||
@Query("page") page: String? = null
|
||||
) : Call<List<Account>>
|
||||
|
||||
@GET("/api/v1/accounts/{id}")
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.h.pixeldroid.fragments
|
|||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.ClipData
|
||||
import android.content.ContentUris
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
|
@ -31,7 +32,6 @@ import androidx.lifecycle.lifecycleScope
|
|||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.h.pixeldroid.PostCreationActivity
|
||||
import com.h.pixeldroid.CameraActivity
|
||||
import com.h.pixeldroid.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -107,8 +107,6 @@ class CameraFragment : Fragment() {
|
|||
|
||||
// Shut down our background executor
|
||||
cameraExecutor.shutdown()
|
||||
|
||||
// Unregister the broadcast receivers and listeners
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -121,7 +119,7 @@ class CameraFragment : Fragment() {
|
|||
return inflater.inflate(R.layout.fragment_camera, container, false)
|
||||
}
|
||||
|
||||
private fun setGalleryThumbnail(uri: String) {
|
||||
private fun setGalleryThumbnail(uri: Uri) {
|
||||
// Reference of the view that holds the gallery thumbnail
|
||||
val thumbnail = container.findViewById<ImageButton>(R.id.photo_view_button)
|
||||
|
||||
|
@ -220,7 +218,7 @@ class CameraFragment : Fragment() {
|
|||
)
|
||||
|
||||
// Attach the viewfinder's surface provider to preview use case
|
||||
preview?.setSurfaceProvider(viewFinder.createSurfaceProvider())
|
||||
preview?.setSurfaceProvider(viewFinder.surfaceProvider)
|
||||
} catch (exc: Exception) {
|
||||
Log.e(TAG, "Use case binding failed", exc)
|
||||
}
|
||||
|
@ -263,19 +261,22 @@ class CameraFragment : Fragment() {
|
|||
// Find the last picture
|
||||
val projection = arrayOf(
|
||||
MediaStore.Images.ImageColumns._ID,
|
||||
MediaStore.Images.ImageColumns.DATA,
|
||||
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
|
||||
MediaStore.Images.ImageColumns.DATE_TAKEN,
|
||||
MediaStore.Images.ImageColumns.MIME_TYPE
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) MediaStore.Images.ImageColumns.DATE_TAKEN
|
||||
else MediaStore.Images.ImageColumns.DATE_MODIFIED,
|
||||
)
|
||||
val cursor = requireContext().contentResolver
|
||||
.query(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null,
|
||||
null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC"
|
||||
null,
|
||||
(if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) MediaStore.Images.ImageColumns.DATE_TAKEN
|
||||
else MediaStore.Images.ImageColumns.DATE_MODIFIED) + " DESC"
|
||||
)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
val uri = Uri.parse(cursor.getString(1)).path ?: ""
|
||||
setGalleryThumbnail(uri)
|
||||
val url = ContentUris.withAppendedId(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
cursor.getLong(0)
|
||||
)
|
||||
setGalleryThumbnail(url)
|
||||
cursor.close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
package com.h.pixeldroid.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.adapters.ProfilePostsRecyclerViewAdapter
|
||||
|
||||
|
||||
/**
|
||||
* A fragment representing a list of statuses of a profile.
|
||||
*/
|
||||
class ProfilePostsFragment : Fragment() {
|
||||
|
||||
private var columnCount = 3
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_profile_posts_list, container, false)
|
||||
|
||||
|
||||
// Set the adapter
|
||||
if (view is RecyclerView) {
|
||||
with(view) {
|
||||
layoutManager = when {
|
||||
columnCount <= 1 -> LinearLayoutManager(context)
|
||||
else -> GridLayoutManager(context, columnCount)
|
||||
}
|
||||
adapter = ProfilePostsRecyclerViewAdapter()
|
||||
}
|
||||
}
|
||||
return view
|
||||
}
|
||||
}
|
|
@ -9,28 +9,28 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.google.android.material.textview.MaterialTextView
|
||||
import com.h.pixeldroid.Pixeldroid
|
||||
import com.h.pixeldroid.PostActivity
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.SearchActivity
|
||||
import com.h.pixeldroid.adapters.ProfilePostViewHolder
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.db.AppDatabase
|
||||
import com.h.pixeldroid.di.PixelfedAPIHolder
|
||||
import com.h.pixeldroid.objects.DiscoverPost
|
||||
import com.h.pixeldroid.objects.DiscoverPosts
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import com.h.pixeldroid.utils.DBUtils
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import com.mikepenz.iconics.IconicsColor
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.padding
|
||||
import com.mikepenz.iconics.utils.color
|
||||
import com.mikepenz.iconics.utils.paddingDp
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import kotlinx.android.synthetic.main.fragment_search.*
|
||||
|
@ -50,6 +50,7 @@ class SearchDiscoverFragment : Fragment() {
|
|||
private lateinit var accessToken: String
|
||||
private lateinit var discoverProgressBar: ProgressBar
|
||||
private lateinit var discoverRefreshLayout: SwipeRefreshLayout
|
||||
|
||||
@Inject
|
||||
lateinit var db: AppDatabase
|
||||
|
||||
|
@ -85,6 +86,7 @@ class SearchDiscoverFragment : Fragment() {
|
|||
discoverText.setCompoundDrawables(IconicsDrawable(requireContext(), GoogleMaterial.Icon.gmd_explore).apply {
|
||||
sizeDp = 24
|
||||
paddingDp = 20
|
||||
color = IconicsColor.colorRes(R.color.colorDrawing)
|
||||
}, null, null, null)
|
||||
|
||||
return view
|
||||
|
@ -107,12 +109,25 @@ class SearchDiscoverFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
fun showError(@StringRes errorText: Int = R.string.loading_toast, show: Boolean = true){
|
||||
val motionLayout = view?.findViewById<MotionLayout>(R.id.motionLayout)
|
||||
if(show){
|
||||
motionLayout?.transitionToEnd()
|
||||
} else {
|
||||
motionLayout?.transitionToStart()
|
||||
}
|
||||
discoverRefreshLayout.isRefreshing = false
|
||||
discoverProgressBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
||||
private fun getDiscover() {
|
||||
|
||||
api.discover("Bearer $accessToken")
|
||||
.enqueue(object : Callback<DiscoverPosts> {
|
||||
|
||||
override fun onFailure(call: Call<DiscoverPosts>, t: Throwable) {
|
||||
showError()
|
||||
Log.e("SearchDiscoverFragment:", t.toString())
|
||||
}
|
||||
|
||||
|
@ -120,16 +135,19 @@ class SearchDiscoverFragment : Fragment() {
|
|||
if(response.code() == 200) {
|
||||
val discoverPosts = response.body()!!
|
||||
adapter.addPosts(discoverPosts.posts)
|
||||
discoverProgressBar.visibility = View.GONE
|
||||
discoverRefreshLayout.isRefreshing = false
|
||||
showError(show = false)
|
||||
}
|
||||
else {
|
||||
showError()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* [RecyclerView.Adapter] that can display a list of [DiscoverPost]s
|
||||
*/
|
||||
class DiscoverRecyclerViewAdapter: RecyclerView.Adapter<DiscoverRecyclerViewAdapter.ViewHolder>() {
|
||||
class DiscoverRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostViewHolder>() {
|
||||
private val posts: ArrayList<DiscoverPost> = ArrayList()
|
||||
|
||||
fun addPosts(newPosts : List<DiscoverPost>) {
|
||||
|
@ -138,14 +156,19 @@ class SearchDiscoverFragment : Fragment() {
|
|||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfilePostViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.fragment_profile_posts, parent, false)
|
||||
return ViewHolder(view)
|
||||
return ProfilePostViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
override fun onBindViewHolder(holder: ProfilePostViewHolder, position: Int) {
|
||||
val post = posts[position]
|
||||
if(post.type?.contains("album") == true) {
|
||||
holder.albumIcon.visibility = View.VISIBLE
|
||||
} else {
|
||||
holder.albumIcon.visibility = View.GONE
|
||||
}
|
||||
ImageConverter.setSquareImageFromURL(holder.postView, post.thumb, holder.postPreview)
|
||||
holder.postPreview.setOnClickListener {
|
||||
val intent = Intent(holder.postView.context, PostActivity::class.java)
|
||||
|
@ -155,9 +178,5 @@ class SearchDiscoverFragment : Fragment() {
|
|||
}
|
||||
|
||||
override fun getItemCount(): Int = posts.size
|
||||
|
||||
inner class ViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
|
||||
val postPreview: ImageView = postView.findViewById(R.id.postPreview)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.lifecycle.Observer
|
||||
|
@ -34,7 +33,9 @@ open class AccountListFragment : FeedFragment() {
|
|||
lateinit var profilePicRequest: RequestBuilder<Drawable>
|
||||
protected lateinit var adapter : FeedsRecyclerViewAdapter<Account, AccountsRecyclerViewAdapter.ViewHolder>
|
||||
lateinit var factory: FeedDataSourceFactory<String, Account>
|
||||
lateinit var content: LiveData<PagedList<Account>>
|
||||
|
||||
private var currentPage = 1
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
|
@ -62,7 +63,7 @@ open class AccountListFragment : FeedFragment() {
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val content = makeContent()
|
||||
content = makeContent()
|
||||
|
||||
content.observe(viewLifecycleOwner,
|
||||
Observer { c ->
|
||||
|
@ -72,6 +73,9 @@ open class AccountListFragment : FeedFragment() {
|
|||
})
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
showError(show = false)
|
||||
|
||||
currentPage = 1
|
||||
//by invalidating data, loadInitial will be called again
|
||||
factory.liveData.value!!.invalidate()
|
||||
}
|
||||
|
@ -96,7 +100,7 @@ open class AccountListFragment : FeedFragment() {
|
|||
|
||||
//We use the id as the key
|
||||
override fun getKey(item: Account): String {
|
||||
return item.id
|
||||
return currentPage.toString()
|
||||
}
|
||||
|
||||
override fun makeInitialCall(requestedLoadSize: Int): Call<List<Account>> {
|
||||
|
@ -114,15 +118,18 @@ open class AccountListFragment : FeedFragment() {
|
|||
}
|
||||
|
||||
override fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Account>> {
|
||||
// Pixelfed and Mastodon don't implement this in the same fashion. Pixelfed uses
|
||||
// Laravel's paging mechanism, while Mastodon uses the Link header for pagination.
|
||||
// No need to know which is which, they should ignore the non-relevant argument
|
||||
return if (following) {
|
||||
pixelfedAPI.followers(
|
||||
id, "Bearer $accessToken",
|
||||
since_id = key, limit = requestedLoadSize
|
||||
limit = requestedLoadSize, page = key, max_id = key
|
||||
)
|
||||
} else {
|
||||
pixelfedAPI.following(
|
||||
id, "Bearer $accessToken",
|
||||
since_id = key, limit = requestedLoadSize
|
||||
limit = requestedLoadSize, page = key, max_id = key
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -133,16 +140,30 @@ open class AccountListFragment : FeedFragment() {
|
|||
override fun onResponse(call: Call<List<Account>>, response: Response<List<Account>>) {
|
||||
if (response.isSuccessful && response.body() != null) {
|
||||
val data = response.body()!!
|
||||
if(response.headers()["Link"] != null){
|
||||
//Header is of the form:
|
||||
// Link: <https://mastodon.social/api/v1/accounts/1/followers?limit=2&max_id=7628164>; rel="next", <https://mastodon.social/api/v1/accounts/1/followers?limit=2&since_id=7628165>; rel="prev"
|
||||
// So we want the first max_id value. In case there are arguments after
|
||||
// the max_id in the URL, we make sure to stop at the first '?'
|
||||
currentPage = response.headers()["Link"]
|
||||
.orEmpty()
|
||||
.substringAfter("max_id=")
|
||||
.substringBefore('?')
|
||||
.substringBefore('>')
|
||||
.toIntOrNull() ?: 0
|
||||
} else {
|
||||
currentPage++
|
||||
}
|
||||
callback.onResult(data)
|
||||
} else{
|
||||
Toast.makeText(context, getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
|
||||
showError()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
loadingIndicator.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Account>>, t: Throwable) {
|
||||
Toast.makeText(context, getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
|
||||
showError(errorText = R.string.feed_failed)
|
||||
Log.e("AccountListFragment", t.toString())
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
package com.h.pixeldroid.fragments.feeds
|
||||
package com.h.pixeldroid.fragments.feeds
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.paging.DataSource
|
||||
|
@ -22,7 +27,9 @@ import com.h.pixeldroid.db.AppDatabase
|
|||
import com.h.pixeldroid.db.UserDatabaseEntity
|
||||
import com.h.pixeldroid.di.PixelfedAPIHolder
|
||||
import com.h.pixeldroid.objects.FeedContent
|
||||
import kotlinx.android.synthetic.main.fragment_feed.*
|
||||
import kotlinx.android.synthetic.main.fragment_feed.view.*
|
||||
import org.w3c.dom.Text
|
||||
import retrofit2.Call
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -65,6 +72,20 @@ open class FeedFragment: Fragment() {
|
|||
return view
|
||||
}
|
||||
|
||||
fun showError(@StringRes errorText: Int = R.string.loading_toast, show: Boolean = true){
|
||||
val errorLayout = view?.findViewById<ConstraintLayout>(R.id.errorLayout)
|
||||
val progressBar = view?.findViewById<ProgressBar>(R.id.progressBar)
|
||||
|
||||
if(show){
|
||||
view?.findViewById<TextView>(R.id.error_text)?.setText(errorText)
|
||||
errorLayout?.visibility = VISIBLE
|
||||
progressBar?.visibility = GONE
|
||||
} else {
|
||||
errorLayout?.visibility = GONE
|
||||
progressBar?.visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
open inner class FeedDataSourceFactory<ObjectId, APIObject: FeedContent>(
|
||||
private val dataSource: FeedDataSource<ObjectId, APIObject>
|
||||
): DataSource.Factory<ObjectId, APIObject>() {
|
||||
|
|
|
@ -10,7 +10,6 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.paging.LivePagedListBuilder
|
||||
|
@ -86,6 +85,8 @@ class NotificationsFragment : FeedFragment() {
|
|||
})
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
showError(show = false)
|
||||
|
||||
//by invalidating data, loadInitial will be called again
|
||||
factory.liveData.value!!.invalidate()
|
||||
}
|
||||
|
@ -125,15 +126,15 @@ class NotificationsFragment : FeedFragment() {
|
|||
if (response.isSuccessful && response.body() != null) {
|
||||
val data = response.body()!!
|
||||
callback.onResult(data)
|
||||
} else{
|
||||
Toast.makeText(context, getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
showError()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
loadingIndicator.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Notification>>, t: Throwable) {
|
||||
Toast.makeText(context, getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
|
||||
showError(errorText = R.string.feed_failed)
|
||||
Log.e("NotificationsFragment", t.toString())
|
||||
}
|
||||
})
|
||||
|
@ -155,7 +156,7 @@ class NotificationsFragment : FeedFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun openPostFromNotifcation(notification: Notification) : Intent {
|
||||
private fun openPostFromNotification(notification: Notification) : Intent {
|
||||
val intent = Intent(context, PostActivity::class.java)
|
||||
intent.putExtra(Status.POST_TAG, notification.status)
|
||||
return intent
|
||||
|
@ -164,11 +165,9 @@ class NotificationsFragment : FeedFragment() {
|
|||
private fun openActivity(notification: Notification){
|
||||
val intent: Intent
|
||||
when (notification.type){
|
||||
Notification.NotificationType.mention, Notification.NotificationType.favourite-> {
|
||||
intent = openPostFromNotifcation(notification)
|
||||
}
|
||||
Notification.NotificationType.reblog-> {
|
||||
intent = openPostFromNotifcation(notification)
|
||||
Notification.NotificationType.mention, Notification.NotificationType.favourite,
|
||||
Notification.NotificationType.poll, Notification.NotificationType.reblog -> {
|
||||
intent = openPostFromNotification(notification)
|
||||
}
|
||||
Notification.NotificationType.follow -> {
|
||||
intent = Intent(context, ProfileActivity::class.java)
|
||||
|
@ -197,7 +196,7 @@ class NotificationsFragment : FeedFragment() {
|
|||
holder.photoThumbnail.visibility = View.GONE
|
||||
}
|
||||
|
||||
setNotificationType(notification.type, notification.account.username, holder.notificationType)
|
||||
setNotificationType(notification.type, notification.account.username!!, holder.notificationType)
|
||||
setTextViewFromISO8601(notification.created_at, holder.notificationTime, false, context)
|
||||
|
||||
//Convert HTML to clickable text
|
||||
|
@ -236,6 +235,9 @@ class NotificationsFragment : FeedFragment() {
|
|||
Notification.NotificationType.favourite -> {
|
||||
setNotificationTypeTextView(context, R.string.liked_notification, R.drawable.ic_like_full)
|
||||
}
|
||||
Notification.NotificationType.poll -> {
|
||||
setNotificationTypeTextView(context, R.string.poll_notification, R.drawable.poll)
|
||||
}
|
||||
}
|
||||
textView.text = format.format(username)
|
||||
textView.setCompoundDrawablesWithIntrinsicBounds(
|
||||
|
|
|
@ -53,15 +53,15 @@ class HomeTimelineFragment: PostsFeedFragment() {
|
|||
val notifications = response.body()!!
|
||||
callback.onResult(notifications)
|
||||
DBUtils.storePosts(db, notifications, user!!)
|
||||
} else{
|
||||
Toast.makeText(context, getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
showError()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
loadingIndicator.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Status>>, t: Throwable) {
|
||||
Toast.makeText(context, getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
|
||||
showError(errorText = R.string.feed_failed)
|
||||
Log.e("PostsFeedFragment", t.toString())
|
||||
}
|
||||
})
|
||||
|
|
|
@ -68,6 +68,8 @@ abstract class PostsFeedFragment : FeedFragment() {
|
|||
})
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
showError(show = false)
|
||||
|
||||
//by invalidating data, loadInitial will be called again
|
||||
factory.liveData.value!!.invalidate()
|
||||
}
|
||||
|
|
|
@ -34,14 +34,14 @@ class PublicTimelineFragment: PostsFeedFragment() {
|
|||
val notifications = response.body()!!
|
||||
callback.onResult(notifications)
|
||||
} else{
|
||||
Toast.makeText(context, getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
|
||||
showError()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
loadingIndicator.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Status>>, t: Throwable) {
|
||||
Toast.makeText(context, getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
|
||||
showError(errorText = R.string.feed_failed)
|
||||
Log.e("PublicTimelineFragment", t.toString())
|
||||
}
|
||||
})
|
||||
|
|
|
@ -42,18 +42,18 @@ class SearchAccountFragment: AccountListFragment(){
|
|||
}
|
||||
|
||||
override fun getKey(item: Account): String {
|
||||
return item.id
|
||||
return content.value?.loadedCount.toString()
|
||||
}
|
||||
|
||||
private fun searchMakeInitialCall(requestedLoadSize: Int): Call<Results> {
|
||||
return pixelfedAPI
|
||||
.search("Bearer $accessToken",
|
||||
limit="$requestedLoadSize", q=query,
|
||||
limit="$requestedLoadSize", q = query,
|
||||
type = Results.SearchType.accounts)
|
||||
}
|
||||
private fun searchMakeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
|
||||
return pixelfedAPI
|
||||
.search("Bearer $accessToken", max_id=key,
|
||||
.search("Bearer $accessToken", offset = key,
|
||||
limit="$requestedLoadSize", q = query,
|
||||
type = Results.SearchType.accounts)
|
||||
}
|
||||
|
@ -75,16 +75,15 @@ class SearchAccountFragment: AccountListFragment(){
|
|||
if (response.code() == 200) {
|
||||
val notifications = response.body()!!.accounts as ArrayList<Account>
|
||||
callback.onResult(notifications as List<Account>)
|
||||
|
||||
} else{
|
||||
Toast.makeText(context, getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
|
||||
showError()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
loadingIndicator.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Results>, t: Throwable) {
|
||||
Toast.makeText(context,getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
|
||||
showError(errorText = R.string.feed_failed)
|
||||
Log.e("FeedFragment", t.toString())
|
||||
}
|
||||
})
|
||||
|
|
|
@ -29,7 +29,7 @@ class SearchHashtagFragment: FeedFragment(){
|
|||
|
||||
private lateinit var query: String
|
||||
private lateinit var content: LiveData<PagedList<Tag>>
|
||||
protected lateinit var adapter : TagsRecyclerViewAdapter
|
||||
private lateinit var adapter : TagsRecyclerViewAdapter
|
||||
lateinit var factory: FeedDataSourceFactory<Int, Tag>
|
||||
|
||||
|
||||
|
@ -59,6 +59,8 @@ class SearchHashtagFragment: FeedFragment(){
|
|||
})
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
showError(show = false)
|
||||
|
||||
//by invalidating data, loadInitial will be called again
|
||||
factory.liveData.value!!.invalidate()
|
||||
}
|
||||
|
@ -78,7 +80,7 @@ class SearchHashtagFragment: FeedFragment(){
|
|||
}
|
||||
private fun searchMakeAfterCall(requestedLoadSize: Int, key: Int): Call<Results> {
|
||||
return pixelfedAPI
|
||||
.search("Bearer $accessToken", offset=key,
|
||||
.search("Bearer $accessToken", offset = key.toString(),
|
||||
limit="$requestedLoadSize", q = query,
|
||||
type = Results.SearchType.hashtags)
|
||||
}
|
||||
|
@ -107,14 +109,14 @@ class SearchHashtagFragment: FeedFragment(){
|
|||
callback.onResult(notifications as List<Tag>)
|
||||
|
||||
} else{
|
||||
Toast.makeText(context,getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
|
||||
showError()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
loadingIndicator.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Results>, t: Throwable) {
|
||||
Toast.makeText(context,getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
|
||||
showError(errorText = R.string.feed_failed)
|
||||
Log.e("FeedFragment", t.toString())
|
||||
}
|
||||
})
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.view.ViewGroup
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.LivePagedListBuilder
|
||||
import androidx.paging.PagedList
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.fragments.feeds.FeedFragment
|
||||
import com.h.pixeldroid.fragments.feeds.postFeeds.PostsFeedFragment
|
||||
import com.h.pixeldroid.objects.Results
|
||||
|
@ -68,15 +69,15 @@ class SearchPostsFragment: PostsFeedFragment(){
|
|||
if (response.code() == 200) {
|
||||
val notifications = response.body()!!.statuses as ArrayList<Status>
|
||||
callback.onResult(notifications as List<Status>)
|
||||
|
||||
} else{
|
||||
Log.e("FeedFragment", "got response code ${response.code()}")
|
||||
} else {
|
||||
showError()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
loadingIndicator.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Results>, t: Throwable) {
|
||||
showError(errorText = R.string.feed_failed)
|
||||
Log.e("FeedFragment", t.toString())
|
||||
}
|
||||
})
|
||||
|
|
|
@ -18,29 +18,29 @@ https://docs.joinmastodon.org/entities/account/
|
|||
|
||||
data class Account(
|
||||
//Base attributes
|
||||
override val id: String,
|
||||
val username: String,
|
||||
val acct: String = "",
|
||||
val url: String = "", //HTTPS URL
|
||||
override val id: String?,
|
||||
val username: String?,
|
||||
val acct: String? = "",
|
||||
val url: String? = "", //HTTPS URL
|
||||
//Display attributes
|
||||
val display_name: String = "",
|
||||
val note: String = "", //HTML
|
||||
val avatar: String = "", //URL
|
||||
val avatar_static: String = "", //URL
|
||||
val header: String = "", //URL
|
||||
val header_static: String = "", //URL
|
||||
val locked: Boolean = false,
|
||||
val display_name: String? = "",
|
||||
val note: String? = "", //HTML
|
||||
val avatar: String? = "", //URL
|
||||
val avatar_static: String? = "", //URL
|
||||
val header: String? = "", //URL
|
||||
val header_static: String? = "", //URL
|
||||
val locked: Boolean? = false,
|
||||
val emojis: List<Emoji>? = null,
|
||||
val discoverable: Boolean = true,
|
||||
val discoverable: Boolean? = true,
|
||||
//Statistical attributes
|
||||
val created_at: String = "", //ISO 8601 Datetime (maybe can use a date type)
|
||||
val statuses_count: Int = 0,
|
||||
val followers_count: Int = 0,
|
||||
val following_count: Int = 0,
|
||||
val created_at: String? = "", //ISO 8601 Datetime (maybe can use a date type)
|
||||
val statuses_count: Int? = 0,
|
||||
val followers_count: Int? = 0,
|
||||
val following_count: Int? = 0,
|
||||
//Optional attributes
|
||||
val moved: Account? = null,
|
||||
val fields: List<Field>? = emptyList(),
|
||||
val bot: Boolean = false,
|
||||
val bot: Boolean? = false,
|
||||
val source: Source? = null
|
||||
) : Serializable, FeedContent() {
|
||||
companion object {
|
||||
|
@ -48,6 +48,7 @@ data class Account(
|
|||
const val ACCOUNT_ID_TAG = "AccountIdTag"
|
||||
const val FOLLOWERS_TAG = "FollowingTag"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Opens an activity of the profile with the given id
|
||||
*/
|
||||
|
@ -76,6 +77,12 @@ data class Account(
|
|||
}
|
||||
}
|
||||
|
||||
fun getDisplayName() : String = when {
|
||||
username.isNullOrBlank() && display_name.isNullOrBlank() -> ""
|
||||
display_name.isNullOrBlank() -> "@$username"
|
||||
else -> display_name.orEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Open profile activity with given account
|
||||
*/
|
||||
|
|
|
@ -16,6 +16,6 @@ data class Notification(
|
|||
val status: Status? = null
|
||||
): FeedContent() {
|
||||
enum class NotificationType {
|
||||
follow, mention, reblog, favourite
|
||||
follow, mention, reblog, favourite, poll
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
package com.h.pixeldroid.objects
|
||||
|
||||
class Source
|
||||
import java.io.Serializable
|
||||
|
||||
class Source: Serializable
|
||||
|
|
|
@ -109,12 +109,6 @@ data class Status(
|
|||
private fun getDescription(api: PixelfedAPI, context: Context, credential: String) : Spanned =
|
||||
parseHTMLText(content ?: "", mentions, api, context, credential)
|
||||
|
||||
fun getUsername() : CharSequence = when {
|
||||
account?.username.isNullOrBlank() && account?.display_name.isNullOrBlank() -> "No Name"
|
||||
account!!.username.isNullOrBlank() -> account.display_name as CharSequence
|
||||
else -> account.username as CharSequence
|
||||
}
|
||||
|
||||
fun getNLikes(context: Context) : CharSequence {
|
||||
return context.getString(R.string.likes).format(favourites_count.toString())
|
||||
}
|
||||
|
@ -229,13 +223,13 @@ data class Status(
|
|||
) {
|
||||
//Setup username as a button that opens the profile
|
||||
rootView.findViewById<TextView>(R.id.username).apply {
|
||||
text = this@Status.getUsername()
|
||||
text = this@Status.account?.getDisplayName() ?: ""
|
||||
setTypeface(null, Typeface.BOLD)
|
||||
setOnClickListener { account?.openProfile(rootView.context) }
|
||||
}
|
||||
|
||||
rootView.findViewById<TextView>(R.id.usernameDesc).apply {
|
||||
text = this@Status.getUsername()
|
||||
text = this@Status.account?.getDisplayName() ?: ""
|
||||
setTypeface(null, Typeface.BOLD)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@ class DBUtils {
|
|||
fun addUser(db: AppDatabase, account: Account, instance_uri: String, activeUser: Boolean = true, accessToken: String) {
|
||||
db.userDao().insertUser(
|
||||
UserDatabaseEntity(
|
||||
user_id = account.id,
|
||||
user_id = account.id!!,
|
||||
//make sure not to normalize to https when localhost, to allow testing
|
||||
instance_uri = normalizeOrNot(instance_uri),
|
||||
username = account.username,
|
||||
display_name = account.display_name,
|
||||
avatar_static = account.avatar_static,
|
||||
username = account.username!!,
|
||||
display_name = account.getDisplayName(),
|
||||
avatar_static = account.avatar_static.orEmpty(),
|
||||
isActive = activeUser,
|
||||
accessToken = accessToken
|
||||
)
|
||||
|
@ -68,7 +68,7 @@ class DBUtils {
|
|||
instance_uri = user.instance_uri,
|
||||
uri = post.uri ?: "",
|
||||
account_profile_picture = post.getProfilePicUrl() ?: "",
|
||||
account_name = post.getUsername().toString(),
|
||||
account_name = (post.account?.getDisplayName() ?: "").toString(),
|
||||
media_urls = post.media_attachments.map {
|
||||
attachment -> attachment.url ?: ""
|
||||
},
|
||||
|
|
|
@ -177,7 +177,7 @@ abstract class PostUtils {
|
|||
holder.commentIn.visibility = View.GONE
|
||||
|
||||
//Add the comment to the comment section
|
||||
addComment(holder.context, holder.commentCont, resp.account!!.username,
|
||||
addComment(holder.context, holder.commentCont, resp.account!!.username!!,
|
||||
resp.content!!
|
||||
)
|
||||
|
||||
|
@ -222,7 +222,7 @@ abstract class PostUtils {
|
|||
|
||||
//Create the new views for each comment
|
||||
for (status in statuses) {
|
||||
addComment(holder.context, holder.commentCont, status.account!!.username,
|
||||
addComment(holder.context, holder.commentCont, status.account!!.username!!,
|
||||
status.content!!
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM9,17L7,17v-7h2v7zM13,17h-2L11,7h2v10zM17,17h-2v-4h2v4z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11,12l2.03,2.71L16,11l4,5L8,16l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h4v-2L5,18L5,8h14v10h-4v2h4c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.89,-2 -2,-2zM12,10l-4,4h3v6h2v-6h3l-4,-4z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM9,17L7,17v-7h2v7zM13,17h-2L11,7h2v10zM17,17h-2v-4h2v4z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
|
@ -0,0 +1,81 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="80dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="80">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M42,54H40c0,-5.646 -7.311,-14 -23,-14V38C34.242,38 42,47.356 42,54Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M21.0394,38.7261l1.9971,-7.0007l1.9242,0.5489l-1.9971,7.0007z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M32,24a2.025,2.025 0,0 1,-1.568 -0.721l-1,-1.2a1.818,1.818 0,0 1,-0.26 -1.954A1.984,1.984 0,0 1,31 19h2a1.984,1.984 0,0 1,1.827 1.127,1.818 1.818,0 0,1 -0.259,1.953l-1,1.2A2.026,2.026 0,0 1,32 24ZM31.132,21 L31.968,22 32.905,20.943Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M37.394,20a1.376,1.376 0,0 1,-1 -0.4c-0.669,-0.669 -0.48,-1.829 0.448,-2.756s2.087,-1.117 2.756,-0.448 0.48,1.829 -0.448,2.756A2.589,2.589 0,0 1,37.394 20Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M26.606,20a2.589,2.589 0,0 1,-1.76 -0.846c-0.928,-0.927 -1.117,-2.087 -0.448,-2.756s1.828,-0.481 2.756,0.448 1.117,2.087 0.448,2.756A1.376,1.376 0,0 1,26.606 20Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M50.707,15.707l-1.414,-1.414c2,-2 2.595,-8.817 2.691,-12.293H50c-7.287,0 -9.3,1.71 -9.314,1.728a1.039,1.039 0,0 1,-1.031 0.21A19.494,19.494 0,0 0,33 3H31V1h2a22.413,22.413 0,0 1,6.834 0.886C40.9,1.216 43.713,0 50,0h3a1,1 0,0 1,1 1C54,2.168 53.907,12.507 50.707,15.707Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M34,34H30V32h4c14.617,0 18.019,-6.558 18.788,-9.11a4.9,4.9 0,0 1,-2.495 -1.183A1,1 0,0 1,51 20a3.81,3.81 0,0 0,2.221 -1.388c-2.275,-0.976 -4.387,-3.662 -6.6,-6.472l-0.41,-0.521A1,1 0,0 1,46 11C46,8.514 49.383,3.2 52.293,0.293l1.414,1.414c-2.766,2.766 -5.38,7.168 -5.679,8.982l0.169,0.215C50.444,13.762 52.991,17 55,17a1,1 0,0 1,0.832 1.555,10.9 10.9,0 0,1 -2.254,2.426 1.7,1.7 0,0 1,1.16 0.344,1 1,0 0,1 0.258,0.766C54.952,22.577 53.687,34 34,34Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M13.293,15.707C10.093,12.507 10,2.168 10,1a1,1 0,0 1,1 -1h3c6.287,0 9.1,1.216 10.166,1.886A22.413,22.413 0,0 1,31 1V3a19.494,19.494 0,0 0,-6.655 0.938,0.992 0.992,0 0,1 -1.052,-0.231h0S21.281,2 14,2H12.016c0.1,3.476 0.689,10.291 2.691,12.293Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M30,34C10.313,34 9.048,22.577 9,22.091A1,1 0,0 1,10 21c0.147,0 0.289,-0.007 0.422,-0.019a10.9,10.9 0,0 1,-2.254 -2.426A1,1 0,0 1,9 17c2.009,0 4.556,-3.238 6.8,-6.1l0.169,-0.215c-0.3,-1.814 -2.913,-6.216 -5.679,-8.982L11.707,0.293C14.617,3.2 18,8.514 18,11a1,1 0,0 1,-0.215 0.619l-0.41,0.521c-2.209,2.809 -4.319,5.494 -6.593,6.471A3.824,3.824 0,0 0,13 20a1,1 0,0 1,0.707 1.707,4.908 4.908,0 0,1 -2.5,1.184C11.979,25.438 15.377,32 30,32Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M40,64H14C4.972,64 0,59.383 0,51c0,-7.411 7.309,-13 17,-13v2C8.448,40 2,44.729 2,51c0,9.092 6.525,11 12,11H40c4.087,0 10,-2.493 10,-7V53c0,-1.638 -0.424,-2.351 -1.469,-3.949a0.8,0.8 0,0 1,-0.054 -0.095l-8.369,-16.5 1.784,-0.9L50.235,48A7.987,7.987 0,0 1,52 53v2C52,60.872 45.031,64 40,64Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M17.99,11.109A0.518,0.518 0,0 0,18 11H16c0,-1.488 5.386,-9 8,-9V4C22.744,4.113 18.309,9.836 17.99,11.109Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M46.01,11.109c-0.319,-1.273 -4.754,-7 -6.017,-7.109L40,2c2.614,0 8,7.512 8,9H46A0.518,0.518 0,0 0,46.01 11.109Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M32,34c-3.5,0 -10,0 -10,-6 0,-3.406 4.336,-7.849 8.754,-8.97l0.492,1.94C27.514,21.916 24,25.732 24,28c0,3.7 3.623,4 8,4s8,-0.3 8,-4c0,-2.265 -3.514,-6.081 -7.247,-7.031l0.494,-1.938C37.664,20.155 42,24.6 42,28 42,34 35.495,34 32,34Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M37,16a2.754,2.754 0,0 1,-3 -2.5A2.754,2.754 0,0 1,37 11a2.754,2.754 0,0 1,3 2.5A2.754,2.754 0,0 1,37 16ZM37,13c-0.62,0 -1,0.323 -1,0.5s0.38,0.5 1,0.5 1,-0.323 1,-0.5S37.62,13 37,13Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M27,16a2.754,2.754 0,0 1,-3 -2.5A2.754,2.754 0,0 1,27 11a2.754,2.754 0,0 1,3 2.5A2.754,2.754 0,0 1,27 16ZM27,13c-0.62,0 -1,0.323 -1,0.5s0.38,0.5 1,0.5 1,-0.323 1,-0.5S27.62,13 27,13Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M42.956,30a1,1 0,0 1,-0.875 -1.485c0.789,-1.421 1.11,-2.244 0.932,-3.357 -0.1,-0.632 -0.306,-1.075 -0.547,-1.187a2.132,2.132 0,0 1,-0.819 -0.721,3.262 3.262,0 0,1 -0.476,-2.984c0.354,-1.034 1.3,-2.266 3.8,-2.266C47.683,18 49,19.517 49,22.637 49,26.6 45.677,30 42.956,30ZM44.974,20c-1.6,0 -1.818,0.642 -1.912,0.916a1.263,1.263 0,0 0,0.2 1.153,0.654 0.654,0 0,0 0.1,0.121 3.237,3.237 0,0 1,1.623 2.653,5.327 5.327,0 0,1 -0.109,2.264A6.114,6.114 0,0 0,47 22.637C47,20.184 46.2,20 44.974,20Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M21.044,30C18.323,30 15,26.6 15,22.637 15,19.517 16.317,18 19.026,18c2.5,0 3.449,1.232 3.8,2.266a3.265,3.265 0,0 1,-0.478 2.987,2.135 2.135,0 0,1 -0.823,0.721c-0.235,0.109 -0.44,0.552 -0.541,1.183 -0.178,1.114 0.143,1.937 0.932,3.358A1,1 0,0 1,21.044 30ZM19.026,20C17.8,20 17,20.184 17,22.637a6.114,6.114 0,0 0,2.122 4.47,5.332 5.332,0 0,1 -0.109,-2.265 3.285,3.285 0,0 1,1.652 -2.672s0.026,-0.036 0.07,-0.1a1.265,1.265 0,0 0,0.2 -1.156C20.844,20.642 20.623,20 19.026,20Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M31,23h2v2h-2z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M34.445,27.832 L32,26.2l-2.445,1.63 -1.11,-1.664 3,-2a1,1 0,0 1,1.11 0l3,2Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14,64c-4.363,0 -9,-4.557 -9,-13 0,-5.508 2.054,-10 5.494,-12.009l1.01,1.727C8.684,42.366 7,46.21 7,51c0,6.886 3.56,11 7,11Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M22,64c-4.363,0 -9,-4.557 -9,-13s4.637,-13 9,-13v2c-3.44,0 -7,4.114 -7,11s3.56,11 7,11Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M29,64c-4.363,0 -9,-4.557 -9,-13 0,-5.508 2.054,-10 5.494,-12.009l1.01,1.727C23.684,42.366 22,46.21 22,51c0,6.886 3.56,11 7,11Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M37,64c-4.363,0 -9,-4.557 -9,-13 0,-4.888 1.539,-7.923 2.83,-9.608l1.588,1.216C31.314,44.048 30,46.673 30,51c0,6.886 3.56,11 7,11Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M45,63c-4.479,0 -9,-5.257 -9,-17h2c0,9.848 3.521,15 7,15Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M37,36h2v11h-2z"/>
|
||||
</vector>
|
|
@ -1,164 +1,147 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
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:id="@+id/profileRefreshLayout"
|
||||
tools:context=".ProfileActivity">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/profilePictureImageView"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nbPostsTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/default_nposts"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/profilePictureImageView"
|
||||
app:layout_constraintHorizontal_chainStyle="spread"
|
||||
app:layout_constraintStart_toEndOf="@+id/profilePictureImageView"
|
||||
app:layout_constraintTop_toTopOf="@+id/profilePictureImageView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nbFollowersTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/default_nfollowers"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/nbPostsTextView"
|
||||
app:layout_constraintEnd_toStartOf="@+id/nbFollowingTextView"
|
||||
app:layout_constraintStart_toEndOf="@+id/nbPostsTextView"
|
||||
app:layout_constraintTop_toTopOf="@+id/nbPostsTextView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nbFollowingTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/default_nfollowing"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/nbFollowersTextView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/nbFollowersTextView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/accountNameTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:text="@string/no_username"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/profilePictureImageView"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="20dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
<TextView
|
||||
android:id="@+id/descriptionTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/accountNameTextView"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/profilePictureImageView"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_weight="1"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
<Button
|
||||
android:id="@+id/followButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/colorButtonBg"
|
||||
android:text="@string/follow"
|
||||
android:textColor="@color/colorButtonText"
|
||||
android:visibility="invisible"
|
||||
tools:layout_editor_absoluteX="16dp"
|
||||
tools:layout_editor_absoluteY="185dp"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintStart_toStartOf="@+id/profilePictureImageView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/descriptionTextView"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="10"
|
||||
android:orientation="horizontal">
|
||||
<Button
|
||||
android:id="@+id/editButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:backgroundTint="@color/colorButtonBg"
|
||||
android:text="@string/edit_profile"
|
||||
android:textColor="@color/colorButtonText"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_baseline_open_in_browser_24"
|
||||
app:iconTint="@color/colorButtonText"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/profilePictureImageView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/descriptionTextView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nbPostsTextView"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:text="@string/default_nposts" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nbFollowersTextView"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:text="@string/default_nfollowers" />
|
||||
<ProgressBar
|
||||
android:id="@+id/profileProgressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/followButton" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nbFollowingTextView"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:text="@string/default_nfollowing" />
|
||||
</LinearLayout>
|
||||
<androidx.constraintlayout.motion.widget.MotionLayout
|
||||
android:id="@+id/motionLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="visible"
|
||||
app:layoutDescription="@xml/error_layout_xml_error_scene"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/followButton"
|
||||
tools:visibility="visible">
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/accountNameTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_username"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/descriptionTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="50dp"
|
||||
android:layout_marginRight="50dp"
|
||||
android:layout_marginBottom="15dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/followButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/follow"
|
||||
android:visibility="invisible"
|
||||
android:textColor="@color/colorButtonText"
|
||||
android:backgroundTint="@color/colorButtonBg"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/editButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/colorButtonText"
|
||||
android:backgroundTint="@color/colorButtonBg"
|
||||
android:visibility="gone"
|
||||
android:text="@string/edit_profile"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/postsButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@color/colorPrimaryTab"
|
||||
android:src="@android:drawable/ic_dialog_dialer"
|
||||
android:contentDescription="TODO" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/collectionButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@color/colorPrimaryTab"
|
||||
android:src="@android:drawable/ic_menu_gallery"
|
||||
android:contentDescription="TODO" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<include
|
||||
layout="@layout/error_layout"
|
||||
tools:layout_editor_absoluteX="50dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/profilePostsRecyclerView"
|
||||
|
@ -167,9 +150,11 @@
|
|||
android:layout_margin="5dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
app:layoutManager="LinearLayoutManager"
|
||||
tools:context=".fragments.ProfileFragment"
|
||||
app:layout_constraintTop_toBottomOf="@id/errorLayout"
|
||||
tools:listitem="@layout/fragment_profile_posts" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.motion.widget.MotionLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/errorLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:showIn="@layout/fragment_feed"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView4"
|
||||
android:importantForAccessibility="no"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/red_panda"
|
||||
app:tint="@color/colorDrawing" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/something_went_wrong"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView4" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/panda_pull_to_refresh_to_try_again"
|
||||
android:textStyle="italic"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/error_text" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,22 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:id="@+id/coordinatorLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
app:layoutManager="LinearLayoutManager"/>
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layoutManager="LinearLayoutManager" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
|
@ -24,5 +28,11 @@
|
|||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<include layout="@layout/error_layout"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -2,6 +2,7 @@
|
|||
<androidx.gridlayout.widget.GridLayout
|
||||
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="wrap_content"
|
||||
app:columnCount="3">
|
||||
|
@ -9,19 +10,35 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/postPreview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:contentDescription="TODO"
|
||||
android:padding="1dp"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_gravity="fill"
|
||||
app:layout_rowWeight="1"
|
||||
app:layout_columnWeight="1"/>
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/albumIcon"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:foreground="@drawable/album_black_24dp"
|
||||
android:foregroundGravity="center"
|
||||
android:foregroundTint="#FFFFFF"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.gridlayout.widget.GridLayout>
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/list"
|
||||
android:name="com.h.pixeldroid.fragments.ProfilePostsFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="16dp"
|
||||
app:layoutManager="LinearLayoutManager"
|
||||
tools:context=".fragments.ProfilePostsFragment"
|
||||
tools:listitem="@layout/fragment_profile_posts" />
|
|
@ -1,7 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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="wrap_content"
|
||||
android:layout_gravity="center_horizontal">
|
||||
|
@ -40,24 +39,36 @@
|
|||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
|
||||
<androidx.constraintlayout.motion.widget.MotionLayout
|
||||
android:id="@+id/motionLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
app:layoutDescription="@xml/error_layout_xml_error_scene">
|
||||
|
||||
<include layout="@layout/error_layout"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/discoverText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/discover"
|
||||
android:layout_gravity="center_horizontal"/>
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/errorLayout" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/discoverList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:nestedScrollingEnabled="false"/>
|
||||
android:nestedScrollingEnabled="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/discoverText" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.motion.widget.MotionLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
|
|
|
@ -2,15 +2,8 @@
|
|||
<resources>
|
||||
<color name="colorPrimary">#FFFFFF</color>
|
||||
<color name="colorPrimaryTab">#6200EE</color>
|
||||
<color name="colorPrimaryActionBar">#6200EE</color>
|
||||
<color name="colorPrimaryDark">#3700B3</color>
|
||||
<color name="colorAccent">#03DAC5</color>
|
||||
<color name="colorButtonBg">#6200EE</color>
|
||||
<color name="colorButtonText">#FFFFFF</color>
|
||||
|
||||
<color name="filterLabelNormal">#8A8889</color>
|
||||
<color name="filterLabelSelected">#221F20</color>
|
||||
<color name="colorPrimaryError">#FF0000</color>
|
||||
<color name="colorText">#000000</color>
|
||||
<color name="colorDrawing">#FFFFFF</color>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -14,4 +14,5 @@
|
|||
<color name="filterLabelSelected">#221F20</color>
|
||||
<color name="colorPrimaryError">#FF0000</color>
|
||||
<color name="colorText">#FFFFFF</color>
|
||||
<color name="colorDrawing">#000000</color>
|
||||
</resources>
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
<string name="mention_notification">%1$s mentioned you</string>
|
||||
<string name="shared_notification">%1$s shared your post</string>
|
||||
<string name="liked_notification">%1$s liked your post</string>
|
||||
<string name="poll_notification">"%1$s's poll has ended"</string>
|
||||
|
||||
<!-- Login page -->
|
||||
<string name="whats_an_instance">"What's an instance?"</string>
|
||||
<string name="domain_of_your_instance">Domain of your instance</string>
|
||||
|
@ -135,6 +137,8 @@
|
|||
<string name="profile_picture">Profile picture</string>
|
||||
<string name="open_drawer_menu">Open drawer menu</string>
|
||||
<string name="discover">DISCOVER</string>
|
||||
<string name="something_went_wrong">Something went wrong…</string>
|
||||
<string name="panda_pull_to_refresh_to_try_again">This panda is not happy. Pull to refresh to try again.</string>
|
||||
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<MotionScene
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<Transition
|
||||
android:id="@+id/transition"
|
||||
app:constraintSetEnd="@+id/end"
|
||||
app:constraintSetStart="@id/start"
|
||||
app:duration="500">
|
||||
|
||||
</Transition>
|
||||
|
||||
<ConstraintSet android:id="@+id/start">
|
||||
<Constraint
|
||||
android:id="@+id/errorLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="parent" />
|
||||
</ConstraintSet>
|
||||
|
||||
<ConstraintSet android:id="@+id/end">
|
||||
<Constraint
|
||||
android:id="@+id/errorLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</ConstraintSet>
|
||||
</MotionScene>
|
|
@ -1,13 +1,13 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.4.0'
|
||||
ext.kotlin_version = '1.4.10'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.0.1'
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
|
Loading…
Reference in New Issue