Merge branch 'feature/di' into develop

This commit is contained in:
Shinokuni 2020-10-23 21:21:01 +02:00
commit 02a0702aac
55 changed files with 706 additions and 647 deletions

View File

@ -49,6 +49,7 @@ dependencies {
androidTestImplementation 'androidx.test:rules:1.3.0' androidTestImplementation 'androidx.test:rules:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.9.0' androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.9.0'
testImplementation "org.koin:koin-test:2.1.6"
implementation 'com.gitlab.mvysny.konsume-xml:konsume-xml:0.12' implementation 'com.gitlab.mvysny.konsume-xml:konsume-xml:0.12'

View File

@ -4,13 +4,13 @@ import android.accounts.NetworkErrorException
import android.content.Context import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import com.readrops.api.utils.HttpManager
import com.readrops.api.utils.LibUtils import com.readrops.api.utils.LibUtils
import com.readrops.api.utils.ParseException import com.readrops.api.utils.ParseException
import com.readrops.api.utils.UnknownFormatException import com.readrops.api.utils.UnknownFormatException
import junit.framework.TestCase.* import junit.framework.TestCase.*
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.MockWebServer
import okio.Buffer import okio.Buffer
@ -28,7 +28,7 @@ class LocalRSSDataSourceTest {
private lateinit var url: HttpUrl private lateinit var url: HttpUrl
private val mockServer: MockWebServer = MockWebServer() private val mockServer: MockWebServer = MockWebServer()
private val localRSSDataSource = LocalRSSDataSource(HttpManager.getInstance().okHttpClient) private val localRSSDataSource = LocalRSSDataSource(OkHttpClient())
@Before @Before
fun before() { fun before() {

View File

@ -0,0 +1,54 @@
package com.readrops.api.utils
import com.readrops.api.services.freshrss.FreshRSSCredentials
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Test
class AuthInterceptorTest {
private val interceptor = AuthInterceptor()
private val mockServer = MockWebServer()
private lateinit var okHttpClient: OkHttpClient
@Before
fun before() {
okHttpClient = OkHttpClient.Builder().addInterceptor(interceptor).build()
mockServer.start(8080)
}
@After
fun tearDown() {
mockServer.close()
}
@Test
fun credentialsUrlTest() {
mockServer.enqueue(MockResponse())
interceptor.credentials = FreshRSSCredentials("token", "http://localhost:8080/rss")
okHttpClient.newCall(Request.Builder().url(mockServer.url("/url")).build()).execute()
val request = mockServer.takeRequest()
assertEquals(request.requestUrl.toString(), "http://localhost:8080/rss/url")
assertEquals(request.headers["Authorization"], "GoogleLogin auth=token")
}
@Test
fun nullCredentialsTest() {
mockServer.enqueue(MockResponse())
interceptor.credentials = null
okHttpClient.newCall(Request.Builder().url(mockServer.url("/url")).build()).execute()
val request = mockServer.takeRequest()
assertEquals(request.requestUrl.toString(), "http://localhost:8080/url")
assertNull(request.headers["Authorization"])
}
}

View File

@ -0,0 +1,102 @@
package com.readrops.api
import com.readrops.api.localfeed.LocalRSSDataSource
import com.readrops.api.services.freshrss.FreshRSSDataSource
import com.readrops.api.services.freshrss.FreshRSSService
import com.readrops.api.services.freshrss.adapters.FreshRSSFeedsAdapter
import com.readrops.api.services.freshrss.adapters.FreshRSSFoldersAdapter
import com.readrops.api.services.freshrss.adapters.FreshRSSItemsAdapter
import com.readrops.api.services.nextcloudnews.NextNewsDataSource
import com.readrops.api.services.nextcloudnews.NextNewsService
import com.readrops.api.services.nextcloudnews.adapters.NextNewsFeedsAdapter
import com.readrops.api.services.nextcloudnews.adapters.NextNewsFoldersAdapter
import com.readrops.api.services.nextcloudnews.adapters.NextNewsItemsAdapter
import com.readrops.api.utils.AuthInterceptor
import com.readrops.db.entities.Item
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import okhttp3.OkHttpClient
import org.koin.core.qualifier.named
import org.koin.dsl.module
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.moshi.MoshiConverterFactory
import java.util.concurrent.TimeUnit
val apiModule = module {
single(createdAtStart = true) {
OkHttpClient.Builder()
.callTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.HOURS)
.addInterceptor(get<AuthInterceptor>())
.build()
}
single { LocalRSSDataSource(get()) }
//region freshrss
single {
FreshRSSDataSource(get<FreshRSSService>())
}
single {
get<Retrofit>(named("freshrssRetrofit"))
.create(FreshRSSService::class.java)
}
single(named("freshrssRetrofit")) {
get<Retrofit.Builder>()
.addConverterFactory(MoshiConverterFactory.create(get<Moshi>(named("freshrssMoshi"))))
.build()
}
single(named("freshrssMoshi")) {
Moshi.Builder()
.add(Types.newParameterizedType(List::class.java, Item::class.java), FreshRSSItemsAdapter())
.add(FreshRSSFeedsAdapter())
.add(FreshRSSFoldersAdapter())
.build()
}
//endregion freshrss
//region nextcloud news
single {
NextNewsDataSource(get<NextNewsService>())
}
single {
get<Retrofit>(named("nextcloudNewsRetrofit"))
.create(NextNewsService::class.java)
}
single(named("nextcloudNewsRetrofit")) {
get<Retrofit.Builder>()
.addConverterFactory(MoshiConverterFactory.create(get<Moshi>(named("nextcloudNewsMoshi"))))
.build()
}
single(named("nextcloudNewsMoshi")) {
Moshi.Builder()
.add(NextNewsFeedsAdapter())
.add(NextNewsFoldersAdapter())
.add(Types.newParameterizedType(List::class.java, Item::class.java), NextNewsItemsAdapter())
.build()
}
//endregion nextcloud news
single {
Retrofit.Builder() // url will be set dynamically in an interceptor
.baseUrl("https://baseurl.com")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(get<OkHttpClient>())
}
single {
AuthInterceptor()
}
}

View File

@ -1,61 +0,0 @@
package com.readrops.api.services;
import androidx.annotation.NonNull;
import com.readrops.api.utils.HttpManager;
import com.squareup.moshi.Moshi;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.moshi.MoshiConverterFactory;
/**
* Abstraction level for services APIs
*
* @param <T> an API service interface
*/
public abstract class API<T> {
protected static final int MAX_ITEMS = 5000;
protected T api;
private Retrofit retrofit;
private Class<T> clazz;
private String endPoint;
public API(Credentials credentials, @NonNull Class<T> clazz, @NonNull String endPoint) {
this.clazz = clazz;
this.endPoint = endPoint;
api = createAPI(credentials);
}
protected abstract Moshi buildMoshi();
protected Retrofit getConfiguredRetrofitInstance() {
return new Retrofit.Builder()
.baseUrl(HttpManager.getInstance().getCredentials().getUrl() + endPoint)
.addConverterFactory(MoshiConverterFactory.create(buildMoshi()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(HttpManager.getInstance().getOkHttpClient())
.build();
}
private T createAPI(@NonNull Credentials credentials) {
HttpManager.getInstance().setCredentials(credentials);
retrofit = getConfiguredRetrofitInstance();
return retrofit.create(clazz);
}
public void setCredentials(@NonNull Credentials credentials) {
HttpManager.getInstance().setCredentials(credentials);
retrofit = retrofit.newBuilder()
.baseUrl(credentials.getUrl() + endPoint)
.build();
api = retrofit.create(clazz);
}
}

View File

@ -1,16 +1,17 @@
package com.readrops.api.services; package com.readrops.api.services;
import androidx.annotation.Nullable;
import com.readrops.db.entities.account.Account;
import com.readrops.api.services.freshrss.FreshRSSCredentials; import com.readrops.api.services.freshrss.FreshRSSCredentials;
import com.readrops.api.services.freshrss.FreshRSSService;
import com.readrops.api.services.nextcloudnews.NextNewsCredentials; import com.readrops.api.services.nextcloudnews.NextNewsCredentials;
import com.readrops.api.services.nextcloudnews.NextNewsService;
import com.readrops.db.entities.account.Account;
import com.readrops.db.entities.account.AccountType;
public abstract class Credentials { public abstract class Credentials {
private String authorization; private final String authorization;
private String url; private final String url;
public Credentials(String authorization, String url) { public Credentials(String authorization, String url) {
this.authorization = authorization; this.authorization = authorization;
@ -25,15 +26,27 @@ public abstract class Credentials {
return url; return url;
} }
@Nullable
public static Credentials toCredentials(Account account) { public static Credentials toCredentials(Account account) {
String endPoint = getEndPoint(account.getAccountType());
switch (account.getAccountType()) { switch (account.getAccountType()) {
case NEXTCLOUD_NEWS: case NEXTCLOUD_NEWS:
return new NextNewsCredentials(account.getLogin(), account.getPassword(), account.getUrl()); return new NextNewsCredentials(account.getLogin(), account.getPassword(), account.getUrl() + endPoint);
case FRESHRSS: case FRESHRSS:
return new FreshRSSCredentials(account.getToken(), account.getUrl()); return new FreshRSSCredentials(account.getToken(), account.getUrl() + endPoint);
default: default:
return null; throw new IllegalArgumentException("Unknown account type");
}
}
private static String getEndPoint(AccountType accountType) {
switch (accountType) {
case FRESHRSS:
return FreshRSSService.END_POINT;
case NEXTCLOUD_NEWS:
return NextNewsService.END_POINT;
default:
throw new IllegalArgumentException("Unknown account type");
} }
} }
} }

View File

@ -3,19 +3,12 @@ package com.readrops.api.services.freshrss;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.readrops.api.services.SyncResult;
import com.readrops.api.services.SyncType;
import com.readrops.api.services.freshrss.json.FreshRSSUserInfo;
import com.readrops.db.entities.Feed; import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Folder; import com.readrops.db.entities.Folder;
import com.readrops.db.entities.Item; import com.readrops.db.entities.Item;
import com.readrops.api.services.API;
import com.readrops.api.services.Credentials;
import com.readrops.api.services.SyncResult;
import com.readrops.api.services.SyncType;
import com.readrops.api.services.freshrss.adapters.FreshRSSFeedsAdapter;
import com.readrops.api.services.freshrss.adapters.FreshRSSFoldersAdapter;
import com.readrops.api.services.freshrss.adapters.FreshRSSItemsAdapter;
import com.readrops.api.services.freshrss.json.FreshRSSUserInfo;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.io.StringReader; import java.io.StringReader;
import java.util.List; import java.util.List;
@ -26,23 +19,18 @@ import io.reactivex.Single;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.RequestBody; import okhttp3.RequestBody;
public class FreshRSSAPI extends API<FreshRSSService> { public class FreshRSSDataSource {
private static final int MAX_ITEMS = 5000;
public static final String GOOGLE_READ = "user/-/state/com.google/read"; public static final String GOOGLE_READ = "user/-/state/com.google/read";
private static final String FEED_PREFIX = "feed/"; private static final String FEED_PREFIX = "feed/";
public FreshRSSAPI(Credentials credentials) { private FreshRSSService api;
super(credentials, FreshRSSService.class, FreshRSSService.END_POINT);
}
@Override public FreshRSSDataSource(FreshRSSService api) {
protected Moshi buildMoshi() { this.api = api;
return new Moshi.Builder()
.add(Types.newParameterizedType(List.class, Item.class), new FreshRSSItemsAdapter())
.add(new FreshRSSFeedsAdapter())
.add(new FreshRSSFoldersAdapter())
.build();
} }
/** /**

View File

@ -2,7 +2,7 @@ package com.readrops.api.services.freshrss.adapters
import android.util.TimingLogger import android.util.TimingLogger
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.api.services.freshrss.FreshRSSAPI.GOOGLE_READ import com.readrops.api.services.freshrss.FreshRSSDataSource.GOOGLE_READ
import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter import com.squareup.moshi.JsonWriter

View File

@ -5,6 +5,6 @@ import com.readrops.api.services.Credentials;
public class NextNewsCredentials extends Credentials { public class NextNewsCredentials extends Credentials {
public NextNewsCredentials(String login, String password, String url) { public NextNewsCredentials(String login, String password, String url) {
super(okhttp3.Credentials.basic(login, password), url); super(login != null && password != null ? okhttp3.Credentials.basic(login, password) : null, url);
} }
} }

View File

@ -5,22 +5,15 @@ import android.content.res.Resources;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Folder;
import com.readrops.db.entities.Item;
import com.readrops.api.services.API;
import com.readrops.api.services.Credentials;
import com.readrops.api.services.SyncResult; import com.readrops.api.services.SyncResult;
import com.readrops.api.services.SyncType; import com.readrops.api.services.SyncType;
import com.readrops.api.services.nextcloudnews.adapters.NextNewsFeedsAdapter;
import com.readrops.api.services.nextcloudnews.adapters.NextNewsFoldersAdapter;
import com.readrops.api.services.nextcloudnews.adapters.NextNewsItemsAdapter;
import com.readrops.api.services.nextcloudnews.json.NextNewsUser; import com.readrops.api.services.nextcloudnews.json.NextNewsUser;
import com.readrops.api.utils.ConflictException; import com.readrops.api.utils.ConflictException;
import com.readrops.api.utils.LibUtils; import com.readrops.api.utils.LibUtils;
import com.readrops.api.utils.UnknownFormatException; import com.readrops.api.utils.UnknownFormatException;
import com.squareup.moshi.Moshi; import com.readrops.db.entities.Feed;
import com.squareup.moshi.Types; import com.readrops.db.entities.Folder;
import com.readrops.db.entities.Item;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -30,21 +23,16 @@ import java.util.Map;
import retrofit2.Response; import retrofit2.Response;
public class NextNewsAPI extends API<NextNewsService> { public class NextNewsDataSource {
private static final String TAG = NextNewsAPI.class.getSimpleName(); private static final String TAG = NextNewsDataSource.class.getSimpleName();
public NextNewsAPI(Credentials credentials) { protected static final int MAX_ITEMS = 5000;
super(credentials, NextNewsService.class, NextNewsService.END_POINT);
}
@Override private NextNewsService api;
protected Moshi buildMoshi() {
return new Moshi.Builder() public NextNewsDataSource(NextNewsService api) {
.add(new NextNewsFeedsAdapter()) this.api = api;
.add(new NextNewsFoldersAdapter())
.add(Types.newParameterizedType(List.class, Item.class), new NextNewsItemsAdapter())
.build();
} }
@Nullable @Nullable

View File

@ -0,0 +1,35 @@
package com.readrops.api.utils
import android.net.Uri
import com.readrops.api.services.Credentials
import com.readrops.api.services.freshrss.FreshRSSService
import com.readrops.api.services.nextcloudnews.NextNewsService
import com.readrops.db.entities.account.Account
import com.readrops.db.entities.account.AccountType
import okhttp3.Interceptor
import okhttp3.Response
import java.lang.IllegalArgumentException
class AuthInterceptor(var credentials: Credentials? = null) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val requestBuilder = chain.request().newBuilder()
val urlBuilder = chain.request().url.newBuilder()
if (credentials != null) {
if (credentials!!.url != null) {
val uri = Uri.parse(credentials!!.url)
urlBuilder
.scheme(uri.scheme!!)
.host(uri.host!!)
.encodedPath(uri.encodedPath + chain.request().url.encodedPath)
}
if (credentials!!.authorization != null) {
requestBuilder.addHeader("Authorization", credentials!!.authorization)
}
}
return chain.proceed(requestBuilder.url(urlBuilder.build()).build())
}
}

View File

@ -1,77 +0,0 @@
package com.readrops.api.utils;
import androidx.annotation.Nullable;
import com.readrops.api.services.Credentials;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class HttpManager {
private OkHttpClient okHttpClient;
private Credentials credentials;
public HttpManager() {
buildOkHttp();
}
private void buildOkHttp() {
okHttpClient = new OkHttpClient.Builder()
.callTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.HOURS)
.addInterceptor(new AuthInterceptor())
.build();
}
public OkHttpClient getOkHttpClient() {
return okHttpClient;
}
public void setCredentials(@Nullable Credentials credentials) {
this.credentials = credentials;
}
public Credentials getCredentials() {
return credentials;
}
private static HttpManager instance;
public static HttpManager getInstance() {
if (instance == null) {
instance = new HttpManager();
}
return instance;
}
public static void setInstance(OkHttpClient client) {
instance.okHttpClient = client;
}
public class AuthInterceptor implements Interceptor {
public AuthInterceptor() {
// empty constructor
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (credentials != null && credentials.getAuthorization() != null) {
request = request.newBuilder()
.addHeader("Authorization", credentials.getAuthorization())
.build();
}
return chain.proceed(request);
}
}
}

View File

@ -76,6 +76,7 @@ dependencies {
implementation "androidx.work:work-runtime-ktx:2.4.0" implementation "androidx.work:work-runtime-ktx:2.4.0"
implementation "androidx.fragment:fragment-ktx:1.2.5" implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation "androidx.browser:browser:1.2.0" implementation "androidx.browser:browser:1.2.0"
testImplementation "org.koin:koin-test:2.1.6"
implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'com.github.bumptech.glide:glide:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0' kapt 'com.github.bumptech.glide:compiler:4.11.0'

View File

@ -13,13 +13,10 @@ import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping; import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.navigation.NavigationFlipperPlugin; import com.facebook.flipper.plugins.navigation.NavigationFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.soloader.SoLoader; import com.facebook.soloader.SoLoader;
import com.icapps.niddler.core.AndroidNiddler; import com.icapps.niddler.core.AndroidNiddler;
import com.icapps.niddler.interceptor.okhttp.NiddlerOkHttpInterceptor;
import com.readrops.api.utils.HttpManager;
public class ReadropsDebugApp extends ReadropsApp implements Configuration.Provider { public class ReadropsDebugApp extends ReadropsApp implements Configuration.Provider {
@ -40,13 +37,6 @@ public class ReadropsDebugApp extends ReadropsApp implements Configuration.Provi
NetworkFlipperPlugin networkPlugin = new NetworkFlipperPlugin(); NetworkFlipperPlugin networkPlugin = new NetworkFlipperPlugin();
client.addPlugin(networkPlugin); client.addPlugin(networkPlugin);
HttpManager.setInstance(
HttpManager.getInstance()
.getOkHttpClient()
.newBuilder()
.addInterceptor(new FlipperOkhttpInterceptor(networkPlugin))
.build());
client.addPlugin(new DatabasesFlipperPlugin(this)); client.addPlugin(new DatabasesFlipperPlugin(this));
client.addPlugin(CrashReporterPlugin.getInstance()); client.addPlugin(CrashReporterPlugin.getInstance());
client.addPlugin(NavigationFlipperPlugin.getInstance()); client.addPlugin(NavigationFlipperPlugin.getInstance());
@ -65,12 +55,6 @@ public class ReadropsDebugApp extends ReadropsApp implements Configuration.Provi
niddler.attachToApplication(this); niddler.attachToApplication(this);
HttpManager.setInstance(HttpManager.getInstance().
getOkHttpClient().
newBuilder().
addInterceptor(new NiddlerOkHttpInterceptor(niddler, "default"))
.build());
niddler.start(); niddler.start();
} }

View File

@ -0,0 +1,54 @@
package com.readrops.app
import androidx.preference.PreferenceManager
import com.readrops.app.repositories.FreshRSSRepository
import com.readrops.app.repositories.LocalFeedRepository
import com.readrops.app.repositories.NextNewsRepository
import com.readrops.app.utils.GlideApp
import com.readrops.app.viewmodels.*
import com.readrops.db.entities.account.Account
import com.readrops.db.entities.account.AccountType
import org.koin.android.ext.koin.androidApplication
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val appModule = module {
factory { (account: Account) ->
when (account.accountType) {
AccountType.LOCAL -> LocalFeedRepository(get(), get(), androidContext(), account)
AccountType.NEXTCLOUD_NEWS -> NextNewsRepository(get(), get(), androidContext(), account)
AccountType.FRESHRSS -> FreshRSSRepository(get(), get(), androidContext(), account)
else -> throw IllegalArgumentException("Account type not supported")
}
}
viewModel {
MainViewModel(get())
}
viewModel {
AddFeedsViewModel(get(), get())
}
viewModel {
ItemViewModel(get())
}
viewModel {
ManageFeedsFoldersViewModel(get())
}
viewModel {
NotificationPermissionViewModel(get())
}
viewModel {
AccountViewModel(get())
}
single { GlideApp.with(androidApplication()) }
single { PreferenceManager.getDefaultSharedPreferences(androidContext()) }
}

View File

@ -1,61 +0,0 @@
package com.readrops.app;
import android.annotation.SuppressLint;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager;
import com.readrops.app.utils.SharedPreferencesManager;
import io.reactivex.plugins.RxJavaPlugins;
@SuppressLint("Registered")
public class ReadropsApp extends Application {
public static final String FEEDS_COLORS_CHANNEL_ID = "feedsColorsChannel";
public static final String OPML_EXPORT_CHANNEL_ID = "opmlExportChannel";
public static final String SYNC_CHANNEL_ID = "syncChannel";
@Override
public void onCreate() {
super.onCreate();
RxJavaPlugins.setErrorHandler(e -> {
});
createNotificationChannels();
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
if (Boolean.valueOf(SharedPreferencesManager.readString(this, SharedPreferencesManager.SharedPrefKey.DARK_THEME)))
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
else
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
private void createNotificationChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel feedsColorsChannel = new NotificationChannel(FEEDS_COLORS_CHANNEL_ID,
getString(R.string.feeds_colors), NotificationManager.IMPORTANCE_DEFAULT);
feedsColorsChannel.setDescription(getString(R.string.get_feeds_colors));
NotificationChannel opmlExportChannel = new NotificationChannel(OPML_EXPORT_CHANNEL_ID,
getString(R.string.opml_export), NotificationManager.IMPORTANCE_DEFAULT);
opmlExportChannel.setDescription(getString(R.string.opml_export_description));
NotificationChannel syncChannel = new NotificationChannel(SYNC_CHANNEL_ID,
getString(R.string.auto_synchro), NotificationManager.IMPORTANCE_LOW);
syncChannel.setDescription(getString(R.string.account_synchro));
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(feedsColorsChannel);
manager.createNotificationChannel(opmlExportChannel);
manager.createNotificationChannel(syncChannel);
}
}
}

View File

@ -0,0 +1,68 @@
package com.readrops.app
import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.os.Build
import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.PreferenceManager
import com.readrops.api.apiModule
import com.readrops.app.utils.SharedPreferencesManager
import com.readrops.db.dbModule
import io.reactivex.plugins.RxJavaPlugins
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
import org.koin.core.logger.Level
open class ReadropsApp : Application() {
override fun onCreate() {
super.onCreate()
RxJavaPlugins.setErrorHandler { e: Throwable? -> }
createNotificationChannels()
PreferenceManager.setDefaultValues(this, R.xml.preferences, false)
startKoin {
androidLogger(Level.ERROR)
androidContext(this@ReadropsApp)
modules(apiModule, dbModule, appModule)
}
if (SharedPreferencesManager.readString(SharedPreferencesManager.SharedPrefKey.DARK_THEME).toBoolean())
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
else
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
}
private fun createNotificationChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val feedsColorsChannel = NotificationChannel(FEEDS_COLORS_CHANNEL_ID,
getString(R.string.feeds_colors), NotificationManager.IMPORTANCE_DEFAULT)
feedsColorsChannel.description = getString(R.string.get_feeds_colors)
val opmlExportChannel = NotificationChannel(OPML_EXPORT_CHANNEL_ID,
getString(R.string.opml_export), NotificationManager.IMPORTANCE_DEFAULT)
opmlExportChannel.description = getString(R.string.opml_export_description)
val syncChannel = NotificationChannel(SYNC_CHANNEL_ID,
getString(R.string.auto_synchro), NotificationManager.IMPORTANCE_LOW)
syncChannel.description = getString(R.string.account_synchro)
val manager = getSystemService(NotificationManager::class.java)!!
manager.createNotificationChannel(feedsColorsChannel)
manager.createNotificationChannel(opmlExportChannel)
manager.createNotificationChannel(syncChannel)
}
}
companion object {
const val FEEDS_COLORS_CHANNEL_ID = "feedsColorsChannel"
const val OPML_EXPORT_CHANNEL_ID = "opmlExportChannel"
const val SYNC_CHANNEL_ID = "syncChannel"
}
}

View File

@ -11,7 +11,6 @@ import android.widget.LinearLayout;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -25,6 +24,8 @@ import com.readrops.app.viewmodels.AccountViewModel;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.db.entities.account.AccountType; import com.readrops.db.entities.account.AccountType;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -55,7 +56,7 @@ public class AccountTypeListActivity extends AppCompatActivity {
binding = ActivityAccountTypeListBinding.inflate(getLayoutInflater()); binding = ActivityAccountTypeListBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
viewModel = new ViewModelProvider(this).get(AccountViewModel.class); viewModel = ViewModelCompat.getViewModel(this, AccountViewModel.class);
setTitle(R.string.new_account); setTitle(R.string.new_account);
@ -158,7 +159,7 @@ public class AccountTypeListActivity extends AppCompatActivity {
account.setId(id.intValue()); account.setId(id.intValue());
viewModel.setAccount(account); viewModel.setAccount(account);
return viewModel.parseOPMLFile(uri); return viewModel.parseOPMLFile(uri, this);
}) })
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View File

@ -8,7 +8,6 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import com.readrops.app.R; import com.readrops.app.R;
import com.readrops.app.databinding.ActivityAddAccountBinding; import com.readrops.app.databinding.ActivityAddAccountBinding;
@ -18,6 +17,8 @@ import com.readrops.app.viewmodels.AccountViewModel;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.db.entities.account.AccountType; import com.readrops.db.entities.account.AccountType;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import io.reactivex.Completable; import io.reactivex.Completable;
import io.reactivex.CompletableObserver; import io.reactivex.CompletableObserver;
import io.reactivex.SingleObserver; import io.reactivex.SingleObserver;
@ -46,7 +47,7 @@ public class AddAccountActivity extends AppCompatActivity {
binding = ActivityAddAccountBinding.inflate(getLayoutInflater()); binding = ActivityAddAccountBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
viewModel = new ViewModelProvider(this).get(AccountViewModel.class); viewModel = ViewModelCompat.getViewModel(this, AccountViewModel.class);
accountType = getIntent().getParcelableExtra(ACCOUNT_TYPE); accountType = getIntent().getParcelableExtra(ACCOUNT_TYPE);
@ -58,7 +59,6 @@ public class AddAccountActivity extends AppCompatActivity {
if (forwardResult || accountToEdit != null) if (forwardResult || accountToEdit != null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
try {
if (accountToEdit != null) { if (accountToEdit != null) {
viewModel.setAccountType(accountToEdit.getAccountType()); viewModel.setAccountType(accountToEdit.getAccountType());
editAccount = true; editAccount = true;
@ -73,11 +73,6 @@ public class AddAccountActivity extends AppCompatActivity {
binding.addAccountPasswordLayout.setHelperText(getString(R.string.password_helper)); binding.addAccountPasswordLayout.setHelperText(getString(R.string.password_helper));
} }
} }
} catch (Exception e) {
// TODO : see how to handle this exception
e.printStackTrace();
}
} }
public void createAccount(View view) { public void createAccount(View view) {
@ -183,8 +178,8 @@ public class AddAccountActivity extends AppCompatActivity {
} }
private void saveLoginPassword(Account account) { private void saveLoginPassword(Account account) {
SharedPreferencesManager.writeValue(this, account.getLoginKey(), account.getLogin()); SharedPreferencesManager.writeValue(account.getLoginKey(), account.getLogin());
SharedPreferencesManager.writeValue(this, account.getPasswordKey(), account.getPassword()); SharedPreferencesManager.writeValue(account.getPasswordKey(), account.getPassword());
account.setLogin(null); account.setLogin(null);
account.setPassword(null); account.setPassword(null);
@ -196,8 +191,8 @@ public class AddAccountActivity extends AppCompatActivity {
binding.addAccountUrl.setText(accountToEdit.getUrl()); binding.addAccountUrl.setText(accountToEdit.getUrl());
binding.addAccountName.setText(accountToEdit.getAccountName()); binding.addAccountName.setText(accountToEdit.getAccountName());
binding.addAccountLogin.setText(SharedPreferencesManager.readString(this, accountToEdit.getLoginKey())); binding.addAccountLogin.setText(SharedPreferencesManager.readString(accountToEdit.getLoginKey()));
binding.addAccountPassword.setText(SharedPreferencesManager.readString(this, accountToEdit.getPasswordKey())); binding.addAccountPassword.setText(SharedPreferencesManager.readString(accountToEdit.getPasswordKey()));
} }
private void updateAccount() { private void updateAccount() {

View File

@ -11,7 +11,6 @@ import android.view.View;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -33,6 +32,8 @@ import com.readrops.app.viewmodels.AddFeedsViewModel;
import com.readrops.db.entities.Feed; import com.readrops.db.entities.Feed;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -69,7 +70,7 @@ public class AddFeedActivity extends AppCompatActivity implements View.OnClickLi
binding.addFeedOk.setOnClickListener(this); binding.addFeedOk.setOnClickListener(this);
binding.addFeedOk.setEnabled(false); binding.addFeedOk.setEnabled(false);
viewModel = new ViewModelProvider(this).get(AddFeedsViewModel.class); viewModel = ViewModelCompat.getViewModel(this, AddFeedsViewModel.class);
parseItemsAdapter = new ItemAdapter<>(); parseItemsAdapter = new ItemAdapter<>();
fastAdapter = FastAdapter.with(parseItemsAdapter); fastAdapter = FastAdapter.with(parseItemsAdapter);
@ -259,8 +260,8 @@ public class AddFeedActivity extends AppCompatActivity implements View.OnClickLi
Account account = (Account) binding.addFeedAccountSpinner.getSelectedItem(); Account account = (Account) binding.addFeedAccountSpinner.getSelectedItem();
account.setLogin(SharedPreferencesManager.readString(this, account.getLoginKey())); account.setLogin(SharedPreferencesManager.readString(account.getLoginKey()));
account.setPassword(SharedPreferencesManager.readString(this, account.getPasswordKey())); account.setPassword(SharedPreferencesManager.readString(account.getPasswordKey()));
viewModel.addFeeds(feedsToInsert, account) viewModel.addFeeds(feedsToInsert, account)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View File

@ -25,16 +25,15 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.browser.customtabs.CustomTabsIntent; import androidx.browser.customtabs.CustomTabsIntent;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.app.ShareCompat; import androidx.core.app.ShareCompat;
import androidx.lifecycle.ViewModelProvider;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import com.readrops.api.utils.DateUtils;
import com.readrops.app.R; import com.readrops.app.R;
import com.readrops.app.databinding.ActivityItemBinding; import com.readrops.app.databinding.ActivityItemBinding;
import com.readrops.api.utils.DateUtils; import com.readrops.app.utils.GlideRequests;
import com.readrops.app.utils.GlideApp;
import com.readrops.app.utils.PermissionManager; import com.readrops.app.utils.PermissionManager;
import com.readrops.app.utils.SharedPreferencesManager; import com.readrops.app.utils.SharedPreferencesManager;
import com.readrops.app.utils.Utils; import com.readrops.app.utils.Utils;
@ -42,6 +41,9 @@ import com.readrops.app.viewmodels.ItemViewModel;
import com.readrops.db.entities.Item; import com.readrops.db.entities.Item;
import com.readrops.db.pojo.ItemWithFeed; import com.readrops.db.pojo.ItemWithFeed;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import org.koin.java.KoinJavaComponent;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -92,7 +94,7 @@ public class ItemActivity extends AppCompatActivity {
binding.appBarLayout.setExpanded(true); binding.appBarLayout.setExpanded(true);
binding.collapsingLayout.setTitleEnabled(true); binding.collapsingLayout.setTitleEnabled(true);
GlideApp.with(this) KoinJavaComponent.get(GlideRequests.class)
.load(imageUrl) .load(imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL) .diskCacheStrategy(DiskCacheStrategy.ALL)
.into(binding.collapsingLayoutImage); .into(binding.collapsingLayoutImage);
@ -110,7 +112,7 @@ public class ItemActivity extends AppCompatActivity {
invalidateOptionsMenu(); invalidateOptionsMenu();
})); }));
viewModel = new ViewModelProvider(this).get(ItemViewModel.class); viewModel = ViewModelCompat.getViewModel(this, ItemViewModel.class);
viewModel.getItemById(itemId).observe(this, this::bindUI); viewModel.getItemById(itemId).observe(this, this::bindUI);
binding.activityItemFab.setOnClickListener(v -> openInNavigator()); binding.activityItemFab.setOnClickListener(v -> openInNavigator());
} }
@ -214,7 +216,7 @@ public class ItemActivity extends AppCompatActivity {
} }
private void openUrl() { private void openUrl() {
int value = Integer.parseInt(SharedPreferencesManager.readString(this, int value = Integer.parseInt(SharedPreferencesManager.readString(
SharedPreferencesManager.SharedPrefKey.OPEN_ITEMS_IN)); SharedPreferencesManager.SharedPrefKey.OPEN_ITEMS_IN));
switch (value) { switch (value) {
case 0: case 0:
@ -243,7 +245,7 @@ public class ItemActivity extends AppCompatActivity {
} }
private void openInCustomTab() { private void openInCustomTab() {
boolean darkTheme = Boolean.parseBoolean(SharedPreferencesManager.readString(this, SharedPreferencesManager.SharedPrefKey.DARK_THEME)); boolean darkTheme = Boolean.parseBoolean(SharedPreferencesManager.readString(SharedPreferencesManager.SharedPrefKey.DARK_THEME));
int color = itemWithFeed.getBgColor() != 0 ? itemWithFeed.getBgColor() : itemWithFeed.getColor(); int color = itemWithFeed.getBgColor() != 0 ? itemWithFeed.getBgColor() : itemWithFeed.getColor();
CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder() CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder()
@ -357,7 +359,7 @@ public class ItemActivity extends AppCompatActivity {
} }
private void shareImage(String url) { private void shareImage(String url) {
GlideApp.with(this) KoinJavaComponent.get(GlideRequests.class)
.asBitmap() .asBitmap()
.diskCacheStrategy(DiskCacheStrategy.ALL) .diskCacheStrategy(DiskCacheStrategy.ALL)
.load(url) .load(url)
@ -365,7 +367,7 @@ public class ItemActivity extends AppCompatActivity {
@Override @Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) { public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
try { try {
Uri uri = viewModel.saveImageInCache(resource); Uri uri = viewModel.saveImageInCache(resource, ItemActivity.this);
Intent intent = ShareCompat.IntentBuilder.from(ItemActivity.this) Intent intent = ShareCompat.IntentBuilder.from(ItemActivity.this)
.setType("image/png") .setType("image/png")
.setStream(uri) .setStream(uri)

View File

@ -16,7 +16,6 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat; import androidx.core.graphics.drawable.DrawableCompat;
import androidx.drawerlayout.widget.DrawerLayout; import androidx.drawerlayout.widget.DrawerLayout;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.PagedList; import androidx.paging.PagedList;
import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
@ -38,7 +37,7 @@ import com.readrops.app.R;
import com.readrops.app.adapters.MainItemListAdapter; import com.readrops.app.adapters.MainItemListAdapter;
import com.readrops.app.databinding.ActivityMainBinding; import com.readrops.app.databinding.ActivityMainBinding;
import com.readrops.app.utils.DrawerManager; import com.readrops.app.utils.DrawerManager;
import com.readrops.app.utils.GlideApp; import com.readrops.app.utils.GlideRequests;
import com.readrops.app.utils.ReadropsItemTouchCallback; import com.readrops.app.utils.ReadropsItemTouchCallback;
import com.readrops.app.utils.SharedPreferencesManager; import com.readrops.app.utils.SharedPreferencesManager;
import com.readrops.app.utils.Utils; import com.readrops.app.utils.Utils;
@ -51,6 +50,8 @@ import com.readrops.db.filters.ListSortType;
import com.readrops.db.pojo.ItemWithFeed; import com.readrops.db.pojo.ItemWithFeed;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import org.koin.java.KoinJavaComponent;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Collections; import java.util.Collections;
@ -116,9 +117,9 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
feedCount = 0; feedCount = 0;
initRecyclerView(); initRecyclerView();
viewModel = new ViewModelProvider(this).get(MainViewModel.class); viewModel = ViewModelCompat.getViewModel(this, MainViewModel.class);
viewModel.setShowReadItems(SharedPreferencesManager.readBoolean(this, viewModel.setShowReadItems(SharedPreferencesManager.readBoolean(
SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES)); SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES));
viewModel.getItemsWithFeed().observe(this, itemWithFeeds -> { viewModel.getItemsWithFeed().observe(this, itemWithFeeds -> {
@ -300,7 +301,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
private void initRecyclerView() { private void initRecyclerView() {
ViewPreloadSizeProvider preloadSizeProvider = new ViewPreloadSizeProvider(); ViewPreloadSizeProvider preloadSizeProvider = new ViewPreloadSizeProvider();
adapter = new MainItemListAdapter(GlideApp.with(this), preloadSizeProvider); adapter = new MainItemListAdapter(KoinJavaComponent.get(GlideRequests.class), preloadSizeProvider);
adapter.setOnItemClickListener(new MainItemListAdapter.OnItemClickListener() { adapter.setOnItemClickListener(new MainItemListAdapter.OnItemClickListener() {
@Override @Override
public void onItemClick(ItemWithFeed itemWithFeed, int position) { public void onItemClick(ItemWithFeed itemWithFeed, int position) {
@ -349,7 +350,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
binding.itemsRecyclerView.setRecyclerListener(viewHolder -> { binding.itemsRecyclerView.setRecyclerListener(viewHolder -> {
MainItemListAdapter.ItemViewHolder vh = (MainItemListAdapter.ItemViewHolder) viewHolder; MainItemListAdapter.ItemViewHolder vh = (MainItemListAdapter.ItemViewHolder) viewHolder;
GlideApp.with(this).clear(vh.getItemImage()); KoinJavaComponent.get(GlideRequests.class).clear(vh.getItemImage());
}); });
LinearLayoutManager layoutManager = new LinearLayoutManager(this); LinearLayoutManager layoutManager = new LinearLayoutManager(this);
@ -659,12 +660,12 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
if (item.isChecked()) { if (item.isChecked()) {
item.setChecked(false); item.setChecked(false);
viewModel.setShowReadItems(false); viewModel.setShowReadItems(false);
SharedPreferencesManager.writeValue(this, SharedPreferencesManager.writeValue(
SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES, false); SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES, false);
} else { } else {
item.setChecked(true); item.setChecked(true);
viewModel.setShowReadItems(true); viewModel.setShowReadItems(true);
SharedPreferencesManager.writeValue(this, SharedPreferencesManager.writeValue(
SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES, true); SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES, true);
} }
@ -708,16 +709,16 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
private void getAccountCredentials(List<Account> accounts) { private void getAccountCredentials(List<Account> accounts) {
for (Account account : accounts) { for (Account account : accounts) {
if (account.getLogin() == null) if (account.getLogin() == null)
account.setLogin(SharedPreferencesManager.readString(this, account.getLoginKey())); account.setLogin(SharedPreferencesManager.readString(account.getLoginKey()));
if (account.getPassword() == null) if (account.getPassword() == null)
account.setPassword(SharedPreferencesManager.readString(this, account.getPasswordKey())); account.setPassword(SharedPreferencesManager.readString(account.getPasswordKey()));
} }
} }
private void startAboutActivity() { private void startAboutActivity() {
Libs.ActivityStyle activityStyle; Libs.ActivityStyle activityStyle;
if (Boolean.valueOf(SharedPreferencesManager.readString(this, SharedPreferencesManager.SharedPrefKey.DARK_THEME))) if (Boolean.valueOf(SharedPreferencesManager.readString(SharedPreferencesManager.SharedPrefKey.DARK_THEME)))
activityStyle = Libs.ActivityStyle.DARK; activityStyle = Libs.ActivityStyle.DARK;
else else
activityStyle = Libs.ActivityStyle.LIGHT_DARK_TOOLBAR; activityStyle = Libs.ActivityStyle.LIGHT_DARK_TOOLBAR;

View File

@ -9,9 +9,10 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter; import androidx.fragment.app.FragmentPagerAdapter;
import androidx.lifecycle.ViewModelProvider;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.readrops.api.utils.ConflictException;
import com.readrops.api.utils.UnknownFormatException;
import com.readrops.app.R; import com.readrops.app.R;
import com.readrops.app.databinding.ActivityManageFeedsFoldersBinding; import com.readrops.app.databinding.ActivityManageFeedsFoldersBinding;
import com.readrops.app.fragments.FeedsFragment; import com.readrops.app.fragments.FeedsFragment;
@ -20,8 +21,8 @@ import com.readrops.app.utils.Utils;
import com.readrops.app.viewmodels.ManageFeedsFoldersViewModel; import com.readrops.app.viewmodels.ManageFeedsFoldersViewModel;
import com.readrops.db.entities.Folder; import com.readrops.db.entities.Folder;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.api.utils.ConflictException;
import com.readrops.api.utils.UnknownFormatException; import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
@ -52,7 +53,7 @@ public class ManageFeedsFoldersActivity extends AppCompatActivity {
binding.manageFeedsFoldersViewpager.setAdapter(pageAdapter); binding.manageFeedsFoldersViewpager.setAdapter(pageAdapter);
binding.manageFeedsFoldersTablayout.setupWithViewPager(binding.manageFeedsFoldersViewpager); binding.manageFeedsFoldersTablayout.setupWithViewPager(binding.manageFeedsFoldersViewpager);
viewModel = new ViewModelProvider(this).get(ManageFeedsFoldersViewModel.class); viewModel = ViewModelCompat.getViewModel(this, ManageFeedsFoldersViewModel.class);
viewModel.setAccount(account); viewModel.setAccount(account);
} }

View File

@ -3,7 +3,6 @@ package com.readrops.app.activities
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -20,11 +19,12 @@ import com.readrops.db.entities.Feed
import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.Account
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.koin.androidx.viewmodel.ext.android.getViewModel
class NotificationPermissionActivity : AppCompatActivity() { class NotificationPermissionActivity : AppCompatActivity() {
private lateinit var binding: ActivityNotificationPermissionBinding private lateinit var binding: ActivityNotificationPermissionBinding
private val viewModel by viewModels<NotificationPermissionViewModel>() private val viewModel = getViewModel<NotificationPermissionViewModel>()
private var adapter: NotificationPermissionListAdapter? = null private var adapter: NotificationPermissionListAdapter? = null
private var isFirstCheck = true private var isFirstCheck = true
@ -120,7 +120,7 @@ class NotificationPermissionActivity : AppCompatActivity() {
} }
private fun displayAutoSynchroPopup() { private fun displayAutoSynchroPopup() {
val autoSynchroValue = SharedPreferencesManager.readString(this, SharedPreferencesManager.SharedPrefKey.AUTO_SYNCHRO) val autoSynchroValue = SharedPreferencesManager.readString(SharedPreferencesManager.SharedPrefKey.AUTO_SYNCHRO)
if (autoSynchroValue.toFloat() <= 0) { if (autoSynchroValue.toFloat() <= 0) {
MaterialDialog.Builder(this) MaterialDialog.Builder(this)

View File

@ -4,11 +4,12 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import com.readrops.app.R; import com.readrops.app.R;
import com.readrops.app.viewmodels.AccountViewModel; import com.readrops.app.viewmodels.AccountViewModel;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DisposableSingleObserver; import io.reactivex.observers.DisposableSingleObserver;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
@ -22,7 +23,7 @@ public class SplashActivity extends AppCompatActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash); setContentView(R.layout.activity_splash);
viewModel = new ViewModelProvider(this).get(AccountViewModel.class); viewModel = ViewModelCompat.getViewModel(this, AccountViewModel.class);
viewModel.getAccountCount() viewModel.getAccountCount()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View File

@ -13,9 +13,11 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.readrops.app.R; import com.readrops.app.R;
import com.readrops.app.databinding.FeedLayoutBinding; import com.readrops.app.databinding.FeedLayoutBinding;
import com.readrops.app.utils.GlideApp; import com.readrops.app.utils.GlideRequests;
import com.readrops.db.pojo.FeedWithFolder; import com.readrops.db.pojo.FeedWithFolder;
import org.koin.java.KoinJavaComponent;
import java.util.List; import java.util.List;
public class FeedsAdapter extends ListAdapter<FeedWithFolder, FeedsAdapter.FeedViewHolder> { public class FeedsAdapter extends ListAdapter<FeedWithFolder, FeedsAdapter.FeedViewHolder> {
@ -64,7 +66,7 @@ public class FeedsAdapter extends ListAdapter<FeedWithFolder, FeedsAdapter.FeedV
FeedWithFolder feedWithFolder = getItem(i); FeedWithFolder feedWithFolder = getItem(i);
if (feedWithFolder.getFeed().getIconUrl() != null) { if (feedWithFolder.getFeed().getIconUrl() != null) {
GlideApp.with(viewHolder.itemView.getContext()) KoinJavaComponent.get(GlideRequests.class)
.load(feedWithFolder.getFeed().getIconUrl()) .load(feedWithFolder.getFeed().getIconUrl())
.diskCacheStrategy(DiskCacheStrategy.ALL) .diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.drawable.ic_rss_feed_grey) .placeholder(R.drawable.ic_rss_feed_grey)

View File

@ -9,10 +9,13 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.readrops.app.R import com.readrops.app.R
import com.readrops.app.databinding.NotificationPermissionLayoutBinding import com.readrops.app.databinding.NotificationPermissionLayoutBinding
import com.readrops.app.utils.GlideApp import com.readrops.app.utils.GlideApp
import com.readrops.app.utils.GlideRequests
import com.readrops.db.entities.Feed import com.readrops.db.entities.Feed
import org.koin.core.KoinComponent
import org.koin.core.get
class NotificationPermissionListAdapter(var enableAll: Boolean, val listener: (feed: Feed) -> Unit) : class NotificationPermissionListAdapter(var enableAll: Boolean, val listener: (feed: Feed) -> Unit) :
ListAdapter<Feed, NotificationPermissionListAdapter.NotificationPermissionViewHolder>(DIFF_CALLBACK) { ListAdapter<Feed, NotificationPermissionListAdapter.NotificationPermissionViewHolder>(DIFF_CALLBACK), KoinComponent {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationPermissionViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationPermissionViewHolder {
val binding = NotificationPermissionLayoutBinding.inflate(LayoutInflater.from(parent.context)) val binding = NotificationPermissionLayoutBinding.inflate(LayoutInflater.from(parent.context))
@ -30,7 +33,7 @@ class NotificationPermissionListAdapter(var enableAll: Boolean, val listener: (f
holder.itemView.setOnClickListener { if (enableAll) listener(getItem(position)) } holder.itemView.setOnClickListener { if (enableAll) listener(getItem(position)) }
GlideApp.with(holder.itemView.context) get<GlideRequests>()
.load(feed.iconUrl) .load(feed.iconUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL) .diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.drawable.ic_rss_feed_grey) .placeholder(R.drawable.ic_rss_feed_grey)

View File

@ -11,7 +11,6 @@ import android.widget.Spinner;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
import com.readrops.app.R; import com.readrops.app.R;
@ -21,6 +20,8 @@ import com.readrops.db.entities.Folder;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.db.pojo.FeedWithFolder; import com.readrops.db.pojo.FeedWithFolder;
import org.koin.androidx.viewmodel.compat.SharedViewModelCompat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
@ -59,7 +60,7 @@ public class EditFeedDialogFragment extends DialogFragment implements AdapterVie
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
viewModel = new ViewModelProvider(getActivity()).get(ManageFeedsFoldersViewModel.class); viewModel = SharedViewModelCompat.getSharedViewModel(this, ManageFeedsFoldersViewModel.class);
feedWithFolder = getArguments().getParcelable("feedWithFolder"); feedWithFolder = getArguments().getParcelable("feedWithFolder");
account = getArguments().getParcelable(ACCOUNT); account = getArguments().getParcelable(ACCOUNT);

View File

@ -10,7 +10,6 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
@ -24,6 +23,8 @@ import com.readrops.db.entities.Feed;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.db.pojo.FeedWithFolder; import com.readrops.db.pojo.FeedWithFolder;
import org.koin.androidx.viewmodel.compat.SharedViewModelCompat;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DisposableCompletableObserver; import io.reactivex.observers.DisposableCompletableObserver;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
@ -60,11 +61,11 @@ public class FeedsFragment extends Fragment {
account = getArguments().getParcelable(ACCOUNT); account = getArguments().getParcelable(ACCOUNT);
if (account.getLogin() == null) if (account.getLogin() == null)
account.setLogin(SharedPreferencesManager.readString(getContext(), account.getLoginKey())); account.setLogin(SharedPreferencesManager.readString(account.getLoginKey()));
if (account.getPassword() == null) if (account.getPassword() == null)
account.setPassword(SharedPreferencesManager.readString(getContext(), account.getPasswordKey())); account.setPassword(SharedPreferencesManager.readString(account.getPasswordKey()));
viewModel = new ViewModelProvider(this).get(ManageFeedsFoldersViewModel.class); viewModel = SharedViewModelCompat.getSharedViewModel(this, ManageFeedsFoldersViewModel.class);
viewModel.setAccount(account); viewModel.setAccount(account);
viewModel.getFeedsWithFolder().observe(this, feedWithFolders -> { viewModel.getFeedsWithFolder().observe(this, feedWithFolders -> {

View File

@ -10,10 +10,11 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.readrops.api.utils.ConflictException;
import com.readrops.api.utils.UnknownFormatException;
import com.readrops.app.R; import com.readrops.app.R;
import com.readrops.app.adapters.FoldersAdapter; import com.readrops.app.adapters.FoldersAdapter;
import com.readrops.app.databinding.FragmentFoldersBinding; import com.readrops.app.databinding.FragmentFoldersBinding;
@ -22,8 +23,8 @@ import com.readrops.app.utils.Utils;
import com.readrops.app.viewmodels.ManageFeedsFoldersViewModel; import com.readrops.app.viewmodels.ManageFeedsFoldersViewModel;
import com.readrops.db.entities.Folder; import com.readrops.db.entities.Folder;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.api.utils.ConflictException;
import com.readrops.api.utils.UnknownFormatException; import org.koin.androidx.viewmodel.compat.SharedViewModelCompat;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DisposableSingleObserver; import io.reactivex.observers.DisposableSingleObserver;
@ -60,12 +61,12 @@ public class FoldersFragment extends Fragment {
account = getArguments().getParcelable(ACCOUNT); account = getArguments().getParcelable(ACCOUNT);
if (account.getLogin() == null) if (account.getLogin() == null)
account.setLogin(SharedPreferencesManager.readString(getContext(), account.getLoginKey())); account.setLogin(SharedPreferencesManager.readString(account.getLoginKey()));
if (account.getPassword() == null) if (account.getPassword() == null)
account.setPassword(SharedPreferencesManager.readString(getContext(), account.getPasswordKey())); account.setPassword(SharedPreferencesManager.readString(account.getPasswordKey()));
adapter = new FoldersAdapter(this::openFolderOptionsDialog); adapter = new FoldersAdapter(this::openFolderOptionsDialog);
viewModel = new ViewModelProvider(this).get(ManageFeedsFoldersViewModel.class); viewModel = SharedViewModelCompat.getSharedViewModel(this, ManageFeedsFoldersViewModel.class);
viewModel.setAccount(account); viewModel.setAccount(account);
viewModel.getFeedCountByAccount() viewModel.getFeedCountByAccount()

View File

@ -16,7 +16,6 @@ import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
@ -36,6 +35,8 @@ import com.readrops.app.viewmodels.AccountViewModel;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.db.entities.account.AccountType; import com.readrops.db.entities.account.AccountType;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DisposableCompletableObserver; import io.reactivex.observers.DisposableCompletableObserver;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
@ -136,7 +137,7 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(AccountViewModel.class); viewModel = ViewModelCompat.getViewModel(this, AccountViewModel.class);
viewModel.setAccount(account); viewModel.setAccount(account);
} }
@ -146,8 +147,8 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
.positiveText(R.string.validate) .positiveText(R.string.validate)
.negativeText(R.string.cancel) .negativeText(R.string.cancel)
.onPositive(((dialog, which) -> { .onPositive(((dialog, which) -> {
SharedPreferencesManager.remove(getContext(), account.getLoginKey()); SharedPreferencesManager.remove(account.getLoginKey());
SharedPreferencesManager.remove(getContext(), account.getPasswordKey()); SharedPreferencesManager.remove(account.getPasswordKey());
viewModel.delete(account) viewModel.delete(account)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -204,7 +205,7 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
} }
private void parseOPMLFile(Uri uri, MaterialDialog dialog) { private void parseOPMLFile(Uri uri, MaterialDialog dialog) {
viewModel.parseOPMLFile(uri) viewModel.parseOPMLFile(uri, getContext())
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableCompletableObserver() { .subscribe(new DisposableCompletableObserver() {

View File

@ -19,6 +19,8 @@ import com.readrops.app.utils.SyncWorker;
import com.readrops.app.utils.feedscolors.FeedsColorsIntentService; import com.readrops.app.utils.feedscolors.FeedsColorsIntentService;
import com.readrops.db.Database; import com.readrops.db.Database;
import org.koin.java.KoinJavaComponent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -38,7 +40,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
AtomicBoolean serviceStarted = new AtomicBoolean(false); AtomicBoolean serviceStarted = new AtomicBoolean(false);
feedsColorsPreference.setOnPreferenceClickListener(preference -> { feedsColorsPreference.setOnPreferenceClickListener(preference -> {
Database database = Database.getInstance(getContext()); Database database = KoinJavaComponent.get(Database.class);
database.feedDao().getAllFeeds().observe(getActivity(), feeds -> { database.feedDao().getAllFeeds().observe(getActivity(), feeds -> {
if (!serviceStarted.get()) { if (!serviceStarted.get()) {

View File

@ -6,6 +6,9 @@ import android.content.Intent;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.readrops.api.services.Credentials;
import com.readrops.api.services.SyncResult;
import com.readrops.api.utils.AuthInterceptor;
import com.readrops.app.utils.FeedInsertionResult; import com.readrops.app.utils.FeedInsertionResult;
import com.readrops.app.utils.ParsingResult; import com.readrops.app.utils.ParsingResult;
import com.readrops.app.utils.feedscolors.FeedColorsKt; import com.readrops.app.utils.feedscolors.FeedColorsKt;
@ -15,8 +18,8 @@ import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Folder; import com.readrops.db.entities.Folder;
import com.readrops.db.entities.Item; import com.readrops.db.entities.Item;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.db.entities.account.AccountType;
import com.readrops.api.services.SyncResult; import org.koin.java.KoinJavaComponent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
@ -30,25 +33,26 @@ import io.reactivex.Single;
import static com.readrops.app.utils.ReadropsKeys.FEEDS; import static com.readrops.app.utils.ReadropsKeys.FEEDS;
public abstract class ARepository<T> { public abstract class ARepository {
protected Context context; protected Context context;
protected Database database; protected Database database;
protected Account account; protected Account account;
protected T api;
protected SyncResult syncResult; protected SyncResult syncResult;
protected ARepository(@NonNull Context context, @Nullable Account account) { protected ARepository(Database database, @NonNull Context context, @Nullable Account account) {
this.context = context; this.context = context;
this.database = Database.getInstance(context); this.database = database;
this.account = account; this.account = account;
api = createAPI(); setCredentials(account);
} }
protected abstract T createAPI(); protected void setCredentials(@Nullable Account account) {
KoinJavaComponent.get(AuthInterceptor.class)
.setCredentials(account != null && !account.isLocal() ? Credentials.toCredentials(account) : null);
}
// TODO : replace Single by Completable // TODO : replace Single by Completable
public abstract Single<Boolean> login(Account account, boolean insert); public abstract Single<Boolean> login(Account account, boolean insert);
@ -171,23 +175,6 @@ public abstract class ARepository<T> {
context.startService(intent); context.startService(intent);
} }
public static ARepository repositoryFactory(Account account, AccountType accountType, Context context) throws Exception {
switch (accountType) {
case LOCAL:
return new LocalFeedRepository(context, account);
case NEXTCLOUD_NEWS:
return new NextNewsRepository(context, account);
case FRESHRSS:
return new FreshRSSRepository(context, account);
default:
throw new Exception("account type not supported");
}
}
public static ARepository repositoryFactory(Account account, Context context) throws Exception {
return ARepository.repositoryFactory(account, account.getAccountType(), context);
}
public SyncResult getSyncResult() { public SyncResult getSyncResult() {
return syncResult; return syncResult;
} }

View File

@ -7,18 +7,17 @@ import android.util.TimingLogger;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.readrops.api.services.SyncType;
import com.readrops.api.services.freshrss.FreshRSSDataSource;
import com.readrops.api.services.freshrss.FreshRSSSyncData;
import com.readrops.app.utils.FeedInsertionResult; import com.readrops.app.utils.FeedInsertionResult;
import com.readrops.app.utils.ParsingResult; import com.readrops.app.utils.ParsingResult;
import com.readrops.app.utils.Utils; import com.readrops.app.utils.Utils;
import com.readrops.db.Database;
import com.readrops.db.entities.Feed; import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Folder; import com.readrops.db.entities.Folder;
import com.readrops.db.entities.Item; import com.readrops.db.entities.Item;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.api.services.Credentials;
import com.readrops.api.services.SyncType;
import com.readrops.api.services.freshrss.FreshRSSAPI;
import com.readrops.api.services.freshrss.FreshRSSCredentials;
import com.readrops.api.services.freshrss.FreshRSSSyncData;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -30,40 +29,33 @@ import io.reactivex.Completable;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.Single; import io.reactivex.Single;
public class FreshRSSRepository extends ARepository<FreshRSSAPI> { public class FreshRSSRepository extends ARepository {
private static final String TAG = FreshRSSRepository.class.getSimpleName(); private static final String TAG = FreshRSSRepository.class.getSimpleName();
public FreshRSSRepository(@NonNull Context context, @Nullable Account account) { private final FreshRSSDataSource dataSource;
super(context, account);
}
@Override public FreshRSSRepository(FreshRSSDataSource dataSource, Database database, @NonNull Context context, @Nullable Account account) {
protected FreshRSSAPI createAPI() { super(database, context, account);
if (account != null)
return new FreshRSSAPI(Credentials.toCredentials(account));
return null; this.dataSource = dataSource;
} }
@Override @Override
public Single<Boolean> login(Account account, boolean insert) { public Single<Boolean> login(Account account, boolean insert) {
if (api == null) setCredentials(account);
api = new FreshRSSAPI(Credentials.toCredentials(account));
else
api.setCredentials(Credentials.toCredentials(account));
return api.login(account.getLogin(), account.getPassword()) return dataSource.login(account.getLogin(), account.getPassword())
.flatMap(token -> { .flatMap(token -> {
account.setToken(token); account.setToken(token);
api.setCredentials(new FreshRSSCredentials(token, account.getUrl())); setCredentials(account);
return api.getWriteToken(); return dataSource.getWriteToken();
}) })
.flatMap(writeToken -> { .flatMap(writeToken -> {
account.setWriteToken(writeToken); account.setWriteToken(writeToken);
return api.getUserInfo(); return dataSource.getUserInfo();
}) })
.flatMap(userInfo -> { .flatMap(userInfo -> {
account.setDisplayedName(userInfo.getUserName()); account.setDisplayedName(userInfo.getUserName());
@ -100,7 +92,7 @@ public class FreshRSSRepository extends ARepository<FreshRSSAPI> {
syncData.setUnreadItemsIds(database.itemDao().getUnreadChanges(account.getId())); syncData.setUnreadItemsIds(database.itemDao().getUnreadChanges(account.getId()));
emitter.onSuccess(syncData); emitter.onSuccess(syncData);
}).flatMap(syncData1 -> api.sync(syncType, syncData1, account.getWriteToken())) }).flatMap(syncData1 -> dataSource.sync(syncType, syncData1, account.getWriteToken()))
.flatMapObservable(syncResult -> { .flatMapObservable(syncResult -> {
logger.addSplit("server queries"); logger.addSplit("server queries");
@ -131,7 +123,7 @@ public class FreshRSSRepository extends ARepository<FreshRSSAPI> {
List<FeedInsertionResult> insertionResults = new ArrayList<>(); List<FeedInsertionResult> insertionResults = new ArrayList<>();
for (ParsingResult result : results) { for (ParsingResult result : results) {
completableList.add(api.createFeed(account.getWriteToken(), result.getUrl()) completableList.add(dataSource.createFeed(account.getWriteToken(), result.getUrl())
.doOnComplete(() -> { .doOnComplete(() -> {
FeedInsertionResult feedInsertionResult = new FeedInsertionResult(); FeedInsertionResult feedInsertionResult = new FeedInsertionResult();
feedInsertionResult.setParsingResult(result); feedInsertionResult.setParsingResult(result);
@ -159,26 +151,26 @@ public class FreshRSSRepository extends ARepository<FreshRSSAPI> {
Folder folder = feed.getFolderId() == null ? null : database.folderDao().select(feed.getFolderId()); Folder folder = feed.getFolderId() == null ? null : database.folderDao().select(feed.getFolderId());
emitter.onSuccess(folder); emitter.onSuccess(folder);
}).flatMapCompletable(folder -> api.updateFeed(account.getWriteToken(), }).flatMapCompletable(folder -> dataSource.updateFeed(account.getWriteToken(),
feed.getUrl(), feed.getName(), folder == null ? null : folder.getRemoteId()) feed.getUrl(), feed.getName(), folder == null ? null : folder.getRemoteId())
.andThen(super.updateFeed(feed))); .andThen(super.updateFeed(feed)));
} }
@Override @Override
public Completable deleteFeed(Feed feed) { public Completable deleteFeed(Feed feed) {
return api.deleteFeed(account.getWriteToken(), feed.getUrl()) return dataSource.deleteFeed(account.getWriteToken(), feed.getUrl())
.andThen(super.deleteFeed(feed)); .andThen(super.deleteFeed(feed));
} }
@Override @Override
public Single<Long> addFolder(Folder folder) { public Single<Long> addFolder(Folder folder) {
return api.createFolder(account.getWriteToken(), folder.getName()) return dataSource.createFolder(account.getWriteToken(), folder.getName())
.andThen(super.addFolder(folder)); .andThen(super.addFolder(folder));
} }
@Override @Override
public Completable updateFolder(Folder folder) { public Completable updateFolder(Folder folder) {
return api.updateFolder(account.getWriteToken(), folder.getRemoteId(), folder.getName()) return dataSource.updateFolder(account.getWriteToken(), folder.getRemoteId(), folder.getName())
.andThen(Completable.create(emitter -> { .andThen(Completable.create(emitter -> {
folder.setRemoteId("user/-/label/" + folder.getName()); folder.setRemoteId("user/-/label/" + folder.getName());
emitter.onComplete(); emitter.onComplete();
@ -188,7 +180,7 @@ public class FreshRSSRepository extends ARepository<FreshRSSAPI> {
@Override @Override
public Completable deleteFolder(Folder folder) { public Completable deleteFolder(Folder folder) {
return api.deleteFolder(account.getWriteToken(), folder.getRemoteId()) return dataSource.deleteFolder(account.getWriteToken(), folder.getRemoteId())
.andThen(super.deleteFolder(folder)); .andThen(super.deleteFolder(folder));
} }

View File

@ -9,7 +9,6 @@ import androidx.annotation.Nullable;
import com.readrops.api.localfeed.LocalRSSDataSource; import com.readrops.api.localfeed.LocalRSSDataSource;
import com.readrops.api.services.SyncResult; import com.readrops.api.services.SyncResult;
import com.readrops.api.utils.HttpManager;
import com.readrops.api.utils.LibUtils; import com.readrops.api.utils.LibUtils;
import com.readrops.api.utils.ParseException; import com.readrops.api.utils.ParseException;
import com.readrops.api.utils.UnknownFormatException; import com.readrops.api.utils.UnknownFormatException;
@ -17,6 +16,7 @@ import com.readrops.app.utils.FeedInsertionResult;
import com.readrops.app.utils.ParsingResult; import com.readrops.app.utils.ParsingResult;
import com.readrops.app.utils.SharedPreferencesManager; import com.readrops.app.utils.SharedPreferencesManager;
import com.readrops.app.utils.Utils; import com.readrops.app.utils.Utils;
import com.readrops.db.Database;
import com.readrops.db.entities.Feed; import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Item; import com.readrops.db.entities.Item;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
@ -34,22 +34,17 @@ import io.reactivex.Single;
import kotlin.Pair; import kotlin.Pair;
import okhttp3.Headers; import okhttp3.Headers;
public class LocalFeedRepository extends ARepository<Void> { public class LocalFeedRepository extends ARepository {
private static final String TAG = LocalFeedRepository.class.getSimpleName(); private static final String TAG = LocalFeedRepository.class.getSimpleName();
private LocalRSSDataSource dataSource; private LocalRSSDataSource dataSource;
public LocalFeedRepository(@NonNull Context context, @Nullable Account account) { public LocalFeedRepository(LocalRSSDataSource dataSource, Database database, @NonNull Context context, @Nullable Account account) {
super(context, account); super(database, context, account);
syncResult = new SyncResult(); syncResult = new SyncResult();
dataSource = new LocalRSSDataSource(HttpManager.getInstance().getOkHttpClient()); this.dataSource = dataSource;
}
@Override
protected Void createAPI() {
return null;
} }
@Override @Override
@ -138,7 +133,7 @@ public class LocalFeedRepository extends ARepository<Void> {
Collections.sort(items, Item::compareTo); Collections.sort(items, Item::compareTo);
int maxItems = Integer.parseInt(SharedPreferencesManager.readString(context, int maxItems = Integer.parseInt(SharedPreferencesManager.readString(
SharedPreferencesManager.SharedPrefKey.ITEMS_TO_PARSE_MAX_NB)); SharedPreferencesManager.SharedPrefKey.ITEMS_TO_PARSE_MAX_NB));
if (maxItems > 0 && items.size() > maxItems) { if (maxItems > 0 && items.size() > maxItems) {
items = items.subList(items.size() - maxItems, items.size()); items = items.subList(items.size() - maxItems, items.size());

View File

@ -7,16 +7,16 @@ import android.util.TimingLogger;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.readrops.api.services.Credentials;
import com.readrops.api.services.SyncResult; import com.readrops.api.services.SyncResult;
import com.readrops.api.services.SyncType; import com.readrops.api.services.SyncType;
import com.readrops.api.services.nextcloudnews.NextNewsAPI; import com.readrops.api.services.nextcloudnews.NextNewsDataSource;
import com.readrops.api.services.nextcloudnews.NextNewsSyncData; import com.readrops.api.services.nextcloudnews.NextNewsSyncData;
import com.readrops.api.services.nextcloudnews.json.NextNewsUser; import com.readrops.api.services.nextcloudnews.json.NextNewsUser;
import com.readrops.api.utils.UnknownFormatException; import com.readrops.api.utils.UnknownFormatException;
import com.readrops.app.utils.FeedInsertionResult; import com.readrops.app.utils.FeedInsertionResult;
import com.readrops.app.utils.ParsingResult; import com.readrops.app.utils.ParsingResult;
import com.readrops.app.utils.Utils; import com.readrops.app.utils.Utils;
import com.readrops.db.Database;
import com.readrops.db.entities.Feed; import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Folder; import com.readrops.db.entities.Folder;
import com.readrops.db.entities.Item; import com.readrops.db.entities.Item;
@ -33,31 +33,23 @@ import io.reactivex.Completable;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.Single; import io.reactivex.Single;
public class NextNewsRepository extends ARepository<NextNewsAPI> { public class NextNewsRepository extends ARepository {
private static final String TAG = NextNewsRepository.class.getSimpleName(); private static final String TAG = NextNewsRepository.class.getSimpleName();
public NextNewsRepository(@NonNull Context context, @Nullable Account account) { private final NextNewsDataSource dataSource;
super(context, account);
}
@Override public NextNewsRepository(NextNewsDataSource dataSource, Database database, @NonNull Context context, @Nullable Account account) {
protected NextNewsAPI createAPI() { super(database, context, account);
if (account != null)
return new NextNewsAPI(Credentials.toCredentials(account));
return null; this.dataSource = dataSource;
} }
@Override @Override
public Single<Boolean> login(Account account, boolean insert) { public Single<Boolean> login(Account account, boolean insert) {
setCredentials(account);
return Single.<NextNewsUser>create(emitter -> { return Single.<NextNewsUser>create(emitter -> {
if (api == null) NextNewsUser user = dataSource.login();
api = new NextNewsAPI(Credentials.toCredentials(account));
else
api.setCredentials(Credentials.toCredentials(account));
NextNewsUser user = api.login();
if (user != null) { if (user != null) {
emitter.onSuccess(user); emitter.onSuccess(user);
@ -101,7 +93,7 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
} }
TimingLogger timings = new TimingLogger(TAG, "nextcloud news " + syncType.name().toLowerCase()); TimingLogger timings = new TimingLogger(TAG, "nextcloud news " + syncType.name().toLowerCase());
SyncResult result = api.sync(syncType, syncData); SyncResult result = dataSource.sync(syncType, syncData);
timings.addSplit("server queries"); timings.addSplit("server queries");
if (!result.isError()) { if (!result.isError()) {
@ -141,11 +133,11 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
FeedInsertionResult insertionResult = new FeedInsertionResult(); FeedInsertionResult insertionResult = new FeedInsertionResult();
try { try {
List<Feed> nextNewsFeeds = api.createFeed(result.getUrl(), 0); List<Feed> nextNewsFeeds = dataSource.createFeed(result.getUrl(), 0);
if (nextNewsFeeds != null) { if (nextNewsFeeds != null) {
List<Feed> newFeeds = insertFeeds(nextNewsFeeds, true); List<Feed> newFeeds = insertFeeds(nextNewsFeeds, true);
// there is always only one object in the list, see nextcloud news api doc // there is always only one object in the list, see nextcloud news dataSource doc
insertionResult.setFeed(newFeeds.get(0)); insertionResult.setFeed(newFeeds.get(0));
} else } else
insertionResult.setInsertionError(FeedInsertionResult.FeedInsertionError.UNKNOWN_ERROR); insertionResult.setInsertionError(FeedInsertionResult.FeedInsertionError.UNKNOWN_ERROR);
@ -180,7 +172,7 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
feed.setRemoteFolderId(String.valueOf(0)); // 0 for no folder feed.setRemoteFolderId(String.valueOf(0)); // 0 for no folder
try { try {
if (api.renameFeed(feed) && api.changeFeedFolder(feed)) { if (dataSource.renameFeed(feed) && dataSource.changeFeedFolder(feed)) {
emitter.onComplete(); emitter.onComplete();
} else } else
emitter.onError(new Exception("Unknown error when updating feed")); emitter.onError(new Exception("Unknown error when updating feed"));
@ -194,7 +186,7 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
public Completable deleteFeed(Feed feed) { public Completable deleteFeed(Feed feed) {
return Completable.create(emitter -> { return Completable.create(emitter -> {
try { try {
if (api.deleteFeed(Integer.parseInt(feed.getRemoteId()))) { if (dataSource.deleteFeed(Integer.parseInt(feed.getRemoteId()))) {
emitter.onComplete(); emitter.onComplete();
} else } else
emitter.onError(new Exception("Unknown error")); emitter.onError(new Exception("Unknown error"));
@ -210,7 +202,7 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
public Single<Long> addFolder(Folder folder) { public Single<Long> addFolder(Folder folder) {
return Single.<Folder>create(emitter -> { return Single.<Folder>create(emitter -> {
try { try {
List<Folder> folders = api.createFolder(folder); List<Folder> folders = dataSource.createFolder(folder);
if (folders != null) { if (folders != null) {
Folder nextNewsFolder = folders.get(0); // always only one item returned by the server, see doc Folder nextNewsFolder = folders.get(0); // always only one item returned by the server, see doc
@ -229,7 +221,7 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
public Completable updateFolder(Folder folder) { public Completable updateFolder(Folder folder) {
return Completable.create(emitter -> { return Completable.create(emitter -> {
try { try {
if (api.renameFolder(folder)) { if (dataSource.renameFolder(folder)) {
emitter.onComplete(); emitter.onComplete();
} else } else
emitter.onError(new Exception("Unknown error")); emitter.onError(new Exception("Unknown error"));
@ -246,7 +238,7 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
public Completable deleteFolder(Folder folder) { public Completable deleteFolder(Folder folder) {
return Completable.create(emitter -> { return Completable.create(emitter -> {
try { try {
if (api.deleteFolder(folder)) { if (dataSource.deleteFolder(folder)) {
emitter.onComplete(); emitter.onComplete();
} else } else
emitter.onError(new Exception("Unknown error")); emitter.onError(new Exception("Unknown error"));

View File

@ -6,18 +6,19 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.readrops.api.localfeed.LocalRSSHelper; import com.readrops.api.localfeed.LocalRSSHelper;
import com.readrops.api.utils.HttpManager;
import com.readrops.api.utils.LibUtils; import com.readrops.api.utils.LibUtils;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import org.koin.java.KoinJavaComponent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@ -83,7 +84,7 @@ public final class HtmlParser {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
try { try {
Response response = HttpManager.getInstance().getOkHttpClient() Response response = KoinJavaComponent.get(OkHttpClient.class)
.newCall(new Request.Builder().url(url).build()).execute(); .newCall(new Request.Builder().url(url).build()).execute();
if (response.header("Content-Type").contains(LibUtils.HTML_CONTENT_TYPE)) { if (response.header("Content-Type").contains(LibUtils.HTML_CONTENT_TYPE)) {
@ -98,6 +99,7 @@ public final class HtmlParser {
return null; return null;
} }
} catch (Exception e) { } catch (Exception e) {
Log.d(TAG, e.getMessage());
return null; return null;
} }

View File

@ -7,14 +7,16 @@ import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.module.AppGlideModule import com.bumptech.glide.module.AppGlideModule
import com.readrops.api.utils.HttpManager import okhttp3.OkHttpClient
import org.koin.core.KoinComponent
import org.koin.core.get
import java.io.InputStream import java.io.InputStream
@GlideModule @GlideModule
class ReadropsGlideModule : AppGlideModule() { class ReadropsGlideModule : AppGlideModule(), KoinComponent {
override fun registerComponents(context: Context, glide: Glide, registry: Registry) { override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
val factory = OkHttpUrlLoader.Factory(HttpManager.getInstance().okHttpClient) val factory = OkHttpUrlLoader.Factory(get<OkHttpClient>())
glide.registry.replace(GlideUrl::class.java, InputStream::class.java, factory) glide.registry.replace(GlideUrl::class.java, InputStream::class.java, factory)
} }

View File

@ -1,19 +1,15 @@
package com.readrops.app.utils; package com.readrops.app.utils;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.koin.java.KoinJavaComponent;
public final class SharedPreferencesManager { public final class SharedPreferencesManager {
private static SharedPreferences getSharedPreferences(Context context) { public static void writeValue(String key, Object value) {
return PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sharedPref = KoinJavaComponent.get(SharedPreferences.class);
}
public static void writeValue(Context context, String key, Object value) {
SharedPreferences sharedPref = getSharedPreferences(context);
SharedPreferences.Editor editor = sharedPref.edit(); SharedPreferences.Editor editor = sharedPref.edit();
if (value instanceof Boolean) if (value instanceof Boolean)
@ -24,32 +20,32 @@ public final class SharedPreferencesManager {
editor.apply(); editor.apply();
} }
public static void writeValue(Context context, SharedPrefKey sharedPrefKey, Object value) { public static void writeValue(SharedPrefKey sharedPrefKey, Object value) {
writeValue(context, sharedPrefKey.key, value); writeValue(sharedPrefKey.key, value);
} }
public static int readInt(Context context, SharedPrefKey sharedPrefKey) { public static int readInt(SharedPrefKey sharedPrefKey) {
SharedPreferences sharedPreferences = getSharedPreferences(context); SharedPreferences sharedPreferences = KoinJavaComponent.get(SharedPreferences.class);
return sharedPreferences.getInt(sharedPrefKey.key, sharedPrefKey.getIntDefaultValue()); return sharedPreferences.getInt(sharedPrefKey.key, sharedPrefKey.getIntDefaultValue());
} }
public static boolean readBoolean(Context context, SharedPrefKey sharedPrefKey) { public static boolean readBoolean(SharedPrefKey sharedPrefKey) {
SharedPreferences sharedPreferences = getSharedPreferences(context); SharedPreferences sharedPreferences = KoinJavaComponent.get(SharedPreferences.class);
return sharedPreferences.getBoolean(sharedPrefKey.key, sharedPrefKey.getBooleanDefaultValue()); return sharedPreferences.getBoolean(sharedPrefKey.key, sharedPrefKey.getBooleanDefaultValue());
} }
public static String readString(Context context, String key) { public static String readString(String key) {
SharedPreferences sharedPreferences = getSharedPreferences(context); SharedPreferences sharedPreferences = KoinJavaComponent.get(SharedPreferences.class);
return sharedPreferences.getString(key, null); return sharedPreferences.getString(key, null);
} }
public static String readString(Context context, SharedPrefKey sharedPrefKey) { public static String readString(SharedPrefKey sharedPrefKey) {
SharedPreferences sharedPreferences = getSharedPreferences(context); SharedPreferences sharedPreferences = KoinJavaComponent.get(SharedPreferences.class);
return sharedPreferences.getString(sharedPrefKey.key, sharedPrefKey.getStringDefaultValue()); return sharedPreferences.getString(sharedPrefKey.key, sharedPrefKey.getStringDefaultValue());
} }
public static void remove(Context context, String key) { public static void remove(String key) {
SharedPreferences sharedPreferences = getSharedPreferences(context); SharedPreferences sharedPreferences = KoinJavaComponent.get(SharedPreferences.class);
SharedPreferences.Editor editor = sharedPreferences.edit(); SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove(key); editor.remove(key);

View File

@ -9,11 +9,13 @@ import com.readrops.db.entities.Feed
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.Account
import com.readrops.api.services.SyncResult import com.readrops.api.services.SyncResult
import org.koin.core.KoinComponent
import org.koin.core.get
/** /**
* Simple class to get synchro notification content (title, content and largeIcon) according to some rules * Simple class to get synchro notification content (title, content and largeIcon) according to some rules
*/ */
class SyncResultAnalyser(val context: Context, private val syncResults: Map<Account, SyncResult>, val database: Database) { class SyncResultAnalyser(val context: Context, private val syncResults: Map<Account, SyncResult>, val database: Database) : KoinComponent {
private val notifContent = SyncResultNotifContent() private val notifContent = SyncResultNotifContent()
@ -66,7 +68,7 @@ class SyncResultAnalyser(val context: Context, private val syncResults: Map<Acco
notifContent.title = feed?.name notifContent.title = feed?.name
feed?.iconUrl?.let { feed?.iconUrl?.let {
val target = GlideApp.with(context) val target = get<GlideRequests>()
.asBitmap() .asBitmap()
.load(it) .load(it)
.diskCacheStrategy(DiskCacheStrategy.ALL) .diskCacheStrategy(DiskCacheStrategy.ALL)

View File

@ -1,20 +1,22 @@
package com.readrops.app.utils package com.readrops.app.utils
import android.content.Context import android.content.Context
import com.readrops.api.services.SyncResult
import com.readrops.db.Database import com.readrops.db.Database
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.Account
import com.readrops.db.entities.account.AccountType import com.readrops.db.entities.account.AccountType
import com.readrops.api.services.SyncResult
import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.TestOnly
import org.koin.core.KoinComponent
import org.koin.core.get
class SyncResultDebugData { class SyncResultDebugData {
companion object { companion object : KoinComponent {
@TestOnly @TestOnly
fun oneAccountOneFeedOneItem(context: Context): Map<Account, SyncResult> { fun oneAccountOneFeedOneItem(): Map<Account, SyncResult> {
val database = Database.getInstance(context) val database = get<Database>()
val account1 = database.accountDao().select(2) val account1 = database.accountDao().select(2)
@ -27,14 +29,14 @@ class SyncResultDebugData {
} }
@TestOnly @TestOnly
fun oneAccountOneFeedMultipleItems(context: Context): Map<Account, SyncResult> { fun oneAccountOneFeedMultipleItems(): Map<Account, SyncResult> {
val account1 = Account().apply { val account1 = Account().apply {
id = 1 id = 1
accountType = AccountType.FRESHRSS accountType = AccountType.FRESHRSS
isNotificationsEnabled = true isNotificationsEnabled = true
} }
val database = Database.getInstance(context) val database = get<Database>()
val item = database.itemDao().select(5055) val item = database.itemDao().select(5055)
database.feedDao().updateFeedNotificationState(item.feedId, false).subscribe() database.feedDao().updateFeedNotificationState(item.feedId, false).subscribe()

View File

@ -19,13 +19,16 @@ import com.readrops.db.entities.Item
import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.Account
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.koin.core.KoinComponent
import org.koin.core.get
import org.koin.core.parameter.parametersOf
class SyncWorker(context: Context, parameters: WorkerParameters) : Worker(context, parameters) { class SyncWorker(context: Context, parameters: WorkerParameters) : Worker(context, parameters), KoinComponent {
private var disposable: Disposable? = null private var disposable: Disposable? = null
private val notificationManager = NotificationManagerCompat.from(applicationContext) private val notificationManager = NotificationManagerCompat.from(applicationContext)
private val database = Database.getInstance(applicationContext) private val database = get<Database>()
override fun doWork(): Result { override fun doWork(): Result {
var result = Result.success() var result = Result.success()
@ -44,10 +47,10 @@ class SyncWorker(context: Context, parameters: WorkerParameters) : Worker(contex
notificationBuilder.setContentText(it.accountName) notificationBuilder.setContentText(it.accountName)
notificationManager.notify(SYNC_NOTIFICATION_ID, notificationBuilder.build()) notificationManager.notify(SYNC_NOTIFICATION_ID, notificationBuilder.build())
it.login = SharedPreferencesManager.readString(applicationContext, it.loginKey) it.login = SharedPreferencesManager.readString(it.loginKey)
it.password = SharedPreferencesManager.readString(applicationContext, it.passwordKey) it.password = SharedPreferencesManager.readString(it.passwordKey)
val repository = ARepository.repositoryFactory(it, applicationContext) val repository = get<ARepository>(parameters = { parametersOf(it) })
disposable = repository.sync(null) disposable = repository.sync(null)
.doOnError { throwable -> .doOnError { throwable ->
@ -138,12 +141,12 @@ class SyncWorker(context: Context, parameters: WorkerParameters) : Worker(contex
.build() .build()
} }
class MarkReadReceiver : BroadcastReceiver() { class MarkReadReceiver : BroadcastReceiver(), KoinComponent {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
val itemId = intent?.getIntExtra(ReadropsKeys.ITEM_ID, 0)!! val itemId = intent?.getIntExtra(ReadropsKeys.ITEM_ID, 0)!!
with(Database.getInstance(context)) { with(get<Database>()) {
itemDao().setReadState(itemId, true, true) itemDao().setReadState(itemId, true, true)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe() .subscribe()
@ -155,12 +158,12 @@ class SyncWorker(context: Context, parameters: WorkerParameters) : Worker(contex
} }
} }
class ReadLaterReceiver : BroadcastReceiver() { class ReadLaterReceiver : BroadcastReceiver(), KoinComponent {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
val itemId = intent?.getIntExtra(ReadropsKeys.ITEM_ID, 0)!! val itemId = intent?.getIntExtra(ReadropsKeys.ITEM_ID, 0)!!
with(Database.getInstance(context)) { with(get<Database>()) {
itemDao().setReadItLater(itemId) itemDao().setReadItLater(itemId)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe() .subscribe()

View File

@ -15,7 +15,8 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import com.readrops.api.utils.HttpManager;
import org.koin.java.KoinJavaComponent;
import java.io.InputStream; import java.io.InputStream;
import java.util.Locale; import java.util.Locale;
@ -34,10 +35,9 @@ public final class Utils {
public static Bitmap getImageFromUrl(String url) { public static Bitmap getImageFromUrl(String url) {
try { try {
OkHttpClient okHttpClient = HttpManager.getInstance().getOkHttpClient();
Request request = new Request.Builder().url(url).build(); Request request = new Request.Builder().url(url).build();
Response response = okHttpClient.newCall(request).execute(); Response response = KoinJavaComponent.get(OkHttpClient.class).newCall(request).execute();
if (response.isSuccessful()) { if (response.isSuccessful()) {
InputStream inputStream = response.body().byteStream(); InputStream inputStream = response.body().byteStream();

View File

@ -6,15 +6,17 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import com.readrops.app.R import com.readrops.app.R
import com.readrops.app.ReadropsApp import com.readrops.app.ReadropsApp
import com.readrops.app.utils.ReadropsKeys.FEEDS
import com.readrops.db.Database import com.readrops.db.Database
import com.readrops.db.entities.Feed import com.readrops.db.entities.Feed
import com.readrops.app.utils.ReadropsKeys.FEEDS import org.koin.core.KoinComponent
import org.koin.core.get
class FeedsColorsIntentService : IntentService("FeedsColorsIntentService") { class FeedsColorsIntentService : IntentService("FeedsColorsIntentService"), KoinComponent {
override fun onHandleIntent(intent: Intent?) { override fun onHandleIntent(intent: Intent?) {
val feeds: List<Feed> = intent!!.getParcelableArrayListExtra(FEEDS)!! val feeds: List<Feed> = intent!!.getParcelableArrayListExtra(FEEDS)!!
val database = Database.getInstance(this) val database = get<Database>()
val notificationBuilder = NotificationCompat.Builder(this, ReadropsApp.FEEDS_COLORS_CHANNEL_ID) val notificationBuilder = NotificationCompat.Builder(this, ReadropsApp.FEEDS_COLORS_CHANNEL_ID)
.setContentTitle(getString(R.string.get_feeds_colors)) .setContentTitle(getString(R.string.get_feeds_colors))

View File

@ -1,11 +1,10 @@
package com.readrops.app.viewmodels; package com.readrops.app.viewmodels;
import android.app.Application; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.ViewModel;
import com.readrops.api.opml.OPMLParser; import com.readrops.api.opml.OPMLParser;
import com.readrops.app.repositories.ARepository; import com.readrops.app.repositories.ARepository;
@ -15,35 +14,32 @@ import com.readrops.db.entities.Folder;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.db.entities.account.AccountType; import com.readrops.db.entities.account.AccountType;
import org.koin.core.parameter.DefinitionParametersKt;
import org.koin.java.KoinJavaComponent;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.reactivex.Completable; import io.reactivex.Completable;
import io.reactivex.Single; import io.reactivex.Single;
public class AccountViewModel extends AndroidViewModel { public class AccountViewModel extends ViewModel {
private static final String TAG = AccountViewModel.class.getSimpleName();
private ARepository repository; private ARepository repository;
private Database database; private final Database database;
public AccountViewModel(@NonNull Application application) { public AccountViewModel(@NonNull Database database) {
super(application); this.database = database;
database = Database.getInstance(application);
} }
public void setAccountType(AccountType accountType) throws Exception { public void setAccountType(AccountType accountType) {
repository = ARepository.repositoryFactory(null, accountType, getApplication()); repository = KoinJavaComponent.get(ARepository.class, null,
() -> DefinitionParametersKt.parametersOf(new Account(null, null, accountType)));
} }
public void setAccount(Account account) { public void setAccount(Account account) {
try { repository = KoinJavaComponent.get(ARepository.class, null,
repository = ARepository.repositoryFactory(account, getApplication()); () -> DefinitionParametersKt.parametersOf(account));
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
} }
public Single<Boolean> login(Account account, boolean insert) { public Single<Boolean> login(Account account, boolean insert) {
@ -71,8 +67,8 @@ public class AccountViewModel extends AndroidViewModel {
return repository.getFoldersWithFeeds(); return repository.getFoldersWithFeeds();
} }
public Completable parseOPMLFile(Uri uri) { public Completable parseOPMLFile(Uri uri, Context context) {
return OPMLParser.read(uri, getApplication()) return OPMLParser.read(uri, context)
.flatMapCompletable(foldersAndFeeds -> repository.insertOPMLFoldersAndFeeds(foldersAndFeeds)); .flatMapCompletable(foldersAndFeeds -> repository.insertOPMLFoldersAndFeeds(foldersAndFeeds));
} }
} }

View File

@ -1,14 +1,10 @@
package com.readrops.app.viewmodels; package com.readrops.app.viewmodels;
import android.app.Application;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.readrops.api.localfeed.LocalRSSDataSource; import com.readrops.api.localfeed.LocalRSSDataSource;
import com.readrops.api.utils.HttpManager;
import com.readrops.app.repositories.ARepository; import com.readrops.app.repositories.ARepository;
import com.readrops.app.utils.FeedInsertionResult; import com.readrops.app.utils.FeedInsertionResult;
import com.readrops.app.utils.HtmlParser; import com.readrops.app.utils.HtmlParser;
@ -16,42 +12,36 @@ import com.readrops.app.utils.ParsingResult;
import com.readrops.db.Database; import com.readrops.db.Database;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import org.koin.core.parameter.DefinitionParametersKt;
import org.koin.java.KoinJavaComponent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import io.reactivex.Single; import io.reactivex.Single;
public class AddFeedsViewModel extends AndroidViewModel { public class AddFeedsViewModel extends ViewModel {
private static final String TAG = AddFeedsViewModel.class.getSimpleName(); private final Database database;
private final LocalRSSDataSource localRSSDataSource;
private ARepository repository; public AddFeedsViewModel(@NonNull Database database, @NonNull LocalRSSDataSource localRSSDataSource) {
private Database database; this.database = database;
this.localRSSDataSource = localRSSDataSource;
public AddFeedsViewModel(@NonNull Application application) {
super(application);
database = Database.getInstance(application);
} }
public Single<List<FeedInsertionResult>> addFeeds(List<ParsingResult> results, Account account) { public Single<List<FeedInsertionResult>> addFeeds(List<ParsingResult> results, Account account) {
try { ARepository repository = KoinJavaComponent.get(ARepository.class, null,
repository = ARepository.repositoryFactory(account, getApplication()); () -> DefinitionParametersKt.parametersOf(account));
return repository.addFeeds(results); return repository.addFeeds(results);
} catch (Exception e) {
Log.d(TAG, e.getMessage());
}
return null;
} }
public Single<List<ParsingResult>> parseUrl(String url) { public Single<List<ParsingResult>> parseUrl(String url) {
return Single.create(emitter -> { return Single.create(emitter -> {
LocalRSSDataSource dataSource = new LocalRSSDataSource(HttpManager.getInstance().getOkHttpClient());
List<ParsingResult> results = new ArrayList<>(); List<ParsingResult> results = new ArrayList<>();
if (dataSource.isUrlRSSResource(url)) { if (localRSSDataSource.isUrlRSSResource(url)) {
ParsingResult parsingResult = new ParsingResult(url, null); ParsingResult parsingResult = new ParsingResult(url, null);
results.add(parsingResult); results.add(parsingResult);
} else { } else {

View File

@ -1,16 +1,15 @@
package com.readrops.app.viewmodels; package com.readrops.app.viewmodels;
import android.app.Application; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.readrops.db.Database; import com.readrops.db.Database;
import com.readrops.db.dao.ItemDao;
import com.readrops.db.pojo.ItemWithFeed; import com.readrops.db.pojo.ItemWithFeed;
import java.io.File; import java.io.File;
@ -18,22 +17,21 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
public class ItemViewModel extends AndroidViewModel { public class ItemViewModel extends ViewModel {
private ItemDao itemDao; private final Database database;
public ItemViewModel(@NonNull Application application) { public ItemViewModel(@NonNull Database database) {
super(application); this.database = database;
itemDao = Database.getInstance(application).itemDao();
} }
public LiveData<ItemWithFeed> getItemById(int id) { public LiveData<ItemWithFeed> getItemById(int id) {
return itemDao.getItemById(id); return database.itemDao().getItemById(id);
} }
public Uri saveImageInCache(Bitmap bitmap) throws IOException { public Uri saveImageInCache(Bitmap bitmap, Context context) throws IOException {
File imagesFolder = new File(getApplication().getCacheDir().getAbsolutePath(), "images"); File imagesFolder = new File(context.getCacheDir().getAbsolutePath(), "images");
if (!imagesFolder.exists()) if (!imagesFolder.exists())
imagesFolder.mkdirs(); imagesFolder.mkdirs();
@ -45,6 +43,6 @@ public class ItemViewModel extends AndroidViewModel {
stream.flush(); stream.flush();
stream.close(); stream.close();
return FileProvider.getUriForFile(getApplication(), getApplication().getPackageName(), image); return FileProvider.getUriForFile(context, context.getPackageName(), image);
} }
} }

View File

@ -1,14 +1,13 @@
package com.readrops.app.viewmodels; package com.readrops.app.viewmodels;
import android.app.Application;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData; import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.ViewModel;
import androidx.paging.LivePagedListBuilder; import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList; import androidx.paging.PagedList;
import com.readrops.app.repositories.ARepository;
import com.readrops.db.Database; import com.readrops.db.Database;
import com.readrops.db.ItemsListQueryBuilder; import com.readrops.db.ItemsListQueryBuilder;
import com.readrops.db.RoomFactoryWrapper; import com.readrops.db.RoomFactoryWrapper;
@ -18,7 +17,9 @@ import com.readrops.db.entities.account.Account;
import com.readrops.db.filters.FilterType; import com.readrops.db.filters.FilterType;
import com.readrops.db.filters.ListSortType; import com.readrops.db.filters.ListSortType;
import com.readrops.db.pojo.ItemWithFeed; import com.readrops.db.pojo.ItemWithFeed;
import com.readrops.app.repositories.ARepository;
import org.koin.core.parameter.DefinitionParametersKt;
import org.koin.java.KoinJavaComponent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -31,45 +32,40 @@ import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
public class MainViewModel extends AndroidViewModel { public class MainViewModel extends ViewModel {
private MediatorLiveData<PagedList<ItemWithFeed>> itemsWithFeed; private final MediatorLiveData<PagedList<ItemWithFeed>> itemsWithFeed;
private LiveData<PagedList<ItemWithFeed>> lastFetch; private LiveData<PagedList<ItemWithFeed>> lastFetch;
private ARepository repository; private ARepository repository;
private Database db; private final Database database;
private ItemsListQueryBuilder queryBuilder; private final ItemsListQueryBuilder queryBuilder;
private Account currentAccount; private Account currentAccount;
private List<Account> accounts; private List<Account> accounts;
public MainViewModel(@NonNull Application application) { public MainViewModel(@NonNull Database database) {
super(application);
queryBuilder = new ItemsListQueryBuilder(); queryBuilder = new ItemsListQueryBuilder();
queryBuilder.setFilterType(FilterType.NO_FILTER); queryBuilder.setFilterType(FilterType.NO_FILTER);
queryBuilder.setSortType(ListSortType.NEWEST_TO_OLDEST); queryBuilder.setSortType(ListSortType.NEWEST_TO_OLDEST);
db = Database.getInstance(application); this.database = database;
itemsWithFeed = new MediatorLiveData<>(); itemsWithFeed = new MediatorLiveData<>();
} }
//region main query //region main query
private void setRepository() { private void setRepository() {
try { repository = KoinJavaComponent.get(ARepository.class, null,
repository = ARepository.repositoryFactory(currentAccount, getApplication()); () -> DefinitionParametersKt.parametersOf(currentAccount));
} catch (Exception e) {
e.printStackTrace();
}
} }
private void buildPagedList() { private void buildPagedList() {
if (lastFetch != null) if (lastFetch != null)
itemsWithFeed.removeSource(lastFetch); itemsWithFeed.removeSource(lastFetch);
lastFetch = new LivePagedListBuilder<>(new RoomFactoryWrapper<>(db.itemDao().selectAll(queryBuilder.getQuery())), lastFetch = new LivePagedListBuilder<>(new RoomFactoryWrapper<>(database.itemDao().selectAll(queryBuilder.getQuery())),
new PagedList.Config.Builder() new PagedList.Config.Builder()
.setPageSize(100) .setPageSize(100)
.setPrefetchDistance(150) .setPrefetchDistance(150)
@ -77,7 +73,7 @@ public class MainViewModel extends AndroidViewModel {
.build()) .build())
.build(); .build();
itemsWithFeed.addSource(lastFetch, itemWithFeeds -> itemsWithFeed.setValue(itemWithFeeds)); itemsWithFeed.addSource(lastFetch, itemsWithFeed::setValue);
} }
public void invalidate() { public void invalidate() {
@ -124,7 +120,6 @@ public class MainViewModel extends AndroidViewModel {
return repository.getFeedCount(currentAccount.getId()); return repository.getFeedCount(currentAccount.getId());
} }
@SuppressWarnings("unchecked")
public Single<Map<Folder, List<Feed>>> getFoldersWithFeeds() { public Single<Map<Folder, List<Feed>>> getFoldersWithFeeds() {
return repository.getFoldersWithFeeds(); return repository.getFoldersWithFeeds();
} }
@ -134,12 +129,12 @@ public class MainViewModel extends AndroidViewModel {
//region Account //region Account
public LiveData<List<Account>> getAllAccounts() { public LiveData<List<Account>> getAllAccounts() {
return db.accountDao().selectAllAsync(); return database.accountDao().selectAllAsync();
} }
private Completable deselectOldCurrentAccount(int accountId) { private Completable deselectOldCurrentAccount(int accountId) {
return Completable.create(emitter -> { return Completable.create(emitter -> {
db.accountDao().deselectOldCurrentAccount(accountId); database.accountDao().deselectOldCurrentAccount(accountId);
emitter.onComplete(); emitter.onComplete();
}); });
} }
@ -170,7 +165,7 @@ public class MainViewModel extends AndroidViewModel {
// set the new account as the current one // set the new account as the current one
Completable setCurrentAccount = Completable.create(emitter -> { Completable setCurrentAccount = Completable.create(emitter -> {
db.accountDao().setCurrentAccount(currentAccount.getId()); database.accountDao().setCurrentAccount(currentAccount.getId());
emitter.onComplete(); emitter.onComplete();
}); });
@ -202,7 +197,7 @@ public class MainViewModel extends AndroidViewModel {
} }
} }
if (!currentAccountExists && accounts.size() > 0) { if (!currentAccountExists && !accounts.isEmpty()) {
setCurrentAccount(accounts.get(0)); setCurrentAccount(accounts.get(0));
accounts.get(0).setCurrentAccount(true); accounts.get(0).setCurrentAccount(true);
} }
@ -242,7 +237,7 @@ public class MainViewModel extends AndroidViewModel {
} }
public Completable setItemReadItLater(int itemId) { public Completable setItemReadItLater(int itemId) {
return db.itemDao().setReadItLater(itemId); return database.itemDao().setReadItLater(itemId);
} }
//endregion //endregion

View File

@ -1,48 +1,44 @@
package com.readrops.app.viewmodels; package com.readrops.app.viewmodels;
import android.app.Application;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.readrops.app.repositories.ARepository;
import com.readrops.db.Database; import com.readrops.db.Database;
import com.readrops.db.entities.Feed; import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Folder; import com.readrops.db.entities.Folder;
import com.readrops.db.entities.account.Account; import com.readrops.db.entities.account.Account;
import com.readrops.db.pojo.FeedWithFolder; import com.readrops.db.pojo.FeedWithFolder;
import com.readrops.db.pojo.FolderWithFeedCount; import com.readrops.db.pojo.FolderWithFeedCount;
import com.readrops.app.repositories.ARepository;
import org.koin.core.parameter.DefinitionParametersKt;
import org.koin.java.KoinJavaComponent;
import java.util.List; import java.util.List;
import io.reactivex.Completable; import io.reactivex.Completable;
import io.reactivex.Single; import io.reactivex.Single;
public class ManageFeedsFoldersViewModel extends AndroidViewModel { public class ManageFeedsFoldersViewModel extends ViewModel {
private Database db; private final Database database;
private LiveData<List<FeedWithFolder>> feedsWithFolder; private LiveData<List<FeedWithFolder>> feedsWithFolder;
private LiveData<List<Folder>> folders; private LiveData<List<Folder>> folders;
private ARepository repository; private ARepository repository;
private Account account; private Account account;
public ManageFeedsFoldersViewModel(@NonNull Application application) { public ManageFeedsFoldersViewModel(@NonNull Database database) {
super(application); this.database = database;
db = Database.getInstance(application);
} }
private void setup() { private void setup() {
try { repository = KoinJavaComponent.get(ARepository.class, null,
repository = ARepository.repositoryFactory(account, getApplication()); () -> DefinitionParametersKt.parametersOf(account));
feedsWithFolder = db.feedDao().getAllFeedsWithFolder(account.getId()); feedsWithFolder = database.feedDao().getAllFeedsWithFolder(account.getId());
folders = db.folderDao().getAllFolders(account.getId()); folders = database.folderDao().getAllFolders(account.getId());
} catch (Exception e) {
e.printStackTrace();
}
} }
public LiveData<List<FeedWithFolder>> getFeedsWithFolder() { public LiveData<List<FeedWithFolder>> getFeedsWithFolder() {
@ -67,7 +63,7 @@ public class ManageFeedsFoldersViewModel extends AndroidViewModel {
} }
public LiveData<List<FolderWithFeedCount>> getFoldersWithFeedCount() { public LiveData<List<FolderWithFeedCount>> getFoldersWithFeedCount() {
return db.folderDao().getFoldersWithFeedCount(account.getId()); return database.folderDao().getFoldersWithFeedCount(account.getId());
} }
public Single<Long> addFolder(Folder folder) { public Single<Long> addFolder(Folder folder) {
@ -87,6 +83,6 @@ public class ManageFeedsFoldersViewModel extends AndroidViewModel {
} }
public Single<Integer> getFeedCountByAccount() { public Single<Integer> getFeedCountByAccount() {
return db.feedDao().getFeedCount(account.getId()); return database.feedDao().getFeedCount(account.getId());
} }
} }

View File

@ -1,16 +1,14 @@
package com.readrops.app.viewmodels package com.readrops.app.viewmodels
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import com.readrops.db.Database import com.readrops.db.Database
import com.readrops.db.entities.Feed import com.readrops.db.entities.Feed
import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.Account
import io.reactivex.Completable import io.reactivex.Completable
class NotificationPermissionViewModel(application: Application) : AndroidViewModel(application) { class NotificationPermissionViewModel(val database: Database) : ViewModel() {
val database: Database = Database.getInstance(application)
var account: Account? = null var account: Account? = null
fun getAccount(accountId: Int): LiveData<Account> = database.accountDao().selectAsync(accountId) fun getAccount(accountId: Int): LiveData<Account> = database.accountDao().selectAsync(accountId)

View File

@ -1,35 +0,0 @@
package com.readrops.app;
import com.readrops.app.utils.HtmlParser;
import com.readrops.app.utils.ParsingResult;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static junit.framework.TestCase.assertEquals;
public class HtmlParserTest {
@Test
public void getFeedLinkTest() throws Exception {
String url = "https://github.com/readrops/Readrops";
ParsingResult parsingResult = new ParsingResult("https://github.com/readrops/Readrops/commits/develop.atom", "Recent Commits to Readrops:develop");
List<ParsingResult> parsingResultList = new ArrayList<>();
parsingResultList.add(parsingResult);
List<ParsingResult> parsingResultList1 = HtmlParser.getFeedLink(url);
Assert.assertEquals(parsingResultList, parsingResultList1);
}
@Test
public void getFaviconLinkTest() {
String url = "https://github.com/readrops/Readrops";
assertEquals("https://github.com/fluidicon.png", HtmlParser.getFaviconLink(url));
}
}

View File

@ -0,0 +1,40 @@
package com.readrops.app
import com.readrops.app.utils.HtmlParser
import com.readrops.app.utils.ParsingResult
import junit.framework.TestCase
import okhttp3.OkHttpClient
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.koin.dsl.module
import org.koin.test.KoinTestRule
class HtmlParserTest {
@get:Rule
val koinTestRule = KoinTestRule.create {
modules(module {
single { OkHttpClient() }
})
}
@Test
fun getFeedLinkTest() {
val url = "https://github.com/readrops/Readrops"
val parsingResult = ParsingResult("https://github.com/readrops/Readrops/commits/develop.atom",
"Recent Commits to Readrops:develop")
val parsingResultList = mutableListOf(parsingResult)
val parsingResultList1 = HtmlParser.getFeedLink(url)
Assert.assertEquals(parsingResultList, parsingResultList1)
}
@Test
fun getFaviconLinkTest() {
val url = "https://github.com/readrops/Readrops"
TestCase.assertEquals("https://github.com/fluidicon.png", HtmlParser.getFaviconLink(url))
}
}

View File

@ -72,4 +72,9 @@ dependencies {
api 'androidx.paging:paging-common:2.1.2' api 'androidx.paging:paging-common:2.1.2'
api 'joda-time:joda-time:2.10.5' api 'joda-time:joda-time:2.10.5'
def koin_version = "2.1.6"
api "org.koin:koin-android:$koin_version"
api "org.koin:koin-androidx-scope:$koin_version"
api "org.koin:koin-androidx-viewmodel:$koin_version"
} }

View File

@ -1,9 +1,6 @@
package com.readrops.db; package com.readrops.db;
import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.room.Room;
import androidx.room.RoomDatabase; import androidx.room.RoomDatabase;
import androidx.room.TypeConverters; import androidx.room.TypeConverters;
import androidx.room.migration.Migration; import androidx.room.migration.Migration;
@ -31,17 +28,6 @@ public abstract class Database extends RoomDatabase {
public abstract AccountDao accountDao(); public abstract AccountDao accountDao();
private static Database database;
public static Database getInstance(Context context) {
if (database == null)
database = Room.databaseBuilder(context, Database.class, "readrops-db")
.addMigrations(MIGRATION_1_2)
.build();
return database;
}
public static final Migration MIGRATION_1_2 = new Migration(1, 2) { public static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override @Override
public void migrate(@NonNull SupportSQLiteDatabase database) { public void migrate(@NonNull SupportSQLiteDatabase database) {

View File

@ -0,0 +1,13 @@
package com.readrops.db
import androidx.room.Room
import org.koin.dsl.module
val dbModule = module {
single(createdAtStart = true) {
Room.databaseBuilder(get(), Database::class.java, "readrops-db")
.addMigrations(Database.MIGRATION_1_2)
.build()
}
}