diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 644028492..7cd9a1e8e 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -53,7 +53,6 @@ configurations.all {
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
- implementation(Dependencies.kotlin_stdlib)
implementation(Dependencies.design)
implementation(Dependencies.constraint_layout)
implementation(Dependencies.appCompat)
@@ -78,4 +77,11 @@ dependencies {
implementation(Dependencies.viewPager2)
implementation(Dependencies.swipe_refresh_layout)
+
+ implementation(Dependencies.room_ktx)
+ implementation(Dependencies.room_runtime)
+ kapt(Dependencies.room_compiler)
+ implementation(Dependencies.room_paging)
+
+ implementation(Dependencies.paging3)
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 63009251f..027da5301 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,12 +19,10 @@
android:theme="@style/AppTheme.NoActionBar" />
-
+ android:theme="@style/AppTheme.NoActionBar"
+ android:exported="true">
diff --git a/app/src/main/java/org/anitab/mentorship/Injection.kt b/app/src/main/java/org/anitab/mentorship/Injection.kt
new file mode 100644
index 000000000..ef560c91a
--- /dev/null
+++ b/app/src/main/java/org/anitab/mentorship/Injection.kt
@@ -0,0 +1,20 @@
+package org.anitab.mentorship
+
+import org.anitab.mentorship.local.UserDatabase
+/**
+ * Class that handles object creation.
+ * Like this, objects can be passed as parameters in the constructors and then replaced for
+ * testing, where needed.
+ */
+object Injection {
+
+ /**
+ * Provides context of [MentorshipApplication]
+ */
+ private fun provideApplicationContext() = MentorshipApplication.getContext()
+
+ /**
+ * Provides an instance of [UserDatabase]
+ */
+ fun provideUserDatabase(): UserDatabase = UserDatabase.getInstance(provideApplicationContext())
+}
diff --git a/app/src/main/java/org/anitab/mentorship/local/RemoteKeysDao.kt b/app/src/main/java/org/anitab/mentorship/local/RemoteKeysDao.kt
new file mode 100644
index 000000000..d43cff203
--- /dev/null
+++ b/app/src/main/java/org/anitab/mentorship/local/RemoteKeysDao.kt
@@ -0,0 +1,22 @@
+package org.anitab.mentorship.local
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+
+@Dao
+interface RemoteKeysDao {
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertAll(remoteKey: List)
+
+ @Query("SELECT * FROM remote_keys")
+ suspend fun getRemoteKeys(): List
+
+ @Query("SELECT * FROM remote_keys WHERE userId = :userId")
+ suspend fun remoteKeysRepoId(userId: Int): RemoteKeysEntity?
+
+ @Query("DELETE FROM remote_keys")
+ suspend fun clearRemoteKeys()
+}
diff --git a/app/src/main/java/org/anitab/mentorship/local/RemoteKeysEntity.kt b/app/src/main/java/org/anitab/mentorship/local/RemoteKeysEntity.kt
new file mode 100644
index 000000000..b1f023f85
--- /dev/null
+++ b/app/src/main/java/org/anitab/mentorship/local/RemoteKeysEntity.kt
@@ -0,0 +1,14 @@
+package org.anitab.mentorship.local
+
+import android.os.Parcelable
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import kotlinx.android.parcel.Parcelize
+
+@Entity(tableName = "remote_keys")
+@Parcelize
+data class RemoteKeysEntity(
+ @PrimaryKey val userId: Int?,
+ val prevKey: Int?,
+ val nextKey: Int?
+) : Parcelable
diff --git a/app/src/main/java/org/anitab/mentorship/local/UserDao.kt b/app/src/main/java/org/anitab/mentorship/local/UserDao.kt
new file mode 100644
index 000000000..c431b0424
--- /dev/null
+++ b/app/src/main/java/org/anitab/mentorship/local/UserDao.kt
@@ -0,0 +1,21 @@
+package org.anitab.mentorship.local
+
+import androidx.paging.PagingSource
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import org.anitab.mentorship.models.User
+
+@Dao
+interface UserDao {
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertUsers(users: List)
+
+ @Query("SELECT * from user_table")
+ fun getAllUsers(): PagingSource
+
+ @Query("DELETE from user_table")
+ suspend fun clearAllUsers()
+}
diff --git a/app/src/main/java/org/anitab/mentorship/local/UserDatabase.kt b/app/src/main/java/org/anitab/mentorship/local/UserDatabase.kt
new file mode 100644
index 000000000..b5796e767
--- /dev/null
+++ b/app/src/main/java/org/anitab/mentorship/local/UserDatabase.kt
@@ -0,0 +1,38 @@
+package org.anitab.mentorship.local
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import org.anitab.mentorship.models.User
+
+@Database(
+ entities = [User::class, RemoteKeysEntity::class],
+ version = 2,
+ exportSchema = false
+)
+abstract class UserDatabase : RoomDatabase() {
+
+ abstract fun userDao(): UserDao
+ abstract fun remoteKeyDao(): RemoteKeysDao
+
+ companion object {
+
+ @Volatile
+ private var INSTANCE: UserDatabase? = null
+
+ fun getInstance(context: Context): UserDatabase =
+ INSTANCE ?: synchronized(this) {
+ INSTANCE
+ ?: buildDatabase(context).also { INSTANCE = it }
+ }
+
+ private fun buildDatabase(context: Context) =
+ Room.databaseBuilder(
+ context,
+ UserDatabase::class.java, "User.db"
+ )
+ .fallbackToDestructiveMigration()
+ .build()
+ }
+}
diff --git a/app/src/main/java/org/anitab/mentorship/models/User.kt b/app/src/main/java/org/anitab/mentorship/models/User.kt
index 815d312c3..54110c5fa 100644
--- a/app/src/main/java/org/anitab/mentorship/models/User.kt
+++ b/app/src/main/java/org/anitab/mentorship/models/User.kt
@@ -1,6 +1,9 @@
package org.anitab.mentorship.models
import android.os.Parcelable
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.google.gson.annotations.SerializedName
import kotlinx.android.parcel.Parcelize
/**
@@ -20,19 +23,21 @@ import kotlinx.android.parcel.Parcelize
* @param availableToMentor true, if user is available to mentor, false if otherwise
* @param slackUsername Slack username
*/
+
+@Entity(tableName = "user_table")
@Parcelize
data class User(
- var id: Int? = null,
- var username: String? = null,
- var name: String? = null,
- var email: String? = null,
- var bio: String? = null,
- var location: String? = null,
- var occupation: String? = null,
- var organization: String? = null,
- var interests: String? = null,
- var skills: String? = null,
- var needMentoring: Boolean? = null,
- var availableToMentor: Boolean? = null,
- var slackUsername: String? = null
+ @PrimaryKey @field:SerializedName("id") var id: Int,
+ @field:SerializedName("username") var username: String? = null,
+ @field:SerializedName("name") var name: String? = null,
+ @field:SerializedName("email") var email: String? = null,
+ @field:SerializedName("bio") var bio: String? = null,
+ @field:SerializedName("location") var location: String? = null,
+ @field:SerializedName("occupation") var occupation: String? = null,
+ @field:SerializedName("organization") var organization: String? = null,
+ @field:SerializedName("interests") var interests: String? = null,
+ @field:SerializedName("skills") var skills: String? = null,
+ @field:SerializedName("need_mentoring") var needMentoring: Boolean? = null,
+ @field:SerializedName("available_to_mentor") var availableToMentor: Boolean? = null,
+ @field:SerializedName("slack_username") var slackUsername: String? = null
) : Parcelable
diff --git a/app/src/main/java/org/anitab/mentorship/remote/datamanager/UserDataManager.kt b/app/src/main/java/org/anitab/mentorship/remote/datamanager/UserDataManager.kt
index f6d7f89c1..0e5a3851c 100644
--- a/app/src/main/java/org/anitab/mentorship/remote/datamanager/UserDataManager.kt
+++ b/app/src/main/java/org/anitab/mentorship/remote/datamanager/UserDataManager.kt
@@ -1,14 +1,23 @@
package org.anitab.mentorship.remote.datamanager
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.map
+import androidx.paging.ExperimentalPagingApi
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import androidx.paging.filter
+import androidx.paging.liveData
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import org.anitab.mentorship.Injection
import org.anitab.mentorship.models.HomeStatistics
import org.anitab.mentorship.models.User
import org.anitab.mentorship.remote.ApiManager
import org.anitab.mentorship.remote.requests.ChangePassword
-import org.anitab.mentorship.remote.requests.PaginationRequest
import org.anitab.mentorship.remote.responses.CustomResponse
+import org.anitab.mentorship.utils.Constants.ITEMS_PER_PAGE
/**
* This class represents the data manager related to Users API
@@ -16,23 +25,93 @@ import org.anitab.mentorship.remote.responses.CustomResponse
class UserDataManager(private val dispatcher: CoroutineDispatcher = Dispatchers.IO) {
private val apiManager = ApiManager.instance
+ private val userDatabase = Injection.provideUserDatabase()
+
+ /** Paging 3 user data management starts **/
/**
- * This will call the getVerifiedUsers method of UserService interface
- * @return an Observable of a list of [User]
+ * This will call the getAllUsers() method from UserDao interface
+ * @return stream of [User] list as PagingData with the help of [UserRemoteMediator]
+ * which manages making network call and storing user list in room db.
*/
- suspend fun getUsers(): List {
- return withContext(dispatcher) { apiManager.userService.getVerifiedUsers() }
+
+ @OptIn(ExperimentalPagingApi::class)
+ fun getAllUsers(): LiveData> {
+ val pagingSourceFactory = { userDatabase.userDao().getAllUsers() }
+
+ return Pager(
+ config = PagingConfig(
+ pageSize = ITEMS_PER_PAGE,
+ enablePlaceholders = false
+ ),
+ remoteMediator = UserRemoteMediator(apiManager.userService, userDatabase),
+ pagingSourceFactory = pagingSourceFactory
+ ).liveData
}
/**
- * This will call the getVerifiedUsers(pagination) method of UserService interface
- * @return an Observable of a list of [User]
+ * This will call the getUsersWhoAreAvailableToMentor() method from UserDao interface
+ * @return stream of [User] list, who are available to be mentored as PagingData with
+ * the help of [UserRemoteMediator] which manages making network call and storing user
+ * list in room db.
*/
- suspend fun getUsers(paginationRequest: PaginationRequest): List {
- return withContext(dispatcher) { apiManager.userService.getVerifiedUsers(paginationRequest.pagination) }
+ @OptIn(ExperimentalPagingApi::class)
+ fun getUsersWhoAreAvailableToMentor(): LiveData> {
+ val pagingSourceFactory = { userDatabase.userDao().getAllUsers() }
+
+ return Pager(
+ config = PagingConfig(
+ pageSize = ITEMS_PER_PAGE,
+ enablePlaceholders = false
+ ),
+ remoteMediator = UserRemoteMediator(apiManager.userService, userDatabase),
+ pagingSourceFactory = pagingSourceFactory
+ ).liveData
+ .map { pagingData -> pagingData.filter { user -> user.availableToMentor != null && user.availableToMentor == true } }
}
+ /**
+ * This will call the getUsersWhoAreNeedMentoring() method from UserDao interface
+ * @return stream of [User] list, who are need mentoring as PagingData with the help of
+ * [UserRemoteMediator] which manages making network call and storing user list in room db.
+ */
+ @OptIn(ExperimentalPagingApi::class)
+ fun getUsersWhoAreNeedMentoring(): LiveData> {
+ val pagingSourceFactory = { userDatabase.userDao().getAllUsers() }
+
+ return Pager(
+ config = PagingConfig(
+ pageSize = ITEMS_PER_PAGE,
+ enablePlaceholders = false
+ ),
+ remoteMediator = UserRemoteMediator(apiManager.userService, userDatabase),
+ pagingSourceFactory = pagingSourceFactory
+ ).liveData
+ .map { pagingData -> pagingData.filter { user -> user.needMentoring != null && user.needMentoring == true } }
+ }
+
+ /**
+ * This will call the getUserWhoHaveSkills() method from UserDao interface
+ * @return stream of [User] list, who have at least 1 skill as PagingData with the help of
+ * [UserRemoteMediator] which manages making network call and storing user list in room db.
+ */
+ @OptIn(ExperimentalPagingApi::class)
+ fun getUserWhoHaveSkills(): LiveData> {
+ val pagingSourceFactory = { userDatabase.userDao().getAllUsers() }
+
+ return Pager(
+ config = PagingConfig(
+ pageSize = ITEMS_PER_PAGE,
+ enablePlaceholders = false
+ ),
+ remoteMediator = UserRemoteMediator(apiManager.userService, userDatabase),
+ pagingSourceFactory = pagingSourceFactory
+ ).liveData
+ .map { pagingData -> pagingData.filter { user -> user.skills != null } }
+ }
+
+ /** Paging 3 user data management end **/
+
/**
* This will call the getUser method of UserService interface
* @return an Observable of [User]
diff --git a/app/src/main/java/org/anitab/mentorship/remote/datamanager/UserRemoteMediator.kt b/app/src/main/java/org/anitab/mentorship/remote/datamanager/UserRemoteMediator.kt
new file mode 100644
index 000000000..d91cf276b
--- /dev/null
+++ b/app/src/main/java/org/anitab/mentorship/remote/datamanager/UserRemoteMediator.kt
@@ -0,0 +1,94 @@
+package org.anitab.mentorship.remote.datamanager
+
+import androidx.paging.ExperimentalPagingApi
+import androidx.paging.LoadType
+import androidx.paging.PagingState
+import androidx.paging.RemoteMediator
+import androidx.room.withTransaction
+import java.io.IOException
+import org.anitab.mentorship.local.RemoteKeysEntity
+import org.anitab.mentorship.local.UserDatabase
+import org.anitab.mentorship.models.User
+import org.anitab.mentorship.remote.services.UserService
+import org.anitab.mentorship.utils.Constants.START_PAGE_INDEX
+import retrofit2.HttpException
+
+@ExperimentalPagingApi
+class UserRemoteMediator(
+ private val userService: UserService,
+ private val userDatabase: UserDatabase
+) : RemoteMediator() {
+
+ override suspend fun initialize(): InitializeAction {
+ // Launching paging with refresh first, without triggering prepend or append.
+ return InitializeAction.LAUNCH_INITIAL_REFRESH
+ }
+
+ override suspend fun load(
+ loadType: LoadType,
+ state: PagingState
+ ): MediatorResult {
+ // calculating page number as per load type.
+ val page = when (loadType) {
+ LoadType.APPEND -> {
+ val remoteKey = userDatabase.remoteKeyDao().getRemoteKeys().lastOrNull()
+ val nextKey = remoteKey?.nextKey
+ ?: return MediatorResult.Success(endOfPaginationReached = true)
+ nextKey
+ }
+ LoadType.PREPEND -> {
+ val remoteKey = userDatabase.remoteKeyDao().getRemoteKeys().firstOrNull()
+ val prevKey = remoteKey?.prevKey
+ ?: return MediatorResult.Success(endOfPaginationReached = remoteKey != null)
+ prevKey
+ }
+ LoadType.REFRESH -> {
+ val remoteKey = getRemoteKeyClosestToCurrentPosition(state)
+ remoteKey?.nextKey?.minus(1) ?: START_PAGE_INDEX
+ }
+ }
+
+ try {
+ // making network call
+ val users: List = userService.getVerifiedUsers(page, state.config.pageSize)
+
+ // storing if user list is empty
+ val endOfPaginationReached = users.isEmpty()
+
+ userDatabase.withTransaction {
+ // clear all table in database
+ if (loadType == LoadType.REFRESH) {
+ userDatabase.remoteKeyDao().clearRemoteKeys()
+ userDatabase.userDao().clearAllUsers()
+ }
+
+ // storing previous and next keys
+ val prevKey = if (page == START_PAGE_INDEX) null else page - 1
+ val nextKey = if (endOfPaginationReached) null else page + 1
+
+ // storing the remote key and fetched users
+ val keys = users.map {
+ RemoteKeysEntity(userId = it.id, prevKey = prevKey, nextKey = nextKey)
+ }
+ userDatabase.remoteKeyDao().insertAll(keys)
+ userDatabase.userDao().insertUsers(users)
+ }
+ return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
+ } catch (e: IOException) {
+ return MediatorResult.Error(e)
+ } catch (e: HttpException) {
+ return MediatorResult.Error(e)
+ }
+ }
+
+ private suspend fun getRemoteKeyClosestToCurrentPosition(state: PagingState): RemoteKeysEntity? {
+ // Getting the remote key closest to anchor position
+ // Paging library will load data after the anchor position
+ // and get that item, which is closest to anchor position
+ return state.anchorPosition?.let { position ->
+ state.closestItemToPosition(position)?.id?.let { userId ->
+ userDatabase.remoteKeyDao().remoteKeysRepoId(userId)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/org/anitab/mentorship/remote/services/UserService.kt b/app/src/main/java/org/anitab/mentorship/remote/services/UserService.kt
index 726527ed0..364f13e66 100644
--- a/app/src/main/java/org/anitab/mentorship/remote/services/UserService.kt
+++ b/app/src/main/java/org/anitab/mentorship/remote/services/UserService.kt
@@ -8,6 +8,7 @@ import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.PUT
import retrofit2.http.Path
+import retrofit2.http.Query
import retrofit2.http.QueryMap
/**
@@ -35,7 +36,10 @@ interface UserService {
* @return an observable instance of a list of [User]s
*/
@GET("users/verified")
- suspend fun getVerifiedUsers(): List
+ suspend fun getVerifiedUsers(
+ @Query("page") page: Int,
+ @Query("per_page") perPage: Int
+ ): List
/**
* This function returns a user's public profile of the system
diff --git a/app/src/main/java/org/anitab/mentorship/utils/BindingUtils.kt b/app/src/main/java/org/anitab/mentorship/utils/BindingUtils.kt
new file mode 100644
index 000000000..d5a088a17
--- /dev/null
+++ b/app/src/main/java/org/anitab/mentorship/utils/BindingUtils.kt
@@ -0,0 +1,20 @@
+package org.anitab.mentorship.utils
+
+import android.widget.TextView
+import androidx.databinding.BindingAdapter
+import org.anitab.mentorship.R
+
+@BindingAdapter("isAvailableToMentor", "isMentoringNeeded", requireAll = true)
+fun TextView.showAvailability(availableToMentor: Boolean?, needMentoring: Boolean?) {
+ text = if (availableToMentor != null && needMentoring != null) {
+ if (availableToMentor && needMentoring) resources.getString(R.string.available_to_mentor_and_mentee)
+ else if (availableToMentor) resources.getString(R.string.only_available_to_mentor)
+ else if (needMentoring) resources.getString(R.string.only_available_to_mentee)
+ else resources.getString(R.string.not_available_to_mentor_or_mentee)
+ } else resources.getString(R.string.not_available_to_mentor_or_mentee)
+}
+
+@BindingAdapter("setInterest")
+fun TextView.showInterests(interests: String?) {
+ text = interests?.let { "Interests: $it" } ?: "Interests: --"
+}
diff --git a/app/src/main/java/org/anitab/mentorship/utils/Constants.kt b/app/src/main/java/org/anitab/mentorship/utils/Constants.kt
index 98cf08f1b..c3f858708 100644
--- a/app/src/main/java/org/anitab/mentorship/utils/Constants.kt
+++ b/app/src/main/java/org/anitab/mentorship/utils/Constants.kt
@@ -2,16 +2,15 @@ package org.anitab.mentorship.utils
object Constants {
- const val ITEMS_PER_PAGE = 20
+ const val ITEMS_PER_PAGE = 50
+ const val START_PAGE_INDEX = 1
const val TOTAL_REQUEST_TABS = 3
- const val MEMBER_USER_ID = "member_user_id"
+ const val MEMBER_USER_EXTRAS = "member_user_extras"
const val REQUEST_LIST = "request_list"
const val REQUEST_EMPTY_LIST_TEXT = "request_empty_list_text"
const val RELATIONSHIP_EXTRA = "relationship_extra"
const val DELETE_REQUEST_RESULT_ID = 1000
const val REQUEST_ID = "request_id"
- // filter function in MembersFragment
- const val FILTER_REQUEST_CODE = 8000
const val FILTER_MAP = "filter_map"
const val SORT_KEY = "sort_key"
const val NEED_MENTORING_KEY = "need_mentoring"
diff --git a/app/src/main/java/org/anitab/mentorship/utils/PreferenceManager.kt b/app/src/main/java/org/anitab/mentorship/utils/PreferenceManager.kt
index a2b73d79e..c9ed0a448 100644
--- a/app/src/main/java/org/anitab/mentorship/utils/PreferenceManager.kt
+++ b/app/src/main/java/org/anitab/mentorship/utils/PreferenceManager.kt
@@ -30,7 +30,7 @@ class PreferenceManager {
}
val authToken: String
- get() = sharedPreferences.getString(AUTH_TOKEN, "")
+ get() = sharedPreferences.getString(AUTH_TOKEN, "").toString()
/**
* Clears all the data that has been saved in the preferences file.
diff --git a/app/src/main/java/org/anitab/mentorship/utils/RecyclerViewItemDecoration.kt b/app/src/main/java/org/anitab/mentorship/utils/RecyclerViewItemDecoration.kt
new file mode 100644
index 000000000..9d779f922
--- /dev/null
+++ b/app/src/main/java/org/anitab/mentorship/utils/RecyclerViewItemDecoration.kt
@@ -0,0 +1,23 @@
+package org.anitab.mentorship.utils
+
+import android.graphics.Rect
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+class RecyclerViewItemDecoration :
+ RecyclerView.ItemDecoration() {
+ private val decorationHeightWidth: Int = 10
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ super.getItemOffsets(outRect, view, parent, state)
+ val itemPosition = parent.getChildAdapterPosition(view)
+ val totalCount = parent.adapter!!.itemCount
+ if (itemPosition >= 0 && itemPosition <= totalCount - 1) {
+ outRect.bottom = decorationHeightWidth
+ }
+ }
+}
diff --git a/app/src/main/java/org/anitab/mentorship/utils/UsersDiffCallback.kt b/app/src/main/java/org/anitab/mentorship/utils/UsersDiffCallback.kt
deleted file mode 100644
index ddad8291d..000000000
--- a/app/src/main/java/org/anitab/mentorship/utils/UsersDiffCallback.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.anitab.mentorship.utils
-
-import androidx.recyclerview.widget.DiffUtil
-import org.anitab.mentorship.models.User
-
-class UsersDiffCallback(private val mOldUsersList: List, private val mNewUsersList: List) : DiffUtil.Callback() {
-
- override fun getOldListSize(): Int {
- return mOldUsersList.size
- }
-
- override fun getNewListSize(): Int {
- return mNewUsersList.size
- }
-
- override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
- return mOldUsersList[oldItemPosition].id == mNewUsersList[newItemPosition].id
- }
-
- override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
- return mOldUsersList[oldItemPosition] == mNewUsersList[newItemPosition]
- }
-}
diff --git a/app/src/main/java/org/anitab/mentorship/view/activities/FilterActivity.kt b/app/src/main/java/org/anitab/mentorship/view/activities/FilterActivity.kt
deleted file mode 100644
index 47319a68c..000000000
--- a/app/src/main/java/org/anitab/mentorship/view/activities/FilterActivity.kt
+++ /dev/null
@@ -1,203 +0,0 @@
-package org.anitab.mentorship.view.activities
-
-import android.app.Activity
-import android.content.Intent
-import android.os.Bundle
-import android.view.MenuItem
-import android.view.View
-import android.widget.TextView
-import androidx.cardview.widget.CardView
-import androidx.core.content.res.ResourcesCompat
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.android.synthetic.main.activity_filter.*
-import org.anitab.mentorship.R
-import org.anitab.mentorship.utils.Constants.AVAILABLE_TO_MENTOR_KEY
-import org.anitab.mentorship.utils.Constants.FILTER_MAP
-import org.anitab.mentorship.utils.Constants.INTERESTS_KEY
-import org.anitab.mentorship.utils.Constants.LOCATION_KEY
-import org.anitab.mentorship.utils.Constants.NEED_MENTORING_KEY
-import org.anitab.mentorship.utils.Constants.SKILLS_KEY
-import org.anitab.mentorship.utils.Constants.SORT_KEY
-import org.anitab.mentorship.view.fragments.MembersFragment
-
-class FilterActivity : BaseActivity() {
-
- // a backup variable for the view that was selected in sort by fragment
- private var previousSelectionSort: View? = null
-
- private var needMentoring = false
- private var availableToMentor = false
-
- private var map: HashMap? = hashMapOf()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_filter)
-
- setTitle(R.string.filter)
-
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_close_black_24dp)
-
- map = intent?.extras?.get(FILTER_MAP) as HashMap?
- initializeViews()
- initializeClickListeners()
- }
-
- private fun initializeViews() {
- when (map?.get(SORT_KEY)) {
- MembersFragment.SortValues.REGISTRATION_DATE.name -> {
- cardSortRegistrationDate.setBackgroundResource(R.drawable.background_rounded_primary)
- tvSortRegistrationDate.setTextColor(ResourcesCompat.getColor(
- resources, R.color.white, null))
- previousSelectionSort = cardSortRegistrationDate
- }
- MembersFragment.SortValues.NAMEAZ.name -> {
- cardSortNameAZ.setBackgroundResource(R.drawable.background_rounded_primary)
- tvSortNameAZ.setTextColor(ResourcesCompat.getColor(
- resources, R.color.white, null))
- previousSelectionSort = cardSortNameAZ
- }
- MembersFragment.SortValues.NAMEZA.name -> {
- cardSortNameZA.setBackgroundResource(R.drawable.background_rounded_primary)
- tvSortNameZA.setTextColor(ResourcesCompat.getColor(
- resources, R.color.white, null))
- previousSelectionSort = cardSortNameZA
- }
- }
-
- if (map?.get(NEED_MENTORING_KEY) == "true") {
- cardFilterNeedMentoring.setBackgroundResource(R.drawable.background_rounded_primary)
- tvFilterNeedMentoring.setTextColor(ResourcesCompat.getColor(
- resources, R.color.white, null))
- needMentoring = true
- }
- if (map?.get(AVAILABLE_TO_MENTOR_KEY) == "true") {
- cardFilterAvailableToMentor.setBackgroundResource(R.drawable.background_rounded_primary)
- tvFilterAvailableToMentor.setTextColor(ResourcesCompat.getColor(
- resources, R.color.white, null))
- availableToMentor = true
- }
-
- etFilterInterests.setText(map?.get(INTERESTS_KEY) ?: "")
- etFilterLocation.setText(map?.get(LOCATION_KEY) ?: "")
- etFilterSkills.setText(map?.get(SKILLS_KEY) ?: "")
- }
-
- private fun initializeClickListeners() {
- cardSortAge.setOnClickListener {
- Snackbar.make(scrollViewFilter, getString(R.string.not_implemented), Snackbar.LENGTH_SHORT).show()
- // TODO: Add DateOfBirth field to the user model
- }
-
- cardFilterNeedMentoring.setOnClickListener {
- if (needMentoring) {
- it.setBackgroundResource(R.drawable.background_rounded_white)
- tvFilterNeedMentoring.setTextColor(ResourcesCompat.getColor(
- resources, R.color.textColorBlack, null))
- } else {
- it.setBackgroundResource(R.drawable.background_rounded_primary)
- tvFilterNeedMentoring.setTextColor(ResourcesCompat.getColor(
- resources, R.color.white, null))
- }
- needMentoring = !needMentoring
- map?.put(NEED_MENTORING_KEY, needMentoring.toString())
- }
- cardFilterAvailableToMentor.setOnClickListener {
- if (availableToMentor) {
- it.setBackgroundResource(R.drawable.background_rounded_white)
- tvFilterAvailableToMentor.setTextColor(ResourcesCompat.getColor(
- resources, R.color.textColorBlack, null))
- } else {
- it.setBackgroundResource(R.drawable.background_rounded_primary)
- tvFilterAvailableToMentor.setTextColor(ResourcesCompat.getColor(
- resources, R.color.white, null))
- }
- availableToMentor = !availableToMentor
- map?.put(AVAILABLE_TO_MENTOR_KEY, availableToMentor.toString())
- }
-
- btnClearAll.setOnClickListener {
- // sort by fragment
- with(previousSelectionSort) {
- this?.setBackgroundResource(R.drawable.background_rounded_white)
- ((this as CardView?)?.getChildAt(0) as TextView?)
- ?.setTextColor(ResourcesCompat.getColor(
- resources, R.color.textColorBlack, null))
- }
- cardSortRegistrationDate.setBackgroundResource(R.drawable.background_rounded_primary)
- tvSortRegistrationDate.setTextColor(ResourcesCompat.getColor(
- resources, R.color.white, null))
- previousSelectionSort = cardSortRegistrationDate
-
- // filter by fragment
- // buttons
- cardFilterNeedMentoring.setBackgroundResource(R.drawable.background_rounded_white)
- tvFilterNeedMentoring.setTextColor(ResourcesCompat.getColor(
- resources, R.color.textColorBlack, null))
- cardFilterAvailableToMentor.setBackgroundResource(R.drawable.background_rounded_white)
- tvFilterAvailableToMentor.setTextColor(ResourcesCompat.getColor(
- resources, R.color.textColorBlack, null))
-
- // EditTexts
- etFilterInterests.setText("")
- etFilterLocation.setText("")
- etFilterSkills.setText("")
-
- map = hashMapOf(SORT_KEY to MembersFragment.SortValues.REGISTRATION_DATE.name)
- }
-
- btnApplyFilter.setOnClickListener {
- map?.put(SORT_KEY, when (previousSelectionSort?.id) {
- R.id.cardSortNameAZ ->
- MembersFragment.SortValues.NAMEAZ.name
- R.id.cardSortNameZA ->
- MembersFragment.SortValues.NAMEZA.name
- R.id.cardSortRegistrationDate ->
- MembersFragment.SortValues.REGISTRATION_DATE.name
- else ->
- MembersFragment.SortValues.REGISTRATION_DATE.name
- })
-
- map?.put(INTERESTS_KEY, etFilterInterests.text.toString())
- map?.put(LOCATION_KEY, etFilterLocation.text.toString())
- map?.put(SKILLS_KEY, etFilterSkills.text.toString())
-
- finishActivity()
- }
- }
-
- private fun finishActivity() {
- val resultIntent = Intent()
- resultIntent.putExtra(FILTER_MAP, map)
- setResult(Activity.RESULT_OK, resultIntent)
- onBackPressed()
- }
-
- fun buttonOnClickSort(view: View) {
- with(previousSelectionSort) {
- this?.setBackgroundResource(R.drawable.background_rounded_white)
- ((this as CardView?)?.getChildAt(0) as TextView?)
- ?.setTextColor(ResourcesCompat.getColor(
- resources, R.color.textColorBlack, null))
- }
-
- view.setBackgroundResource(R.drawable.background_rounded_primary)
- ((view as CardView).getChildAt(0) as TextView)
- .setTextColor(ResourcesCompat.getColor(
- resources, R.color.white, null))
-
- previousSelectionSort = view
- }
-
- override fun onOptionsItemSelected(item: MenuItem?): Boolean {
- if (item?.itemId == android.R.id.home)
- onBackPressed()
- return super.onOptionsItemSelected(item)
- }
-
- override fun onBackPressed() {
- super.onBackPressed()
- overridePendingTransition(R.anim.anim_stay, R.anim.anim_slide_to_bottom)
- }
-}
diff --git a/app/src/main/java/org/anitab/mentorship/view/activities/MainActivity.kt b/app/src/main/java/org/anitab/mentorship/view/activities/MainActivity.kt
index 93feda963..73fa70f05 100644
--- a/app/src/main/java/org/anitab/mentorship/view/activities/MainActivity.kt
+++ b/app/src/main/java/org/anitab/mentorship/view/activities/MainActivity.kt
@@ -6,7 +6,8 @@ import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import com.google.android.material.bottomnavigation.BottomNavigationView
-import kotlinx.android.synthetic.main.activity_main.*
+import kotlinx.android.synthetic.main.activity_main.bottomNavigation
+import kotlinx.android.synthetic.main.activity_main.toolbar
import org.anitab.mentorship.R
import org.anitab.mentorship.utils.PreferenceManager
import org.anitab.mentorship.view.fragments.HomeFragment
diff --git a/app/src/main/java/org/anitab/mentorship/view/activities/MemberProfileActivity.kt b/app/src/main/java/org/anitab/mentorship/view/activities/MemberProfileActivity.kt
index 7207726a7..9c53f4ed8 100644
--- a/app/src/main/java/org/anitab/mentorship/view/activities/MemberProfileActivity.kt
+++ b/app/src/main/java/org/anitab/mentorship/view/activities/MemberProfileActivity.kt
@@ -2,11 +2,22 @@ package org.anitab.mentorship.view.activities
import android.content.Intent
import android.os.Bundle
-import android.view.Menu
import android.view.MenuItem
import androidx.activity.viewModels
import com.google.android.material.snackbar.Snackbar
-import kotlinx.android.synthetic.main.activity_member_profile.*
+import kotlinx.android.synthetic.main.activity_member_profile.btnSendRequest
+import kotlinx.android.synthetic.main.activity_member_profile.srlMemberProfile
+import kotlinx.android.synthetic.main.activity_member_profile.tvAvailableToMentor
+import kotlinx.android.synthetic.main.activity_member_profile.tvBio
+import kotlinx.android.synthetic.main.activity_member_profile.tvInterests
+import kotlinx.android.synthetic.main.activity_member_profile.tvLocation
+import kotlinx.android.synthetic.main.activity_member_profile.tvName
+import kotlinx.android.synthetic.main.activity_member_profile.tvNeedMentoring
+import kotlinx.android.synthetic.main.activity_member_profile.tvOccupation
+import kotlinx.android.synthetic.main.activity_member_profile.tvOrganization
+import kotlinx.android.synthetic.main.activity_member_profile.tvSkills
+import kotlinx.android.synthetic.main.activity_member_profile.tvSlackUsername
+import kotlinx.android.synthetic.main.activity_member_profile.tvUsername
import org.anitab.mentorship.R
import org.anitab.mentorship.models.User
import org.anitab.mentorship.utils.Constants
@@ -18,7 +29,10 @@ import org.anitab.mentorship.viewmodels.ProfileViewModel
* This activity will show the public profile of a user of the system
*/
class MemberProfileActivity : BaseActivity() {
+
private val memberProfileViewModel: MemberProfileViewModel by viewModels()
+ private val profileViewModel: ProfileViewModel by viewModels()
+
private lateinit var userProfile: User
private lateinit var currentUser: User
@@ -27,117 +41,122 @@ class MemberProfileActivity : BaseActivity() {
setContentView(R.layout.activity_member_profile)
supportActionBar?.title = getString(R.string.member_profile)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
- val profileViewModel: ProfileViewModel by viewModels()
- profileViewModel.successfulGet.observe(this, {
- successful ->
+
+ /**
+ * getting passed member, showing UI and saving on to memberProfileViewModel.
+ * Showing snackbar if any error.
+ */
+ val member: User? = intent.getParcelableExtra(Constants.MEMBER_USER_EXTRAS)
+ member?.let {
+ setUserProfile(it)
+ memberProfileViewModel.userId = it.id
+ } ?: Snackbar.make(getRootView(), getString(R.string.error_filter_not_found), Snackbar.LENGTH_LONG)
+ .show()
+
+ // Refresh data from network on swipe down gesture
+ srlMemberProfile.setOnRefreshListener { fetchNewest() }
+
+ btnSendRequest.setOnClickListener {
+ if (userProfile.availableToMentor == true && userProfile.needMentoring != true &&
+ (currentUser.availableToMentor == true && currentUser.needMentoring != true)
+ ) {
+ Snackbar.make(getRootView(), getString(R.string.both_users_only_available_to_mentor), Snackbar.LENGTH_LONG)
+ .show()
+ } else {
+ val intent = Intent(this@MemberProfileActivity, SendRequestActivity::class.java)
+ intent.putExtra(SendRequestActivity.OTHER_USER_ID_INTENT_EXTRA, userProfile.id)
+ intent.putExtra(SendRequestActivity.OTHER_USER_NAME_INTENT_EXTRA, userProfile.name)
+ startActivity(intent)
+ }
+ }
+
+ setObservers()
+ }
+
+ private fun setObservers() {
+ // observing from profile viewmodel
+ profileViewModel.successfulGet.observe(this) { successful ->
if (successful != null) {
if (successful) {
setCurrentUser(profileViewModel.user)
} else {
Snackbar.make(getRootView(), profileViewModel.message, Snackbar.LENGTH_LONG)
- .show()
+ .show()
}
}
- })
+ }
profileViewModel.getProfile()
- srlMemberProfile.setOnRefreshListener { fetchNewest() }
-
- memberProfileViewModel.successful.observe(this, {
- successful ->
+ // observing from member profile viewmodel
+ memberProfileViewModel.successful.observe(this) { successful ->
srlMemberProfile.isRefreshing = false
if (successful != null) {
if (successful) {
setUserProfile(memberProfileViewModel.userProfile)
} else {
Snackbar.make(getRootView(), memberProfileViewModel.message, Snackbar.LENGTH_LONG)
- .show()
- }
- }
- })
-
- val memberId = intent.getIntExtra(Constants.MEMBER_USER_ID, -1)
-
- memberProfileViewModel.userId = memberId
-
- fetchNewest()
-
- btnSendRequest.setOnClickListener {
- if (userProfile?.availableToMentor ?: false && !(userProfile?.needMentoring ?:false) &&
- (currentUser?.availableToMentor ?: false && !(currentUser?.needMentoring ?:false))) {
- Snackbar.make(getRootView(), getString(R.string.both_users_only_available_to_mentor), Snackbar.LENGTH_LONG)
.show()
- } else {
- val intent = Intent(this@MemberProfileActivity, SendRequestActivity::class.java)
- intent.putExtra(SendRequestActivity.OTHER_USER_ID_INTENT_EXTRA, userProfile.id)
- intent.putExtra(SendRequestActivity.OTHER_USER_NAME_INTENT_EXTRA, userProfile.name)
- startActivity(intent)
+ }
}
}
}
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.main_menu, menu)
- return true
- }
-
+ // To set back button
override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
android.R.id.home -> {
onBackPressed()
true
}
- R.id.menu_refresh -> {
- fetchNewest()
- true
- }
else -> super.onOptionsItemSelected(menuItem)
}
}
+ // Called when swipe down to refresh triggered
private fun fetchNewest() {
srlMemberProfile.isRefreshing = true
memberProfileViewModel.getUserProfile()
}
+ // To set current user profile data
private fun setCurrentUser(user: User) {
currentUser = user
}
+
+ // Setting user data to textviews
private fun setUserProfile(user: User) {
userProfile = user
tvName.text = user.name
+ // checker for available to mentor
if (user.availableToMentor != null) {
- setTextViewStartingWithBoldSpan(
- tvAvailableToMentor,
- getString(R.string.available_to_mentor),
- if (user.availableToMentor!!)
- getString(R.string.yes) else getString(R.string.no))
+ setTextViewStartingWithBoldSpan(tvAvailableToMentor, getString(R.string.available_to_mentor),
+ if (user.availableToMentor == true) getString(R.string.yes) else getString(R.string.no)
+ )
}
+
+ // checker for need mentoring
if (user.needMentoring != null) {
- setTextViewStartingWithBoldSpan(
- tvNeedMentoring,
- getString(R.string.need_mentoring),
- if (user.needMentoring!!)
- getString(R.string.yes) else getString(R.string.no))
+ setTextViewStartingWithBoldSpan(tvNeedMentoring, getString(R.string.need_mentoring),
+ if (user.needMentoring == true) getString(R.string.yes) else getString(R.string.no)
+ )
}
+
setTextViewStartingWithBoldSpan(tvBio, getString(R.string.bio), user.bio)
- setTextViewStartingWithBoldSpan(
- tvLocation, getString(R.string.location), user.location)
- setTextViewStartingWithBoldSpan(
- tvOrganization, getString(R.string.organization), user.organization)
- setTextViewStartingWithBoldSpan(
- tvOccupation, getString(R.string.occupation), user.occupation)
- setTextViewStartingWithBoldSpan(
- tvInterests, getString(R.string.interests), user.interests)
- setTextViewStartingWithBoldSpan(
- tvSkills, getString(R.string.skills), user.skills)
- setTextViewStartingWithBoldSpan(
- tvUsername, getString(R.string.username), user.username)
- setTextViewStartingWithBoldSpan(
- tvSlackUsername, getString(R.string.slack_username), user.slackUsername)
- if (!user.availableToMentor!! && !user.needMentoring!!)
- btnSendRequest.isEnabled = false
+ setTextViewStartingWithBoldSpan(tvLocation, getString(R.string.location), user.location)
+ setTextViewStartingWithBoldSpan(tvOrganization, getString(R.string.organization), user.organization)
+ setTextViewStartingWithBoldSpan(tvOccupation, getString(R.string.occupation), user.occupation)
+ setTextViewStartingWithBoldSpan(tvInterests, getString(R.string.interests), user.interests)
+ setTextViewStartingWithBoldSpan(tvSkills, getString(R.string.skills), user.skills)
+ setTextViewStartingWithBoldSpan(tvUsername, getString(R.string.username), user.username)
+ setTextViewStartingWithBoldSpan(tvSlackUsername, getString(R.string.slack_username), user.slackUsername)
+
+ // disable 'send request' button when [availableToMentor] & [needMentoring] is null or false
+ user.run {
+ if ((availableToMentor == null || availableToMentor == false) &&
+ (needMentoring == null || needMentoring == false)
+ ) btnSendRequest.isEnabled = false
+ }
}
override fun onDestroy() {
diff --git a/app/src/main/java/org/anitab/mentorship/view/activities/RequestDetailActivity.kt b/app/src/main/java/org/anitab/mentorship/view/activities/RequestDetailActivity.kt
index 9b9f57ac7..4f34100df 100644
--- a/app/src/main/java/org/anitab/mentorship/view/activities/RequestDetailActivity.kt
+++ b/app/src/main/java/org/anitab/mentorship/view/activities/RequestDetailActivity.kt
@@ -39,9 +39,11 @@ class RequestDetailActivity : BaseActivity() {
supportActionBar?.title = getString(R.string.request_detail)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
- populateView(mentorshipRelationResponse)
- setObservables(mentorshipRelationResponse)
- setOnClickListeners(mentorshipRelationResponse)
+ mentorshipRelationResponse?.let {
+ populateView(it)
+ setObservables(it)
+ setOnClickListeners(it)
+ }
}
private fun populateView(relationResponse: Relationship) {
diff --git a/app/src/main/java/org/anitab/mentorship/view/activities/SendRequestActivity.kt b/app/src/main/java/org/anitab/mentorship/view/activities/SendRequestActivity.kt
index 54846d4de..b0013b113 100644
--- a/app/src/main/java/org/anitab/mentorship/view/activities/SendRequestActivity.kt
+++ b/app/src/main/java/org/anitab/mentorship/view/activities/SendRequestActivity.kt
@@ -44,7 +44,7 @@ class SendRequestActivity : BaseActivity() {
tvRequestEndDate.isEnabled = false
supportActionBar?.title = getString(R.string.send_request)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
- val otherUserName = intent.getStringExtra(OTHER_USER_NAME_INTENT_EXTRA)
+ val otherUserName = intent.getStringExtra(OTHER_USER_NAME_INTENT_EXTRA).toString()
val otherUserId = intent.getIntExtra(OTHER_USER_ID_INTENT_EXTRA, 0)
val currentUserId = getAuthTokenPayload().identity
setObservables()
diff --git a/app/src/main/java/org/anitab/mentorship/view/adapters/MemberLoadingStateAdapter.kt b/app/src/main/java/org/anitab/mentorship/view/adapters/MemberLoadingStateAdapter.kt
new file mode 100644
index 000000000..95ba4b378
--- /dev/null
+++ b/app/src/main/java/org/anitab/mentorship/view/adapters/MemberLoadingStateAdapter.kt
@@ -0,0 +1,56 @@
+package org.anitab.mentorship.view.adapters
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.paging.LoadState
+import androidx.paging.LoadStateAdapter
+import androidx.recyclerview.widget.RecyclerView
+import org.anitab.mentorship.databinding.ListMemberNetworkStateItemBinding
+
+class MemberLoadingStateAdapter(private val retry: () -> Unit) :
+ LoadStateAdapter() {
+
+ override fun onBindViewHolder(
+ holder: MemberLoadingStateAdapter.LoadStateViewHolder,
+ loadState: LoadState
+ ) {
+
+ holder.binding.apply {
+ when (loadState) {
+ is LoadState.Loading -> {
+ pbMembersListState.isVisible = true
+ btnRetry.isVisible = false
+ tvErrorMsg.isVisible = false
+ }
+ is LoadState.Error -> {
+ pbMembersListState.isVisible = false
+ btnRetry.isVisible = true
+ tvErrorMsg.apply {
+ isVisible = true
+ text = loadState.error.message
+ }
+ }
+ else -> pbMembersListState.isVisible = true
+ }
+
+ btnRetry.setOnClickListener { retry.invoke() }
+ }
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ loadState: LoadState
+ ): MemberLoadingStateAdapter.LoadStateViewHolder {
+ return LoadStateViewHolder(
+ ListMemberNetworkStateItemBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+ }
+
+ inner class LoadStateViewHolder(val binding: ListMemberNetworkStateItemBinding) :
+ RecyclerView.ViewHolder(binding.root)
+}
diff --git a/app/src/main/java/org/anitab/mentorship/view/adapters/MemberPagingAdapter.kt b/app/src/main/java/org/anitab/mentorship/view/adapters/MemberPagingAdapter.kt
new file mode 100644
index 000000000..5a7f585b6
--- /dev/null
+++ b/app/src/main/java/org/anitab/mentorship/view/adapters/MemberPagingAdapter.kt
@@ -0,0 +1,66 @@
+package org.anitab.mentorship.view.adapters
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.view.animation.AnimationUtils
+import androidx.paging.PagingDataAdapter
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import org.anitab.mentorship.R
+import org.anitab.mentorship.databinding.ListMemberItemBinding
+import org.anitab.mentorship.models.User
+
+class MemberPagingAdapter(
+ private val openDetailFunction: (memberId: User) -> Unit
+) : PagingDataAdapter(MemberDiffUtilCallback()) {
+
+ private var lastPosition = -1
+
+ override fun onBindViewHolder(holder: MemberPagingAdapter.MemberViewHolder, position: Int) {
+ val data = getItem(position)
+ holder.bind(data, position)
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): MemberPagingAdapter.MemberViewHolder {
+ return MemberViewHolder(
+ ListMemberItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ )
+ }
+
+ inner class MemberViewHolder(
+ private val binding: ListMemberItemBinding
+ ) : RecyclerView.ViewHolder(binding.root) {
+
+ fun bind(data: User?, position: Int) {
+ data?.let {
+ binding.apply {
+ user = data
+ executePendingBindings()
+ listMemberItemContainer.setOnClickListener {
+ openDetailFunction(data)
+ }
+
+ val animation = AnimationUtils.loadAnimation(
+ root.context,
+ if (position > lastPosition) R.anim.bottom_to_top else R.anim.top_to_bottom
+ )
+ itemView.startAnimation(animation)
+ lastPosition = position
+ }
+ }
+ }
+ }
+}
+
+private class MemberDiffUtilCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
+ return oldItem.hashCode() == newItem.hashCode()
+ }
+}
diff --git a/app/src/main/java/org/anitab/mentorship/view/adapters/MembersAdapter.kt b/app/src/main/java/org/anitab/mentorship/view/adapters/MembersAdapter.kt
deleted file mode 100644
index e1cce6c3d..000000000
--- a/app/src/main/java/org/anitab/mentorship/view/adapters/MembersAdapter.kt
+++ /dev/null
@@ -1,170 +0,0 @@
-package org.anitab.mentorship.view.adapters
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.animation.AnimationUtils
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.annotation.NonNull
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.RecyclerView
-import kotlinx.android.synthetic.main.list_member_item.view.*
-import org.anitab.mentorship.MentorshipApplication
-import org.anitab.mentorship.R
-import org.anitab.mentorship.models.User
-import org.anitab.mentorship.utils.Constants
-import org.anitab.mentorship.utils.Constants.INTERESTS_KEY
-import org.anitab.mentorship.utils.Constants.LOCATION_KEY
-import org.anitab.mentorship.utils.Constants.SKILLS_KEY
-import org.anitab.mentorship.utils.NON_VALID_VALUE_REPLACEMENT
-import org.anitab.mentorship.utils.UsersDiffCallback
-import org.anitab.mentorship.view.fragments.MembersFragment
-
-/**
- * This class represents the adapter that fills in each view of the Members recyclerView
- * @param userList list of users to show
- * @param openDetailFunction function to be called when an item from Members list is clicked
- */
-class MembersAdapter(
- private var userList: ArrayList = arrayListOf(),
- private val openDetailFunction: (memberId: Int, sharedImageView: ImageView, sharedTextView: TextView) -> Unit
-
-) : RecyclerView.Adapter() {
-
- val context = MentorshipApplication.getContext()
- var lastPosition = -1
- private var filterMap = hashMapOf(Constants.SORT_KEY to MembersFragment.SortValues.REGISTRATION_DATE.name)
- private var filteredUserList = mutableListOf()
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MembersViewHolder =
- MembersViewHolder(
- LayoutInflater.from(parent.context)
- .inflate(R.layout.list_member_item, parent, false)
- )
-
- override fun onBindViewHolder(@NonNull holder: MembersViewHolder, position: Int) {
- val item = filteredUserList[position]
- val itemView = holder.itemView
-
- itemView.tvName.text = item.name
- itemView.tvUsername.text = item.username
- itemView.tvMentorshipAvailability.text = getMentorshipAvailabilityText(item.availableToMentor, item.needMentoring)
-
- val userInterests = item.interests
- val validText = if (userInterests.isNullOrBlank()) NON_VALID_VALUE_REPLACEMENT else userInterests
- val keyText = context.getString(R.string.interests)
- val keyValueText = "$keyText: $validText"
- itemView.tvInterests.text = keyValueText
-
- itemView.setOnClickListener { openDetailFunction(item.id!!, itemView.circleImageView, itemView.tvName) }
-
- val animation = AnimationUtils.loadAnimation(context,
- if (position > lastPosition) R.anim.bottom_to_top else R.anim.top_to_bottom)
- holder.itemView.startAnimation(animation)
- lastPosition = position
- }
-
- override fun getItemCount(): Int = filteredUserList.size
-
- fun updateUsersList(map: HashMap, newUsers: List) {
- // updating users list
- setData(newUsers)
- // getting updated filtered users
- val newFilteredUsers = getFilteredUsers(map, newUsers)
-
- // applying changes to adapter
- val usersDiffCallback = UsersDiffCallback(filteredUserList, newFilteredUsers)
- val diffResult = DiffUtil.calculateDiff(usersDiffCallback)
- filteredUserList.clear()
- filteredUserList.addAll(newFilteredUsers)
- diffResult.dispatchUpdatesTo(this)
- }
-
- private fun getFilteredUsers(map: HashMap, newUsers: List): List {
- var newFilteredList: List = arrayListOf()
- when (map[Constants.SORT_KEY]) {
- MembersFragment.SortValues.REGISTRATION_DATE.name -> {
- newFilteredList = newUsers
- }
- MembersFragment.SortValues.NAMEAZ.name -> {
- newFilteredList = newUsers.sortedBy {
- it.name
- } as MutableList
- }
- MembersFragment.SortValues.NAMEZA.name -> {
- newFilteredList = newUsers.sortedByDescending {
- it.name
- } as MutableList
- }
- }
-
- if (map[Constants.NEED_MENTORING_KEY] == "true")
- newFilteredList = newFilteredList.filter {
- it.needMentoring == true
- } as MutableList
-
- if (map[Constants.AVAILABLE_TO_MENTOR_KEY] == "true")
- newFilteredList = newFilteredList.filter {
- it.availableToMentor == true
- } as MutableList
-
- val interests = map[INTERESTS_KEY]
- val location = map[LOCATION_KEY]
- val skills = map[SKILLS_KEY]
-
- newFilteredList = newFilteredList.filter {
- var valid = true
-
- if (!interests.isNullOrEmpty())
- if (it.interests.isNullOrEmpty())
- valid = false
- else if (it.interests?.contains(interests, ignoreCase = true) == false)
- valid = false
-
- if (!location.isNullOrEmpty())
- if (it.location.isNullOrEmpty())
- valid = false
- else if (it.location?.contains(location, ignoreCase = true) == false)
- valid = false
-
- if (!skills.isNullOrEmpty())
- if (it.skills.isNullOrEmpty())
- valid = false
- else if (it.skills?.contains(skills, ignoreCase = true) == false)
- valid = false
-
- valid
- } as MutableList
-
- return newFilteredList
- }
-
- private fun setData(users: List) {
- userList.clear()
- userList.addAll(users)
- }
-
- /**
- * This class holds a view for each item of the Members list
- * @param itemView represents each view of Members list
- */
- class MembersViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
-
- private fun getMentorshipAvailabilityText(availableToMentor: Boolean?, needMentoring: Boolean?): String {
-
- if (availableToMentor != null && needMentoring != null) {
- return if (availableToMentor && needMentoring) context.getString(R.string.available_to_mentor_and_mentee)
- else if (availableToMentor) context.getString(R.string.only_available_to_mentor)
- else if (needMentoring) context.getString(R.string.only_available_to_mentee)
- else context.getString(R.string.not_available_to_mentor_or_mentee)
- }
-
- return context.getString(R.string.not_available_to_mentor_or_mentee)
- }
-
- override fun onViewDetachedFromWindow(holder: MembersViewHolder) {
- super.onViewDetachedFromWindow(holder)
- holder.itemView.clearAnimation()
- }
-}
diff --git a/app/src/main/java/org/anitab/mentorship/view/fragments/ChangePasswordFragment.kt b/app/src/main/java/org/anitab/mentorship/view/fragments/ChangePasswordFragment.kt
index 134ed0194..28cfebbeb 100644
--- a/app/src/main/java/org/anitab/mentorship/view/fragments/ChangePasswordFragment.kt
+++ b/app/src/main/java/org/anitab/mentorship/view/fragments/ChangePasswordFragment.kt
@@ -10,8 +10,11 @@ import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.viewModels
-import kotlinx.android.synthetic.main.fragment_change_password.*
-import kotlinx.android.synthetic.main.fragment_change_password.view.*
+import kotlinx.android.synthetic.main.fragment_change_password.tilConfirmPassword
+import kotlinx.android.synthetic.main.fragment_change_password.tilNewPassword
+import kotlinx.android.synthetic.main.fragment_change_password.view.tilConfirmPassword
+import kotlinx.android.synthetic.main.fragment_change_password.view.tilCurrentPassword
+import kotlinx.android.synthetic.main.fragment_change_password.view.tilNewPassword
import org.anitab.mentorship.R
import org.anitab.mentorship.remote.requests.ChangePassword
import org.anitab.mentorship.utils.checkPasswordSecurity
diff --git a/app/src/main/java/org/anitab/mentorship/view/fragments/EditProfileFragment.kt b/app/src/main/java/org/anitab/mentorship/view/fragments/EditProfileFragment.kt
index 2711f5843..3b6ce95e5 100644
--- a/app/src/main/java/org/anitab/mentorship/view/fragments/EditProfileFragment.kt
+++ b/app/src/main/java/org/anitab/mentorship/view/fragments/EditProfileFragment.kt
@@ -31,7 +31,7 @@ class EditProfileFragment : DialogFragment() {
* Creates an instance of EditProfileFragment
*/
fun newInstance(user: User): EditProfileFragment {
- tempUser = user.copy(id = null, username = null, email = null)
+ tempUser = user.copy(id = 0, username = null, email = null)
return EditProfileFragment()
}
}
diff --git a/app/src/main/java/org/anitab/mentorship/view/fragments/HomeFragment.kt b/app/src/main/java/org/anitab/mentorship/view/fragments/HomeFragment.kt
index fd93ffcb2..432778296 100644
--- a/app/src/main/java/org/anitab/mentorship/view/fragments/HomeFragment.kt
+++ b/app/src/main/java/org/anitab/mentorship/view/fragments/HomeFragment.kt
@@ -10,7 +10,10 @@ import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
-import kotlinx.android.synthetic.main.fragment_home.*
+import kotlinx.android.synthetic.main.fragment_home.homeContainer
+import kotlinx.android.synthetic.main.fragment_home.rvAchievements
+import kotlinx.android.synthetic.main.fragment_home.srlHome
+import kotlinx.android.synthetic.main.fragment_home.tvNoAchievements
import org.anitab.mentorship.R
import org.anitab.mentorship.databinding.FragmentHomeBinding
import org.anitab.mentorship.view.adapters.AchievementsAdapter
@@ -31,7 +34,6 @@ class HomeFragment : BaseFragment() {
* Creates an instance of HomeFragment
*/
fun newInstance() = HomeFragment()
- val TAG: String = HomeFragment::class.java.simpleName
}
override fun getLayoutResourceId(): Int = R.layout.fragment_home
diff --git a/app/src/main/java/org/anitab/mentorship/view/fragments/MembersFragment.kt b/app/src/main/java/org/anitab/mentorship/view/fragments/MembersFragment.kt
index 6e6f727bc..786d01e82 100644
--- a/app/src/main/java/org/anitab/mentorship/view/fragments/MembersFragment.kt
+++ b/app/src/main/java/org/anitab/mentorship/view/fragments/MembersFragment.kt
@@ -1,37 +1,28 @@
package org.anitab.mentorship.view.fragments
-import android.app.Activity.RESULT_OK
import android.content.Intent
import android.os.Bundle
-import android.view.Menu
-import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
-import android.view.animation.AnimationUtils
-import android.widget.ImageView
-import android.widget.SearchView
-import android.widget.TextView
-import android.widget.Toast
-import androidx.core.app.ActivityOptionsCompat
-import androidx.core.util.Pair
-import androidx.core.view.ViewCompat
+import android.widget.PopupMenu
+import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
-import androidx.recyclerview.widget.DividerItemDecoration
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.android.synthetic.main.fragment_members.*
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.whenStarted
+import androidx.paging.LoadState
+import kotlinx.android.synthetic.main.fragment_members.fabFilter
+import kotlinx.android.synthetic.main.fragment_members.rvMembers
+import kotlinx.android.synthetic.main.fragment_members.srlMembers
+import kotlinx.android.synthetic.main.fragment_members.tvEmptyList
+import kotlinx.coroutines.launch
import org.anitab.mentorship.R
import org.anitab.mentorship.models.User
import org.anitab.mentorship.utils.Constants
-import org.anitab.mentorship.utils.Constants.FILTER_MAP
-import org.anitab.mentorship.utils.Constants.FILTER_REQUEST_CODE
-import org.anitab.mentorship.utils.Constants.SORT_KEY
-import org.anitab.mentorship.utils.EndlessRecyclerScrollListener
-import org.anitab.mentorship.view.activities.FilterActivity
-import org.anitab.mentorship.view.activities.MainActivity
+import org.anitab.mentorship.utils.RecyclerViewItemDecoration
import org.anitab.mentorship.view.activities.MemberProfileActivity
-import org.anitab.mentorship.view.adapters.MembersAdapter
+import org.anitab.mentorship.view.adapters.MemberLoadingStateAdapter
+import org.anitab.mentorship.view.adapters.MemberPagingAdapter
+import org.anitab.mentorship.viewmodels.ListFilter
import org.anitab.mentorship.viewmodels.MembersViewModel
/**
@@ -46,188 +37,109 @@ class MembersFragment : BaseFragment() {
fun newInstance() = MembersFragment()
}
- private var memberListInitialized = false
+ private lateinit var memberAdapter: MemberPagingAdapter
private val membersViewModel: MembersViewModel by viewModels()
- private lateinit var rvAdapter: MembersAdapter
- private var isLoading = false
- private var isRecyclerView = false
- private var filterMap = hashMapOf(SORT_KEY to SortValues.REGISTRATION_DATE.name)
override fun getLayoutResourceId(): Int = R.layout.fragment_members
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- super.onCreateOptionsMenu(menu, inflater)
- inflater.inflate(R.menu.menu_members, menu)
- menu.findItem(R.id.search_item)?.let { searchItem ->
- val searchView = searchItem.actionView as SearchView
- searchView.queryHint = "Search members"
- searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
- override fun onQueryTextChange(newText: String): Boolean {
- if (memberListInitialized)
- searchUsers(newText)
- return false
- }
- override fun onQueryTextSubmit(query: String): Boolean {
- return false
- }
- })
- }
- }
-
- fun searchUsers(query: String) {
- val userList = arrayListOf()
- for (user in membersViewModel.userList) {
- // ""+ to convert String to CharSequence
- if (("" + user.username).contains(query, ignoreCase = true)) {
- userList.add(user)
- }
- }
- rvMembers.apply {
- layoutManager = LinearLayoutManager(context)
- addLoadMoreListener(this)
- adapter = MembersAdapter(userList, ::openUserProfile)
- }
- tvEmptyList.visibility = View.GONE
- }
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setHasOptionsMenu(true)
- rvAdapter = MembersAdapter(arrayListOf(), ::openUserProfile)
- srlMembers.setOnRefreshListener {
- if (!isLoading) {
- fetchNewest(true)
- isLoading = true
- }
- }
- membersViewModel.successful.observe(viewLifecycleOwner, { successful ->
- (activity as MainActivity).hideProgressDialog()
- srlMembers.isRefreshing = false
- isLoading = false
- pbMembers.visibility = View.INVISIBLE
- if (successful != null) {
- if (successful) {
-
- rvAdapter.updateUsersList(filterMap, membersViewModel.userList)
- if (membersViewModel.userList.isEmpty()) {
- tvEmptyList.text = getString(R.string.empty_members_list)
- rvMembers.visibility = View.GONE
- } else {
- if (!isRecyclerView) {
- rvMembers.apply {
- layoutManager = LinearLayoutManager(context)
- adapter = MembersAdapter(membersViewModel.userList, ::openUserProfile)
-
- addLoadMoreListener(this)
- runLayoutAnimation(this)
-
- val dividerItemDecoration = DividerItemDecoration(
- this.context, DividerItemDecoration.VERTICAL)
- addItemDecoration(dividerItemDecoration)
- adapter = rvAdapter
- isRecyclerView = true
- }
- } else {
- if (!filterMap["location"].isNullOrEmpty()) {
-
- val hasUsersWithLocation = membersViewModel.userList.any {
- (it.location)?.contains(filterMap["location"]!!, ignoreCase = true) == true
- }
-
- if (!hasUsersWithLocation) {
- Toast.makeText(activity, getString(R.string.error_filter_not_found),
- Toast.LENGTH_SHORT).show()
- }
- }
- }
- memberListInitialized = true
- tvEmptyList.visibility = View.GONE
- }
- } else {
- view?.let {
- Snackbar.make(it, membersViewModel.message, Snackbar.LENGTH_LONG).show()
- }
- }
- }
- })
- fetchNewest(true)
+ // initializing MemberPagingAdaptermember
+ memberAdapter = MemberPagingAdapter(::openUserProfile)
+
+ // calling adapter and livedata observer methods
+ setUpAdapter()
+ setObserver()
+
+ // setting member list refresh listener
+ srlMembers.setOnRefreshListener { memberAdapter.refresh() }
+
+ // setting member list filter option fab
+ with(fabFilter) {
+ setOnClickListener { view -> view?.let { showListFilterPopup(it) } }
+ show()
+ }
}
- private fun runLayoutAnimation(recyclerView: RecyclerView) {
- val context = recyclerView.context
- recyclerView.layoutAnimation = AnimationUtils.loadLayoutAnimation(context,
- R.anim.layout_fall_down)
- recyclerView.adapter?.notifyDataSetChanged()
- recyclerView.scheduleLayoutAnimation()
+ // Setting up member list recyclerview adapter and item decor
+ private fun setUpAdapter() {
+ rvMembers.apply {
+ addItemDecoration(RecyclerViewItemDecoration())
+ adapter =
+ memberAdapter.withLoadStateFooter(footer = MemberLoadingStateAdapter { memberAdapter.retry() })
+ }
}
- private fun addLoadMoreListener(recyclerView: RecyclerView) {
- recyclerView.addOnScrollListener(object :
- EndlessRecyclerScrollListener(recyclerView.layoutManager as LinearLayoutManager) {
- override fun onLoadMore(page: Int, totalItemsCount: Int) {
- if (!isLoading) {
- fetchNewest(false)
- isLoading = true
- pbMembers.visibility = View.VISIBLE
+ // Setting observer to viewmodel to listen changes in member list livedata
+ private fun setObserver() {
+ lifecycleScope.launch {
+ lifecycle.whenStarted {
+ showMemberListLoadingState()
+
+ membersViewModel.userList.observe(viewLifecycleOwner) { data ->
+ rvMembers.smoothScrollToPosition(0)
+ memberAdapter.submitData(viewLifecycleOwner.lifecycle, data)
}
}
- })
+ }
}
- private fun openUserProfile(memberId: Int, sharedImageView: ImageView, sharedTextView: TextView) {
- val intent = Intent(activity, MemberProfileActivity::class.java)
- intent.putExtra(Constants.MEMBER_USER_ID, memberId)
- val imgAnim = Pair.create(sharedImageView,
- ViewCompat.getTransitionName(sharedImageView)!!)
-
- val options = ActivityOptionsCompat.makeSceneTransitionAnimation(baseActivity, imgAnim)
-
- startActivity(intent, options.toBundle())
- }
- private val openUserProfile: (Int) -> Unit =
- { memberId ->
- val intent = Intent(activity, MemberProfileActivity::class.java)
- intent.putExtra(Constants.MEMBER_USER_ID, memberId)
- startActivity(intent)
- }
+ // Setting member list loading states
+ private fun showMemberListLoadingState() {
+ memberAdapter.addLoadStateListener { loadState ->
+ if (loadState.refresh is LoadState.Loading) {
+ srlMembers.isRefreshing = memberAdapter.snapshot().isEmpty()
+ tvEmptyList.isVisible = false
+ } else {
+ srlMembers.isRefreshing = false
+
+ val error = loadState.let { state ->
+ when {
+ state.prepend is LoadState.Error -> state.prepend as LoadState.Error
+ state.append is LoadState.Error -> state.append as LoadState.Error
+ state.refresh is LoadState.Error -> state.refresh as LoadState.Error
+ else -> null
+ }
+ }
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.action_filter -> {
- val intent = Intent(context, FilterActivity::class.java)
- intent.putExtra(FILTER_MAP, filterMap)
- startActivityForResult(intent, FILTER_REQUEST_CODE)
- activity?.overridePendingTransition(
- R.anim.anim_slide_from_bottom,
- R.anim.anim_stay)
- true
- }
- R.id.menu_refresh -> {
- fetchNewest(true)
- true
+ error?.let {
+ if (memberAdapter.snapshot().isEmpty()) {
+ tvEmptyList.apply {
+ isVisible = true
+ text = it.error.message ?: "No such users available"
+ }
+ }
+ }
}
- else -> super.onOptionsItemSelected(item)
}
}
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- if (requestCode == FILTER_REQUEST_CODE && resultCode == RESULT_OK) {
- filterMap = data?.extras?.get(FILTER_MAP) as HashMap?
- ?: hashMapOf(SORT_KEY to SortValues.REGISTRATION_DATE.name)
- rvAdapter.updateUsersList(filterMap, membersViewModel.userList)
+ // On click method on recyclerview member item
+ private fun openUserProfile(member: User?) {
+ member?.let {
+ val intent = Intent(activity, MemberProfileActivity::class.java)
+ intent.putExtra(Constants.MEMBER_USER_EXTRAS, it)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ startActivity(intent)
}
}
- enum class SortValues {
- NAMEAZ,
- NAMEZA,
- REGISTRATION_DATE
- }
-
- private fun fetchNewest(isRefresh: Boolean) {
- srlMembers.isRefreshing = isRefresh
- membersViewModel.getUsers(isRefresh)
+ // Show member list filter option pop-up
+ private fun showListFilterPopup(view: View) {
+ val popup = PopupMenu(requireContext(), view)
+ popup.inflate(R.menu.menu_members)
+ popup.setOnMenuItemClickListener { item: MenuItem? ->
+ when (item!!.itemId) {
+ R.id.allMembers -> membersViewModel.getFilteredUserList(ListFilter.NO_FILTER)
+ R.id.availableToMentor -> membersViewModel.getFilteredUserList(ListFilter.AVAILABLE_TO_MENTOR)
+ R.id.needMentoring -> membersViewModel.getFilteredUserList(ListFilter.NEED_MENTORING)
+ R.id.haveSkill -> membersViewModel.getFilteredUserList(ListFilter.HAVE_SKILL)
+ }
+ true
+ }
+ popup.show()
}
}
diff --git a/app/src/main/java/org/anitab/mentorship/view/fragments/ProfileFragment.kt b/app/src/main/java/org/anitab/mentorship/view/fragments/ProfileFragment.kt
index 2bd37a44a..5d03b0b1a 100644
--- a/app/src/main/java/org/anitab/mentorship/view/fragments/ProfileFragment.kt
+++ b/app/src/main/java/org/anitab/mentorship/view/fragments/ProfileFragment.kt
@@ -10,7 +10,7 @@ import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.viewModels
import com.google.android.material.snackbar.Snackbar
-import kotlinx.android.synthetic.main.fragment_profile.*
+import kotlinx.android.synthetic.main.fragment_profile.srlProfile
import org.anitab.mentorship.R
import org.anitab.mentorship.databinding.FragmentProfileBinding
import org.anitab.mentorship.viewmodels.ProfileViewModel
@@ -25,7 +25,6 @@ class ProfileFragment : BaseFragment() {
* Creates an instance of ProfileFragment
*/
fun newInstance() = ProfileFragment()
- val TAG: String = ProfileFragment::class.java.simpleName
}
private lateinit var fragmentProfileBinding: FragmentProfileBinding
diff --git a/app/src/main/java/org/anitab/mentorship/view/fragments/RequestPagerFragment.kt b/app/src/main/java/org/anitab/mentorship/view/fragments/RequestPagerFragment.kt
index 974219c6f..cc4ed7b5e 100644
--- a/app/src/main/java/org/anitab/mentorship/view/fragments/RequestPagerFragment.kt
+++ b/app/src/main/java/org/anitab/mentorship/view/fragments/RequestPagerFragment.kt
@@ -4,7 +4,8 @@ import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
-import kotlinx.android.synthetic.main.fragment_request_pager.*
+import kotlinx.android.synthetic.main.fragment_request_pager.rvRequestsList
+import kotlinx.android.synthetic.main.fragment_request_pager.tvEmptyList
import org.anitab.mentorship.R
import org.anitab.mentorship.models.Relationship
import org.anitab.mentorship.utils.Constants
@@ -44,8 +45,8 @@ class RequestPagerFragment : BaseFragment() {
super.onActivityCreated(savedInstanceState)
arguments?.let {
- requestsList = it.getParcelableArrayList(Constants.REQUEST_LIST)
- emptyListText = it.getString(Constants.REQUEST_EMPTY_LIST_TEXT)
+ requestsList = it.getParcelableArrayList(Constants.REQUEST_LIST) ?: mutableListOf()
+ emptyListText = it.getString(Constants.REQUEST_EMPTY_LIST_TEXT).toString()
}
setView()
}
diff --git a/app/src/main/java/org/anitab/mentorship/viewmodels/MembersViewModel.kt b/app/src/main/java/org/anitab/mentorship/viewmodels/MembersViewModel.kt
index 124b57c43..4e38d5fb9 100644
--- a/app/src/main/java/org/anitab/mentorship/viewmodels/MembersViewModel.kt
+++ b/app/src/main/java/org/anitab/mentorship/viewmodels/MembersViewModel.kt
@@ -1,46 +1,72 @@
package org.anitab.mentorship.viewmodels
-import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.launch
+import androidx.paging.PagingData
+import androidx.paging.cachedIn
import org.anitab.mentorship.models.User
import org.anitab.mentorship.remote.datamanager.UserDataManager
-import org.anitab.mentorship.remote.requests.PaginationRequest
-import org.anitab.mentorship.utils.CommonUtils
-import org.anitab.mentorship.utils.Constants.ITEMS_PER_PAGE
/**
* This class represents the [ViewModel] component used for the Members Activity
*/
class MembersViewModel : ViewModel() {
- var tag = MembersViewModel::class.java.simpleName
-
private val userDataManager: UserDataManager = UserDataManager()
- val successful: MutableLiveData = MutableLiveData()
- var currentPage = 1
- lateinit var message: String
- var userList: ArrayList = arrayListOf()
-
- /**
- * Fetches users list from getUsers method of the UserService
- */
- fun getUsers(isRefresh: Boolean) {
- viewModelScope.launch {
- if (isRefresh) {
- userList.clear()
- currentPage = 1
+ private val userListNoFilter: LiveData> =
+ userDataManager.getAllUsers().cachedIn(viewModelScope)
+
+ private val userLisFilterByNeedMentoring: LiveData> =
+ userDataManager.getUsersWhoAreNeedMentoring().cachedIn(viewModelScope)
+
+ private val userListFilterByAvailableToMentor: LiveData> =
+ userDataManager.getUsersWhoAreAvailableToMentor().cachedIn(viewModelScope)
+
+ private val userListFilterByHaveSkill: LiveData> =
+ userDataManager.getUserWhoHaveSkills().cachedIn(viewModelScope)
+
+ val userList = MediatorLiveData>()
+
+ // setting default filter sa no filter
+ private var selectedUserFilter = ListFilter.NO_FILTER
+
+ init {
+ userList.addSource(userListNoFilter) { list ->
+ if (selectedUserFilter == ListFilter.NO_FILTER) {
+ list?.let { userList.value = it }
+ }
+ }
+
+ userList.addSource(userLisFilterByNeedMentoring) { list ->
+ if (selectedUserFilter == ListFilter.NEED_MENTORING) {
+ list?.let { userList.value = it }
}
- try {
- userList.addAll(userDataManager.getUsers(PaginationRequest(currentPage, ITEMS_PER_PAGE)))
- currentPage++
- successful.postValue(true)
- } catch (throwable: Throwable) {
- message = CommonUtils.getErrorMessage(throwable, tag)
- successful.postValue(false)
+ }
+
+ userList.addSource(userListFilterByAvailableToMentor) { list ->
+ if (selectedUserFilter == ListFilter.AVAILABLE_TO_MENTOR) {
+ list?.let { userList.value = it }
+ }
+ }
+
+ userList.addSource(userListFilterByHaveSkill) { list ->
+ if (selectedUserFilter == ListFilter.HAVE_SKILL) {
+ list?.let { userList.value = it }
}
}
}
+
+ fun getFilteredUserList(filter: ListFilter) = when (filter) {
+ ListFilter.NO_FILTER -> userListNoFilter.value?.let { userList.value = it }
+ ListFilter.NEED_MENTORING -> userLisFilterByNeedMentoring.value?.let { userList.value = it }
+ ListFilter.AVAILABLE_TO_MENTOR -> userListFilterByAvailableToMentor.value?.let { userList.value = it }
+ ListFilter.HAVE_SKILL -> userListFilterByHaveSkill.value?.let { userList.value = it }
+ }.also { selectedUserFilter = filter }
+}
+
+enum class ListFilter {
+ NO_FILTER, NEED_MENTORING, AVAILABLE_TO_MENTOR, HAVE_SKILL
}
diff --git a/app/src/main/res/layout/activity_filter.xml b/app/src/main/res/layout/activity_filter.xml
deleted file mode 100644
index 0b6e9a105..000000000
--- a/app/src/main/res/layout/activity_filter.xml
+++ /dev/null
@@ -1,285 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_members.xml b/app/src/main/res/layout/fragment_members.xml
index e61eda731..1a08be6ed 100644
--- a/app/src/main/res/layout/fragment_members.xml
+++ b/app/src/main/res/layout/fragment_members.xml
@@ -1,9 +1,10 @@
+ android:layout_height="match_parent">
-
-
-
+ app:layout_constraintTop_toTopOf="parent"
+ tools:itemCount="3"
+ tools:listitem="@layout/list_member_item" />
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_member_item.xml b/app/src/main/res/layout/list_member_item.xml
index fed8f9d54..88417e743 100644
--- a/app/src/main/res/layout/list_member_item.xml
+++ b/app/src/main/res/layout/list_member_item.xml
@@ -1,80 +1,93 @@
-
+ xmlns:tools="http://schemas.android.com/tools">
-
+
+
+
-
+ android:background="?attr/selectableItemBackground">
-
+
-
+
-
+
-
+
+
+
+
+
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_member_network_state_item.xml b/app/src/main/res/layout/list_member_network_state_item.xml
new file mode 100644
index 000000000..cee5501b6
--- /dev/null
+++ b/app/src/main/res/layout/list_member_network_state_item.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_members.xml b/app/src/main/res/menu/menu_members.xml
index 3773a22f9..b83f2e01b 100644
--- a/app/src/main/res/menu/menu_members.xml
+++ b/app/src/main/res/menu/menu_members.xml
@@ -2,15 +2,20 @@
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3fcf004b6..dc2cd3f98 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -22,6 +22,7 @@
Add a new task
Age
All
+ All members
Mentorship System
Apply filter
Available to be a:
@@ -114,6 +115,7 @@
Mentorship Requests
From
Github Repository
+ Have skill
I will be a …
Please fill in the input fields and/or assign a rating
Interests
@@ -201,6 +203,7 @@
%1$s %2$s
You want to be %1$s\'s %2$s until %3$s
%1$s wants to be your %2$s until %3$s
+ Retry
You have already sent a similar request to\u0020
Save
Send Request
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 431e64373..2f95afd50 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -16,13 +16,6 @@
- true
-
-