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 { android {
compileSdkVersion 29 compileSdkVersion 29
buildToolsVersion "29.0.3" buildToolsVersion '30.0.1'
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
@ -56,12 +56,12 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0' 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.preference:preference:1.1.1'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.navigation:navigation-fragment:2.2.2' implementation 'androidx.navigation:navigation-fragment:2.3.0'
implementation 'androidx.navigation:navigation-ui:2.2.2' implementation 'androidx.navigation:navigation-ui:2.3.0'
implementation 'com.squareup.okhttp3:okhttp:4.7.2' implementation 'com.squareup.okhttp3:okhttp:4.8.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
@ -78,9 +78,9 @@ dependencies {
testImplementation "androidx.room:room-testing:$room_version" testImplementation "androidx.room:room-testing:$room_version"
implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2' implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2' implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
implementation 'info.androidhive:imagefilters:1.0.7' implementation 'info.androidhive:imagefilters:1.0.7'
implementation 'com.github.yalantis:ucrop:2.2.5-native' implementation 'com.github.yalantis:ucrop:2.2.5-native'
@ -97,11 +97,11 @@ dependencies {
kapt 'com.github.bumptech.glide:compiler:4.11.0' kapt 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.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 "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
testImplementation 'junit:junit:4.13' 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.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules: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:iconics-views:5.0.2"
implementation 'com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar' 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:runner:1.2.0'
androidTestImplementation 'androidx.test:rules: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" debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
// Use the most recent version of CameraX // 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-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}"
// CameraX Lifecycle library // CameraX Lifecycle library
implementation "androidx.camera:camera-lifecycle:$camerax_version" implementation "androidx.camera:camera-lifecycle:$camerax_version"
// CameraX View class // 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' 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.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.testUtility.MockServer import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils import com.h.pixeldroid.utils.DBUtils
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
@ -40,7 +41,7 @@ class DrawerMenuTest {
val baseUrl = mockServer.getUrl() val baseUrl = mockServer.getUrl()
context = ApplicationProvider.getApplicationContext() context = ApplicationProvider.getApplicationContext()
db = DBUtils.initDB(context) db = initDB(context)
db.clearAllTables() db.clearAllTables()
db.instanceDao().insertInstance( db.instanceDao().insertInstance(
InstanceDatabaseEntity( InstanceDatabaseEntity(

View File

@ -6,7 +6,6 @@ import android.widget.TextView
import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.swipeDown
import androidx.test.espresso.action.ViewActions.swipeUp import androidx.test.espresso.action.ViewActions.swipeUp
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition 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.AppDatabase
import com.h.pixeldroid.db.InstanceDatabaseEntity import com.h.pixeldroid.db.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity 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.clickChildViewWithId
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.first import com.h.pixeldroid.testUtility.CustomMatchers.Companion.first
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.getText 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.slowSwipeUp
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.typeTextInViewWithId import com.h.pixeldroid.testUtility.CustomMatchers.Companion.typeTextInViewWithId
import com.h.pixeldroid.testUtility.MockServer import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils import com.h.pixeldroid.utils.DBUtils
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
@ -49,7 +49,7 @@ class HomeFeedTest {
mockServer.start() mockServer.start()
val baseUrl = mockServer.getUrl() val baseUrl = mockServer.getUrl()
context = ApplicationProvider.getApplicationContext() context = ApplicationProvider.getApplicationContext()
db = DBUtils.initDB(context) db = initDB(context)
db.clearAllTables() db.clearAllTables()
db.instanceDao().insertInstance( db.instanceDao().insertInstance(
InstanceDatabaseEntity( InstanceDatabaseEntity(

View File

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

View File

@ -13,7 +13,7 @@ import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.UiSelector
import com.h.pixeldroid.db.AppDatabase 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.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
@ -43,7 +43,7 @@ class LoginActivityOfflineTest {
fun before() { fun before() {
switchAirplaneMode() switchAirplaneMode()
val context = ApplicationProvider.getApplicationContext<Context>() val context = ApplicationProvider.getApplicationContext<Context>()
db = DBUtils.initDB(context) db = initDB(context)
db.clearAllTables() db.clearAllTables()
ActivityScenario.launch(LoginActivity::class.java) 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.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.testUtility.MockServer import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils import com.h.pixeldroid.utils.DBUtils
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
@ -45,7 +46,7 @@ class LoginActivityOnlineTest {
context = ApplicationProvider.getApplicationContext() context = ApplicationProvider.getApplicationContext()
pref = context.getSharedPreferences("com.h.pixeldroid.pref", Context.MODE_PRIVATE) pref = context.getSharedPreferences("com.h.pixeldroid.pref", Context.MODE_PRIVATE)
pref.edit().clear().apply() pref.edit().clear().apply()
db = DBUtils.initDB(context) db = initDB(context)
db.clearAllTables() db.clearAllTables()
db.close() db.close()
} }
@ -97,7 +98,7 @@ class LoginActivityOnlineTest {
@Test @Test
fun correctIntentReturnLoadsMainActivity() { fun correctIntentReturnLoadsMainActivity() {
context = ApplicationProvider.getApplicationContext() context = ApplicationProvider.getApplicationContext()
db = DBUtils.initDB(context) db = initDB(context)
db.clearAllTables() db.clearAllTables()
db.instanceDao().insertInstance( 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.AppDatabase
import com.h.pixeldroid.db.InstanceDatabaseEntity import com.h.pixeldroid.db.InstanceDatabaseEntity
import com.h.pixeldroid.db.UserDatabaseEntity 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.clickChildViewWithId
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.first import com.h.pixeldroid.testUtility.CustomMatchers.Companion.first
import com.h.pixeldroid.testUtility.MockServer import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils import com.h.pixeldroid.utils.DBUtils
import com.h.pixeldroid.utils.PostUtils.Companion.censorColorMatrix import com.h.pixeldroid.utils.PostUtils.Companion.censorColorMatrix
import com.h.pixeldroid.utils.PostUtils.Companion.uncensorColorMatrix import com.h.pixeldroid.utils.PostUtils.Companion.uncensorColorMatrix
@ -46,7 +47,7 @@ class MockedServerTest {
mockServer.start() mockServer.start()
val baseUrl = mockServer.getUrl() val baseUrl = mockServer.getUrl()
context = ApplicationProvider.getApplicationContext() context = ApplicationProvider.getApplicationContext()
db = DBUtils.initDB(context) db = initDB(context)
db.clearAllTables() db.clearAllTables()
db.instanceDao().insertInstance( db.instanceDao().insertInstance(
InstanceDatabaseEntity( InstanceDatabaseEntity(

View File

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

View File

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

View File

@ -27,6 +27,7 @@ import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.objects.Tag import com.h.pixeldroid.objects.Tag
import com.h.pixeldroid.testUtility.MockServer import com.h.pixeldroid.testUtility.MockServer
import com.h.pixeldroid.testUtility.initDB
import com.h.pixeldroid.utils.DBUtils import com.h.pixeldroid.utils.DBUtils
import org.hamcrest.Matcher import org.hamcrest.Matcher
import org.junit.* import org.junit.*
@ -49,7 +50,7 @@ class PostTest {
val mockServer = MockServer() val mockServer = MockServer()
mockServer.start() mockServer.start()
val baseUrl = mockServer.getUrl() val baseUrl = mockServer.getUrl()
db = DBUtils.initDB(context) db = initDB(context)
db.clearAllTables() db.clearAllTables()
db.instanceDao().insertInstance( db.instanceDao().insertInstance(
InstanceDatabaseEntity( 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 <application
android:name=".Pixeldroid" android:name=".Pixeldroid"
android:allowBackup="true" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme"
tools:replace="android:allowBackup">
<activity <activity
android:name=".PhotoEditActivity" android:name=".PhotoEditActivity"

View File

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

View File

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

View File

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

View File

@ -2,13 +2,29 @@ package com.h.pixeldroid
import android.app.Application import android.app.Application
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.h.pixeldroid.di.*
import com.h.pixeldroid.utils.ThemeUtils import com.h.pixeldroid.utils.ThemeUtils
class Pixeldroid: Application() { class Pixeldroid: Application() {
private lateinit var mApplicationComponent: ApplicationComponent
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
val sharedPreferences = val sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(this) PreferenceManager.getDefaultSharedPreferences(this)
ThemeUtils.setThemeFromPreferences(sharedPreferences, resources) 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 android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.h.pixeldroid.api.PixelfedAPI 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.fragments.PostFragment
import com.h.pixeldroid.objects.DiscoverPost import com.h.pixeldroid.objects.DiscoverPost
import com.h.pixeldroid.objects.Status import com.h.pixeldroid.objects.Status
@ -16,25 +18,32 @@ import kotlinx.android.synthetic.main.activity_post.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import javax.inject.Inject
class PostActivity : AppCompatActivity() { class PostActivity : AppCompatActivity() {
private lateinit var postFragment : PostFragment private lateinit var postFragment : PostFragment
lateinit var domain : String lateinit var domain : String
private lateinit var accessToken : String private lateinit var accessToken : String
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_post) setContentView(R.layout.activity_post)
(this.application as Pixeldroid).getAppComponent().inject(this)
val status = intent.getSerializableExtra(POST_TAG) as Status? val status = intent.getSerializableExtra(POST_TAG) as Status?
val discoverPost: DiscoverPost? = intent.getSerializableExtra(DISCOVER_TAG) as DiscoverPost? val discoverPost: DiscoverPost? = intent.getSerializableExtra(DISCOVER_TAG) as DiscoverPost?
val db = DBUtils.initDB(applicationContext)
val user = db.userDao().getActiveUser() val user = db.userDao().getActiveUser()
domain = user?.instance_uri.orEmpty() domain = user?.instance_uri.orEmpty()
accessToken = user?.accessToken.orEmpty() accessToken = user?.accessToken.orEmpty()
db.close()
postFragment = PostFragment() postFragment = PostFragment()
val arguments = Bundle() val arguments = Bundle()
@ -52,7 +61,7 @@ class PostActivity : AppCompatActivity() {
arguments: Bundle, arguments: Bundle,
discoverPost: DiscoverPost discoverPost: DiscoverPost
) { ) {
val api = PixelfedAPI.create(domain) val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
val id = discoverPost.url?.substringAfterLast('/') ?: "" val id = discoverPost.url?.substringAfterLast('/') ?: ""
api.getStatus("Bearer $accessToken", id).enqueue(object : Callback<Status> { 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.bumptech.glide.Glide
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.h.pixeldroid.api.PixelfedAPI import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.UserDatabaseEntity import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.interfaces.PostCreationListener import com.h.pixeldroid.interfaces.PostCreationListener
import com.h.pixeldroid.objects.Attachment import com.h.pixeldroid.objects.Attachment
import com.h.pixeldroid.objects.Instance import com.h.pixeldroid.objects.Instance
@ -35,6 +37,7 @@ import okhttp3.MultipartBody
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import javax.inject.Inject
class PostCreationActivity : AppCompatActivity(), PostCreationListener { class PostCreationActivity : AppCompatActivity(), PostCreationListener {
@ -58,22 +61,28 @@ class PostCreationActivity : AppCompatActivity(), PostCreationListener {
private var maxLength: Int = Instance.DEFAULT_MAX_TOOT_CHARS private var maxLength: Int = Instance.DEFAULT_MAX_TOOT_CHARS
private var description: String = "" private var description: String = ""
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_post_creation) setContentView(R.layout.activity_post_creation)
(this.application as Pixeldroid).getAppComponent().inject(this)
// load images // load images
posts = intent.getStringArrayListExtra("pictures_uri")!! posts = intent.getStringArrayListExtra("pictures_uri")!!
progressList = posts.map { 0 } as ArrayList<Int> progressList = posts.map { 0 } as ArrayList<Int>
muListOfIds = posts.map { "" }.toMutableList() muListOfIds = posts.map { "" }.toMutableList()
val db = DBUtils.initDB(applicationContext)
user = db.userDao().getActiveUser() user = db.userDao().getActiveUser()
val instances = db.instanceDao().getAll() val instances = db.instanceDao().getAll()
db.close()
maxLength = if (user!=null){ maxLength = if (user!=null){
val thisInstances = val thisInstances =
instances.filter { instanceDatabaseEntity -> instances.filter { instanceDatabaseEntity ->
@ -84,9 +93,8 @@ class PostCreationActivity : AppCompatActivity(), PostCreationListener {
Instance.DEFAULT_MAX_TOOT_CHARS Instance.DEFAULT_MAX_TOOT_CHARS
} }
val domain = user?.instance_uri.orEmpty()
accessToken = user?.accessToken.orEmpty() accessToken = user?.accessToken.orEmpty()
pixelfedAPI = PixelfedAPI.create(domain) pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
// check if the pictures are alright // check if the pictures are alright
// TODO // TODO

View File

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

View File

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

View File

@ -16,8 +16,8 @@ interface UserDao {
@Query("SELECT * FROM users WHERE isActive=1 LIMIT 1") @Query("SELECT * FROM users WHERE isActive=1 LIMIT 1")
fun getActiveUser(): UserDatabaseEntity? fun getActiveUser(): UserDatabaseEntity?
@Query("UPDATE users SET isActive=0 WHERE isActive=1") @Query("UPDATE users SET isActive=0")
fun deActivateActiveUser() fun deActivateActiveUsers()
@Query("UPDATE users SET isActive=1 WHERE user_id=:id") @Query("UPDATE users SET isActive=1 WHERE user_id=:id")
fun activateUser(id: String) 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 android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.h.pixeldroid.Pixeldroid
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI 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
import com.h.pixeldroid.objects.Status.Companion.DOMAIN_TAG import com.h.pixeldroid.objects.Status.Companion.DOMAIN_TAG
import com.h.pixeldroid.objects.Status.Companion.POST_TAG import com.h.pixeldroid.objects.Status.Companion.POST_TAG
import com.h.pixeldroid.utils.DBUtils import javax.inject.Inject
class PostFragment : Fragment() { class PostFragment : Fragment() {
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): 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 statusDomain = arguments?.getString(DOMAIN_TAG)!!
val root: View = inflater.inflate(R.layout.post_fragment, container, false) val root: View = inflater.inflate(R.layout.post_fragment, container, false)
val picRequest = Glide.with(this) val picRequest = Glide.with(this)
.asDrawable().fitCenter() .asDrawable().fitCenter()
.placeholder(ColorDrawable(Color.GRAY)) .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 //Setup arguments needed for the onclicklisteners
val holder = PostViewHolder(root, requireContext()) val holder = PostViewHolder(
val db = DBUtils.initDB(requireContext()) root,
requireContext()
)
(requireActivity().application as Pixeldroid).getAppComponent().inject(this)
val user = db.userDao().getActiveUser() val user = db.userDao().getActiveUser()
val domain = user?.instance_uri.orEmpty() val domain = user?.instance_uri.orEmpty()
val accessToken = user?.accessToken.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 //Activate onclickListeners
current_status?.activateLiker(holder, api, "Bearer $accessToken", currentStatus?.activateLiker(holder, api, "Bearer $accessToken",
current_status.favourited ?: false currentStatus.favourited ?: false
) )
current_status?.activateReblogger(holder, api, "Bearer $accessToken", currentStatus?.activateReblogger(holder, api, "Bearer $accessToken",
current_status.reblogged ?: false currentStatus.reblogged ?: false
) )
current_status?.activateCommenter(holder, api, "Bearer $accessToken") currentStatus?.activateCommenter(holder, api, "Bearer $accessToken")
current_status?.showComments(holder, api, "Bearer $accessToken") currentStatus?.showComments(holder, api, "Bearer $accessToken")
//Activate double tap liking //Activate double tap liking
current_status?.activateDoubleTapLiker(holder, api, "Bearer $accessToken") currentStatus?.activateDoubleTapLiker(holder, api, "Bearer $accessToken")
return root return root
} }

View File

@ -14,10 +14,13 @@ import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.h.pixeldroid.Pixeldroid
import com.h.pixeldroid.PostActivity import com.h.pixeldroid.PostActivity
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.SearchActivity import com.h.pixeldroid.SearchActivity
import com.h.pixeldroid.api.PixelfedAPI 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.DiscoverPost
import com.h.pixeldroid.objects.DiscoverPosts import com.h.pixeldroid.objects.DiscoverPosts
import com.h.pixeldroid.objects.Status import com.h.pixeldroid.objects.Status
@ -26,6 +29,7 @@ import com.h.pixeldroid.utils.ImageConverter
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import javax.inject.Inject
/** /**
* This fragment lets you search and use Pixelfed's Discover feature * 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 accessToken: String
private lateinit var discoverProgressBar: ProgressBar private lateinit var discoverProgressBar: ProgressBar
private lateinit var discoverRefreshLayout: SwipeRefreshLayout private lateinit var discoverRefreshLayout: SwipeRefreshLayout
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreateView( override fun onCreateView(
@ -47,6 +57,9 @@ class SearchDiscoverFragment : Fragment() {
val view = inflater.inflate(R.layout.fragment_search, container, false) val view = inflater.inflate(R.layout.fragment_search, container, false)
val button = view.findViewById<Button>(R.id.searchButton) val button = view.findViewById<Button>(R.id.searchButton)
val search = view.findViewById<EditText>(R.id.searchEditText) val search = view.findViewById<EditText>(R.id.searchEditText)
(requireActivity().application as Pixeldroid).getAppComponent().inject(this)
button.setOnClickListener { button.setOnClickListener {
val intent = Intent(context, SearchActivity::class.java) val intent = Intent(context, SearchActivity::class.java)
intent.putExtra("searchFeed", search.text.toString()) intent.putExtra("searchFeed", search.text.toString())
@ -64,13 +77,9 @@ class SearchDiscoverFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val db = DBUtils.initDB(requireContext()) api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
val user = db.userDao().getActiveUser() accessToken = db.userDao().getActiveUser()?.accessToken.orEmpty()
val domain = user?.instance_uri.orEmpty()
api = PixelfedAPI.create(domain)
accessToken = user?.accessToken.orEmpty()
discoverProgressBar = view.findViewById(R.id.discoverProgressBar) discoverProgressBar = view.findViewById(R.id.discoverProgressBar)
discoverRefreshLayout = view.findViewById(R.id.discoverRefreshLayout) 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.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.lifecycle.Observer 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 com.h.pixeldroid.objects.Account.Companion.FOLLOWING_TAG
import kotlinx.android.synthetic.main.account_list_entry.view.* import kotlinx.android.synthetic.main.account_list_entry.view.*
import retrofit2.Call 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> lateinit var profilePicRequest: RequestBuilder<Drawable>
protected lateinit var adapter : FeedsRecyclerViewAdapter<Account, AccountsRecyclerViewAdapter.ViewHolder>
lateinit var factory: FeedDataSourceFactory<String, Account>
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@ -39,13 +46,13 @@ open class AccountListFragment : FeedFragment<Account, AccountListFragment.Follo
.asDrawable().apply(RequestOptions().circleCrop()) .asDrawable().apply(RequestOptions().circleCrop())
.placeholder(R.drawable.ic_default_user) .placeholder(R.drawable.ic_default_user)
adapter = FollowsRecyclerViewAdapter() adapter = AccountsRecyclerViewAdapter()
list.adapter = adapter list.adapter = adapter
//Make Glide be aware of the recyclerview and pre-load images //Make Glide be aware of the recyclerview and pre-load images
val sizeProvider: ListPreloader.PreloadSizeProvider<Account> = ViewPreloadSizeProvider() val sizeProvider: ListPreloader.PreloadSizeProvider<Account> = ViewPreloadSizeProvider()
val preloader: RecyclerViewPreloader<Account> = RecyclerViewPreloader( 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) list.addOnScrollListener(preloader)
@ -54,7 +61,7 @@ open class AccountListFragment : FeedFragment<Account, AccountListFragment.Follo
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
content = makeContent() val content = makeContent()
content.observe(viewLifecycleOwner, content.observe(viewLifecycleOwner,
Observer { c -> 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 //after a refresh is done we need to stop the pull to refresh spinner
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
}) })
swipeRefreshLayout.setOnRefreshListener {
//by invalidating data, loadInitial will be called again
factory.liveData.value!!.invalidate()
}
} }
internal open fun makeContent(): LiveData<PagedList<Account>> { internal open fun makeContent(): LiveData<PagedList<Account>> {
val id = arguments?.getSerializable(ACCOUNT_ID_TAG) as String val id = arguments?.getSerializable(ACCOUNT_ID_TAG) as String
val following = arguments?.getSerializable(FOLLOWING_TAG) as Boolean 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 config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
val dataSource = FeedDataSource(makeInitialCall, makeAfterCall) val dataSource = AccountListDataSource(following, id)
factory = FeedDataSourceFactory(dataSource) factory = FeedDataSourceFactory(dataSource)
return LivePagedListBuilder(factory, config).build() return LivePagedListBuilder(factory, config).build()
} }
private fun makeCalls(following: Boolean, id: String): inner class AccountListDataSource(private val following: Boolean, private val id: String) :
Pair<(Int) -> Call<List<Account>>, (Int, String) -> Call<List<Account>>> { FeedDataSource<String, Account>() {
val makeInitialCall: (Int) -> Call<List<Account>> =
if (following) { requestedLoadSize -> 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( pixelfedAPI.followers(
id, "Bearer $accessToken", id, "Bearer $accessToken",
limit = requestedLoadSize limit = requestedLoadSize
) )
} else { requestedLoadSize -> } else {
pixelfedAPI.following( pixelfedAPI.following(
id, "Bearer $accessToken", id, "Bearer $accessToken",
limit = requestedLoadSize 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( pixelfedAPI.followers(
id, "Bearer $accessToken", id, "Bearer $accessToken",
since_id = key, limit = requestedLoadSize since_id = key, limit = requestedLoadSize
) )
} else { requestedLoadSize, key -> } else {
pixelfedAPI.following( pixelfedAPI.following(
id, "Bearer $accessToken", id, "Bearer $accessToken",
since_id = key, limit = requestedLoadSize 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> { ListPreloader.PreloadModelProvider<Account> {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {

View File

@ -1,52 +1,45 @@
package com.h.pixeldroid.fragments.feeds package com.h.pixeldroid.fragments.feeds
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.Toast
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.paging.DataSource import androidx.paging.DataSource
import androidx.paging.ItemKeyedDataSource import androidx.paging.ItemKeyedDataSource
import androidx.paging.PagedList
import androidx.paging.PagedListAdapter import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.h.pixeldroid.MainActivity import com.h.pixeldroid.Pixeldroid
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.db.AppDatabase import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.UserDatabaseEntity import com.h.pixeldroid.db.UserDatabaseEntity
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.objects.FeedContent 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 kotlinx.android.synthetic.main.fragment_feed.view.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import javax.inject.Inject
import retrofit2.Response
open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment() { open class FeedFragment: Fragment() {
lateinit var content: LiveData<PagedList<T>>
lateinit var factory: FeedDataSourceFactory<FeedDataSource>
protected var accessToken: String? = null protected var accessToken: String? = null
@Inject
lateinit var apiHolder: PixelfedAPIHolder
protected lateinit var pixelfedAPI: PixelfedAPI protected lateinit var pixelfedAPI: PixelfedAPI
protected lateinit var list : RecyclerView protected lateinit var list : RecyclerView
protected lateinit var adapter : FeedsRecyclerViewAdapter<T, VH>
protected lateinit var swipeRefreshLayout: SwipeRefreshLayout protected lateinit var swipeRefreshLayout: SwipeRefreshLayout
internal lateinit var loadingIndicator: ProgressBar internal lateinit var loadingIndicator: ProgressBar
private var user: UserDatabaseEntity? = null var user: UserDatabaseEntity? = null
private lateinit var db: AppDatabase @Inject
lateinit var db: AppDatabase
@ -57,101 +50,80 @@ open class FeedFragment<T: FeedContent, VH: RecyclerView.ViewHolder?>: Fragment(
): View? { ): View? {
val view = inflater.inflate(R.layout.fragment_feed, container, false) 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 //Initialize lateinit fields that are needed as soon as the view is created
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout) swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout)
loadingIndicator = view.findViewById(R.id.progressBar) loadingIndicator = view.findViewById(R.id.progressBar)
list = swipeRefreshLayout.list list = swipeRefreshLayout.list
list.layoutManager = LinearLayoutManager(context) list.layoutManager = LinearLayoutManager(context)
db = DBUtils.initDB(requireContext())
user = db.userDao().getActiveUser() user = db.userDao().getActiveUser()
pixelfedAPI = PixelfedAPI.create(user?.instance_uri.orEmpty())
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
accessToken = user?.accessToken.orEmpty() accessToken = user?.accessToken.orEmpty()
return view return view
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { open inner class FeedDataSourceFactory<ObjectId, APIObject: FeedContent>(
super.onViewCreated(view, savedInstanceState) private val dataSource: FeedDataSource<ObjectId, APIObject>
): DataSource.Factory<ObjectId, APIObject>() {
internal lateinit var liveData: MutableLiveData<FeedDataSource<ObjectId, APIObject>>
swipeRefreshLayout.setOnRefreshListener { override fun create(): DataSource<ObjectId, APIObject> {
//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> {
val dataSource = dataSource.newSource() val dataSource = dataSource.newSource()
liveData = MutableLiveData() liveData = MutableLiveData()
liveData.postValue(dataSource as DS) liveData.postValue(dataSource)
return 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 protected lateinit var context: Context
} }

View File

@ -4,11 +4,13 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder import androidx.paging.LivePagedListBuilder
@ -29,14 +31,20 @@ import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText
import kotlinx.android.synthetic.main.fragment_notifications.view.* import kotlinx.android.synthetic.main.fragment_notifications.view.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
/** /**
* A fragment representing a list of Items. * A fragment representing a list of Items.
*/ */
class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.NotificationsRecyclerViewAdapter.ViewHolder>() { class NotificationsFragment : FeedFragment() {
lateinit var profilePicRequest: RequestBuilder<Drawable> lateinit var profilePicRequest: RequestBuilder<Drawable>
protected lateinit var adapter : FeedsRecyclerViewAdapter<Notification, NotificationsRecyclerViewAdapter.ViewHolder>
lateinit var factory: FeedDataSourceFactory<String, Notification>
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@ -67,7 +75,7 @@ class NotificationsFragment : FeedFragment<Notification, NotificationsFragment.N
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
content = makeContent() val content = makeContent()
content.observe(viewLifecycleOwner, content.observe(viewLifecycleOwner,
Observer { c -> 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 //after a refresh is done we need to stop the pull to refresh spinner
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
}) })
swipeRefreshLayout.setOnRefreshListener {
//by invalidating data, loadInitial will be called again
factory.liveData.value!!.invalidate()
}
} }
private fun makeContent(): LiveData<PagedList<Notification>> { 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 return pixelfedAPI
.notifications("Bearer $accessToken", limit="$requestedLoadSize") .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 return pixelfedAPI
.notifications("Bearer $accessToken", max_id=key, limit="$requestedLoadSize") .notifications("Bearer $accessToken", max_id=key, limit="$requestedLoadSize")
} }
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build() override fun enqueueCall(call: Call<List<Notification>>, callback: LoadCallback<Notification>){
val dataSource = FeedDataSource(::makeInitialCall, ::makeAfterCall)
factory = FeedDataSourceFactory(dataSource) call.enqueue(object : Callback<List<Notification>> {
return LivePagedListBuilder(factory, config).build() 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.bumptech.glide.RequestBuilder
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.h.pixeldroid.MainActivity import com.h.pixeldroid.MainActivity
import com.h.pixeldroid.Pixeldroid
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.PostDatabaseEntity import com.h.pixeldroid.db.PostDatabaseEntity
import com.h.pixeldroid.fragments.ImageFragment import com.h.pixeldroid.fragments.ImageFragment
import com.h.pixeldroid.objects.Status import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.* import com.h.pixeldroid.utils.*
import kotlinx.android.synthetic.main.fragment_offline_feed.view.* import kotlinx.android.synthetic.main.fragment_offline_feed.view.*
import kotlinx.android.synthetic.main.post_fragment.view.* import kotlinx.android.synthetic.main.post_fragment.view.*
import javax.inject.Inject
class OfflineFeedFragment: Fragment() { class OfflineFeedFragment: Fragment() {
@ -35,6 +38,9 @@ class OfflineFeedFragment: Fragment() {
private lateinit var viewAdapter: RecyclerView.Adapter<*> private lateinit var viewAdapter: RecyclerView.Adapter<*>
private lateinit var viewManager: RecyclerView.LayoutManager private lateinit var viewManager: RecyclerView.LayoutManager
lateinit var picRequest: RequestBuilder<Drawable> lateinit var picRequest: RequestBuilder<Drawable>
@Inject
lateinit var db: AppDatabase
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -53,7 +59,9 @@ class OfflineFeedFragment: Fragment() {
picRequest = Glide.with(this) picRequest = Glide.with(this)
.asDrawable().fitCenter() .asDrawable().fitCenter()
.placeholder(ColorDrawable(Color.GRAY)) .placeholder(ColorDrawable(Color.GRAY))
val db = DBUtils.initDB(requireContext())
(requireActivity().application as Pixeldroid).getAppComponent().inject(this)
val user = db.userDao().getActiveUser()!! val user = db.userDao().getActiveUser()!!
if (db.postDao().numberOfPosts(user.user_id, user.instance_uri) > 0) { if (db.postDao().numberOfPosts(user.user_id, user.instance_uri) > 0) {
val posts = db.postDao().getAll(user.user_id, user.instance_uri) 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.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
@ -10,7 +10,6 @@ import android.view.ViewGroup
import android.widget.* import android.widget.*
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList import androidx.paging.PagedList
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import at.connyduck.sparkbutton.SparkButton 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.integration.recyclerview.RecyclerViewPreloader
import com.bumptech.glide.util.ViewPreloadSizeProvider import com.bumptech.glide.util.ViewPreloadSizeProvider
import com.h.pixeldroid.R 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.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 picRequest: RequestBuilder<Drawable>
lateinit var domain : String 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( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
val view = super.onCreateView(inflater, container, savedInstanceState) val view = super.onCreateView(inflater, container, savedInstanceState)
val db = DBUtils.initDB(requireContext())
user = db.userDao().getActiveUser()
domain = user?.instance_uri.orEmpty() domain = user?.instance_uri.orEmpty()
//RequestBuilder that is re-used for every image //RequestBuilder that is re-used for every image
@ -45,14 +43,13 @@ open class PostsFeedFragment : FeedFragment<Status, PostViewHolder>() {
.asDrawable().fitCenter() .asDrawable().fitCenter()
.placeholder(ColorDrawable(Color.GRAY)) .placeholder(ColorDrawable(Color.GRAY))
adapter = PostsFeedRecyclerViewAdapter(this) adapter = PostsFeedRecyclerViewAdapter()
list.adapter = adapter list.adapter = adapter
//Make Glide be aware of the recyclerview and pre-load images //Make Glide be aware of the recyclerview and pre-load images
val sizeProvider: ListPreloader.PreloadSizeProvider<Status> = ViewPreloadSizeProvider() val sizeProvider: ListPreloader.PreloadSizeProvider<Status> = ViewPreloadSizeProvider()
val preloader: RecyclerViewPreloader<Status> = RecyclerViewPreloader( 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) list.addOnScrollListener(preloader)
@ -61,35 +58,26 @@ open class PostsFeedFragment : FeedFragment<Status, PostViewHolder>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
content = makeContent() val content = makeContent()
content.observe(viewLifecycleOwner, content.observe(viewLifecycleOwner,
Observer { c -> Observer { c ->
adapter.submitList(c) adapter.submitList(c)
//after a refresh is done we need to stop the pull to refresh spinner //after a refresh is done we need to stop the pull to refresh spinner
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
}) })
swipeRefreshLayout.setOnRefreshListener {
//by invalidating data, loadInitial will be called again
factory.liveData.value!!.invalidate()
}
} }
internal open fun makeContent(): LiveData<PagedList<Status>> { internal abstract 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()
}
/** /**
* [RecyclerView.Adapter] that can display a list of Statuses * [RecyclerView.Adapter] that can display a list of Statuses
*/ */
inner class PostsFeedRecyclerViewAdapter(private val postsFeedFragment: PostsFeedFragment) inner class PostsFeedRecyclerViewAdapter
: FeedsRecyclerViewAdapter<Status, PostViewHolder>(), : FeedsRecyclerViewAdapter<Status, PostViewHolder>(),
ListPreloader.PreloadModelProvider<Status> { ListPreloader.PreloadModelProvider<Status> {
private val api = pixelfedAPI private val api = pixelfedAPI
@ -98,7 +86,10 @@ open class PostsFeedFragment : FeedFragment<Status, PostViewHolder>() {
val view = LayoutInflater.from(parent.context) val view = LayoutInflater.from(parent.context)
.inflate(R.layout.post_fragment, parent, false) .inflate(R.layout.post_fragment, parent, false)
context = view.context 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.fragments.feeds.FeedFragment
import com.h.pixeldroid.objects.Account import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Results import com.h.pixeldroid.objects.Results
import com.h.pixeldroid.objects.Tag
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
@ -34,35 +35,38 @@ class SearchAccountFragment: AccountListFragment(){
return view return view
} }
inner class SearchAccountListDataSource: FeedDataSource(null, null){ inner class SearchAccountListDataSource: FeedDataSource<String, Account>(){
override fun newSource(): FeedDataSource { override fun newSource(): SearchAccountListDataSource {
return 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 return pixelfedAPI
.search("Bearer $accessToken", .search("Bearer $accessToken",
limit="$requestedLoadSize", q=query, limit="$requestedLoadSize", q=query,
type = Results.SearchType.accounts) type = Results.SearchType.accounts)
} }
private fun makeAfterCall(requestedLoadSize: Int, key: String): Call<Results> { private fun searchMakeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
return pixelfedAPI return pixelfedAPI
.search("Bearer $accessToken", max_id=key, .search("Bearer $accessToken", max_id=key,
limit="$requestedLoadSize", q = query, limit="$requestedLoadSize", q = query,
type = Results.SearchType.accounts) type = Results.SearchType.accounts)
} }
override fun loadInitial( override fun loadInitial(
params: LoadInitialParams<String>, params: LoadInitialParams<String>,
callback: LoadInitialCallback<Account> 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>) { 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>) { 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>> { override fun makeContent(): LiveData<PagedList<Account>> {
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build() val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
factory = factory = FeedFragment().FeedDataSourceFactory(SearchAccountListDataSource())
FeedFragment<Account, FollowsRecyclerViewAdapter.ViewHolder>()
.FeedDataSourceFactory(
SearchAccountListDataSource()
)
return LivePagedListBuilder(factory, config).build() return LivePagedListBuilder(factory, config).build()
} }
} }

View File

@ -14,8 +14,10 @@ import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList import androidx.paging.PagedList
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.h.pixeldroid.R 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.FeedFragment
import com.h.pixeldroid.fragments.feeds.FeedsRecyclerViewAdapter import com.h.pixeldroid.fragments.feeds.FeedsRecyclerViewAdapter
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Results import com.h.pixeldroid.objects.Results
import com.h.pixeldroid.objects.Tag import com.h.pixeldroid.objects.Tag
import kotlinx.android.synthetic.main.fragment_tags.view.* import kotlinx.android.synthetic.main.fragment_tags.view.*
@ -23,9 +25,13 @@ import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
class SearchHashtagFragment: FeedFragment<Tag, SearchHashtagFragment.TagsRecyclerViewAdapter.ViewHolder>(){ class SearchHashtagFragment: FeedFragment(){
private lateinit var query: String 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( override fun onCreateView(
inflater: LayoutInflater, 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 //after a refresh is done we need to stop the pull to refresh spinner
swipeRefreshLayout.isRefreshing = false 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() return SearchTagsListDataSource()
} }
private fun makeInitialCall(requestedLoadSize: Int): Call<Results> { private fun searchMakeInitialCall(requestedLoadSize: Int): Call<Results> {
return pixelfedAPI return pixelfedAPI
.search("Bearer $accessToken", .search("Bearer $accessToken",
limit="$requestedLoadSize", q=query, limit="$requestedLoadSize", q=query,
type = Results.SearchType.hashtags) type = Results.SearchType.hashtags)
} }
private fun makeAfterCall(requestedLoadSize: Int, key: String): Call<Results> { private fun searchMakeAfterCall(requestedLoadSize: Int, key: Int): Call<Results> {
return pixelfedAPI return pixelfedAPI
.search("Bearer $accessToken", offset=key.toInt(), .search("Bearer $accessToken", offset=key,
limit="$requestedLoadSize", q = query, limit="$requestedLoadSize", q = query,
type = Results.SearchType.hashtags) type = Results.SearchType.hashtags)
} }
override fun getKey(item: Tag): String { override fun getKey(item: Tag): Int {
val value = content.value val value = content.value
val count = value?.loadedCount ?: 0 return value?.loadedCount ?: 0
return count.toString()
} }
override fun loadInitial( override fun loadInitial(
params: LoadInitialParams<String>, params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Tag> 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 override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Tag>) {
//older than the given key (params.key) searchEnqueueCall(searchMakeAfterCall(params.requestedLoadSize, params.key), callback)
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Tag>) {
searchEnqueueCall(makeAfterCall(params.requestedLoadSize, params.key), callback)
} }
private fun searchEnqueueCall(call: Call<Results>, callback: LoadCallback<Tag>){ 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>> { private fun makeContent(): LiveData<PagedList<Tag>> {
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build() val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
factory = factory =
FeedFragment<Tag, TagsRecyclerViewAdapter.ViewHolder>() FeedFragment()
.FeedDataSourceFactory( .FeedDataSourceFactory(
SearchTagsListDataSource() SearchTagsListDataSource()
) )

View File

@ -9,8 +9,7 @@ import androidx.lifecycle.LiveData
import androidx.paging.LivePagedListBuilder import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList import androidx.paging.PagedList
import com.h.pixeldroid.fragments.feeds.FeedFragment import com.h.pixeldroid.fragments.feeds.FeedFragment
import com.h.pixeldroid.fragments.feeds.PostViewHolder import com.h.pixeldroid.fragments.feeds.postFeeds.PostsFeedFragment
import com.h.pixeldroid.fragments.feeds.PostsFeedFragment
import com.h.pixeldroid.objects.Results import com.h.pixeldroid.objects.Results
import com.h.pixeldroid.objects.Status import com.h.pixeldroid.objects.Status
import retrofit2.Call import retrofit2.Call
@ -33,19 +32,19 @@ class SearchPostsFragment: PostsFeedFragment(){
return view return view
} }
inner class SearchFeedDataSource : FeedDataSource(null, null){ inner class SearchFeedDataSource : FeedDataSource<String, Status>(){
override fun newSource(): FeedDataSource { override fun newSource(): SearchFeedDataSource {
return SearchFeedDataSource() return SearchFeedDataSource()
} }
private fun makeInitialCall(requestedLoadSize: Int): Call<Results> { private fun searchMakeInitialCall(requestedLoadSize: Int): Call<Results> {
return pixelfedAPI return pixelfedAPI
.search("Bearer $accessToken", .search("Bearer $accessToken",
limit="$requestedLoadSize", q=query, limit="$requestedLoadSize", q=query,
type = Results.SearchType.statuses) type = Results.SearchType.statuses)
} }
private fun makeAfterCall(requestedLoadSize: Int, key: String): Call<Results> { private fun searchMakeAfterCall(requestedLoadSize: Int, key: String): Call<Results> {
return pixelfedAPI return pixelfedAPI
.search("Bearer $accessToken", max_id=key, .search("Bearer $accessToken", max_id=key,
limit="$requestedLoadSize", q = query, limit="$requestedLoadSize", q = query,
@ -55,13 +54,11 @@ class SearchPostsFragment: PostsFeedFragment(){
params: LoadInitialParams<String>, params: LoadInitialParams<String>,
callback: LoadInitialCallback<Status> 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>) { 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>){ 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>> { override fun makeContent(): LiveData<PagedList<Status>> {
val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build() val config: PagedList.Config = PagedList.Config.Builder().setPageSize(10).build()
factory = FeedFragment<Status, PostViewHolder>() factory = FeedFragment()
.FeedDataSourceFactory(SearchFeedDataSource()) .FeedDataSourceFactory(SearchFeedDataSource())
return LivePagedListBuilder(factory, config).build() return LivePagedListBuilder(factory, config).build()
} }

View File

@ -11,7 +11,6 @@ import android.view.View
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.widget.* import android.widget.*
import androidx.core.text.toSpanned
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter 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.R
import com.h.pixeldroid.api.PixelfedAPI import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.fragments.ImageFragment 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.getDomain
import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText
import com.h.pixeldroid.utils.ImageConverter import com.h.pixeldroid.utils.ImageConverter

View File

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

View File

@ -8,7 +8,7 @@ import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI 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.Context
import com.h.pixeldroid.objects.Status import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.ImageConverter.Companion.setImageFromDrawable import com.h.pixeldroid.utils.ImageConverter.Companion.setImageFromDrawable

View File

@ -7,7 +7,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { 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" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // 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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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