Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1166: Migration to Paging3 for MembersFragment #1221

Open
wants to merge 35 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6743c2c
added androidx paging3 library, to replace current pagination impleme…
bosankus Dec 2, 2021
2527493
implemented data flow via paging3 from UserDataManager to MembersView…
bosankus Dec 2, 2021
aef5c56
Completed flow from MembersViewModel to the UI via MemberPagingAdapter
bosankus Dec 5, 2021
b61ef0b
Connected Room, RemoteMediator, UserDataManager to let data flow to M…
bosankus Jan 7, 2022
28da24b
fix: Crash on MemberProfileActivity.kt
bosankus Mar 1, 2022
5d984a7
Merge branch 'anitab-org:develop' into paging3-member-list
bosankus Sep 25, 2022
08ad17e
Code clean: Removed unused methods and imports
bosankus Oct 3, 2022
aca6bf6
Merge branch 'develop' into paging3-member-list
bosankus Oct 10, 2022
08d74b0
Undo random code changes.
bosankus Oct 11, 2022
f285da2
Merge remote-tracking branch 'origin/paging3-member-list' into paging…
bosankus Oct 11, 2022
d651fea
Fixed serialized names in User.kt
bosankus Oct 11, 2022
33fa76b
Fixed data layer for filtering user list
bosankus Oct 15, 2022
a28a236
Added mediatior livedata to manage user filtered data
bosankus Oct 15, 2022
be39931
Removed filtering paged list to UserDataManager.kt
bosankus Oct 15, 2022
e7328a1
Connected UI to paged member list with filter
bosankus Oct 15, 2022
4bf0818
Fixed spotless errors
bosankus Oct 15, 2022
41f8b9b
Removed redudant kotlin-stdlib-jdk7 dep
bosankus Oct 15, 2022
0ee37b9
MemberProfileActivity: Code clean
bosankus Oct 16, 2022
67379dc
MainActivity: Code clean
bosankus Oct 16, 2022
fbf64a7
ChangePasswordFragment: Code clean
bosankus Oct 16, 2022
1e5694f
EditProfileFragment: Code clean
bosankus Oct 16, 2022
c1f55a0
EditProfileFragment: Code clean
bosankus Oct 16, 2022
8663377
HomeFragment: Code clean
bosankus Oct 16, 2022
14c3896
MembersFragment: Code clean
bosankus Oct 16, 2022
e70895d
ProfileFragment.kt: Code clean
bosankus Oct 16, 2022
864b16e
RequestPagerFragment.kt.kt: spotless fix
bosankus Oct 16, 2022
d71923f
MemberProfileViewModel: Code clean
bosankus Oct 16, 2022
8fbdc2f
MemberProfileViewModel: fix catch block
bosankus Oct 16, 2022
950f8cf
MemberProfileViewModel: fix CoroutineDispatcher
bosankus Oct 16, 2022
f11ea42
Removed ViewModelFactory.kt & it's implementations
bosankus Oct 17, 2022
f28a607
MemberProfileViewModel.kt - Code clean
bosankus Oct 17, 2022
7038a97
ProfileViewModel.kt - Code clean
bosankus Oct 17, 2022
280d681
ChangePasswordFragment.kt - Code clean
bosankus Oct 17, 2022
f43ee12
ChangePasswordFragment.kt - Code clean
bosankus Oct 17, 2022
72f7f2f
Merge branch 'develop' into paging3-member-list
bosankus Nov 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}
6 changes: 2 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@
android:theme="@style/AppTheme.NoActionBar" />
<activity android:name=".view.activities.AboutActivity" />
<activity android:name=".view.activities.FeedbackActivity" />
<activity
android:name=".view.activities.FilterActivity"
android:theme="@style/FilterTheme" />
<activity
android:name=".view.activities.SplashScreenActivity"
android:theme="@style/AppTheme.NoActionBar">
android:theme="@style/AppTheme.NoActionBar"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/org/anitab/mentorship/Injection.kt
Original file line number Diff line number Diff line change
@@ -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())
}
22 changes: 22 additions & 0 deletions app/src/main/java/org/anitab/mentorship/local/RemoteKeysDao.kt
Original file line number Diff line number Diff line change
@@ -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<RemoteKeysEntity>)

@Query("SELECT * FROM remote_keys")
suspend fun getRemoteKeys(): List<RemoteKeysEntity>

@Query("SELECT * FROM remote_keys WHERE userId = :userId")
suspend fun remoteKeysRepoId(userId: Int): RemoteKeysEntity?

@Query("DELETE FROM remote_keys")
suspend fun clearRemoteKeys()
}
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions app/src/main/java/org/anitab/mentorship/local/UserDao.kt
Original file line number Diff line number Diff line change
@@ -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<User>)

@Query("SELECT * from user_table")
fun getAllUsers(): PagingSource<Int, User>

@Query("DELETE from user_table")
suspend fun clearAllUsers()
}
38 changes: 38 additions & 0 deletions app/src/main/java/org/anitab/mentorship/local/UserDatabase.kt
Original file line number Diff line number Diff line change
@@ -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()
}
}
31 changes: 18 additions & 13 deletions app/src/main/java/org/anitab/mentorship/models/User.kt
Original file line number Diff line number Diff line change
@@ -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

/**
Expand All @@ -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
Original file line number Diff line number Diff line change
@@ -1,38 +1,117 @@
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
*/
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<User> {
return withContext(dispatcher) { apiManager.userService.getVerifiedUsers() }

@OptIn(ExperimentalPagingApi::class)
fun getAllUsers(): LiveData<PagingData<User>> {
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<User> {
return withContext(dispatcher) { apiManager.userService.getVerifiedUsers(paginationRequest.pagination) }
@OptIn(ExperimentalPagingApi::class)
fun getUsersWhoAreAvailableToMentor(): LiveData<PagingData<User>> {
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<PagingData<User>> {
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<PagingData<User>> {
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]
Expand Down
Loading