User profile #79 (#90)

* Refactored myProfile page

* Total refactor of profile posts

* Merged with my-profile

* Posts displayed on profile page

* Added links to profile activity where needed

* Removed MyProfileTest with swipes

* Tests ProfileActivity from Notifications

* Add test, fix progressbar being null
This commit is contained in:
mjaillot 2020-04-09 22:36:59 +02:00 committed by GitHub
parent 5372e7e9ee
commit 1b6753d119
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 917 additions and 164 deletions

View File

@ -75,6 +75,7 @@ dependencies {
implementation("com.github.bumptech.glide:glide:4.11.0") {
exclude group: "com.android.support"
}
implementation "com.github.bumptech.glide:okhttp-integration:4.11.0"
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
// Excludes the support library because it's already included by Glide.
@ -104,6 +105,7 @@ tasks.withType(Test) {
jacoco.excludes = ['jdk.internal.*']
}
task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {
reports {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,12 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".PostActivity"></activity>
<activity android:name=".PostActivity">
</activity>
<activity android:name=".ProfileActivity">
</activity>
<activity
android:name=".SettingsActivity"
android:label="@string/title_activity_settings2">

View File

@ -6,7 +6,6 @@ import com.h.pixeldroid.fragments.PostFragment
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
import com.h.pixeldroid.objects.Status
class PostActivity : AppCompatActivity() {
lateinit var postFragment : PostFragment

View File

@ -0,0 +1,104 @@
package com.h.pixeldroid
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Typeface
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.fragments.ProfilePostsRecyclerViewAdapter
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_TAG
import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.ImageConverter.Companion.setRoundImageFromURL
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class ProfileActivity : AppCompatActivity() {
private lateinit var adapter : ProfilePostsRecyclerViewAdapter
private lateinit var recycler : RecyclerView
private lateinit var preferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
// Set RecyclerView as a grid with 3 columns
recycler = findViewById(R.id.profilePostsRecyclerView)
recycler.layoutManager = GridLayoutManager(this, 3)
adapter = ProfilePostsRecyclerViewAdapter(this)
recycler.adapter = adapter
preferences = getSharedPreferences(
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
)
// Set profile according to given account
val account = intent.getSerializableExtra(ACCOUNT_TAG) as Account
setContent(account)
// Set profile picture
val profilePicture = findViewById<ImageView>(R.id.profilePictureImageView)
setRoundImageFromURL(View(this), account.avatar, profilePicture)
setPosts(account)
}
private fun setContent(account: Account) {
val profilePicture = findViewById<ImageView>(R.id.profilePictureImageView)
setRoundImageFromURL(View(this), account.avatar, profilePicture)
val description = findViewById<TextView>(R.id.descriptionTextView)
description.text = account.note
val accountName = findViewById<TextView>(R.id.accountNameTextView)
accountName.text = account.username
val nbPosts = findViewById<TextView>(R.id.nbPostsTextView)
nbPosts.text = "${account.statuses_count}\nPosts"
nbPosts.setTypeface(null, Typeface.BOLD)
val nbFollowers = findViewById<TextView>(R.id.nbFollowersTextView)
nbFollowers.text = "${account.followers_count}\nFollowers"
nbFollowers.setTypeface(null, Typeface.BOLD)
val nbFollowing = findViewById<TextView>(R.id.nbFollowingTextView)
nbFollowing.text = "${account.following_count}\nFollowing"
nbFollowing.setTypeface(null, Typeface.BOLD)
}
// Populate profile page with user's posts
private fun setPosts(account: Account) {
val pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
val accessToken = preferences.getString("accessToken", "")
pixelfedAPI.accountPosts("Bearer $accessToken", account_id = account.id).enqueue(object :
Callback<List<Status>> {
override fun onFailure(call: Call<List<Status>>, t: Throwable) {
Log.e("ProfileFragment.Posts:", t.toString())
}
override fun onResponse(
call: Call<List<Status>>,
response: Response<List<Status>>
) {
if(response.code() == 200) {
val posts = ArrayList<Status>()
val statuses = response.body()!!
for(status in statuses) {
posts.add(status)
}
adapter.addPosts(posts)
}
}
})
}
}

View File

@ -38,6 +38,52 @@ interface PixelfedAPI {
@Field("grant_type") grant_type: String? = null
): Call<Token>
@POST("api/v1/statuses/{id}/favourite")
fun likePost(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String,
@Path("id") statusId: String
) : Call<Status>
@POST("/api/v1/statuses/{id}/unfavourite")
fun unlikePost(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String,
@Path("id") statusId: String
) : Call<Status>
@POST("/api/v1/statuses/{id}/favourited_by")
fun postLikedBy(
@Path("id") statusId: String
) : Call<List<Account>>
//Used in our case to post a comment
@FormUrlEncoded
@POST("/api/v1/statuses")
fun commentStatus(
//The authorization header needs to be of the form "Bearer <token>"
@Header("Authorization") authorization: String,
@Field("status") statusText : String,
@Field("in_reply_to_id") in_reply_to_id : String,
@Field("media_ids[]") media_ids : List<String> = emptyList(),
@Field("poll[options][]") poll_options : List<String>? = null,
@Field("poll[expires_in]") poll_expires : List<String>? = null,
@Field("poll[multiple]") poll_multiple : List<String>? = null,
@Field("poll[hide_totals]") poll_hideTotals : List<String>? = null,
@Field("sensitive") sensitive : Boolean? = null,
@Field("spoiler_text") spoiler_text : String? = null,
@Field("visibility") visibility : String = "public",
@Field("scheduled_at") scheduled_at : String? = null,
@Field("language") language : String? = null
) : Call<Status>
//Used in our case to retrieve comments for a given status
@GET("/api/v1/statuses/{id}/context")
fun statusComments(
@Path("id") statusId: String,
@Header("Authorization") authorization: String? = null
) : Call<Context>
@GET("/api/v1/timelines/public")
fun timelinePublic(
@Query("local") local: Boolean? = null,
@ -47,7 +93,6 @@ interface PixelfedAPI {
@Query("limit") limit: String? = null
): Call<List<Status>>
@GET("/api/v1/timelines/home")
fun timelineHome(
//The authorization header needs to be of the form "Bearer <token>"
@ -82,6 +127,12 @@ interface PixelfedAPI {
@Header("Authorization") authorization: String
): Call<Account>
@GET("/api/v1/accounts/{id}/statuses")
fun accountPosts(
@Header("Authorization") authorization: String,
@Path("id") account_id: String? = null
): Call<List<Status>>
companion object {
fun create(baseUrl: String): PixelfedAPI {
return Retrofit.Builder()

View File

@ -14,18 +14,22 @@ import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.h.pixeldroid.BuildConfig
import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.ImageConverter.Companion.setRoundImageFromURL
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class MyProfileFragment : Fragment() {
private lateinit var preferences: SharedPreferences
private lateinit var adapter : ProfilePostsRecyclerViewAdapter
private lateinit var recycler : RecyclerView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@ -36,9 +40,16 @@ class MyProfileFragment : Fragment() {
)
val view = inflater.inflate(R.layout.fragment_my_profile, container, false)
// Edit button redirects to pixelfed's "edit account" page
val editButton: Button = view.findViewById(R.id.editButton)
editButton.setOnClickListener((View.OnClickListener { onClickEditButton() }))
// Set RecyclerView as a grid with 3 columns
recycler = view.findViewById(R.id.myProfilePostsRecyclerView)
recycler.layoutManager = GridLayoutManager(context, 3)
adapter = ProfilePostsRecyclerViewAdapter(context!!)
recycler.adapter = adapter
return view
}
@ -55,6 +66,27 @@ class MyProfileFragment : Fragment() {
val account = response.body()!!
setContent(view, account)
// Populate profile page with user's posts
pixelfedAPI.accountPosts("Bearer $accessToken", account_id = account.id).enqueue(object : Callback<List<Status>> {
override fun onFailure(call: Call<List<Status>>, t: Throwable) {
Log.e("ProfileFragment.Posts:", t.toString())
}
override fun onResponse(
call: Call<List<Status>>,
response: Response<List<Status>>
) {
if(response.code() == 200) {
val posts = ArrayList<Status>()
val statuses = response.body()!!
for (status in statuses) {
posts.add(status)
}
adapter.addPosts(posts)
}
}
})
}
}
@ -64,32 +96,28 @@ class MyProfileFragment : Fragment() {
})
}
// Populate myProfile page with user's data
private fun setContent(view: View, account: Account) {
// ImageView : profile picture
val profilePicture = view.findViewById<ImageView>(R.id.profilePictureImageView)
Glide.with(view.context).load(account.avatar).into(profilePicture)
setRoundImageFromURL(view, account.avatar, profilePicture)
// TextView : description / bio
val description = view.findViewById<TextView>(R.id.descriptionTextView)
description.text = account.note
// TextView : account name
val accountName = view.findViewById<TextView>(R.id.accountNameTextView)
accountName.text = account.username
accountName.setTypeface(null, Typeface.BOLD)
// TextView : number of posts
val nbPosts = view.findViewById<TextView>(R.id.nbPostsTextView)
nbPosts.text = account.statuses_count.toString() + "\nPosts"
nbPosts.text = "${account.statuses_count}\nPosts"
nbPosts.setTypeface(null, Typeface.BOLD)
// TextView : number of followers
val nbFollowers = view.findViewById<TextView>(R.id.nbFollowersTextView)
nbFollowers.text = account.followers_count.toString() + "\nFollowers"
nbFollowers.text = "${account.followers_count}\nFollowers"
nbFollowers.setTypeface(null, Typeface.BOLD)
// TextView : number of following
val nbFollowing = view.findViewById<TextView>(R.id.nbFollowingTextView)
nbFollowing.text = account.following_count.toString() + "\nFollowing"
nbFollowing.text = "${account.following_count}\nFollowing"
nbFollowing.setTypeface(null, Typeface.BOLD)
}

View File

@ -1,10 +1,13 @@
package com.h.pixeldroid.fragments
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import com.h.pixeldroid.R
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
import com.h.pixeldroid.objects.Status
@ -20,19 +23,11 @@ class PostFragment : Fragment() {
): View? {
val status = arguments?.getSerializable(POST_TAG) as Status?
val root = inflater.inflate(R.layout.post_fragment, container, false)
status?.setupPost(root)
//Setup post and profile images
ImageConverter.setImageViewFromURL(
this,
status?.getPostUrl(),
root.postPicture
)
ImageConverter.setImageViewFromURL(
this,
status?.getProfilePicUrl(),
root.profilePic
)
val picRequest = Glide.with(this)
.asDrawable().fitCenter()
.placeholder(ColorDrawable(Color.GRAY))
status?.setupPost(root, picRequest, root.postPicture, root.profilePic)
return root
}
}

View File

@ -0,0 +1,57 @@
package com.h.pixeldroid.fragments
import android.content.Context
import android.content.SharedPreferences
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.BuildConfig
import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI
/**
* A fragment representing a list of Items.
* Activities containing this fragment MUST implement the
* [ProfilePostsFragment.OnListFragmentInteractionListener] interface.
*/
class ProfilePostsFragment : Fragment() {
private lateinit var preferences: SharedPreferences
private lateinit var pixelfedAPI: PixelfedAPI
private var accessToken: String? = null
private var columnCount = 3
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_profile_posts_list, container, false)
preferences = activity!!.getSharedPreferences(
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
)
pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
accessToken = preferences.getString("accessToken", "")
// Set the adapter
if (view is RecyclerView) {
with(view) {
layoutManager = when {
columnCount <= 1 -> LinearLayoutManager(context)
else -> GridLayoutManager(context, columnCount)
}
adapter = ProfilePostsRecyclerViewAdapter(context!!)
}
}
return view
}
}

View File

@ -0,0 +1,51 @@
package com.h.pixeldroid.fragments
import android.content.Context
import android.content.Intent
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.h.pixeldroid.PostActivity
import com.h.pixeldroid.R
import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.ImageConverter.Companion.setSquareImageFromURL
/**
* [RecyclerView.Adapter] that can display a list of [PostMiniature]s and makes a call to the
* specified [OnListFragmentInteractionListener].
*/
class ProfilePostsRecyclerViewAdapter(
private val context: Context
) : RecyclerView.Adapter<ProfilePostsRecyclerViewAdapter.ViewHolder>() {
private val posts: ArrayList<Status> = ArrayList()
fun addPosts(newPosts : List<Status>) {
val size = posts.size
posts.addAll(newPosts)
notifyItemRangeInserted(size, newPosts.size)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_profile_posts, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val post = posts[position]
setSquareImageFromURL(holder.postView, post.getPostPreviewURL(), holder.postPreview)
holder.postPreview.setOnClickListener {
val intent = Intent(context, PostActivity::class.java)
intent.putExtra(Status.POST_TAG, post)
context.startActivity(intent)
}
}
override fun getItemCount(): Int = posts.size
inner class ViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
val postPreview: ImageView = postView.findViewById(R.id.postPreview)
}
}

View File

@ -7,6 +7,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
@ -42,6 +43,7 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
protected lateinit var list : RecyclerView
protected lateinit var adapter : FeedsRecyclerViewAdapter<T, VH>
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
private lateinit var loadingIndicator: ProgressBar
override fun onCreateView(
inflater: LayoutInflater,
@ -51,6 +53,7 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
val view = inflater.inflate(R.layout.fragment_feed, container, false)
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout)
loadingIndicator = view.findViewById(R.id.progressBar)
list = swipeRefreshLayout.list
// Set the adapter
list.layoutManager = LinearLayoutManager(context)
@ -111,7 +114,7 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
Toast.makeText(context,"Something went wrong while loading", Toast.LENGTH_SHORT).show()
}
swipeRefreshLayout.isRefreshing = false
progressBar.visibility = View.GONE
loadingIndicator.visibility = View.GONE
}
override fun onFailure(call: Call<List<T>>, t: Throwable) {

View File

@ -97,32 +97,23 @@ class HomeFragment : FeedFragment<Status, HomeFragment.HomeRecyclerViewAdapter.V
val post = getItem(position) ?: return
val metrics = context.resources.displayMetrics
//Limit the height of the different images
holder.profilePic?.maxHeight = metrics.heightPixels
holder.profilePic.maxHeight = metrics.heightPixels
holder.postPic.maxHeight = metrics.heightPixels
//Set the two images
ImageConverter.setRoundImageFromURL(
holder.postView,
post.getProfilePicUrl(),
holder.profilePic!!
)
picRequest.load(post.getPostUrl()).into(holder.postPic)
//Set up the the post
post.setupPost(holder.postView, picRequest, holder.postPic, holder.profilePic)
//Set the image back to a placeholder if the original is too big
if(holder.postPic.height > metrics.heightPixels) {
ImageConverter.setDefaultImage(holder.postView, holder.postPic)
}
//Set the the text views
post.setupPost(holder.postView)
}
/**
* Represents the posts that will be contained within the feed
*/
inner class ViewHolder(val postView: View) : RecyclerView.ViewHolder(postView) {
val profilePic : ImageView? = postView.findViewById(R.id.profilePic)
val profilePic : ImageView = postView.findViewById(R.id.profilePic)
val postPic : ImageView = postView.findViewById(R.id.postPicture)
val username : TextView = postView.findViewById(R.id.username)
val usernameDesc: TextView = postView.findViewById(R.id.usernameDesc)

View File

@ -23,7 +23,9 @@ import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.util.ViewPreloadSizeProvider
import com.h.pixeldroid.PostActivity
import com.h.pixeldroid.ProfileActivity
import com.h.pixeldroid.R
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Notification
import com.h.pixeldroid.objects.Status
import kotlinx.android.synthetic.main.fragment_feed.*
@ -113,8 +115,8 @@ class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.N
return
}
Notification.NotificationType.follow -> {
val url = notification.status?.url ?: notification.account.url
intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
intent = Intent(context, ProfileActivity::class.java)
intent.putExtra(Account.ACCOUNT_TAG, notification.account)
}
}
context.startActivity(intent)

View File

@ -1,5 +1,9 @@
package com.h.pixeldroid.objects
import android.content.Context
import android.content.Intent
import androidx.core.content.ContextCompat.startActivity
import com.h.pixeldroid.ProfileActivity
import java.io.Serializable
/*
@ -33,5 +37,16 @@ data class Account(
val fields: List<Field>? = emptyList(),
val bot: Boolean = false,
val source: Source? = null
) : Serializable
) : Serializable {
companion object {
const val ACCOUNT_TAG = "AccountTag"
}
// Open profile activity with given account
fun openProfile(context: Context) {
val intent = Intent(context, ProfileActivity::class.java)
intent.putExtra(Account.ACCOUNT_TAG, this)
startActivity(context, intent, null)
}
}

View File

@ -0,0 +1,8 @@
package com.h.pixeldroid.objects
import java.io.Serializable
data class Context(
val ancestors : List<Status>,
val descendants : List<Status>
) : Serializable

View File

@ -1,10 +1,10 @@
package com.h.pixeldroid.objects
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.TextView
import androidx.fragment.app.Fragment
import com.h.pixeldroid.R
import android.widget.ImageView
import com.bumptech.glide.RequestBuilder
import com.h.pixeldroid.utils.ImageConverter
import kotlinx.android.synthetic.main.post_fragment.view.*
import java.io.Serializable
@ -56,8 +56,9 @@ data class Status(
const val POST_FRAG_TAG = "postFragTag"
}
fun getPostUrl() : String? = media_attachments?.getOrNull(0)?.url
fun getProfilePicUrl() : String? = account?.avatar
fun getPostUrl() : String? = media_attachments.getOrNull(0)?.url
fun getProfilePicUrl() : String? = account.avatar
fun getPostPreviewURL() : String? = media_attachments.getOrNull(0)?.preview_url
fun getDescription() : CharSequence {
val description = content as CharSequence
@ -85,10 +86,16 @@ data class Status(
return "$nShares Shares"
}
fun setupPost(rootView : View) {
fun setupPost(
rootView: View,
request: RequestBuilder<Drawable>,
postPic: ImageView,
profilePic: ImageView
) {
//Setup username as a button that opens the profile
rootView.username.text = this.getUsername()
rootView.username.setTypeface(null, Typeface.BOLD)
rootView.username.setOnClickListener { account.openProfile(rootView.context) }
rootView.usernameDesc.text = this.getUsername()
rootView.usernameDesc.setTypeface(null, Typeface.BOLD)
@ -101,7 +108,15 @@ data class Status(
rootView.nshares.text = this.getNShares()
rootView.nshares.setTypeface(null, Typeface.BOLD)
request.load(this.getPostUrl()).into(postPic)
ImageConverter.setRoundImageFromURL(
rootView,
this.getProfilePicUrl(),
profilePic
)
profilePic.setOnClickListener { account.openProfile(rootView.context) }
}
enum class Visibility : Serializable {
public, unlisted, private, direct
}

View File

@ -62,6 +62,16 @@ class ImageConverter {
.placeholder(R.drawable.ic_default_user).into(image)
}
/**
* @brief Loads a given image (via url) as a square image into a given image view
* @param view, the view in which this is happening
* @param url, the url of the image that will be loaded
* @param image, the imageView into which we will load the image
*/
fun setSquareImageFromURL(view : View, url : String?, image : ImageView) {
Glide.with(view).load(url).apply(RequestOptions().centerCrop()).into(image)
}
/**
* @brief Loads a default image into a given image view
* @param view, the view in which this is happening

View File

@ -0,0 +1,161 @@
<?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="match_parent"
tools:context=".ProfileActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<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"
android:layout_margin="20dp">
<ImageView
android:id="@+id/profilePictureImageView"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_weight="1"
tools:srcCompat="@tools:sample/avatars" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="10"
android:orientation="horizontal">
<TextView
android:id="@+id/nbPostsTextView"
android:layout_width="15dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="-\nPosts" />
<TextView
android:id="@+id/nbFollowersTextView"
android:layout_width="15dp"
android:layout_height="120dp"
android:layout_weight="1"
android:gravity="center"
android:text="-\nFollowers" />
<TextView
android:id="@+id/nbFollowingTextView"
android:layout_width="15dp"
android:layout_height="120dp"
android:layout_weight="1"
android:gravity="center"
android:text="-\nFollowing" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp">
<TextView
android:id="@+id/accountNameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="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_marginTop="5dp"
android:layout_marginBottom="10dp"
android:layout_marginRight="20dp"
android:layout_marginLeft="20dp">
<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_marginRight="50dp"
android:layout_marginLeft="50dp"
android:layout_marginBottom="15dp">
<Button
android:id="@+id/followButton"
android:layout_width="100dp"
android:layout_height="30dp"
android:background="@color/browser_actions_divider_color"
android:text="Follow"
android:textColor="@color/colorPrimary"
app:layout_constraintStart_toStartOf="parent"
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:id="@+id/linearLayout2"
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/colorPrimary"
android:src="@android:drawable/ic_dialog_dialer" />
<ImageButton
android:id="@+id/collectionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@color/colorPrimary"
android:src="@android:drawable/ic_menu_gallery" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/profilePostsRecyclerView"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="16dp"
app:layoutManager="LinearLayoutManager"
tools:context=".fragments.MyProfileFragment"
tools:listitem="@layout/fragment_profile_posts"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -16,8 +16,6 @@
android:name="com.h.pixeldroid.FeedFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:listitem="@layout/post_fragment" />

View File

@ -10,114 +10,164 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/profilePictureImageView"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="230dp"
android:layout_height="120dp"
android:layout_marginTop="24dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/profilePictureImageView"
app:layout_constraintTop_toTopOf="parent">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/nbPostsTextView"
android:layout_width="15dp"
<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"
android:layout_margin="20dp">
<ImageView
android:id="@+id/profilePictureImageView"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_weight="1"
tools:srcCompat="@tools:sample/avatars" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="10"
android:orientation="horizontal">
<TextView
android:id="@+id/nbPostsTextView"
android:layout_width="15dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="-\nPosts" />
<TextView
android:id="@+id/nbFollowersTextView"
android:layout_width="15dp"
android:layout_height="120dp"
android:layout_weight="1"
android:gravity="center"
android:text="-\nFollowers" />
<TextView
android:id="@+id/nbFollowingTextView"
android:layout_width="15dp"
android:layout_height="120dp"
android:layout_weight="1"
android:gravity="center"
android:text="-\nFollowing" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp">
<TextView
android:id="@+id/accountNameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="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_marginTop="5dp"
android:layout_marginBottom="10dp"
android:layout_marginRight="20dp"
android:layout_marginLeft="20dp">
<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_marginRight="50dp"
android:layout_marginLeft="50dp"
android:layout_marginBottom="15dp">
<Button
android:id="@+id/editButton"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@color/colorPrimary"
android:text="Edit profile"
android:textColor="@color/cardview_light_background"
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:id="@+id/linearLayout2"
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/colorPrimary"
android:src="@android:drawable/ic_dialog_dialer" />
<ImageButton
android:id="@+id/collectionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@color/colorPrimary"
android:src="@android:drawable/ic_menu_gallery" />
<ImageButton
android:id="@+id/bookmarkButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@color/colorPrimary"
android:src="@android:drawable/ic_input_get" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="-\nPosts" />
<TextView
android:id="@+id/nbFollowersTextView"
android:layout_width="15dp"
android:layout_height="120dp"
android:layout_weight="1"
android:gravity="center"
android:text="-\nFollowers" />
<TextView
android:id="@+id/nbFollowingTextView"
android:layout_width="15dp"
android:layout_height="120dp"
android:layout_weight="1"
android:gravity="center"
android:text="-\nFollowing" />
android:id="@+id/myProfilePostsRecyclerView"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
app:layoutManager="LinearLayoutManager"
tools:context=".fragments.MyProfileFragment"
tools:listitem="@layout/fragment_profile_posts"/>
</LinearLayout>
<TextView
android:id="@+id/accountNameTextView"
android:layout_width="156dp"
android:layout_height="22dp"
android:layout_marginTop="15dp"
android:text="No Username"
app:layout_constraintStart_toStartOf="@+id/profilePictureImageView"
app:layout_constraintTop_toBottomOf="@+id/profilePictureImageView" />
<TextView
android:id="@+id/descriptionTextView"
android:layout_width="348dp"
android:layout_height="85dp"
android:layout_marginTop="14dp"
android:autoSizeMaxTextSize="300sp"
android:autoSizeMinTextSize="2sp"
android:autoSizeStepGranularity="2sp"
android:autoSizeTextType="uniform"
app:layout_constraintStart_toStartOf="@+id/accountNameTextView"
app:layout_constraintTop_toBottomOf="@+id/accountNameTextView" />
<Button
android:id="@+id/editButton"
android:layout_width="350dp"
android:layout_height="37dp"
android:layout_marginTop="14dp"
android:gravity="center"
android:text="Edit profile"
android:background="@color/colorPrimary"
android:textColor="@color/cardview_light_background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/descriptionTextView" />
<LinearLayout
android:layout_width="409dp"
android:layout_height="50dp"
android:layout_marginTop="340dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="@+id/postsButton"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_weight="1"
android:background="@color/cardview_shadow_end_color"
android:gravity="right"
app:srcCompat="@android:drawable/ic_dialog_dialer" />
<ImageButton
android:id="@+id/collectionButton"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_weight="1"
android:background="@color/cardview_shadow_end_color"
android:gravity="left"
app:srcCompat="@android:drawable/ic_menu_gallery" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<ImageView
android:id="@+id/postPreview"
android:layout_width="100dp"
android:layout_height="100dp" />
</androidx.cardview.widget.CardView>
</GridLayout>

View File

@ -0,0 +1,13 @@
<?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_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context=".fragments.ProfilePostsFragment"
tools:listitem="@layout/fragment_profile_posts" />

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".fragments.PostFragment">
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"
tools:context=".fragments.PostFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"