Merge branch 'dependency_injection' into 'master'

dependency injection

See merge request pixeldroid/PixelDroid!236
This commit is contained in:
Matthieu 2020-07-26 20:56:01 +02:00
commit ede4a2ffbf
43 changed files with 758 additions and 346 deletions

View File

@ -6,7 +6,7 @@ apply plugin: 'jacoco'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
buildToolsVersion '30.0.1'
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
@ -56,12 +56,12 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.navigation:navigation-fragment:2.2.2'
implementation 'androidx.navigation:navigation-ui:2.2.2'
implementation 'com.squareup.okhttp3:okhttp:4.7.2'
implementation 'androidx.navigation:navigation-fragment:2.3.0'
implementation 'androidx.navigation:navigation-ui:2.3.0'
implementation 'com.squareup.okhttp3:okhttp:4.8.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
@ -78,9 +78,9 @@ dependencies {
testImplementation "androidx.room:room-testing:$room_version"
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
implementation 'info.androidhive:imagefilters:1.0.7'
implementation 'com.github.yalantis:ucrop:2.2.5-native'
@ -97,11 +97,11 @@ dependencies {
kapt 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation "com.github.tomakehurst:wiremock-jre8:2.26.3"
testImplementation 'com.github.tomakehurst:wiremock-jre8:2.27.1'
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
testImplementation 'junit:junit:4.13'
androidTestImplementation('com.squareup.okhttp3:mockwebserver:4.7.2')
androidTestImplementation('com.squareup.okhttp3:mockwebserver:4.8.0')
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
@ -126,24 +126,30 @@ dependencies {
implementation "com.mikepenz:iconics-views:5.0.2"
implementation 'com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar'
//Dagger (dependency injection)
implementation 'com.google.dagger:dagger-android:2.28.3'
implementation 'com.google.dagger:dagger-android-support:2.28.3'
// if you use the support libraries
kapt 'com.google.dagger:dagger-android-processor:2.28.3'
kapt 'com.google.dagger:dagger-compiler:2.28.3'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
def fragment_version = '1.2.4'
def fragment_version = '1.2.5'
debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
// Use the most recent version of CameraX
def camerax_version = '1.0.0-beta04'
def camerax_version = '1.0.0-beta07'
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
// CameraX Lifecycle library
implementation "androidx.camera:camera-lifecycle:$camerax_version"
// CameraX View class
implementation 'androidx.camera:camera-view:1.0.0-alpha11'
implementation 'androidx.camera:camera-view:1.0.0-alpha14'
implementation 'com.karumi:dexter:6.1.2'
implementation 'com.karumi:dexter:6.2.1'
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'

View File

@ -16,6 +16,7 @@ import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils
import org.junit.Before
import org.junit.Rule
@ -40,7 +41,7 @@ class DrawerMenuTest {
val baseUrl = mockServer.getUrl()
context = ApplicationProvider.getApplicationContext()
db = DBUtils.initDB(context)
db = initDB(context)
db.clearAllTables()
db.instanceDao().insertInstance(
InstanceDatabaseEntity(

View File

@ -6,7 +6,6 @@ import android.widget.TextView
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.swipeDown
import androidx.test.espresso.action.ViewActions.swipeUp
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition
@ -17,7 +16,7 @@ import com.google.android.material.tabs.TabLayout
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.fragments.feeds.PostViewHolder
import com.h.pixeldroid.fragments.feeds.postFeeds.PostViewHolder
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.clickChildViewWithId
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.first
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.getText
@ -25,6 +24,7 @@ import com.h.pixeldroid.testUtility.CustomMatchers.Companion.second
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.slowSwipeUp
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.typeTextInViewWithId
import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils
import org.junit.Before
import org.junit.Rule
@ -49,7 +49,7 @@ class HomeFeedTest {
mockServer.start()
val baseUrl = mockServer.getUrl()
context = ApplicationProvider.getApplicationContext()
db = DBUtils.initDB(context)
db = initDB(context)
db.clearAllTables()
db.instanceDao().insertInstance(
InstanceDatabaseEntity(

View File

@ -26,10 +26,11 @@ import androidx.test.rule.ActivityTestRule
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.fragments.feeds.PostViewHolder
import com.h.pixeldroid.fragments.feeds.postFeeds.PostViewHolder
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_TAG
import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils
import org.hamcrest.CoreMatchers
import org.hamcrest.Matcher
@ -64,7 +65,7 @@ class IntentTest {
val baseUrl = mockServer.getUrl()
context = ApplicationProvider.getApplicationContext()
db = DBUtils.initDB(context)
db = initDB(context)
db.clearAllTables()
db.instanceDao().insertInstance(
InstanceDatabaseEntity(

View File

@ -13,7 +13,7 @@ import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.utils.DBUtils
import com.h.pixeldroid.testUtility.initDB
import org.junit.After
import org.junit.Before
import org.junit.Rule
@ -43,7 +43,7 @@ class LoginActivityOfflineTest {
fun before() {
switchAirplaneMode()
val context = ApplicationProvider.getApplicationContext<Context>()
db = DBUtils.initDB(context)
db = initDB(context)
db.clearAllTables()
ActivityScenario.launch(LoginActivity::class.java)
}

View File

@ -20,6 +20,7 @@ import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils
import org.junit.Before
import org.junit.Rule
@ -45,7 +46,7 @@ class LoginActivityOnlineTest {
context = ApplicationProvider.getApplicationContext()
pref = context.getSharedPreferences("com.h.pixeldroid.pref", Context.MODE_PRIVATE)
pref.edit().clear().apply()
db = DBUtils.initDB(context)
db = initDB(context)
db.clearAllTables()
db.close()
}
@ -97,7 +98,7 @@ class LoginActivityOnlineTest {
@Test
fun correctIntentReturnLoadsMainActivity() {
context = ApplicationProvider.getApplicationContext()
db = DBUtils.initDB(context)
db = initDB(context)
db.clearAllTables()
db.instanceDao().insertInstance(

View File

@ -16,10 +16,11 @@ import com.google.android.material.tabs.TabLayout
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.fragments.feeds.PostViewHolder
import com.h.pixeldroid.fragments.feeds.postFeeds.PostViewHolder
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.clickChildViewWithId
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.first
import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils
import com.h.pixeldroid.utils.PostUtils.Companion.censorColorMatrix
import com.h.pixeldroid.utils.PostUtils.Companion.uncensorColorMatrix
@ -46,7 +47,7 @@ class MockedServerTest {
mockServer.start()
val baseUrl = mockServer.getUrl()
context = ApplicationProvider.getApplicationContext()
db = DBUtils.initDB(context)
db = initDB(context)
db.clearAllTables()
db.instanceDao().insertInstance(
InstanceDatabaseEntity(

View File

@ -24,6 +24,7 @@ import com.h.pixeldroid.db.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.testUtility.CustomMatchers
import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils
import kotlinx.android.synthetic.main.activity_post_creation.*
import org.hamcrest.Matchers.not
@ -60,7 +61,7 @@ class PostCreationActivityTest {
val context = InstrumentationRegistry.getInstrumentation().targetContext
mockServer.start()
val baseUrl = mockServer.getUrl()
db = DBUtils.initDB(context)
db = initDB(context)
db.clearAllTables()
db.instanceDao().insertInstance(
InstanceDatabaseEntity(

View File

@ -19,6 +19,7 @@ import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils
import kotlinx.android.synthetic.main.activity_main.*
import org.hamcrest.Matcher
@ -73,7 +74,7 @@ class PostFragmentUITests {
val context = InstrumentationRegistry.getInstrumentation().targetContext
mockServer.start()
val baseUrl = mockServer.getUrl()
db = DBUtils.initDB(context)
db = initDB(context)
db.clearAllTables()
db.instanceDao().insertInstance(
InstanceDatabaseEntity(

View File

@ -27,6 +27,7 @@ import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.objects.Tag
import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils
import org.hamcrest.Matcher
import org.junit.*
@ -49,7 +50,7 @@ class PostTest {
val mockServer = MockServer()
mockServer.start()
val baseUrl = mockServer.getUrl()
db = DBUtils.initDB(context)
db = initDB(context)
db.clearAllTables()
db.instanceDao().insertInstance(
InstanceDatabaseEntity(

View File

@ -0,0 +1,12 @@
package com.h.pixeldroid.testUtility
import android.content.Context
import androidx.room.Room
import com.h.pixeldroid.db.AppDatabase
fun initDB(context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java, "pixeldroid"
).allowMainThreadQueries().build()
}

View File

@ -16,13 +16,14 @@
<application
android:name=".Pixeldroid"
android:allowBackup="true"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
tools:replace="android:allowBackup">
<activity
android:name=".PhotoEditActivity"

View File

@ -4,6 +4,8 @@ import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.fragments.feeds.AccountListFragment
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_ID_TAG
@ -12,28 +14,32 @@ import com.h.pixeldroid.utils.DBUtils
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
class FollowsActivity : AppCompatActivity() {
private var followsFragment = AccountListFragment()
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_followers)
(this.application as Pixeldroid).getAppComponent().inject(this)
// Get account id
val id = intent.getSerializableExtra(ACCOUNT_ID_TAG) as String?
val following = intent.getSerializableExtra(FOLLOWING_TAG) as Boolean
if(id == null) {
val db = DBUtils.initDB(applicationContext)
val user = db.userDao().getActiveUser()
val domain = user?.instance_uri.orEmpty()
val accessToken = user?.accessToken.orEmpty()
db.close()
val pixelfedAPI = PixelfedAPI.create(domain)
val pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
pixelfedAPI.verifyCredentials("Bearer $accessToken").enqueue(object :
Callback<Account> {

View File

@ -4,7 +4,6 @@ import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.net.ConnectivityManager
import android.net.Uri
import android.os.Bundle
import android.view.View
@ -13,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Application
import com.h.pixeldroid.objects.Instance
@ -26,6 +26,7 @@ import okhttp3.HttpUrl
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
class LoginActivity : AppCompatActivity() {
@ -38,7 +39,12 @@ class LoginActivity : AppCompatActivity() {
private lateinit var oauthScheme: String
private lateinit var appName: String
private lateinit var preferences: SharedPreferences
private lateinit var db: AppDatabase
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
private lateinit var pixelfedAPI: PixelfedAPI
private var inputVisibility: Int = View.GONE
@ -46,11 +52,11 @@ class LoginActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
(application as Pixeldroid).getAppComponent().inject(this)
loadingAnimation(true)
appName = getString(R.string.app_name)
oauthScheme = getString(R.string.auth_scheme)
preferences = getSharedPreferences("$PACKAGE_ID.pref", Context.MODE_PRIVATE)
db = DBUtils.initDB(applicationContext)
if (Utils.hasInternet(applicationContext)) {
connect_instance_button.setOnClickListener {
@ -61,8 +67,8 @@ class LoginActivity : AppCompatActivity() {
} else {
login_activity_connection_required.visibility = View.VISIBLE
login_activity_connection_required_button.setOnClickListener {
finish();
startActivity(intent);
finish()
startActivity(intent)
}
}
loadingAnimation(false)
@ -113,7 +119,7 @@ class LoginActivity : AppCompatActivity() {
hideKeyboard()
loadingAnimation(true)
PixelfedAPI.create(normalizedDomain).registerApplication(
apiHolder.setDomain(normalizedDomain).registerApplication(
appName,"$oauthScheme://$PACKAGE_ID", SCOPE
).enqueue(object : Callback<Application> {
override fun onResponse(call: Call<Application>, response: Response<Application>) {
@ -185,7 +191,7 @@ class LoginActivity : AppCompatActivity() {
}
}
pixelfedAPI = PixelfedAPI.create(domain)
pixelfedAPI = apiHolder.setDomain(domain)
pixelfedAPI.obtainToken(
clientId, clientSecret, "$oauthScheme://$PACKAGE_ID", SCOPE, code,
"authorization_code"
@ -242,7 +248,7 @@ class LoginActivity : AppCompatActivity() {
.enqueue(object : Callback<Account> {
override fun onResponse(call: Call<Account>, response: Response<Account>) {
if (response.body() != null && response.isSuccessful) {
db.userDao().deActivateActiveUser()
db.userDao().deActivateActiveUsers()
val user = response.body() as Account
DBUtils.addUser(
db,
@ -251,7 +257,7 @@ class LoginActivity : AppCompatActivity() {
activeUser = true,
accessToken = accessToken
)
db.close()
apiHolder.setDomainToCurrentUser(db)
val intent = Intent(this@LoginActivity, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)

View File

@ -14,15 +14,15 @@ import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.bumptech.glide.Glide
import com.google.android.material.tabs.TabLayoutMediator
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.fragments.CameraFragment
import com.h.pixeldroid.fragments.SearchDiscoverFragment
import com.h.pixeldroid.fragments.feeds.NotificationsFragment
import com.h.pixeldroid.fragments.feeds.OfflineFeedFragment
import com.h.pixeldroid.fragments.feeds.PostsFeedFragment
import com.h.pixeldroid.fragments.feeds.PublicTimelineFragment
import com.h.pixeldroid.fragments.feeds.postFeeds.HomeTimelineFragment
import com.h.pixeldroid.fragments.feeds.postFeeds.PublicTimelineFragment
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.utils.DBUtils
import com.h.pixeldroid.utils.Utils.Companion.hasInternet
@ -39,11 +39,16 @@ import kotlinx.android.synthetic.main.activity_main.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
class MainActivity : AppCompatActivity() {
private val searchDiscoverFragment: SearchDiscoverFragment = SearchDiscoverFragment()
private lateinit var db: AppDatabase
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
private lateinit var header: AccountHeaderView
private var user: UserDatabaseEntity? = null
@ -56,7 +61,7 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = DBUtils.initDB(applicationContext)
(this.application as Pixeldroid).getAppComponent().inject(this)
//get the currently active user
user = db.userDao().getActiveUser()
@ -67,7 +72,7 @@ class MainActivity : AppCompatActivity() {
} else {
setupDrawer()
val tabs = arrayOf(
if (hasInternet(applicationContext)) PostsFeedFragment()
if (hasInternet(applicationContext)) HomeTimelineFragment()
else OfflineFeedFragment(),
searchDiscoverFragment,
CameraFragment(),
@ -163,14 +168,17 @@ class MainActivity : AppCompatActivity() {
}
private fun getUpdatedAccount(){
private fun getUpdatedAccount() {
if (hasInternet(applicationContext)) {
val domain = user?.instance_uri.orEmpty()
val accessToken = user?.accessToken.orEmpty()
val pixelfedAPI = PixelfedAPI.create(domain)
pixelfedAPI.verifyCredentials("Bearer $accessToken")
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
api.verifyCredentials("Bearer $accessToken")
.enqueue(object : Callback<Account> {
override fun onResponse(call: Call<Account>, response: Response<Account>) {
override fun onResponse(
call: Call<Account>,
response: Response<Account>
) {
if (response.body() != null && response.isSuccessful) {
val account = response.body() as Account
DBUtils.addUser(db, account, domain, accessToken = accessToken)
@ -197,9 +205,9 @@ class MainActivity : AppCompatActivity() {
return false
}
db.userDao().deActivateActiveUser()
db.userDao().deActivateActiveUsers()
db.userDao().activateUser(profile.identifier.toString())
apiHolder.setDomainToCurrentUser(db)
val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
@ -271,9 +279,9 @@ class MainActivity : AppCompatActivity() {
}
/**
Launches the given activity and put it as the current one
Setting argument firstTime to true means the task history will be reset (as if the app were launched anew into
this activity)
* Launches the given activity and put it as the current one
* @param firstTime to true means the task history will be reset (as if the app were
* launched anew into this activity)
*/
private fun launchActivity(activity: AppCompatActivity, firstTime: Boolean = false) {
val intent = Intent(this, activity::class.java)
@ -285,7 +293,7 @@ class MainActivity : AppCompatActivity() {
}
/**
Closes the drawer if it is open, when we press the back button
* Closes the drawer if it is open, when we press the back button
*/
override fun onBackPressed() {
if(drawer_layout.isDrawerOpen(GravityCompat.START)){

View File

@ -2,13 +2,29 @@ package com.h.pixeldroid
import android.app.Application
import androidx.preference.PreferenceManager
import com.h.pixeldroid.di.*
import com.h.pixeldroid.utils.ThemeUtils
class Pixeldroid: Application() {
private lateinit var mApplicationComponent: ApplicationComponent
override fun onCreate() {
super.onCreate()
val sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(this)
ThemeUtils.setThemeFromPreferences(sharedPreferences, resources)
mApplicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(ApplicationModule(this))
.databaseModule(DatabaseModule(applicationContext))
.aPIModule(APIModule())
.build()
mApplicationComponent.inject(this);
}
fun getAppComponent(): ApplicationComponent {
return mApplicationComponent
}
}

View File

@ -5,6 +5,8 @@ import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.fragments.PostFragment
import com.h.pixeldroid.objects.DiscoverPost
import com.h.pixeldroid.objects.Status
@ -16,25 +18,32 @@ import kotlinx.android.synthetic.main.activity_post.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
class PostActivity : AppCompatActivity() {
private lateinit var postFragment : PostFragment
lateinit var domain : String
private lateinit var accessToken : String
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_post)
(this.application as Pixeldroid).getAppComponent().inject(this)
val status = intent.getSerializableExtra(POST_TAG) as Status?
val discoverPost: DiscoverPost? = intent.getSerializableExtra(DISCOVER_TAG) as DiscoverPost?
val db = DBUtils.initDB(applicationContext)
val user = db.userDao().getActiveUser()
domain = user?.instance_uri.orEmpty()
accessToken = user?.accessToken.orEmpty()
db.close()
postFragment = PostFragment()
val arguments = Bundle()
@ -52,7 +61,7 @@ class PostActivity : AppCompatActivity() {
arguments: Bundle,
discoverPost: DiscoverPost
) {
val api = PixelfedAPI.create(domain)
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
val id = discoverPost.url?.substringAfterLast('/') ?: ""
api.getStatus("Bearer $accessToken", id).enqueue(object : Callback<Status> {

View File

@ -19,7 +19,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.google.android.material.textfield.TextInputEditText
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.interfaces.PostCreationListener
import com.h.pixeldroid.objects.Attachment
import com.h.pixeldroid.objects.Instance
@ -35,6 +37,7 @@ import okhttp3.MultipartBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
class PostCreationActivity : AppCompatActivity(), PostCreationListener {
@ -58,22 +61,28 @@ class PostCreationActivity : AppCompatActivity(), PostCreationListener {
private var maxLength: Int = Instance.DEFAULT_MAX_TOOT_CHARS
private var description: String = ""
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_post_creation)
(this.application as Pixeldroid).getAppComponent().inject(this)
// load images
posts = intent.getStringArrayListExtra("pictures_uri")!!
progressList = posts.map { 0 } as ArrayList<Int>
muListOfIds = posts.map { "" }.toMutableList()
val db = DBUtils.initDB(applicationContext)
user = db.userDao().getActiveUser()
val instances = db.instanceDao().getAll()
db.close()
maxLength = if (user!=null){
val thisInstances =
instances.filter { instanceDatabaseEntity ->
@ -84,9 +93,8 @@ class PostCreationActivity : AppCompatActivity(), PostCreationListener {
Instance.DEFAULT_MAX_TOOT_CHARS
}
val domain = user?.instance_uri.orEmpty()
accessToken = user?.accessToken.orEmpty()
pixelfedAPI = PixelfedAPI.create(domain)
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
// check if the pictures are alright
// TODO

View File

@ -16,6 +16,8 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.h.pixeldroid.adapters.ProfilePostsRecyclerViewAdapter
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_TAG
import com.h.pixeldroid.objects.Relationship
@ -26,6 +28,7 @@ import com.h.pixeldroid.utils.ImageConverter
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
class ProfileActivity : AppCompatActivity() {
private lateinit var pixelfedAPI : PixelfedAPI
@ -34,19 +37,24 @@ class ProfileActivity : AppCompatActivity() {
private lateinit var accessToken : String
private lateinit var domain : String
private var account: Account? = null
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
val db = DBUtils.initDB(applicationContext)
(this.application as Pixeldroid).getAppComponent().inject(this)
val user = db.userDao().getActiveUser()
domain = user?.instance_uri.orEmpty()
pixelfedAPI = PixelfedAPI.create(domain)
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
accessToken = user?.accessToken.orEmpty()
db.close()
// Set posts RecyclerView as a grid with 3 columns
recycler = findViewById(R.id.profilePostsRecyclerView)

View File

@ -1,5 +1,6 @@
package com.h.pixeldroid.api
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.objects.*
import io.reactivex.Observable
import okhttp3.MultipartBody
@ -9,6 +10,8 @@ import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.*
import retrofit2.http.Field
import javax.inject.Inject
import javax.inject.Provider
/*
@ -246,4 +249,4 @@ interface PixelfedAPI {
fun discover(
@Header("Authorization") authorization: String
) : Call<DiscoverPosts>
}
}

View File

@ -16,8 +16,8 @@ interface UserDao {
@Query("SELECT * FROM users WHERE isActive=1 LIMIT 1")
fun getActiveUser(): UserDatabaseEntity?
@Query("UPDATE users SET isActive=0 WHERE isActive=1")
fun deActivateActiveUser()
@Query("UPDATE users SET isActive=0")
fun deActivateActiveUsers()
@Query("UPDATE users SET isActive=1 WHERE user_id=:id")
fun activateUser(id: String)

View File

@ -0,0 +1,41 @@
package com.h.pixeldroid.di
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import dagger.Module
import dagger.Provides
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton
@Module
class APIModule{
@Provides
@Singleton
fun providesAPIHolder(db: AppDatabase): PixelfedAPIHolder {
return PixelfedAPIHolder(db.userDao().getActiveUser()?.instance_uri)
}
}
class PixelfedAPIHolder(domain: String?){
private val intermediate: Retrofit.Builder = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
var api: PixelfedAPI? = if (domain != null) setDomain(domain) else null
fun setDomainToCurrentUser(db: AppDatabase): PixelfedAPI {
return setDomain(db.userDao().getActiveUser()!!.instance_uri)
}
fun setDomain(domain: String): PixelfedAPI {
val newAPI = intermediate
.baseUrl(domain)
.build().create(PixelfedAPI::class.java)
api = newAPI
return newAPI
}
}

View File

@ -0,0 +1,34 @@
package com.h.pixeldroid.di
import android.app.Application
import android.content.Context
import com.h.pixeldroid.*
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.fragments.PostFragment
import com.h.pixeldroid.fragments.SearchDiscoverFragment
import com.h.pixeldroid.fragments.feeds.FeedFragment
import com.h.pixeldroid.fragments.feeds.OfflineFeedFragment
import dagger.Component
import javax.inject.Singleton
@Singleton
@Component(modules = [ApplicationModule::class, DatabaseModule::class, APIModule::class])
interface ApplicationComponent {
fun inject(application: Pixeldroid?)
fun inject(activity: LoginActivity?)
fun inject(feedFragment: FeedFragment)
fun inject(activity: FollowsActivity?)
fun inject(activity: PostActivity?)
fun inject(activity: PostCreationActivity?)
fun inject(activity: ProfileActivity?)
fun inject(mainActivity: MainActivity?)
fun inject(fragment: PostFragment)
fun inject(fragment: SearchDiscoverFragment)
fun inject(fragment: OfflineFeedFragment)
val context: Context?
val application: Application?
val database: AppDatabase
}

View File

@ -0,0 +1,27 @@
package com.h.pixeldroid.di
import android.app.Application
import android.content.Context
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@Module
class ApplicationModule(app: Application) {
private val mApplication: Application = app
@Singleton
@Provides
fun provideContext(): Context {
return mApplication
}
@Singleton
@Provides
fun provideApplication(): Application {
return mApplication
}
}

View File

@ -0,0 +1,21 @@
package com.h.pixeldroid.di
import android.content.Context
import androidx.room.Room
import com.h.pixeldroid.db.AppDatabase
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@Module
class DatabaseModule(private val context: Context) {
@Provides
@Singleton
fun providesDatabase(): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java, "pixeldroid"
).allowMainThreadQueries().build()
}
}

View File

@ -8,54 +8,67 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import com.h.pixeldroid.Pixeldroid
import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.fragments.feeds.PostViewHolder
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.fragments.feeds.postFeeds.PostViewHolder
import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.objects.Status.Companion.DOMAIN_TAG
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
import com.h.pixeldroid.utils.DBUtils
import javax.inject.Inject
class PostFragment : Fragment() {
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val current_status = arguments?.getSerializable(POST_TAG) as Status?
val currentStatus = arguments?.getSerializable(POST_TAG) as Status?
val statusDomain = arguments?.getString(DOMAIN_TAG)!!
val root: View = inflater.inflate(R.layout.post_fragment, container, false)
val picRequest = Glide.with(this)
.asDrawable().fitCenter()
.placeholder(ColorDrawable(Color.GRAY))
current_status?.setupPost(root, picRequest, this, statusDomain, true)
currentStatus?.setupPost(root, picRequest, this, statusDomain, true)
//Setup arguments needed for the onclicklisteners
val holder = PostViewHolder(root, requireContext())
val db = DBUtils.initDB(requireContext())
val holder = PostViewHolder(
root,
requireContext()
)
(requireActivity().application as Pixeldroid).getAppComponent().inject(this)
val user = db.userDao().getActiveUser()
val domain = user?.instance_uri.orEmpty()
val accessToken = user?.accessToken.orEmpty()
val api = PixelfedAPI.create(domain)
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
current_status?.setDescription(root, api, "Bearer $accessToken")
currentStatus?.setDescription(root, api, "Bearer $accessToken")
//Activate onclickListeners
current_status?.activateLiker(holder, api, "Bearer $accessToken",
current_status.favourited ?: false
currentStatus?.activateLiker(holder, api, "Bearer $accessToken",
currentStatus.favourited ?: false
)
current_status?.activateReblogger(holder, api, "Bearer $accessToken",
current_status.reblogged ?: false
currentStatus?.activateReblogger(holder, api, "Bearer $accessToken",
currentStatus.reblogged ?: false
)
current_status?.activateCommenter(holder, api, "Bearer $accessToken")
current_status?.showComments(holder, api, "Bearer $accessToken")
currentStatus?.activateCommenter(holder, api, "Bearer $accessToken")
currentStatus?.showComments(holder, api, "Bearer $accessToken")
//Activate double tap liking
current_status?.activateDoubleTapLiker(holder, api, "Bearer $accessToken")
currentStatus?.activateDoubleTapLiker(holder, api, "Bearer $accessToken")
return root
}

View File

@ -14,10 +14,13 @@ import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.h.pixeldroid.Pixeldroid
import com.h.pixeldroid.PostActivity
import com.h.pixeldroid.R
import com.h.pixeldroid.SearchActivity
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.objects.DiscoverPost
import com.h.pixeldroid.objects.DiscoverPosts
import com.h.pixeldroid.objects.Status
@ -26,6 +29,7 @@ import com.h.pixeldroid.utils.ImageConverter
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
/**
* This fragment lets you search and use Pixelfed's Discover feature
@ -38,6 +42,12 @@ class SearchDiscoverFragment : Fragment() {
private lateinit var accessToken: String
private lateinit var discoverProgressBar: ProgressBar
private lateinit var discoverRefreshLayout: SwipeRefreshLayout
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreateView(
@ -47,6 +57,9 @@ class SearchDiscoverFragment : Fragment() {
val view = inflater.inflate(R.layout.fragment_search, container, false)
val button = view.findViewById<Button>(R.id.searchButton)
val search = view.findViewById<EditText>(R.id.searchEditText)
(requireActivity().application as Pixeldroid).getAppComponent().inject(this)
button.setOnClickListener {
val intent = Intent(context, SearchActivity::class.java)
intent.putExtra("searchFeed", search.text.toString())
@ -64,13 +77,9 @@ class SearchDiscoverFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val db = DBUtils.initDB(requireContext())
api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
val user = db.userDao().getActiveUser()
val domain = user?.instance_uri.orEmpty()
api = PixelfedAPI.create(domain)
accessToken = user?.accessToken.orEmpty()
accessToken = db.userDao().getActiveUser()?.accessToken.orEmpty()
discoverProgressBar = view.findViewById(R.id.discoverProgressBar)
discoverRefreshLayout = view.findViewById(R.id.discoverRefreshLayout)

View File

@ -2,11 +2,13 @@ package com.h.pixeldroid.fragments.feeds
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.RecyclerView
import androidx.lifecycle.Observer
@ -24,9 +26,14 @@ import com.h.pixeldroid.objects.Account.Companion.ACCOUNT_ID_TAG
import com.h.pixeldroid.objects.Account.Companion.FOLLOWING_TAG
import kotlinx.android.synthetic.main.account_list_entry.view.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
open class AccountListFragment : FeedFragment<Account, AccountListFragment.FollowsRecyclerViewAdapter.ViewHolder>() {
open class AccountListFragment : FeedFragment() {
lateinit var profilePicRequest: RequestBuilder<Drawable>
protected lateinit var adapter : FeedsRecyclerViewAdapter<Account, AccountsRecyclerViewAdapter.ViewHolder>
lateinit var factory: FeedDataSourceFactory<String, Account>
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@ -39,13 +46,13 @@ open class AccountListFragment : FeedFragment<Account, AccountListFragment.Follo
.asDrawable().apply(RequestOptions().circleCrop())
.placeholder(R.drawable.ic_default_user)
adapter = FollowsRecyclerViewAdapter()
adapter = AccountsRecyclerViewAdapter()
list.adapter = adapter
//Make Glide be aware of the recyclerview and pre-load images
val sizeProvider: ListPreloader.PreloadSizeProvider<Account> = ViewPreloadSizeProvider()
val preloader: RecyclerViewPreloader<Account> = RecyclerViewPreloader(
Glide.with(this), adapter as AccountListFragment.FollowsRecyclerViewAdapter, sizeProvider, 4
Glide.with(this), adapter as AccountListFragment.AccountsRecyclerViewAdapter, sizeProvider, 4
)
list.addOnScrollListener(preloader)
@ -54,7 +61,7 @@ open class AccountListFragment : FeedFragment<Account, AccountListFragment.Follo
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
content = makeContent()
val content = makeContent()
content.observe(viewLifecycleOwner,
Observer { c ->
@ -62,51 +69,86 @@ open class AccountListFragment : FeedFragment<Account, AccountListFragment.Follo
//after a refresh is done we need to stop the pull to refresh spinner
swipeRefreshLayout.isRefreshing = false
})
swipeRefreshLayout.setOnRefreshListener {
//by invalidating data, loadInitial will be called again
factory.liveData.value!!.invalidate()
}
}
internal open fun makeContent(): LiveData<PagedList<Account>> {
val id = arguments?.getSerializable(ACCOUNT_ID_TAG) as String
val following = arguments?.getSerializable(FOLLOWING_TAG) as Boolean
val (makeInitialCall, makeAfterCall)
= makeCalls(following, id)
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
val dataSource = FeedDataSource(makeInitialCall, makeAfterCall)
val dataSource = AccountListDataSource(following, id)
factory = FeedDataSourceFactory(dataSource)
return LivePagedListBuilder(factory, config).build()
}
private fun makeCalls(following: Boolean, id: String):
Pair<(Int) -> Call<List<Account>>, (Int, String) -> Call<List<Account>>> {
val makeInitialCall: (Int) -> Call<List<Account>> =
if (following) { requestedLoadSize ->
inner class AccountListDataSource(private val following: Boolean, private val id: String) :
FeedDataSource<String, Account>() {
override fun newSource(): AccountListDataSource {
return AccountListDataSource(following, id)
}
//We use the id as the key
override fun getKey(item: Account): String {
return item.id
}
override fun makeInitialCall(requestedLoadSize: Int): Call<List<Account>> {
return if (following) {
pixelfedAPI.followers(
id, "Bearer $accessToken",
limit = requestedLoadSize
)
} else { requestedLoadSize ->
} else {
pixelfedAPI.following(
id, "Bearer $accessToken",
limit = requestedLoadSize
)
}
val makeAfterCall: (Int, String) -> Call<List<Account>> =
if (following) { requestedLoadSize, key ->
}
override fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Account>> {
return if (following) {
pixelfedAPI.followers(
id, "Bearer $accessToken",
since_id = key, limit = requestedLoadSize
)
} else { requestedLoadSize, key ->
} else {
pixelfedAPI.following(
id, "Bearer $accessToken",
since_id = key, limit = requestedLoadSize
)
}
return Pair(makeInitialCall, makeAfterCall)
}
override fun enqueueCall(call: Call<List<Account>>, callback: LoadCallback<Account>){
call.enqueue(object : Callback<List<Account>> {
override fun onResponse(call: Call<List<Account>>, response: Response<List<Account>>) {
if (response.isSuccessful && response.body() != null) {
val data = response.body()!!
callback.onResult(data)
} else{
Toast.makeText(context, getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
}
swipeRefreshLayout.isRefreshing = false
loadingIndicator.visibility = View.GONE
}
override fun onFailure(call: Call<List<Account>>, t: Throwable) {
Toast.makeText(context, getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
Log.e("AccountListFragment", t.toString())
}
})
}
}
inner class FollowsRecyclerViewAdapter : FeedsRecyclerViewAdapter<Account,FollowsRecyclerViewAdapter.ViewHolder>(),
inner class AccountsRecyclerViewAdapter : FeedsRecyclerViewAdapter<Account, AccountsRecyclerViewAdapter.ViewHolder>(),
ListPreloader.PreloadModelProvider<Account> {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {

View File

@ -1,52 +1,45 @@
package com.h.pixeldroid.fragments.feeds
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.paging.DataSource
import androidx.paging.ItemKeyedDataSource
import androidx.paging.PagedList
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.h.pixeldroid.MainActivity
import com.h.pixeldroid.Pixeldroid
import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.objects.FeedContent
import com.h.pixeldroid.utils.DBUtils
import com.h.pixeldroid.utils.Utils
import kotlinx.android.synthetic.main.fragment_feed.view.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment() {
lateinit var content: LiveData<PagedList<T>>
lateinit var factory: FeedDataSourceFactory<FeedDataSource>
open class FeedFragment: Fragment() {
protected var accessToken: String? = null
@Inject
lateinit var apiHolder: PixelfedAPIHolder
protected lateinit var pixelfedAPI: PixelfedAPI
protected lateinit var list : RecyclerView
protected lateinit var adapter : FeedsRecyclerViewAdapter<T, VH>
protected lateinit var swipeRefreshLayout: SwipeRefreshLayout
internal lateinit var loadingIndicator: ProgressBar
private var user: UserDatabaseEntity? = null
private lateinit var db: AppDatabase
var user: UserDatabaseEntity? = null
@Inject
lateinit var db: AppDatabase
@ -57,101 +50,80 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
): View? {
val view = inflater.inflate(R.layout.fragment_feed, container, false)
(requireActivity().application as Pixeldroid).getAppComponent().inject(this)
//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
list.layoutManager = LinearLayoutManager(context)
db = DBUtils.initDB(requireContext())
user = db.userDao().getActiveUser()
pixelfedAPI = PixelfedAPI.create(user?.instance_uri.orEmpty())
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
accessToken = user?.accessToken.orEmpty()
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
open inner class FeedDataSourceFactory<ObjectId, APIObject: FeedContent>(
private val dataSource: FeedDataSource<ObjectId, APIObject>
): DataSource.Factory<ObjectId, APIObject>() {
internal lateinit var liveData: MutableLiveData<FeedDataSource<ObjectId, APIObject>>
swipeRefreshLayout.setOnRefreshListener {
//by invalidating data, loadInitial will be called again
if (Utils.hasInternet(requireContext())) {
factory.liveData.value!!.invalidate()
} else {
startActivity(Intent(requireContext(), MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
})
}
}
}
open inner class FeedDataSource(private val makeInitialCall: ((Int) -> Call<List<T>>)?,
private val makeAfterCall: ((Int, String) -> Call<List<T>>)?
): ItemKeyedDataSource<String, T>() {
open fun newSource(): FeedDataSource {
return FeedDataSource(makeInitialCall, makeAfterCall)
}
//We use the id as the key
override fun getKey(item: T): String {
return item.id!!
}
//This is called to initialize the list, so we want some of the latest statuses
override fun loadInitial(
params: LoadInitialParams<String>,
callback: LoadInitialCallback<T>
) {
enqueueCall(makeInitialCall!!(params.requestedLoadSize), callback)
}
//This is called to when we get to the bottom of the loaded content, so we want statuses
//older than the given key (params.key)
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<T>) {
enqueueCall(makeAfterCall!!(params.requestedLoadSize, params.key), callback)
}
override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<T>) {
//do nothing here, it is expected to pull to refresh to load newer notifications
}
protected open 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.isSuccessful && response.body() != null) {
val notifications = response.body()!!
callback.onResult(notifications)
if(this@FeedDataSource.newSource() !is PublicTimelineFragment.SearchFeedDataSource) {
DBUtils.storePosts(db, notifications, user!!)
}
} else{
Toast.makeText(context, getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
}
swipeRefreshLayout.isRefreshing = false
loadingIndicator.visibility = View.GONE
}
override fun onFailure(call: Call<List<T>>, t: Throwable) {
Toast.makeText(context, getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
Log.e("FeedFragment", t.toString())
}
})
}
}
open inner class FeedDataSourceFactory<DS: FeedDataSource>(
private val dataSource: DS
): DataSource.Factory<String, T>() {
lateinit var liveData: MutableLiveData<DS>
override fun create(): DataSource<String, T> {
override fun create(): DataSource<ObjectId, APIObject> {
val dataSource = dataSource.newSource()
liveData = MutableLiveData()
liveData.postValue(dataSource as DS)
liveData.postValue(dataSource)
return dataSource
}
}
abstract inner class FeedDataSource<ObjectId, APIObject: FeedContent>: ItemKeyedDataSource<ObjectId, APIObject>(){
/**
* Used in the initial call to initialize the list [loadInitial].
* @param requestedLoadSize number of objects requested in a call
* @return [Call] that gets the list of [APIObject]
*/
abstract fun makeInitialCall(requestedLoadSize: Int): Call<List<APIObject>>
/**
* Used in the subsequent calls to get more objects.
* @param requestedLoadSize number of objects requested in a call
* @param key of the last object we already have
* @return [Call] that gets the list of [APIObject]
*/
abstract fun makeAfterCall(requestedLoadSize: Int, key: ObjectId): Call<List<APIObject>>
/**
* This is called to initialize the list, so we want some of the most recent objects.
* @param params holds the requestedLoadSize
* @param callback to call after network request completes
*/
override fun loadInitial(
params: LoadInitialParams<ObjectId>,
callback: LoadInitialCallback<APIObject>
) {
enqueueCall(makeInitialCall(params.requestedLoadSize), callback)
}
/**
* This is called to when we get to the bottom of the loaded content, so we want objects
* older than the given key (params.key).
* @param params holds the requestedLoadSize
* @param callback to call after network request completes
*/
override fun loadAfter(params: LoadParams<ObjectId>, callback: LoadCallback<APIObject>) {
enqueueCall(makeAfterCall(params.requestedLoadSize, params.key), callback)
}
/**
* Do nothing here, it is expected to pull to refresh to load newer notifications
*/
override fun loadBefore(params: LoadParams<ObjectId>, callback: LoadCallback<APIObject>) {}
abstract fun enqueueCall(call: Call<List<APIObject>>, callback: LoadCallback<APIObject>)
abstract fun newSource(): FeedDataSource<ObjectId, APIObject>
}
}
@ -167,7 +139,6 @@ abstract class FeedsRecyclerViewAdapter<T: FeedContent, VH : RecyclerView.ViewHo
}
}
){
protected lateinit var context: Context
}

View File

@ -4,11 +4,13 @@ import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder
@ -29,14 +31,20 @@ import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText
import kotlinx.android.synthetic.main.fragment_notifications.view.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
/**
* A fragment representing a list of Items.
*/
class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.NotificationsRecyclerViewAdapter.ViewHolder>() {
class NotificationsFragment : FeedFragment() {
lateinit var profilePicRequest: RequestBuilder<Drawable>
protected lateinit var adapter : FeedsRecyclerViewAdapter<Notification, NotificationsRecyclerViewAdapter.ViewHolder>
lateinit var factory: FeedDataSourceFactory<String, Notification>
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@ -67,7 +75,7 @@ class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.N
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
content = makeContent()
val content = makeContent()
content.observe(viewLifecycleOwner,
Observer { c ->
@ -75,22 +83,60 @@ class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.N
//after a refresh is done we need to stop the pull to refresh spinner
swipeRefreshLayout.isRefreshing = false
})
swipeRefreshLayout.setOnRefreshListener {
//by invalidating data, loadInitial will be called again
factory.liveData.value!!.invalidate()
}
}
private fun makeContent(): LiveData<PagedList<Notification>> {
fun makeInitialCall(requestedLoadSize: Int): Call<List<Notification>> {
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
val dataSource = NotificationListDataSource()
factory = FeedDataSourceFactory(dataSource)
return LivePagedListBuilder(factory, config).build()
}
inner class NotificationListDataSource: FeedDataSource<String, Notification>() {
override fun newSource(): NotificationListDataSource {
return NotificationListDataSource()
}
//We use the id as the key
override fun getKey(item: Notification): String {
return item.id
}
override fun makeInitialCall(requestedLoadSize: Int): Call<List<Notification>> {
return pixelfedAPI
.notifications("Bearer $accessToken", limit="$requestedLoadSize")
}
fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Notification>> {
override fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Notification>> {
return pixelfedAPI
.notifications("Bearer $accessToken", max_id=key, limit="$requestedLoadSize")
}
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
val dataSource = FeedDataSource(::makeInitialCall, ::makeAfterCall)
factory = FeedDataSourceFactory(dataSource)
return LivePagedListBuilder(factory, config).build()
override fun enqueueCall(call: Call<List<Notification>>, callback: LoadCallback<Notification>){
call.enqueue(object : Callback<List<Notification>> {
override fun onResponse(call: Call<List<Notification>>, response: Response<List<Notification>>) {
if (response.isSuccessful && response.body() != null) {
val data = response.body()!!
callback.onResult(data)
} else{
Toast.makeText(context, getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
}
swipeRefreshLayout.isRefreshing = false
loadingIndicator.visibility = View.GONE
}
override fun onFailure(call: Call<List<Notification>>, t: Throwable) {
Toast.makeText(context, getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
Log.e("NotificationsFragment", t.toString())
}
})
}
}
/**

View File

@ -20,13 +20,16 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder
import com.google.android.material.tabs.TabLayoutMediator
import com.h.pixeldroid.MainActivity
import com.h.pixeldroid.Pixeldroid
import com.h.pixeldroid.R
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.PostDatabaseEntity
import com.h.pixeldroid.fragments.ImageFragment
import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.*
import kotlinx.android.synthetic.main.fragment_offline_feed.view.*
import kotlinx.android.synthetic.main.post_fragment.view.*
import javax.inject.Inject
class OfflineFeedFragment: Fragment() {
@ -35,6 +38,9 @@ class OfflineFeedFragment: Fragment() {
private lateinit var viewAdapter: RecyclerView.Adapter<*>
private lateinit var viewManager: RecyclerView.LayoutManager
lateinit var picRequest: RequestBuilder<Drawable>
@Inject
lateinit var db: AppDatabase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -53,7 +59,9 @@ class OfflineFeedFragment: Fragment() {
picRequest = Glide.with(this)
.asDrawable().fitCenter()
.placeholder(ColorDrawable(Color.GRAY))
val db = DBUtils.initDB(requireContext())
(requireActivity().application as Pixeldroid).getAppComponent().inject(this)
val user = db.userDao().getActiveUser()!!
if (db.postDao().numberOfPosts(user.user_id, user.instance_uri) > 0) {
val posts = db.postDao().getAll(user.user_id, user.instance_uri)

View File

@ -1,44 +0,0 @@
package com.h.pixeldroid.fragments.feeds
import androidx.lifecycle.LiveData
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import com.h.pixeldroid.objects.Status
import retrofit2.Call
class PublicTimelineFragment: PostsFeedFragment() {
inner class SearchFeedDataSource : FeedDataSource(null, null){
override fun newSource(): SearchFeedDataSource {
return SearchFeedDataSource()
}
private fun makeInitialCall(requestedLoadSize: Int): Call<List<Status>> {
return pixelfedAPI.timelinePublic(limit="$requestedLoadSize")
}
private fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Status>> {
return pixelfedAPI.timelinePublic( max_id=key, limit="$requestedLoadSize")
}
override fun loadInitial(
params: LoadInitialParams<String>,
callback: LoadInitialCallback<Status>
) {
enqueueCall(makeInitialCall(params.requestedLoadSize), callback)
}
//This is called to when we get to the bottom of the loaded content, so we want statuses
//older than the given key (params.key)
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Status>) {
enqueueCall(makeAfterCall(params.requestedLoadSize, params.key), callback)
}
}
override fun makeContent(): LiveData<PagedList<Status>> {
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
factory = FeedFragment<Status, PostViewHolder>()
.FeedDataSourceFactory(SearchFeedDataSource())
return LivePagedListBuilder(factory, config).build()
}
}

View File

@ -0,0 +1,70 @@
package com.h.pixeldroid.fragments.feeds.postFeeds
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.lifecycle.LiveData
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import com.h.pixeldroid.R
import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.DBUtils
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class HomeTimelineFragment: PostsFeedFragment() {
override fun makeContent(): LiveData<PagedList<Status>> {
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
val dataSource = PostFeedDataSource()
factory = FeedDataSourceFactory(dataSource)
return LivePagedListBuilder(factory, config).build()
}
inner class PostFeedDataSource: FeedDataSource<String, Status>() {
override fun newSource(): PostFeedDataSource {
return PostFeedDataSource()
}
override fun makeInitialCall(requestedLoadSize: Int): Call<List<Status>> {
return pixelfedAPI
.timelineHome("Bearer $accessToken", limit="$requestedLoadSize")
}
override fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Status>> {
return pixelfedAPI
.timelineHome("Bearer $accessToken", max_id=key,
limit="$requestedLoadSize")
}
//We use the id as the key
override fun getKey(item: Status): String {
return item.id!!
}
override fun enqueueCall(call: Call<List<Status>>, callback: LoadCallback<Status>){
call.enqueue(object : Callback<List<Status>> {
override fun onResponse(call: Call<List<Status>>, response: Response<List<Status>>) {
if (response.isSuccessful && response.body() != null) {
val notifications = response.body()!!
callback.onResult(notifications)
DBUtils.storePosts(db, notifications, user!!)
} else{
Toast.makeText(context, getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
}
swipeRefreshLayout.isRefreshing = false
loadingIndicator.visibility = View.GONE
}
override fun onFailure(call: Call<List<Status>>, t: Throwable) {
Toast.makeText(context, getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
Log.e("PostsFeedFragment", t.toString())
}
})
}
}
}

View File

@ -1,4 +1,4 @@
package com.h.pixeldroid.fragments.feeds
package com.h.pixeldroid.fragments.feeds.postFeeds
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
@ -10,7 +10,6 @@ import android.view.ViewGroup
import android.widget.*
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.recyclerview.widget.RecyclerView
import at.connyduck.sparkbutton.SparkButton
@ -20,24 +19,23 @@ import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader
import com.bumptech.glide.util.ViewPreloadSizeProvider
import com.h.pixeldroid.R
import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.fragments.feeds.FeedFragment
import com.h.pixeldroid.fragments.feeds.FeedsRecyclerViewAdapter
import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.DBUtils
import retrofit2.Call
open class PostsFeedFragment : FeedFragment<Status, PostViewHolder>() {
abstract class PostsFeedFragment : FeedFragment() {
lateinit var picRequest: RequestBuilder<Drawable>
lateinit var domain : String
private var user: UserDatabaseEntity? = null
protected lateinit var adapter : FeedsRecyclerViewAdapter<Status, PostViewHolder>
lateinit var factory: FeedDataSourceFactory<String, Status>
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = super.onCreateView(inflater, container, savedInstanceState)
val db = DBUtils.initDB(requireContext())
user = db.userDao().getActiveUser()
domain = user?.instance_uri.orEmpty()
//RequestBuilder that is re-used for every image
@ -45,14 +43,13 @@ open class PostsFeedFragment : FeedFragment<Status, PostViewHolder>() {
.asDrawable().fitCenter()
.placeholder(ColorDrawable(Color.GRAY))
adapter = PostsFeedRecyclerViewAdapter(this)
adapter = PostsFeedRecyclerViewAdapter()
list.adapter = adapter
//Make Glide be aware of the recyclerview and pre-load images
val sizeProvider: ListPreloader.PreloadSizeProvider<Status> = ViewPreloadSizeProvider()
val preloader: RecyclerViewPreloader<Status> = RecyclerViewPreloader(
Glide.with(this), adapter as PostsFeedFragment.PostsFeedRecyclerViewAdapter, sizeProvider, 4
Glide.with(this), adapter as PostsFeedRecyclerViewAdapter, sizeProvider, 4
)
list.addOnScrollListener(preloader)
@ -61,35 +58,26 @@ open class PostsFeedFragment : FeedFragment<Status, PostViewHolder>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
content = makeContent()
val 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
})
swipeRefreshLayout.setOnRefreshListener {
//by invalidating data, loadInitial will be called again
factory.liveData.value!!.invalidate()
}
}
internal open fun makeContent(): LiveData<PagedList<Status>> {
fun makeInitialCall(requestedLoadSize: Int): Call<List<Status>> {
return pixelfedAPI
.timelineHome("Bearer $accessToken", limit="$requestedLoadSize")
}
fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Status>> {
return pixelfedAPI
.timelineHome("Bearer $accessToken", max_id=key,
limit="$requestedLoadSize")
}
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
val dataSource = FeedDataSource(::makeInitialCall, ::makeAfterCall)
factory = FeedDataSourceFactory(dataSource)
return LivePagedListBuilder(factory, config).build()
}
internal abstract fun makeContent(): LiveData<PagedList<Status>>
/**
* [RecyclerView.Adapter] that can display a list of Statuses
*/
inner class PostsFeedRecyclerViewAdapter(private val postsFeedFragment: PostsFeedFragment)
inner class PostsFeedRecyclerViewAdapter
: FeedsRecyclerViewAdapter<Status, PostViewHolder>(),
ListPreloader.PreloadModelProvider<Status> {
private val api = pixelfedAPI
@ -98,7 +86,10 @@ open class PostsFeedFragment : FeedFragment<Status, PostViewHolder>() {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.post_fragment, parent, false)
context = view.context
return PostViewHolder(view, context)
return PostViewHolder(
view,
context
)
}
/**

View File

@ -0,0 +1,60 @@
package com.h.pixeldroid.fragments.feeds.postFeeds
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.lifecycle.LiveData
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import com.h.pixeldroid.R
import com.h.pixeldroid.objects.Status
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class PublicTimelineFragment: PostsFeedFragment() {
inner class PublicFeedDataSource : FeedDataSource<String, Status>(){
override fun newSource(): PublicFeedDataSource {
return PublicFeedDataSource()
}
override fun makeInitialCall(requestedLoadSize: Int): Call<List<Status>> {
return pixelfedAPI.timelinePublic(limit="$requestedLoadSize")
}
override fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Status>> {
return pixelfedAPI.timelinePublic( max_id=key, limit="$requestedLoadSize")
}
override fun enqueueCall(call: Call<List<Status>>, callback: LoadCallback<Status>) {
call.enqueue(object : Callback<List<Status>> {
override fun onResponse(call: Call<List<Status>>, response: Response<List<Status>>) {
if (response.isSuccessful && response.body() != null) {
val notifications = response.body()!!
callback.onResult(notifications)
} else{
Toast.makeText(context, getString(R.string.loading_toast), Toast.LENGTH_SHORT).show()
}
swipeRefreshLayout.isRefreshing = false
loadingIndicator.visibility = View.GONE
}
override fun onFailure(call: Call<List<Status>>, t: Throwable) {
Toast.makeText(context, getString(R.string.feed_failed), Toast.LENGTH_SHORT).show()
Log.e("PublicTimelineFragment", t.toString())
}
})
}
override fun getKey(item: Status): String {
return item.id!!
}
}
override fun makeContent(): LiveData<PagedList<Status>> {
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
factory = FeedDataSourceFactory(PublicFeedDataSource())
return LivePagedListBuilder(factory, config).build()
}
}

View File

@ -14,6 +14,7 @@ import com.h.pixeldroid.fragments.feeds.AccountListFragment
import com.h.pixeldroid.fragments.feeds.FeedFragment
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Results
import com.h.pixeldroid.objects.Tag
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
@ -34,35 +35,38 @@ class SearchAccountFragment: AccountListFragment(){
return view
}
inner class SearchAccountListDataSource: FeedDataSource(null, null){
inner class SearchAccountListDataSource: FeedDataSource<String, Account>(){
override fun newSource(): FeedDataSource {
override fun newSource(): SearchAccountListDataSource {
return SearchAccountListDataSource()
}
private fun makeInitialCall(requestedLoadSize: Int): Call<Results> {
override fun getKey(item: Account): String {
return item.id
}
private fun searchMakeInitialCall(requestedLoadSize: Int): Call<Results> {
return pixelfedAPI
.search("Bearer $accessToken",
limit="$requestedLoadSize", q=query,
type = Results.SearchType.accounts)
}
private fun makeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
private fun searchMakeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
return pixelfedAPI
.search("Bearer $accessToken", max_id=key,
limit="$requestedLoadSize", q = query,
type = Results.SearchType.accounts)
}
override fun loadInitial(
params: LoadInitialParams<String>,
callback: LoadInitialCallback<Account>
) {
searchEnqueueCall(makeInitialCall(params.requestedLoadSize), callback)
searchEnqueueCall(searchMakeInitialCall(params.requestedLoadSize), callback)
}
//This is called to when we get to the bottom of the loaded content, so we want statuses
//older than the given key (params.key)
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Account>) {
searchEnqueueCall(makeAfterCall(params.requestedLoadSize, params.key), callback)
searchEnqueueCall(searchMakeAfterCall(params.requestedLoadSize, params.key), callback)
}
private fun searchEnqueueCall(call: Call<Results>, callback: LoadCallback<Account>) {
@ -86,16 +90,22 @@ class SearchAccountFragment: AccountListFragment(){
})
}
override fun makeInitialCall(requestedLoadSize: Int): Call<List<Account>> {
throw NotImplementedError("Should not be called, reimplemented for Search fragment")
}
override fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Account>> {
throw NotImplementedError("Should not be called, reimplemented for Search fragment")
}
override fun enqueueCall(call: Call<List<Account>>, callback: LoadCallback<Account>) {
throw NotImplementedError("Should not be called, reimplemented for Search fragment")
}
}
override fun makeContent(): LiveData<PagedList<Account>> {
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
factory =
FeedFragment<Account, FollowsRecyclerViewAdapter.ViewHolder>()
.FeedDataSourceFactory(
SearchAccountListDataSource()
)
factory = FeedFragment().FeedDataSourceFactory(SearchAccountListDataSource())
return LivePagedListBuilder(factory, config).build()
}
}

View File

@ -14,8 +14,10 @@ import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.recyclerview.widget.RecyclerView
import com.h.pixeldroid.R
import com.h.pixeldroid.fragments.feeds.AccountListFragment
import com.h.pixeldroid.fragments.feeds.FeedFragment
import com.h.pixeldroid.fragments.feeds.FeedsRecyclerViewAdapter
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Results
import com.h.pixeldroid.objects.Tag
import kotlinx.android.synthetic.main.fragment_tags.view.*
@ -23,9 +25,13 @@ import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class SearchHashtagFragment: FeedFragment<Tag, SearchHashtagFragment.TagsRecyclerViewAdapter.ViewHolder>(){
class SearchHashtagFragment: FeedFragment(){
private lateinit var query: String
private lateinit var content: LiveData<PagedList<Tag>>
protected lateinit var adapter : TagsRecyclerViewAdapter
lateinit var factory: FeedDataSourceFactory<Int, Tag>
override fun onCreateView(
inflater: LayoutInflater,
@ -51,43 +57,45 @@ class SearchHashtagFragment: FeedFragment<Tag, SearchHashtagFragment.TagsRecycle
//after a refresh is done we need to stop the pull to refresh spinner
swipeRefreshLayout.isRefreshing = false
})
swipeRefreshLayout.setOnRefreshListener {
//by invalidating data, loadInitial will be called again
factory.liveData.value!!.invalidate()
}
}
inner class SearchTagsListDataSource: FeedDataSource(null, null){
inner class SearchTagsListDataSource: FeedDataSource<Int, Tag>(){
override fun newSource(): FeedDataSource {
override fun newSource(): SearchTagsListDataSource {
return SearchTagsListDataSource()
}
private fun makeInitialCall(requestedLoadSize: Int): Call<Results> {
private fun searchMakeInitialCall(requestedLoadSize: Int): Call<Results> {
return pixelfedAPI
.search("Bearer $accessToken",
limit="$requestedLoadSize", q=query,
type = Results.SearchType.hashtags)
}
private fun makeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
private fun searchMakeAfterCall(requestedLoadSize: Int, key: Int): Call<Results> {
return pixelfedAPI
.search("Bearer $accessToken", offset=key.toInt(),
.search("Bearer $accessToken", offset=key,
limit="$requestedLoadSize", q = query,
type = Results.SearchType.hashtags)
}
override fun getKey(item: Tag): String {
override fun getKey(item: Tag): Int {
val value = content.value
val count = value?.loadedCount ?: 0
return count.toString()
return value?.loadedCount ?: 0
}
override fun loadInitial(
params: LoadInitialParams<String>,
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Tag>
) {
searchEnqueueCall(makeInitialCall(params.requestedLoadSize), callback)
searchEnqueueCall(searchMakeInitialCall(params.requestedLoadSize), callback)
}
//This is called to when we get to the bottom of the loaded content, so we want statuses
//older than the given key (params.key)
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Tag>) {
searchEnqueueCall(makeAfterCall(params.requestedLoadSize, params.key), callback)
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Tag>) {
searchEnqueueCall(searchMakeAfterCall(params.requestedLoadSize, params.key), callback)
}
private fun searchEnqueueCall(call: Call<Results>, callback: LoadCallback<Tag>){
@ -111,12 +119,24 @@ class SearchHashtagFragment: FeedFragment<Tag, SearchHashtagFragment.TagsRecycle
}
})
}
override fun makeInitialCall(requestedLoadSize: Int): Call<List<Tag>> {
throw NotImplementedError("Should not be called, reimplemented for Search fragment")
}
override fun makeAfterCall(requestedLoadSize: Int, key: Int): Call<List<Tag>> {
throw NotImplementedError("Should not be called, reimplemented for Search fragment")
}
override fun enqueueCall(call: Call<List<Tag>>, callback: LoadCallback<Tag>) {
throw NotImplementedError("Should not be called, reimplemented for Search fragment")
}
}
private fun makeContent(): LiveData<PagedList<Tag>> {
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
factory =
FeedFragment<Tag, TagsRecyclerViewAdapter.ViewHolder>()
FeedFragment()
.FeedDataSourceFactory(
SearchTagsListDataSource()
)

View File

@ -9,8 +9,7 @@ import androidx.lifecycle.LiveData
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import com.h.pixeldroid.fragments.feeds.FeedFragment
import com.h.pixeldroid.fragments.feeds.PostViewHolder
import com.h.pixeldroid.fragments.feeds.PostsFeedFragment
import com.h.pixeldroid.fragments.feeds.postFeeds.PostsFeedFragment
import com.h.pixeldroid.objects.Results
import com.h.pixeldroid.objects.Status
import retrofit2.Call
@ -33,19 +32,19 @@ class SearchPostsFragment: PostsFeedFragment(){
return view
}
inner class SearchFeedDataSource : FeedDataSource(null, null){
inner class SearchFeedDataSource : FeedDataSource<String, Status>(){
override fun newSource(): FeedDataSource {
override fun newSource(): SearchFeedDataSource {
return SearchFeedDataSource()
}
private fun makeInitialCall(requestedLoadSize: Int): Call<Results> {
private fun searchMakeInitialCall(requestedLoadSize: Int): Call<Results> {
return pixelfedAPI
.search("Bearer $accessToken",
limit="$requestedLoadSize", q=query,
type = Results.SearchType.statuses)
}
private fun makeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
private fun searchMakeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
return pixelfedAPI
.search("Bearer $accessToken", max_id=key,
limit="$requestedLoadSize", q = query,
@ -55,13 +54,11 @@ class SearchPostsFragment: PostsFeedFragment(){
params: LoadInitialParams<String>,
callback: LoadInitialCallback<Status>
) {
searchEnqueueCall(makeInitialCall(params.requestedLoadSize), callback)
searchEnqueueCall(searchMakeInitialCall(params.requestedLoadSize), callback)
}
//This is called to when we get to the bottom of the loaded content, so we want statuses
//older than the given key (params.key)
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Status>) {
searchEnqueueCall(makeAfterCall(params.requestedLoadSize, params.key), callback)
searchEnqueueCall(searchMakeAfterCall(params.requestedLoadSize, params.key), callback)
}
private fun searchEnqueueCall(call: Call<Results>, callback: LoadCallback<Status>){
@ -84,13 +81,28 @@ class SearchPostsFragment: PostsFeedFragment(){
}
})
}
override fun makeInitialCall(requestedLoadSize: Int): Call<List<Status>> {
throw NotImplementedError("Should not be called, reimplemented for Search fragment")
}
override fun makeAfterCall(requestedLoadSize: Int, key: String): Call<List<Status>> {
throw NotImplementedError("Should not be called, reimplemented for Search fragment")
}
override fun enqueueCall(call: Call<List<Status>>, callback: LoadCallback<Status>) {
throw NotImplementedError("Should not be called, reimplemented for Search fragment")
}
override fun getKey(item: Status): String {
return item.id!!
}
}
override fun makeContent(): LiveData<PagedList<Status>> {
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
factory = FeedFragment<Status, PostViewHolder>()
factory = FeedFragment()
.FeedDataSourceFactory(SearchFeedDataSource())
return LivePagedListBuilder(factory, config).build()
}

View File

@ -11,7 +11,6 @@ import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.*
import androidx.core.text.toSpanned
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
@ -20,7 +19,7 @@ import com.google.android.material.tabs.TabLayoutMediator
import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.fragments.ImageFragment
import com.h.pixeldroid.fragments.feeds.PostViewHolder
import com.h.pixeldroid.fragments.feeds.postFeeds.PostViewHolder
import com.h.pixeldroid.utils.HtmlUtils.Companion.getDomain
import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText
import com.h.pixeldroid.utils.ImageConverter

View File

@ -1,7 +1,5 @@
package com.h.pixeldroid.utils
import android.content.Context
import androidx.room.Room
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.InstanceDatabaseEntity
import com.h.pixeldroid.db.PostDatabaseEntity
@ -15,12 +13,6 @@ class DBUtils {
companion object {
private const val MAX_NUMBER_OF_STORED_POSTS = 200
fun initDB(context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java, "pixeldroid"
).allowMainThreadQueries().build()
}
private fun normalizeOrNot(uri: String): String{
return if(uri.startsWith("http://localhost")){
uri

View File

@ -8,7 +8,7 @@ import android.widget.LinearLayout
import android.widget.Toast
import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.fragments.feeds.PostViewHolder
import com.h.pixeldroid.fragments.feeds.postFeeds.PostViewHolder
import com.h.pixeldroid.objects.Context
import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.ImageConverter.Companion.setImageFromDrawable

View File

@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
classpath 'com.android.tools.build:gradle:4.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

View File

@ -1,6 +1,6 @@
#Mon Jun 01 23:59:23 CEST 2020
#Sun Jul 26 16:18:03 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip