Comment and like buttons added + a few UI tweaks * WIP posts * WIP posts * trying to add images * trying to add images * Got posts working and linked them to the profile * added tests for Post * layout changes * moved a test file * refactoring * refactoring * removed wrong annotation in unit test * removed an import that was breaking the build * removed tests that broke from merge, will override with master * added UI test for the post activity * WIP posts * WIP posts * trying to add images * trying to add images * Got posts working and linked them to the profile * layout changes * refactoring * refactoring * WIP posts * WIP posts * trying to add images * trying to add images * Got posts working and linked them to the profile * added tests for Post * layout changes * moved a test file * refactoring * refactoring * removed wrong annotation in unit test * removed an import that was breaking the build * removed tests that broke from merge, will override with master * fixed merging errors * trying my best to merge * removed drawable definition in activity_post.xml * Started converting Post to a fragment * got a working feed * WI * removed non-valid test * rebase on other branch * moved the feed to the home page * Add tests * delete test for now * Adapt test to changes (no more profile from drawer) * Add unit test for api * Add test for profile, refactor to allow testing, add exception to security policy to allow tests * Adapt test to new situation * Fix typo due to change * refactor somewhat * added a feed test * WIP posts * trying to add images * WIP posts * trying to add images * Got posts working and linked them to the profile * added tests for Post * layout changes * moved a test file * refactoring * refactoring * WIP posts * WIP posts * trying to add images * trying to add images * Got posts working and linked them to the profile * added tests for Post * layout changes * moved a test file * refactoring * refactoring * removed wrong annotation in unit test * removed an import that was breaking the build * removed tests that broke from merge, will override with master * added UI test for the post activity * WIP posts * trying to add images * WIP posts * trying to add images * Got posts working and linked them to the profile * added tests for Post * layout changes * moved a test file * refactoring * refactoring * WIP posts * WIP posts * trying to add images * trying to add images * Got posts working and linked them to the profile * added tests for Post * layout changes * moved a test file * refactoring * refactoring * removed wrong annotation in unit test * removed an import that was breaking the build * removed tests that broke from merge, will override with master * added UI test for the post activity * fixed merging errors * trying my best to merge * removed drawable definition in activity_post.xml * Started converting Post to a fragment * got a working feed * WI * removed non-valid test * WIP posts * WIP posts * trying to add images * trying to add images * Got posts working and linked them to the profile * added tests for Post * layout changes * moved a test file * WIP posts * WIP posts * trying to add images * trying to add images * Got posts working and linked them to the profile * added tests for Post * layout changes * moved a test file * refactoring * refactoring * refactoring * refactoring * removed wrong annotation in unit test * WIP posts * WIP posts * WIP posts * WIP posts * trying to add images * trying to add images * trying to add images * trying to add images * Got posts working and linked them to the profile * Got posts working and linked them to the profile * added tests for Post * layout changes * layout changes * moved a test file * refactoring * refactoring * refactoring * refactoring * removed wrong annotation in unit test * removed an import that was breaking the build * removed an import that was breaking the build * removed tests that broke from merge, will override with master * removed tests that broke from merge, will override with master * added UI test for the post activity * fixed merging errors * trying my best to merge * removed drawable definition in activity_post.xml * Started converting Post to a fragment * got a working feed * WI * removed non-valid test * rebase on other branch * moved the feed to the home page * added a feed test * added a working feed test * fixed broken test * merged with master * added a max height for images and made profile pictures round * Added a default image for the post * created a PostActivity to look a single posts * fixed buggy postActivity * Complete overhall of the feed UI * removed test that didn't please Travis * removed legacy test * changed feedAdapter init location (outside of network callback) * changed the feed from public timeline to home timeline * Refactored myProfile page * Converted profile picture to round image * restored feed test * I can like a post, but unlike is still a WIP * Liking kind of works now and added tests * fixed an error, now we can unlike as well * fixed travis constraint error * Display user's posts on profile page * moved test to Mock server tests * fixed test * last resort debugging * Changed fixed size of profile posts * last resort debugging * last resort debugging * last resort debugging * made post_activity profilepic round * Total refactor of profile posts * still have a weird bug with the comments: input is always null (WIP) * still trying to fix coments * removed annoying side margins in the home feed * trying to fix comments * fixed null comment * converted all posts back to statuses and got rid of post * Refactored recycler view * Merged with my-profile * Posts displayed on profile page * Added links to profile activity where needed * fixed comment posting * finished implementing comments, but api is buggy so none are visible * removed useless space in profile page * fixed ci config bug * trying to trigger ci hook (github was down last time) * updated tests with master tests * added tests for the comments * added tests for the comments * added first() matcher to fix comment test * still trying to fix comment tests' null progress bar * getting rid of that null progress bar * added comment test * fixed merge error * added like button test * added more post tests * took pr coments into account * added back an old test * added mockServer response for comment test and fixed comment null pointer bug * changed notification UI to better separate notifications * added mockserver response for likes and corrected like toggling error * added a test for posting comments * fixed typo in test * a gift for code climate * refactored stuff * fixed broken imports * comment refactored as xml Co-authored-by: Matthieu <61561059+Wv5twkFEKh54vo4tta9yu7dHa3@users.noreply.github.com> Co-authored-by: mjaillot <marie.jaillot@epfl.ch>
This commit is contained in:
parent
8af19cbd9a
commit
f8d6c67079
|
@ -2,7 +2,6 @@
|
|||
|
||||
# Originally written by Ralf Kistner <ralf@embarkmobile.com>, but placed in the public domain
|
||||
|
||||
#set -x
|
||||
set +e
|
||||
|
||||
bootanim=""
|
||||
|
|
|
@ -77,7 +77,7 @@ dependencies {
|
|||
}
|
||||
|
||||
implementation "com.github.bumptech.glide:okhttp-integration:4.11.0"
|
||||
implementation ("com.github.bumptech.glide:recyclerview-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.
|
||||
transitive = false
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ dependencies {
|
|||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.2'
|
||||
implementation 'androidx.paging:paging-runtime-ktx:2.1.1'
|
||||
implementation 'androidx.paging:paging-runtime-ktx:2.1.2'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
|
||||
|
||||
}
|
||||
|
|
|
@ -1,22 +1,34 @@
|
|||
package com.h.pixeldroid
|
||||
|
||||
import android.R.attr.x
|
||||
import android.content.Context
|
||||
import android.text.Editable
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.core.text.set
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions
|
||||
import androidx.test.espresso.UiController
|
||||
import androidx.test.espresso.ViewAction
|
||||
import androidx.test.espresso.action.*
|
||||
import androidx.test.espresso.action.ViewActions.swipeDown
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.contrib.DrawerActions
|
||||
import androidx.test.espresso.contrib.DrawerMatchers
|
||||
import androidx.test.espresso.contrib.NavigationViewActions
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition
|
||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.h.pixeldroid.fragments.feeds.HomeFragment
|
||||
import com.h.pixeldroid.fragments.feeds.ViewHolder
|
||||
import com.h.pixeldroid.testUtility.MockServer
|
||||
import org.hamcrest.BaseMatcher
|
||||
import org.hamcrest.Matcher
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
@ -26,9 +38,88 @@ import org.junit.runner.RunWith
|
|||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MockedServerTest {
|
||||
private fun <T> first(matcher: Matcher<T>): Matcher<T>? {
|
||||
return object : BaseMatcher<T>() {
|
||||
var isFirst = true
|
||||
override fun describeTo(description: org.hamcrest.Description?) {
|
||||
description?.appendText("first matching item")
|
||||
}
|
||||
|
||||
override fun matches(item: Any?): Boolean {
|
||||
if (isFirst && matcher.matches(item)) {
|
||||
isFirst = false
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param percent can be 1 or 0
|
||||
* 1: swipes all the way up
|
||||
* 0: swipes half way up
|
||||
*/
|
||||
private fun slowSwipeUp(percent: Boolean) : ViewAction {
|
||||
return ViewActions.actionWithAssertions(
|
||||
GeneralSwipeAction(
|
||||
Swipe.SLOW,
|
||||
GeneralLocation.BOTTOM_CENTER,
|
||||
if(percent) GeneralLocation.TOP_CENTER else GeneralLocation.CENTER,
|
||||
Press.FINGER)
|
||||
)
|
||||
}
|
||||
|
||||
fun getText(matcher: Matcher<View?>?): String? {
|
||||
val stringHolder = arrayOf<String?>(null)
|
||||
onView(matcher).perform(object : ViewAction {
|
||||
override fun getConstraints(): Matcher<View> {
|
||||
return isAssignableFrom(TextView::class.java)
|
||||
}
|
||||
|
||||
override fun getDescription(): String {
|
||||
return "getting text from a TextView"
|
||||
}
|
||||
|
||||
override fun perform(
|
||||
uiController: UiController,
|
||||
view: View
|
||||
) {
|
||||
val tv = view as TextView //Save, because of check in getConstraints()
|
||||
stringHolder[0] = tv.text.toString()
|
||||
}
|
||||
})
|
||||
return stringHolder[0]
|
||||
}
|
||||
|
||||
private fun clickChildViewWithId(id: Int) = object : ViewAction {
|
||||
|
||||
override fun getConstraints() = null
|
||||
|
||||
override fun getDescription() = "click child view with id $id"
|
||||
|
||||
override fun perform(uiController: UiController, view: View) {
|
||||
val v = view.findViewById<View>(id)
|
||||
v.performClick()
|
||||
}
|
||||
}
|
||||
|
||||
private fun typeTextInViewWithId(id: Int, text: String) = object : ViewAction {
|
||||
|
||||
override fun getConstraints() = null
|
||||
|
||||
override fun getDescription() = "click child view with id $id"
|
||||
|
||||
override fun perform(uiController: UiController, view: View) {
|
||||
val v = view.findViewById<EditText>(id)
|
||||
v.text.append(text)
|
||||
}
|
||||
}
|
||||
|
||||
val mockServer = MockServer()
|
||||
|
||||
|
||||
@get:Rule
|
||||
var globalTimeout: Timeout = Timeout.seconds(100)
|
||||
@get:Rule
|
||||
|
@ -122,13 +213,13 @@ class MockedServerTest {
|
|||
// Open Drawer to click on navigation.
|
||||
onView(withId(R.id.drawer_layout))
|
||||
.check(matches(DrawerMatchers.isClosed(Gravity.LEFT))) // Left Drawer should be closed.
|
||||
.perform(DrawerActions.open()); // Open Drawer
|
||||
.perform(DrawerActions.open()) // Open Drawer
|
||||
|
||||
// Start the screen of your activity.
|
||||
onView(withId(R.id.nav_view)).perform(NavigationViewActions.navigateTo(R.id.nav_settings))
|
||||
|
||||
// Check that settings activity was opened.
|
||||
onView(withText(R.string.signature_title)).check(matches(ViewMatchers.isDisplayed()))
|
||||
onView(withText(R.string.signature_title)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -139,7 +230,7 @@ class MockedServerTest {
|
|||
.perform(ViewActions.swipeLeft()) // notifications
|
||||
.perform(ViewActions.swipeLeft()) // profile
|
||||
.perform(ViewActions.swipeLeft()) // should stop at profile
|
||||
onView(withId(R.id.nbFollowersTextView)).check(matches(ViewMatchers.isDisplayed()))
|
||||
onView(withId(R.id.nbFollowersTextView)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -153,6 +244,116 @@ class MockedServerTest {
|
|||
.perform(ViewActions.swipeRight()) // search
|
||||
.perform(ViewActions.swipeRight()) // homepage
|
||||
.perform(ViewActions.swipeRight()) // should stop at homepage
|
||||
onView(withId(R.id.list)).check(matches(ViewMatchers.isDisplayed()))
|
||||
onView(withId(R.id.list)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clickingLikeButtonWorks() {
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
Thread.sleep(1000)
|
||||
|
||||
//Get initial like count
|
||||
val likes = getText(withId(R.id.nlikes))
|
||||
|
||||
//Like the post
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<ViewHolder>
|
||||
(0, clickChildViewWithId(R.id.liker)))
|
||||
Thread.sleep(100)
|
||||
//Unlike the post
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<ViewHolder>
|
||||
(0, clickChildViewWithId(R.id.liker)))
|
||||
//...
|
||||
Thread.sleep(100)
|
||||
|
||||
//Profit
|
||||
onView(withId(R.id.nlikes)).check(matches((withText(likes))))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clickingUsernameOpensProfile() {
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
Thread.sleep(1000)
|
||||
|
||||
//Get initial like count
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<ViewHolder>
|
||||
(0, clickChildViewWithId(R.id.username)))
|
||||
|
||||
Thread.sleep(1000)
|
||||
|
||||
//Check that the Profile opened
|
||||
onView(withId(R.id.accountNameTextView)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clickingProfilePicOpensProfile() {
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
Thread.sleep(1000)
|
||||
|
||||
//Get initial like count
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<ViewHolder>
|
||||
(0, clickChildViewWithId(R.id.profilePic)))
|
||||
|
||||
Thread.sleep(1000)
|
||||
|
||||
//Check that the Profile opened
|
||||
onView(withId(R.id.accountNameTextView)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clickingCommentButtonOpensCommentSection() {
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
Thread.sleep(1000)
|
||||
//Click comment button and then try to see if the commenter exists
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<ViewHolder>
|
||||
(0, clickChildViewWithId(R.id.commenter)))
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.commentIn))
|
||||
.check(matches(hasDescendant(withId(R.id.editComment))))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clickingViewCommentShowsTheComments() {
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
Thread.sleep(1000)
|
||||
//Open the comment section
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<ViewHolder>
|
||||
(0, clickChildViewWithId(R.id.ViewComments)))
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.commentContainer))
|
||||
.check(matches(hasDescendant(withId(R.id.comment))))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun postingACommentWorks() {
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
Thread.sleep(1000)
|
||||
|
||||
//Open the comment section
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<ViewHolder>
|
||||
(0, clickChildViewWithId(R.id.commenter)))
|
||||
|
||||
onView(withId(R.id.list)).perform(slowSwipeUp(true))
|
||||
onView(withId(R.id.list)).perform(slowSwipeUp(false))
|
||||
onView(withId(R.id.list)).perform(slowSwipeUp(false))
|
||||
Thread.sleep(1000)
|
||||
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<ViewHolder>
|
||||
(0, typeTextInViewWithId(R.id.editComment, "test")))
|
||||
onView(withId(R.id.list))
|
||||
.perform(actionOnItemAtPosition<ViewHolder>
|
||||
(0, clickChildViewWithId(R.id.submitComment)))
|
||||
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.commentContainer))
|
||||
.check(matches(hasDescendant(withId(R.id.comment))))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -3,8 +3,10 @@ package com.h.pixeldroid
|
|||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.h.pixeldroid.fragments.PostFragment
|
||||
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
|
||||
|
||||
|
||||
|
||||
class PostActivity : AppCompatActivity() {
|
||||
lateinit var postFragment : PostFragment
|
||||
|
|
|
@ -60,7 +60,7 @@ interface PixelfedAPI {
|
|||
//Used in our case to post a comment
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/statuses")
|
||||
fun commentStatus(
|
||||
fun postStatus(
|
||||
//The authorization header needs to be of the form "Bearer <token>"
|
||||
@Header("Authorization") authorization: String,
|
||||
@Field("status") statusText : String,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.h.pixeldroid.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
|
@ -8,9 +9,14 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.bumptech.glide.Glide
|
||||
import com.h.pixeldroid.BuildConfig
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.fragments.feeds.HomeFragment
|
||||
import com.h.pixeldroid.fragments.feeds.ViewHolder
|
||||
import com.h.pixeldroid.objects.Status
|
||||
|
||||
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
|
||||
import kotlinx.android.synthetic.main.post_fragment.view.*
|
||||
|
||||
|
||||
|
@ -27,6 +33,20 @@ class PostFragment : Fragment() {
|
|||
.placeholder(ColorDrawable(Color.GRAY))
|
||||
|
||||
status?.setupPost(root, picRequest, root.postPicture, root.profilePic)
|
||||
|
||||
//Setup arguments needed for the onclicklisteners
|
||||
val holder = ViewHolder(root, context!!)
|
||||
val preferences = requireActivity().getSharedPreferences(
|
||||
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
val accessToken = preferences.getString("accessToken", "")
|
||||
val api = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
|
||||
//Activate onclickListeners
|
||||
status?.activateLiker(holder, api, "Bearer $accessToken")
|
||||
status?.activateCommenter(holder, api, "Bearer $accessToken")
|
||||
status?.showComments(holder, api, "Bearer $accessToken")
|
||||
|
||||
return root
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ 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
|
||||
|
|
|
@ -41,7 +41,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
|
||||
protected lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
||||
private lateinit var loadingIndicator: ProgressBar
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -51,11 +51,16 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
|
|||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_feed, container, false)
|
||||
|
||||
//Initialize lateinit fields that are needed as soon as the view is created
|
||||
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout)
|
||||
loadingIndicator = view.findViewById(R.id.progressBar)
|
||||
list = swipeRefreshLayout.list
|
||||
// Set the adapter
|
||||
preferences = requireActivity().getSharedPreferences(
|
||||
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
list.layoutManager = LinearLayoutManager(context)
|
||||
pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
accessToken = preferences.getString("accessToken", "")
|
||||
|
||||
return view
|
||||
}
|
||||
|
@ -63,13 +68,6 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
preferences = requireActivity().getSharedPreferences(
|
||||
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
|
||||
pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
accessToken = preferences.getString("accessToken", "")
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
//by invalidating data, loadInitial will be called again
|
||||
factory.liveData.value!!.invalidate()
|
||||
|
@ -77,6 +75,7 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
|
|||
|
||||
}
|
||||
|
||||
|
||||
inner class FeedDataSource(private val makeInitialCall: (Int) -> Call<List<T>>,
|
||||
private val makeAfterCall: (Int, String) -> Call<List<T>>
|
||||
): ItemKeyedDataSource<String, T>() {
|
||||
|
@ -104,11 +103,13 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
|
|||
}
|
||||
|
||||
private fun enqueueCall(call: Call<List<T>>, callback: LoadCallback<T>){
|
||||
|
||||
call.enqueue(object : Callback<List<T>> {
|
||||
override fun onResponse(call: Call<List<T>>, response: Response<List<T>>) {
|
||||
if (response.code() == 200) {
|
||||
val notifications = response.body()!! as ArrayList<T>
|
||||
callback.onResult(notifications as List<T>)
|
||||
|
||||
} else{
|
||||
Toast.makeText(context,"Something went wrong while loading", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.*
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.paging.LivePagedListBuilder
|
||||
|
@ -21,12 +20,10 @@ import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader
|
|||
import com.bumptech.glide.util.ViewPreloadSizeProvider
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import kotlinx.android.synthetic.main.fragment_home.*
|
||||
import retrofit2.Call
|
||||
|
||||
|
||||
class HomeFragment : FeedFragment<Status, HomeFragment.HomeRecyclerViewAdapter.ViewHolder>() {
|
||||
class HomeFragment : FeedFragment<Status, ViewHolder>() {
|
||||
|
||||
lateinit var picRequest: RequestBuilder<Drawable>
|
||||
|
||||
|
@ -36,8 +33,6 @@ class HomeFragment : FeedFragment<Status, HomeFragment.HomeRecyclerViewAdapter.V
|
|||
): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
content = makeContent()
|
||||
|
||||
//RequestBuilder that is re-used for every image
|
||||
picRequest = Glide.with(this)
|
||||
.asDrawable().fitCenter()
|
||||
|
@ -46,12 +41,6 @@ class HomeFragment : FeedFragment<Status, HomeFragment.HomeRecyclerViewAdapter.V
|
|||
adapter = HomeRecyclerViewAdapter()
|
||||
list.adapter = adapter
|
||||
|
||||
content.observe(viewLifecycleOwner,
|
||||
Observer { c ->
|
||||
adapter.submitList(c)
|
||||
//after a refresh is done we need to stop the pull to refresh spinner
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
})
|
||||
|
||||
//Make Glide be aware of the recyclerview and pre-load images
|
||||
val sizeProvider: ListPreloader.PreloadSizeProvider<Status> = ViewPreloadSizeProvider()
|
||||
|
@ -63,6 +52,17 @@ class HomeFragment : FeedFragment<Status, HomeFragment.HomeRecyclerViewAdapter.V
|
|||
return view
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
content = makeContent()
|
||||
content.observe(viewLifecycleOwner,
|
||||
Observer { c ->
|
||||
adapter.submitList(c)
|
||||
//after a refresh is done we need to stop the pull to refresh spinner
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
})
|
||||
}
|
||||
|
||||
private fun makeContent(): LiveData<PagedList<Status>> {
|
||||
fun makeInitialCall(requestedLoadSize: Int): Call<List<Status>> {
|
||||
return pixelfedAPI
|
||||
|
@ -81,13 +81,15 @@ class HomeFragment : FeedFragment<Status, HomeFragment.HomeRecyclerViewAdapter.V
|
|||
/**
|
||||
* [RecyclerView.Adapter] that can display a list of Statuses
|
||||
*/
|
||||
inner class HomeRecyclerViewAdapter: FeedsRecyclerViewAdapter<Status, HomeRecyclerViewAdapter.ViewHolder>() {
|
||||
|
||||
inner class HomeRecyclerViewAdapter()
|
||||
: FeedsRecyclerViewAdapter<Status, ViewHolder>() {
|
||||
private val api = pixelfedAPI
|
||||
private val credential = "Bearer $accessToken"
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.post_fragment, parent, false)
|
||||
context = view.context
|
||||
return ViewHolder(view)
|
||||
return ViewHolder(view, context)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,26 +102,20 @@ class HomeFragment : FeedFragment<Status, HomeFragment.HomeRecyclerViewAdapter.V
|
|||
holder.profilePic.maxHeight = metrics.heightPixels
|
||||
holder.postPic.maxHeight = metrics.heightPixels
|
||||
|
||||
//Set up the the post
|
||||
//Setup the post layout
|
||||
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 initial favorite toggle value
|
||||
holder.isLiked = post.favourited
|
||||
|
||||
/**
|
||||
* 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 postPic : ImageView = postView.findViewById(R.id.postPicture)
|
||||
val username : TextView = postView.findViewById(R.id.username)
|
||||
val usernameDesc: TextView = postView.findViewById(R.id.usernameDesc)
|
||||
val description : TextView = postView.findViewById(R.id.description)
|
||||
val nlikes : TextView = postView.findViewById(R.id.nlikes)
|
||||
val nshares : TextView = postView.findViewById(R.id.nshares)
|
||||
//Activate liker
|
||||
post.activateLiker(holder, api, credential)
|
||||
|
||||
//Show comments
|
||||
post.showComments(holder, api, credential)
|
||||
|
||||
//Activate Commenter
|
||||
post.activateCommenter(holder, api, credential)
|
||||
}
|
||||
|
||||
override fun getPreloadItems(position: Int): MutableList<Status> {
|
||||
|
@ -132,3 +128,24 @@ class HomeFragment : FeedFragment<Status, HomeFragment.HomeRecyclerViewAdapter.V
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the posts that will be contained within the feed
|
||||
*/
|
||||
class ViewHolder(val postView: View, val context: android.content.Context) : RecyclerView.ViewHolder(postView) {
|
||||
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)
|
||||
val description : TextView = postView.findViewById(R.id.description)
|
||||
val nlikes : TextView = postView.findViewById(R.id.nlikes)
|
||||
val nshares : TextView = postView.findViewById(R.id.nshares)
|
||||
val liker : ImageView = postView.findViewById(R.id.liker)
|
||||
val submitCmnt : ImageButton = postView.findViewById(R.id.submitComment)
|
||||
val commenter : ImageView = postView.findViewById(R.id.commenter)
|
||||
val comment : EditText = postView.findViewById(R.id.editComment)
|
||||
val commentCont : LinearLayout = postView.findViewById(R.id.commentContainer)
|
||||
val commentIn : LinearLayout = postView.findViewById(R.id.commentIn)
|
||||
val viewComment : TextView = postView.findViewById(R.id.ViewComments)
|
||||
var isLiked : Boolean = false
|
||||
}
|
||||
|
|
|
@ -46,8 +46,6 @@ class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.N
|
|||
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
content = makeContent()
|
||||
|
||||
//RequestBuilder that is re-used for every image
|
||||
profilePicRequest = Glide.with(this)
|
||||
.asDrawable().apply(RequestOptions().circleCrop())
|
||||
|
@ -57,12 +55,6 @@ class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.N
|
|||
adapter = NotificationsRecyclerViewAdapter()
|
||||
list.adapter = adapter
|
||||
|
||||
content.observe(viewLifecycleOwner,
|
||||
Observer { c ->
|
||||
adapter.submitList(c)
|
||||
//after a refresh is done we need to stop the pull to refresh spinner
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
})
|
||||
|
||||
//Make Glide be aware of the recyclerview and pre-load images
|
||||
val sizeProvider: ListPreloader.PreloadSizeProvider<Notification> = ViewPreloadSizeProvider()
|
||||
|
@ -74,6 +66,18 @@ class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.N
|
|||
return view
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
content = makeContent()
|
||||
|
||||
content.observe(viewLifecycleOwner,
|
||||
Observer { c ->
|
||||
adapter.submitList(c)
|
||||
//after a refresh is done we need to stop the pull to refresh spinner
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
})
|
||||
}
|
||||
|
||||
private fun makeContent(): LiveData<PagedList<Notification>> {
|
||||
fun makeInitialCall(requestedLoadSize: Int): Call<List<Notification>> {
|
||||
return pixelfedAPI
|
||||
|
|
|
@ -4,9 +4,19 @@ import android.graphics.Typeface
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import com.bumptech.glide.RequestBuilder
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.fragments.feeds.ViewHolder
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import kotlinx.android.synthetic.main.post_fragment.view.*
|
||||
import com.h.pixeldroid.utils.PostUtils.Companion.likePostCall
|
||||
import com.h.pixeldroid.utils.PostUtils.Companion.postComment
|
||||
import com.h.pixeldroid.utils.PostUtils.Companion.retrieveComments
|
||||
import com.h.pixeldroid.utils.PostUtils.Companion.toggleCommentInput
|
||||
import com.h.pixeldroid.utils.PostUtils.Companion.unLikePostCall
|
||||
import java.io.Serializable
|
||||
|
||||
/*
|
||||
|
@ -93,21 +103,26 @@ data class Status(
|
|||
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) }
|
||||
val username = rootView.findViewById<TextView>(R.id.username)
|
||||
username.text = this.getUsername()
|
||||
username.setTypeface(null, Typeface.BOLD)
|
||||
username.setOnClickListener { account.openProfile(rootView.context) }
|
||||
|
||||
rootView.usernameDesc.text = this.getUsername()
|
||||
rootView.usernameDesc.setTypeface(null, Typeface.BOLD)
|
||||
val usernameDesc = rootView.findViewById<TextView>(R.id.usernameDesc)
|
||||
usernameDesc.text = this.getUsername()
|
||||
usernameDesc.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
rootView.description.text = this.getDescription()
|
||||
rootView.findViewById<TextView>(R.id.description).text = this.getDescription()
|
||||
|
||||
rootView.nlikes.text = this.getNLikes()
|
||||
rootView.nlikes.setTypeface(null, Typeface.BOLD)
|
||||
val nlikes = rootView.findViewById<TextView>(R.id.nlikes)
|
||||
nlikes.text = this.getNLikes()
|
||||
nlikes.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
rootView.nshares.text = this.getNShares()
|
||||
rootView.nshares.setTypeface(null, Typeface.BOLD)
|
||||
val nshares = rootView.findViewById<TextView>(R.id.nshares)
|
||||
nshares.text = this.getNShares()
|
||||
nshares.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
//Setup images
|
||||
request.load(this.getPostUrl()).into(postPic)
|
||||
ImageConverter.setRoundImageFromURL(
|
||||
rootView,
|
||||
|
@ -115,6 +130,67 @@ data class Status(
|
|||
profilePic
|
||||
)
|
||||
profilePic.setOnClickListener { account.openProfile(rootView.context) }
|
||||
|
||||
//Set comment initial visibility
|
||||
rootView.findViewById<LinearLayout>(R.id.commentIn).visibility = View.GONE
|
||||
}
|
||||
|
||||
fun activateLiker(
|
||||
holder : ViewHolder,
|
||||
api: PixelfedAPI,
|
||||
credential: String
|
||||
) {
|
||||
//Activate the liker
|
||||
holder.liker.setOnClickListener {
|
||||
if (holder.isLiked) {
|
||||
//Unlike the post
|
||||
unLikePostCall(holder, api, credential, this)
|
||||
} else {
|
||||
//like the post
|
||||
likePostCall(holder, api, credential, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showComments(
|
||||
holder : ViewHolder,
|
||||
api: PixelfedAPI,
|
||||
credential: String
|
||||
) {
|
||||
//Show all comments of a post
|
||||
if (replies_count == 0) {
|
||||
holder.viewComment.text = "No comments on this post..."
|
||||
} else {
|
||||
holder.viewComment.text = "View all ${replies_count} comments..."
|
||||
holder.viewComment.setOnClickListener {
|
||||
holder.viewComment.visibility = View.GONE
|
||||
|
||||
//Retrieve the comments
|
||||
retrieveComments(holder, api, credential, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun activateCommenter(
|
||||
holder : ViewHolder,
|
||||
api: PixelfedAPI,
|
||||
credential: String
|
||||
) {
|
||||
//Toggle comment button
|
||||
toggleCommentInput(holder)
|
||||
|
||||
//Activate commenter
|
||||
holder.submitCmnt.setOnClickListener {
|
||||
val textIn = holder.comment.text
|
||||
//Open text input
|
||||
if(textIn.isNullOrEmpty()) {
|
||||
Toast.makeText(holder.context,"Comment must not be empty!", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
|
||||
//Post the comment
|
||||
postComment(holder, api, credential, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class Visibility : Serializable {
|
||||
|
|
|
@ -77,8 +77,8 @@ class ImageConverter {
|
|||
* @param view, the view in which this is happening
|
||||
* @param image, the imageView into which we will load the image
|
||||
*/
|
||||
fun setDefaultImage(view : View, image : ImageView) {
|
||||
Glide.with(view).load(R.drawable.ic_default_user).into(image)
|
||||
fun setImageFromDrawable(view : View, image : ImageView, drawable : Int) {
|
||||
Glide.with(view).load(drawable).into(image)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package com.h.pixeldroid.utils
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.cardview.widget.CardView
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.fragments.feeds.ViewHolder
|
||||
import com.h.pixeldroid.objects.Account
|
||||
import com.h.pixeldroid.objects.Context
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import kotlinx.android.synthetic.main.comment.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
class PostUtils {
|
||||
companion object {
|
||||
fun toggleCommentInput(
|
||||
holder : ViewHolder
|
||||
) {
|
||||
//Toggle comment button
|
||||
holder.commenter.setOnClickListener {
|
||||
when(holder.commentIn.visibility) {
|
||||
View.VISIBLE -> holder.commentIn.visibility = View.GONE
|
||||
View.INVISIBLE -> holder.commentIn.visibility = View.VISIBLE
|
||||
View.GONE -> holder.commentIn.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun likePostCall(
|
||||
holder : ViewHolder,
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
post : Status
|
||||
) {
|
||||
api.likePost(credential, post.id).enqueue(object : Callback<Status> {
|
||||
override fun onFailure(call: Call<Status>, t: Throwable) {
|
||||
Log.e("LIKE ERROR", t.toString())
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call<Status>, response: Response<Status>) {
|
||||
if(response.code() == 200) {
|
||||
val resp = response.body()!!
|
||||
|
||||
//Update shown like count and internal like toggle
|
||||
holder.nlikes.text = resp.getNLikes()
|
||||
holder.isLiked = resp.favourited
|
||||
} else {
|
||||
Log.e("RESPOSE_CODE", response.code().toString())
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun unLikePostCall(
|
||||
holder : ViewHolder,
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
post : Status
|
||||
) {
|
||||
api.unlikePost(credential, post.id).enqueue(object : Callback<Status> {
|
||||
override fun onFailure(call: Call<Status>, t: Throwable) {
|
||||
Log.e("UNLIKE ERROR", t.toString())
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call<Status>, response: Response<Status>) {
|
||||
if(response.code() == 200) {
|
||||
val resp = response.body()!!
|
||||
|
||||
//Update shown like count and internal like toggle
|
||||
holder.nlikes.text = resp.getNLikes()
|
||||
holder.isLiked = resp.favourited
|
||||
} else {
|
||||
Log.e("RESPOSE_CODE", response.code().toString())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun postComment(
|
||||
holder : ViewHolder,
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
post : Status
|
||||
) {
|
||||
val textIn = holder.comment.text
|
||||
val nonNullText = textIn.toString()
|
||||
api.postStatus(credential, nonNullText, post.id).enqueue(object :
|
||||
Callback<Status> {
|
||||
override fun onFailure(call: Call<Status>, t: Throwable) {
|
||||
Log.e("COMMENT ERROR", t.toString())
|
||||
Toast.makeText(holder.context,"Comment error!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call<Status>, response: Response<Status>) {
|
||||
//Check that the received response code is valid
|
||||
if(response.code() == 200) {
|
||||
val resp = response.body()!!
|
||||
holder.commentIn.visibility = View.GONE
|
||||
|
||||
//Add the comment to the comment section
|
||||
addComment(holder.context, holder.commentCont, resp.account.username, resp.content)
|
||||
|
||||
Toast.makeText(holder.context,"Comment: \"$textIn\" posted!", Toast.LENGTH_SHORT).show()
|
||||
Log.e("COMMENT SUCCESS", "posted: $textIn")
|
||||
} else {
|
||||
Log.e("ERROR_CODE", response.code().toString())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun addComment(context: android.content.Context, commentContainer: LinearLayout, commentUsername: String, commentContent: String) {
|
||||
|
||||
val view = LayoutInflater.from(context)
|
||||
.inflate(R.layout.comment, commentContainer, true)
|
||||
|
||||
view.user.text = commentUsername
|
||||
view.commentText.text = commentContent
|
||||
}
|
||||
|
||||
fun retrieveComments(
|
||||
holder : ViewHolder,
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
post : Status
|
||||
) {
|
||||
api.statusComments(post.id, credential).enqueue(object :
|
||||
Callback<Context> {
|
||||
override fun onFailure(call: Call<Context>, t: Throwable) {
|
||||
Log.e("COMMENT FETCH ERROR", t.toString())
|
||||
}
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<Context>,
|
||||
response: Response<Context>
|
||||
) {
|
||||
if(response.code() == 200) {
|
||||
val statuses = response.body()!!.descendants
|
||||
|
||||
//Create the new views for each comment
|
||||
for (status in statuses) {
|
||||
addComment(holder.context, holder.commentCont, status.account.username, status.content)
|
||||
}
|
||||
} else {
|
||||
Log.e("COMMENT ERROR", "${response.code()} with body ${response.errorBody()}")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?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"
|
||||
android:layout_margin="20dp">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/comment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/user"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="8"
|
||||
android:textStyle="bold"
|
||||
tools:text="username" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/commentText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
tools:text="This is a comment on this awesome post" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,66 +1,72 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.cardview.widget.CardView 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/notification"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_margin="5dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notification_type"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/notification"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:drawableStart="@drawable/ic_heart"
|
||||
android:drawablePadding="6dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="38dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintLeft_toRightOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/notification_avatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="RtlSymmetry"
|
||||
tools:text="User liked your post"
|
||||
tools:visibility="visible" />
|
||||
android:layout_margin="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/notification_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/notification_type"
|
||||
tools:src="@drawable/ic_default_user" />
|
||||
<TextView
|
||||
android:id="@+id/notification_type"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableStart="@drawable/ic_heart"
|
||||
android:drawablePadding="6dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="38dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintLeft_toRightOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/notification_avatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="RtlSymmetry"
|
||||
tools:text="User liked your post"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/notification_photo_thumbnail"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/notification_type"
|
||||
tools:src="@drawable/ic_default_user"
|
||||
tools:srcCompat="@tools:sample/backgrounds/scenic" />
|
||||
<ImageView
|
||||
android:id="@+id/notification_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/notification_type"
|
||||
tools:src="@drawable/ic_default_user" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notification_post_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/notification_photo_thumbnail"
|
||||
app:layout_constraintHorizontal_bias="0.164"
|
||||
app:layout_constraintStart_toEndOf="@+id/notification_avatar"
|
||||
app:layout_constraintTop_toBottomOf="@+id/notification_type"
|
||||
app:layout_constraintVertical_bias="0.408"
|
||||
tools:text="Post description" />
|
||||
<ImageView
|
||||
android:id="@+id/notification_photo_thumbnail"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/notification_type"
|
||||
tools:src="@drawable/ic_default_user"
|
||||
tools:srcCompat="@tools:sample/backgrounds/scenic" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<TextView
|
||||
android:id="@+id/notification_post_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/notification_photo_thumbnail"
|
||||
app:layout_constraintHorizontal_bias="0.164"
|
||||
app:layout_constraintStart_toEndOf="@+id/notification_avatar"
|
||||
app:layout_constraintTop_toBottomOf="@+id/notification_type"
|
||||
app:layout_constraintVertical_bias="0.408"
|
||||
tools:text="Post description" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
|
|
@ -1,127 +1,200 @@
|
|||
<?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">
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp">
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/profilePic"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:src="@drawable/ic_default_user"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/profilePic"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.516"
|
||||
tools:text="TextView" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraintLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/postPicture"
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/profilePic"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:src="@drawable/ic_default_user"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/profilePic"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.516"
|
||||
tools:text="TextView" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraintLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/postPicture"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/LikeShareConstraint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="10dp"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="409dp"
|
||||
android:layout_height="28dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/liker"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start"
|
||||
android:src="@drawable/ic_heart" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/commenter"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start"
|
||||
android:src="@drawable/ic_add_black_24dp" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nlikes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="30dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_weight="50"
|
||||
tools:text="TextView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nshares"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginRight="30dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_weight="50"
|
||||
android:gravity="right"
|
||||
tools:text="TextView" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/usernameDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
|
||||
tools:text="TextView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
tools:text="TextView" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/commentIn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:orientation="horizontal">
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="3">
|
||||
<EditText
|
||||
android:id="@+id/editComment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:hint="Comment" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
<ImageButton
|
||||
android:id="@+id/submitComment"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:src="@drawable/ic_add_black_24dp"
|
||||
android:layout_weight="1"
|
||||
android:contentDescription="Submit button" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/commentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/ViewComments"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
tools:text="TextView">
|
||||
</TextView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp">
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nlikes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_weight="50"
|
||||
tools:text="TextView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nshares"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_weight="50"
|
||||
android:gravity="end"
|
||||
tools:text="TextView" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/usernameDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
tools:text="TextView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
tools:text="TextView" />
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="30dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:src="@android:drawable/screen_background_dark"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
|
||||
</FrameLayout>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item type="id" name="comment" />
|
||||
</resources>
|
|
@ -8,7 +8,7 @@ buildscript {
|
|||
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.1'
|
||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
|
Loading…
Reference in New Issue