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.espresso:espresso-core:3.3.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'

View File

@ -4,13 +4,13 @@ import android.accounts.NetworkErrorException
import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.readrops.api.utils.HttpManager
import com.readrops.api.utils.LibUtils
import com.readrops.api.utils.ParseException
import com.readrops.api.utils.UnknownFormatException
import junit.framework.TestCase.*
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okio.Buffer
@ -28,7 +28,7 @@ class LocalRSSDataSourceTest {
private lateinit var url: HttpUrl
private val mockServer: MockWebServer = MockWebServer()
private val localRSSDataSource = LocalRSSDataSource(HttpManager.getInstance().okHttpClient)
private val localRSSDataSource = LocalRSSDataSource(OkHttpClient())
@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;
import androidx.annotation.Nullable;
import com.readrops.db.entities.account.Account;
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.NextNewsService;
import com.readrops.db.entities.account.Account;
import com.readrops.db.entities.account.AccountType;
public abstract class Credentials {
private String authorization;
private final String authorization;
private String url;
private final String url;
public Credentials(String authorization, String url) {
this.authorization = authorization;
@ -25,15 +26,27 @@ public abstract class Credentials {
return url;
}
@Nullable
public static Credentials toCredentials(Account account) {
String endPoint = getEndPoint(account.getAccountType());
switch (account.getAccountType()) {
case NEXTCLOUD_NEWS:
return new NextNewsCredentials(account.getLogin(), account.getPassword(), account.getUrl());
return new NextNewsCredentials(account.getLogin(), account.getPassword(), account.getUrl() + endPoint);
case FRESHRSS:
return new FreshRSSCredentials(account.getToken(), account.getUrl());
return new FreshRSSCredentials(account.getToken(), account.getUrl() + endPoint);
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.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.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.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.util.List;
@ -26,23 +19,18 @@ import io.reactivex.Single;
import okhttp3.MultipartBody;
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";
private static final String FEED_PREFIX = "feed/";
public FreshRSSAPI(Credentials credentials) {
super(credentials, FreshRSSService.class, FreshRSSService.END_POINT);
}
private FreshRSSService api;
@Override
protected Moshi buildMoshi() {
return new Moshi.Builder()
.add(Types.newParameterizedType(List.class, Item.class), new FreshRSSItemsAdapter())
.add(new FreshRSSFeedsAdapter())
.add(new FreshRSSFoldersAdapter())
.build();
public FreshRSSDataSource(FreshRSSService api) {
this.api = api;
}
/**

View File

@ -2,7 +2,7 @@ package com.readrops.api.services.freshrss.adapters
import android.util.TimingLogger
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.JsonReader
import com.squareup.moshi.JsonWriter

View File

@ -5,6 +5,6 @@ import com.readrops.api.services.Credentials;
public class NextNewsCredentials extends Credentials {
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.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.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.utils.ConflictException;
import com.readrops.api.utils.LibUtils;
import com.readrops.api.utils.UnknownFormatException;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Folder;
import com.readrops.db.entities.Item;
import java.io.IOException;
import java.util.ArrayList;
@ -30,21 +23,16 @@ import java.util.Map;
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) {
super(credentials, NextNewsService.class, NextNewsService.END_POINT);
}
protected static final int MAX_ITEMS = 5000;
@Override
protected Moshi buildMoshi() {
return new Moshi.Builder()
.add(new NextNewsFeedsAdapter())
.add(new NextNewsFoldersAdapter())
.add(Types.newParameterizedType(List.class, Item.class), new NextNewsItemsAdapter())
.build();
private NextNewsService api;
public NextNewsDataSource(NextNewsService api) {
this.api = api;
}
@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.fragment:fragment-ktx:1.2.5"
implementation "androidx.browser:browser:1.2.0"
testImplementation "org.koin:koin-test:2.1.6"
implementation 'com.github.bumptech.glide:glide: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.InspectorFlipperPlugin;
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.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.soloader.SoLoader;
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 {
@ -40,13 +37,6 @@ public class ReadropsDebugApp extends ReadropsApp implements Configuration.Provi
NetworkFlipperPlugin networkPlugin = new NetworkFlipperPlugin();
client.addPlugin(networkPlugin);
HttpManager.setInstance(
HttpManager.getInstance()
.getOkHttpClient()
.newBuilder()
.addInterceptor(new FlipperOkhttpInterceptor(networkPlugin))
.build());
client.addPlugin(new DatabasesFlipperPlugin(this));
client.addPlugin(CrashReporterPlugin.getInstance());
client.addPlugin(NavigationFlipperPlugin.getInstance());
@ -65,12 +55,6 @@ public class ReadropsDebugApp extends ReadropsApp implements Configuration.Provi
niddler.attachToApplication(this);
HttpManager.setInstance(HttpManager.getInstance().
getOkHttpClient().
newBuilder().
addInterceptor(new NiddlerOkHttpInterceptor(niddler, "default"))
.build());
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.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DividerItemDecoration;
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.AccountType;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import java.util.ArrayList;
import java.util.List;
@ -55,7 +56,7 @@ public class AccountTypeListActivity extends AppCompatActivity {
binding = ActivityAccountTypeListBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
viewModel = new ViewModelProvider(this).get(AccountViewModel.class);
viewModel = ViewModelCompat.getViewModel(this, AccountViewModel.class);
setTitle(R.string.new_account);
@ -158,7 +159,7 @@ public class AccountTypeListActivity extends AppCompatActivity {
account.setId(id.intValue());
viewModel.setAccount(account);
return viewModel.parseOPMLFile(uri);
return viewModel.parseOPMLFile(uri, this);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

View File

@ -8,7 +8,6 @@ import android.view.MenuItem;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import com.readrops.app.R;
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.AccountType;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import io.reactivex.Completable;
import io.reactivex.CompletableObserver;
import io.reactivex.SingleObserver;
@ -46,7 +47,7 @@ public class AddAccountActivity extends AppCompatActivity {
binding = ActivityAddAccountBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
viewModel = new ViewModelProvider(this).get(AccountViewModel.class);
viewModel = ViewModelCompat.getViewModel(this, AccountViewModel.class);
accountType = getIntent().getParcelableExtra(ACCOUNT_TYPE);
@ -58,26 +59,20 @@ public class AddAccountActivity extends AppCompatActivity {
if (forwardResult || accountToEdit != null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
try {
if (accountToEdit != null) {
viewModel.setAccountType(accountToEdit.getAccountType());
editAccount = true;
fillFields();
} else {
viewModel.setAccountType(accountType);
if (accountToEdit != null) {
viewModel.setAccountType(accountToEdit.getAccountType());
editAccount = true;
fillFields();
} else {
viewModel.setAccountType(accountType);
binding.providerImage.setImageResource(accountType.getIconRes());
binding.providerName.setText(accountType.getName());
binding.addAccountName.setText(accountType.getName());
if (accountType == AccountType.FRESHRSS) {
binding.addAccountPasswordLayout.setHelperText(getString(R.string.password_helper));
}
binding.providerImage.setImageResource(accountType.getIconRes());
binding.providerName.setText(accountType.getName());
binding.addAccountName.setText(accountType.getName());
if (accountType == AccountType.FRESHRSS) {
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) {
@ -183,8 +178,8 @@ public class AddAccountActivity extends AppCompatActivity {
}
private void saveLoginPassword(Account account) {
SharedPreferencesManager.writeValue(this, account.getLoginKey(), account.getLogin());
SharedPreferencesManager.writeValue(this, account.getPasswordKey(), account.getPassword());
SharedPreferencesManager.writeValue(account.getLoginKey(), account.getLogin());
SharedPreferencesManager.writeValue(account.getPasswordKey(), account.getPassword());
account.setLogin(null);
account.setPassword(null);
@ -196,8 +191,8 @@ public class AddAccountActivity extends AppCompatActivity {
binding.addAccountUrl.setText(accountToEdit.getUrl());
binding.addAccountName.setText(accountToEdit.getAccountName());
binding.addAccountLogin.setText(SharedPreferencesManager.readString(this, accountToEdit.getLoginKey()));
binding.addAccountPassword.setText(SharedPreferencesManager.readString(this, accountToEdit.getPasswordKey()));
binding.addAccountLogin.setText(SharedPreferencesManager.readString(accountToEdit.getLoginKey()));
binding.addAccountPassword.setText(SharedPreferencesManager.readString(accountToEdit.getPasswordKey()));
}
private void updateAccount() {

View File

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

View File

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

View File

@ -16,7 +16,6 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.ItemTouchHelper;
@ -38,7 +37,7 @@ import com.readrops.app.R;
import com.readrops.app.adapters.MainItemListAdapter;
import com.readrops.app.databinding.ActivityMainBinding;
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.SharedPreferencesManager;
import com.readrops.app.utils.Utils;
@ -51,6 +50,8 @@ import com.readrops.db.filters.ListSortType;
import com.readrops.db.pojo.ItemWithFeed;
import org.jetbrains.annotations.NotNull;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import org.koin.java.KoinJavaComponent;
import java.lang.ref.WeakReference;
import java.util.Collections;
@ -116,9 +117,9 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
feedCount = 0;
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));
viewModel.getItemsWithFeed().observe(this, itemWithFeeds -> {
@ -300,7 +301,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
private void initRecyclerView() {
ViewPreloadSizeProvider preloadSizeProvider = new ViewPreloadSizeProvider();
adapter = new MainItemListAdapter(GlideApp.with(this), preloadSizeProvider);
adapter = new MainItemListAdapter(KoinJavaComponent.get(GlideRequests.class), preloadSizeProvider);
adapter.setOnItemClickListener(new MainItemListAdapter.OnItemClickListener() {
@Override
public void onItemClick(ItemWithFeed itemWithFeed, int position) {
@ -349,7 +350,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
binding.itemsRecyclerView.setRecyclerListener(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);
@ -659,12 +660,12 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
if (item.isChecked()) {
item.setChecked(false);
viewModel.setShowReadItems(false);
SharedPreferencesManager.writeValue(this,
SharedPreferencesManager.writeValue(
SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES, false);
} else {
item.setChecked(true);
viewModel.setShowReadItems(true);
SharedPreferencesManager.writeValue(this,
SharedPreferencesManager.writeValue(
SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES, true);
}
@ -708,16 +709,16 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
private void getAccountCredentials(List<Account> accounts) {
for (Account account : accounts) {
if (account.getLogin() == null)
account.setLogin(SharedPreferencesManager.readString(this, account.getLoginKey()));
account.setLogin(SharedPreferencesManager.readString(account.getLoginKey()));
if (account.getPassword() == null)
account.setPassword(SharedPreferencesManager.readString(this, account.getPasswordKey()));
account.setPassword(SharedPreferencesManager.readString(account.getPasswordKey()));
}
}
private void startAboutActivity() {
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;
else
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.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.lifecycle.ViewModelProvider;
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.databinding.ActivityManageFeedsFoldersBinding;
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.db.entities.Folder;
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.schedulers.Schedulers;
@ -52,7 +53,7 @@ public class ManageFeedsFoldersActivity extends AppCompatActivity {
binding.manageFeedsFoldersViewpager.setAdapter(pageAdapter);
binding.manageFeedsFoldersTablayout.setupWithViewPager(binding.manageFeedsFoldersViewpager);
viewModel = new ViewModelProvider(this).get(ManageFeedsFoldersViewModel.class);
viewModel = ViewModelCompat.getViewModel(this, ManageFeedsFoldersViewModel.class);
viewModel.setAccount(account);
}

View File

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

View File

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

View File

@ -13,9 +13,11 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.readrops.app.R;
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 org.koin.java.KoinJavaComponent;
import java.util.List;
public class FeedsAdapter extends ListAdapter<FeedWithFolder, FeedsAdapter.FeedViewHolder> {
@ -64,7 +66,7 @@ public class FeedsAdapter extends ListAdapter<FeedWithFolder, FeedsAdapter.FeedV
FeedWithFolder feedWithFolder = getItem(i);
if (feedWithFolder.getFeed().getIconUrl() != null) {
GlideApp.with(viewHolder.itemView.getContext())
KoinJavaComponent.get(GlideRequests.class)
.load(feedWithFolder.getFeed().getIconUrl())
.diskCacheStrategy(DiskCacheStrategy.ALL)
.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.databinding.NotificationPermissionLayoutBinding
import com.readrops.app.utils.GlideApp
import com.readrops.app.utils.GlideRequests
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) :
ListAdapter<Feed, NotificationPermissionListAdapter.NotificationPermissionViewHolder>(DIFF_CALLBACK) {
ListAdapter<Feed, NotificationPermissionListAdapter.NotificationPermissionViewHolder>(DIFF_CALLBACK), KoinComponent {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationPermissionViewHolder {
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)) }
GlideApp.with(holder.itemView.context)
get<GlideRequests>()
.load(feed.iconUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.drawable.ic_rss_feed_grey)

View File

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

View File

@ -10,7 +10,6 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
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.pojo.FeedWithFolder;
import org.koin.androidx.viewmodel.compat.SharedViewModelCompat;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DisposableCompletableObserver;
import io.reactivex.schedulers.Schedulers;
@ -60,11 +61,11 @@ public class FeedsFragment extends Fragment {
account = getArguments().getParcelable(ACCOUNT);
if (account.getLogin() == null)
account.setLogin(SharedPreferencesManager.readString(getContext(), account.getLoginKey()));
account.setLogin(SharedPreferencesManager.readString(account.getLoginKey()));
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.getFeedsWithFolder().observe(this, feedWithFolders -> {

View File

@ -10,10 +10,11 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
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.adapters.FoldersAdapter;
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.db.entities.Folder;
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.observers.DisposableSingleObserver;
@ -60,12 +61,12 @@ public class FoldersFragment extends Fragment {
account = getArguments().getParcelable(ACCOUNT);
if (account.getLogin() == null)
account.setLogin(SharedPreferencesManager.readString(getContext(), account.getLoginKey()));
account.setLogin(SharedPreferencesManager.readString(account.getLoginKey()));
if (account.getPassword() == null)
account.setPassword(SharedPreferencesManager.readString(getContext(), account.getPasswordKey()));
account.setPassword(SharedPreferencesManager.readString(account.getPasswordKey()));
adapter = new FoldersAdapter(this::openFolderOptionsDialog);
viewModel = new ViewModelProvider(this).get(ManageFeedsFoldersViewModel.class);
viewModel = SharedViewModelCompat.getSharedViewModel(this, ManageFeedsFoldersViewModel.class);
viewModel.setAccount(account);
viewModel.getFeedCountByAccount()

View File

@ -16,7 +16,6 @@ import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
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.AccountType;
import org.koin.androidx.viewmodel.compat.ViewModelCompat;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DisposableCompletableObserver;
import io.reactivex.schedulers.Schedulers;
@ -136,7 +137,7 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(AccountViewModel.class);
viewModel = ViewModelCompat.getViewModel(this, AccountViewModel.class);
viewModel.setAccount(account);
}
@ -146,8 +147,8 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
.positiveText(R.string.validate)
.negativeText(R.string.cancel)
.onPositive(((dialog, which) -> {
SharedPreferencesManager.remove(getContext(), account.getLoginKey());
SharedPreferencesManager.remove(getContext(), account.getPasswordKey());
SharedPreferencesManager.remove(account.getLoginKey());
SharedPreferencesManager.remove(account.getPasswordKey());
viewModel.delete(account)
.subscribeOn(Schedulers.io())
@ -204,7 +205,7 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
}
private void parseOPMLFile(Uri uri, MaterialDialog dialog) {
viewModel.parseOPMLFile(uri)
viewModel.parseOPMLFile(uri, getContext())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.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.db.Database;
import org.koin.java.KoinJavaComponent;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@ -38,7 +40,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
AtomicBoolean serviceStarted = new AtomicBoolean(false);
feedsColorsPreference.setOnPreferenceClickListener(preference -> {
Database database = Database.getInstance(getContext());
Database database = KoinJavaComponent.get(Database.class);
database.feedDao().getAllFeeds().observe(getActivity(), feeds -> {
if (!serviceStarted.get()) {

View File

@ -6,6 +6,9 @@ import android.content.Intent;
import androidx.annotation.NonNull;
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.ParsingResult;
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.Item;
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.Comparator;
@ -30,25 +33,26 @@ import io.reactivex.Single;
import static com.readrops.app.utils.ReadropsKeys.FEEDS;
public abstract class ARepository<T> {
public abstract class ARepository {
protected Context context;
protected Database database;
protected Account account;
protected T api;
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.database = Database.getInstance(context);
this.database = database;
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
public abstract Single<Boolean> login(Account account, boolean insert);
@ -171,23 +175,6 @@ public abstract class ARepository<T> {
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() {
return syncResult;
}

View File

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

View File

@ -9,7 +9,6 @@ import androidx.annotation.Nullable;
import com.readrops.api.localfeed.LocalRSSDataSource;
import com.readrops.api.services.SyncResult;
import com.readrops.api.utils.HttpManager;
import com.readrops.api.utils.LibUtils;
import com.readrops.api.utils.ParseException;
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.SharedPreferencesManager;
import com.readrops.app.utils.Utils;
import com.readrops.db.Database;
import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Item;
import com.readrops.db.entities.account.Account;
@ -34,22 +34,17 @@ import io.reactivex.Single;
import kotlin.Pair;
import okhttp3.Headers;
public class LocalFeedRepository extends ARepository<Void> {
public class LocalFeedRepository extends ARepository {
private static final String TAG = LocalFeedRepository.class.getSimpleName();
private LocalRSSDataSource dataSource;
public LocalFeedRepository(@NonNull Context context, @Nullable Account account) {
super(context, account);
public LocalFeedRepository(LocalRSSDataSource dataSource, Database database, @NonNull Context context, @Nullable Account account) {
super(database, context, account);
syncResult = new SyncResult();
dataSource = new LocalRSSDataSource(HttpManager.getInstance().getOkHttpClient());
}
@Override
protected Void createAPI() {
return null;
this.dataSource = dataSource;
}
@Override
@ -138,7 +133,7 @@ public class LocalFeedRepository extends ARepository<Void> {
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));
if (maxItems > 0 && items.size() > maxItems) {
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.Nullable;
import com.readrops.api.services.Credentials;
import com.readrops.api.services.SyncResult;
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.json.NextNewsUser;
import com.readrops.api.utils.UnknownFormatException;
import com.readrops.app.utils.FeedInsertionResult;
import com.readrops.app.utils.ParsingResult;
import com.readrops.app.utils.Utils;
import com.readrops.db.Database;
import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Folder;
import com.readrops.db.entities.Item;
@ -33,31 +33,23 @@ import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.Single;
public class NextNewsRepository extends ARepository<NextNewsAPI> {
public class NextNewsRepository extends ARepository {
private static final String TAG = NextNewsRepository.class.getSimpleName();
public NextNewsRepository(@NonNull Context context, @Nullable Account account) {
super(context, account);
}
private final NextNewsDataSource dataSource;
@Override
protected NextNewsAPI createAPI() {
if (account != null)
return new NextNewsAPI(Credentials.toCredentials(account));
public NextNewsRepository(NextNewsDataSource dataSource, Database database, @NonNull Context context, @Nullable Account account) {
super(database, context, account);
return null;
this.dataSource = dataSource;
}
@Override
public Single<Boolean> login(Account account, boolean insert) {
setCredentials(account);
return Single.<NextNewsUser>create(emitter -> {
if (api == null)
api = new NextNewsAPI(Credentials.toCredentials(account));
else
api.setCredentials(Credentials.toCredentials(account));
NextNewsUser user = api.login();
NextNewsUser user = dataSource.login();
if (user != null) {
emitter.onSuccess(user);
@ -101,7 +93,7 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
}
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");
if (!result.isError()) {
@ -141,11 +133,11 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
FeedInsertionResult insertionResult = new FeedInsertionResult();
try {
List<Feed> nextNewsFeeds = api.createFeed(result.getUrl(), 0);
List<Feed> nextNewsFeeds = dataSource.createFeed(result.getUrl(), 0);
if (nextNewsFeeds != null) {
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));
} else
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
try {
if (api.renameFeed(feed) && api.changeFeedFolder(feed)) {
if (dataSource.renameFeed(feed) && dataSource.changeFeedFolder(feed)) {
emitter.onComplete();
} else
emitter.onError(new Exception("Unknown error when updating feed"));
@ -194,7 +186,7 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
public Completable deleteFeed(Feed feed) {
return Completable.create(emitter -> {
try {
if (api.deleteFeed(Integer.parseInt(feed.getRemoteId()))) {
if (dataSource.deleteFeed(Integer.parseInt(feed.getRemoteId()))) {
emitter.onComplete();
} else
emitter.onError(new Exception("Unknown error"));
@ -210,7 +202,7 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
public Single<Long> addFolder(Folder folder) {
return Single.<Folder>create(emitter -> {
try {
List<Folder> folders = api.createFolder(folder);
List<Folder> folders = dataSource.createFolder(folder);
if (folders != null) {
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) {
return Completable.create(emitter -> {
try {
if (api.renameFolder(folder)) {
if (dataSource.renameFolder(folder)) {
emitter.onComplete();
} else
emitter.onError(new Exception("Unknown error"));
@ -246,7 +238,7 @@ public class NextNewsRepository extends ARepository<NextNewsAPI> {
public Completable deleteFolder(Folder folder) {
return Completable.create(emitter -> {
try {
if (api.deleteFolder(folder)) {
if (dataSource.deleteFolder(folder)) {
emitter.onComplete();
} else
emitter.onError(new Exception("Unknown error"));

View File

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

View File

@ -7,15 +7,17 @@ import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
import com.bumptech.glide.load.model.GlideUrl
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
@GlideModule
class ReadropsGlideModule : AppGlideModule() {
class ReadropsGlideModule : AppGlideModule(), KoinComponent {
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)
}
}

View File

@ -1,19 +1,15 @@
package com.readrops.app.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import androidx.annotation.NonNull;
import org.koin.java.KoinJavaComponent;
public final class SharedPreferencesManager {
private static SharedPreferences getSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
public static void writeValue(Context context, String key, Object value) {
SharedPreferences sharedPref = getSharedPreferences(context);
public static void writeValue(String key, Object value) {
SharedPreferences sharedPref = KoinJavaComponent.get(SharedPreferences.class);
SharedPreferences.Editor editor = sharedPref.edit();
if (value instanceof Boolean)
@ -24,32 +20,32 @@ public final class SharedPreferencesManager {
editor.apply();
}
public static void writeValue(Context context, SharedPrefKey sharedPrefKey, Object value) {
writeValue(context, sharedPrefKey.key, value);
public static void writeValue(SharedPrefKey sharedPrefKey, Object value) {
writeValue(sharedPrefKey.key, value);
}
public static int readInt(Context context, SharedPrefKey sharedPrefKey) {
SharedPreferences sharedPreferences = getSharedPreferences(context);
public static int readInt(SharedPrefKey sharedPrefKey) {
SharedPreferences sharedPreferences = KoinJavaComponent.get(SharedPreferences.class);
return sharedPreferences.getInt(sharedPrefKey.key, sharedPrefKey.getIntDefaultValue());
}
public static boolean readBoolean(Context context, SharedPrefKey sharedPrefKey) {
SharedPreferences sharedPreferences = getSharedPreferences(context);
public static boolean readBoolean(SharedPrefKey sharedPrefKey) {
SharedPreferences sharedPreferences = KoinJavaComponent.get(SharedPreferences.class);
return sharedPreferences.getBoolean(sharedPrefKey.key, sharedPrefKey.getBooleanDefaultValue());
}
public static String readString(Context context, String key) {
SharedPreferences sharedPreferences = getSharedPreferences(context);
public static String readString(String key) {
SharedPreferences sharedPreferences = KoinJavaComponent.get(SharedPreferences.class);
return sharedPreferences.getString(key, null);
}
public static String readString(Context context, SharedPrefKey sharedPrefKey) {
SharedPreferences sharedPreferences = getSharedPreferences(context);
public static String readString(SharedPrefKey sharedPrefKey) {
SharedPreferences sharedPreferences = KoinJavaComponent.get(SharedPreferences.class);
return sharedPreferences.getString(sharedPrefKey.key, sharedPrefKey.getStringDefaultValue());
}
public static void remove(Context context, String key) {
SharedPreferences sharedPreferences = getSharedPreferences(context);
public static void remove(String key) {
SharedPreferences sharedPreferences = KoinJavaComponent.get(SharedPreferences.class);
SharedPreferences.Editor editor = sharedPreferences.edit();
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.account.Account
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
*/
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()
@ -66,7 +68,7 @@ class SyncResultAnalyser(val context: Context, private val syncResults: Map<Acco
notifContent.title = feed?.name
feed?.iconUrl?.let {
val target = GlideApp.with(context)
val target = get<GlideRequests>()
.asBitmap()
.load(it)
.diskCacheStrategy(DiskCacheStrategy.ALL)

View File

@ -1,20 +1,22 @@
package com.readrops.app.utils
import android.content.Context
import com.readrops.api.services.SyncResult
import com.readrops.db.Database
import com.readrops.db.entities.Item
import com.readrops.db.entities.account.Account
import com.readrops.db.entities.account.AccountType
import com.readrops.api.services.SyncResult
import org.jetbrains.annotations.TestOnly
import org.koin.core.KoinComponent
import org.koin.core.get
class SyncResultDebugData {
companion object {
companion object : KoinComponent {
@TestOnly
fun oneAccountOneFeedOneItem(context: Context): Map<Account, SyncResult> {
val database = Database.getInstance(context)
fun oneAccountOneFeedOneItem(): Map<Account, SyncResult> {
val database = get<Database>()
val account1 = database.accountDao().select(2)
@ -27,14 +29,14 @@ class SyncResultDebugData {
}
@TestOnly
fun oneAccountOneFeedMultipleItems(context: Context): Map<Account, SyncResult> {
fun oneAccountOneFeedMultipleItems(): Map<Account, SyncResult> {
val account1 = Account().apply {
id = 1
accountType = AccountType.FRESHRSS
isNotificationsEnabled = true
}
val database = Database.getInstance(context)
val database = get<Database>()
val item = database.itemDao().select(5055)
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 io.reactivex.disposables.Disposable
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 val notificationManager = NotificationManagerCompat.from(applicationContext)
private val database = Database.getInstance(applicationContext)
private val database = get<Database>()
override fun doWork(): Result {
var result = Result.success()
@ -39,15 +42,15 @@ class SyncWorker(context: Context, parameters: WorkerParameters) : Worker(contex
.setProgress(0, 0, true)
.setSmallIcon(R.drawable.ic_notif)
.setOnlyAlertOnce(true)
accounts.forEach {
notificationBuilder.setContentText(it.accountName)
notificationManager.notify(SYNC_NOTIFICATION_ID, notificationBuilder.build())
it.login = SharedPreferencesManager.readString(applicationContext, it.loginKey)
it.password = SharedPreferencesManager.readString(applicationContext, it.passwordKey)
it.login = SharedPreferencesManager.readString(it.loginKey)
it.password = SharedPreferencesManager.readString(it.passwordKey)
val repository = ARepository.repositoryFactory(it, applicationContext)
val repository = get<ARepository>(parameters = { parametersOf(it) })
disposable = repository.sync(null)
.doOnError { throwable ->
@ -138,12 +141,12 @@ class SyncWorker(context: Context, parameters: WorkerParameters) : Worker(contex
.build()
}
class MarkReadReceiver : BroadcastReceiver() {
class MarkReadReceiver : BroadcastReceiver(), KoinComponent {
override fun onReceive(context: Context?, intent: Intent?) {
val itemId = intent?.getIntExtra(ReadropsKeys.ITEM_ID, 0)!!
with(Database.getInstance(context)) {
with(get<Database>()) {
itemDao().setReadState(itemId, true, true)
.subscribeOn(Schedulers.io())
.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?) {
val itemId = intent?.getIntExtra(ReadropsKeys.ITEM_ID, 0)!!
with(Database.getInstance(context)) {
with(get<Database>()) {
itemDao().setReadItLater(itemId)
.subscribeOn(Schedulers.io())
.subscribe()

View File

@ -15,7 +15,8 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import com.google.android.material.snackbar.Snackbar;
import com.readrops.api.utils.HttpManager;
import org.koin.java.KoinJavaComponent;
import java.io.InputStream;
import java.util.Locale;
@ -34,10 +35,9 @@ public final class Utils {
public static Bitmap getImageFromUrl(String url) {
try {
OkHttpClient okHttpClient = HttpManager.getInstance().getOkHttpClient();
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()) {
InputStream inputStream = response.body().byteStream();

View File

@ -6,15 +6,17 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.readrops.app.R
import com.readrops.app.ReadropsApp
import com.readrops.app.utils.ReadropsKeys.FEEDS
import com.readrops.db.Database
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?) {
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)
.setContentTitle(getString(R.string.get_feeds_colors))

View File

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

View File

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

View File

@ -1,16 +1,15 @@
package com.readrops.app.viewmodels;
import android.app.Application;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.readrops.db.Database;
import com.readrops.db.dao.ItemDao;
import com.readrops.db.pojo.ItemWithFeed;
import java.io.File;
@ -18,22 +17,21 @@ import java.io.FileOutputStream;
import java.io.IOException;
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) {
super(application);
itemDao = Database.getInstance(application).itemDao();
public ItemViewModel(@NonNull Database database) {
this.database = database;
}
public LiveData<ItemWithFeed> getItemById(int id) {
return itemDao.getItemById(id);
return database.itemDao().getItemById(id);
}
public Uri saveImageInCache(Bitmap bitmap) throws IOException {
File imagesFolder = new File(getApplication().getCacheDir().getAbsolutePath(), "images");
public Uri saveImageInCache(Bitmap bitmap, Context context) throws IOException {
File imagesFolder = new File(context.getCacheDir().getAbsolutePath(), "images");
if (!imagesFolder.exists())
imagesFolder.mkdirs();
@ -45,6 +43,6 @@ public class ItemViewModel extends AndroidViewModel {
stream.flush();
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;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.ViewModel;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;
import com.readrops.app.repositories.ARepository;
import com.readrops.db.Database;
import com.readrops.db.ItemsListQueryBuilder;
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.ListSortType;
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.Arrays;
@ -31,45 +32,40 @@ import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
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 ARepository repository;
private Database db;
private final Database database;
private ItemsListQueryBuilder queryBuilder;
private final ItemsListQueryBuilder queryBuilder;
private Account currentAccount;
private List<Account> accounts;
public MainViewModel(@NonNull Application application) {
super(application);
public MainViewModel(@NonNull Database database) {
queryBuilder = new ItemsListQueryBuilder();
queryBuilder.setFilterType(FilterType.NO_FILTER);
queryBuilder.setSortType(ListSortType.NEWEST_TO_OLDEST);
db = Database.getInstance(application);
this.database = database;
itemsWithFeed = new MediatorLiveData<>();
}
//region main query
private void setRepository() {
try {
repository = ARepository.repositoryFactory(currentAccount, getApplication());
} catch (Exception e) {
e.printStackTrace();
}
repository = KoinJavaComponent.get(ARepository.class, null,
() -> DefinitionParametersKt.parametersOf(currentAccount));
}
private void buildPagedList() {
if (lastFetch != null)
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()
.setPageSize(100)
.setPrefetchDistance(150)
@ -77,7 +73,7 @@ public class MainViewModel extends AndroidViewModel {
.build())
.build();
itemsWithFeed.addSource(lastFetch, itemWithFeeds -> itemsWithFeed.setValue(itemWithFeeds));
itemsWithFeed.addSource(lastFetch, itemsWithFeed::setValue);
}
public void invalidate() {
@ -124,7 +120,6 @@ public class MainViewModel extends AndroidViewModel {
return repository.getFeedCount(currentAccount.getId());
}
@SuppressWarnings("unchecked")
public Single<Map<Folder, List<Feed>>> getFoldersWithFeeds() {
return repository.getFoldersWithFeeds();
}
@ -134,12 +129,12 @@ public class MainViewModel extends AndroidViewModel {
//region Account
public LiveData<List<Account>> getAllAccounts() {
return db.accountDao().selectAllAsync();
return database.accountDao().selectAllAsync();
}
private Completable deselectOldCurrentAccount(int accountId) {
return Completable.create(emitter -> {
db.accountDao().deselectOldCurrentAccount(accountId);
database.accountDao().deselectOldCurrentAccount(accountId);
emitter.onComplete();
});
}
@ -170,7 +165,7 @@ public class MainViewModel extends AndroidViewModel {
// set the new account as the current one
Completable setCurrentAccount = Completable.create(emitter -> {
db.accountDao().setCurrentAccount(currentAccount.getId());
database.accountDao().setCurrentAccount(currentAccount.getId());
emitter.onComplete();
});
@ -202,7 +197,7 @@ public class MainViewModel extends AndroidViewModel {
}
}
if (!currentAccountExists && accounts.size() > 0) {
if (!currentAccountExists && !accounts.isEmpty()) {
setCurrentAccount(accounts.get(0));
accounts.get(0).setCurrentAccount(true);
}
@ -242,7 +237,7 @@ public class MainViewModel extends AndroidViewModel {
}
public Completable setItemReadItLater(int itemId) {
return db.itemDao().setReadItLater(itemId);
return database.itemDao().setReadItLater(itemId);
}
//endregion

View File

@ -1,48 +1,44 @@
package com.readrops.app.viewmodels;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.readrops.app.repositories.ARepository;
import com.readrops.db.Database;
import com.readrops.db.entities.Feed;
import com.readrops.db.entities.Folder;
import com.readrops.db.entities.account.Account;
import com.readrops.db.pojo.FeedWithFolder;
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 io.reactivex.Completable;
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<Folder>> folders;
private ARepository repository;
private Account account;
public ManageFeedsFoldersViewModel(@NonNull Application application) {
super(application);
db = Database.getInstance(application);
public ManageFeedsFoldersViewModel(@NonNull Database database) {
this.database = database;
}
private void setup() {
try {
repository = ARepository.repositoryFactory(account, getApplication());
repository = KoinJavaComponent.get(ARepository.class, null,
() -> DefinitionParametersKt.parametersOf(account));
feedsWithFolder = db.feedDao().getAllFeedsWithFolder(account.getId());
folders = db.folderDao().getAllFolders(account.getId());
} catch (Exception e) {
e.printStackTrace();
}
feedsWithFolder = database.feedDao().getAllFeedsWithFolder(account.getId());
folders = database.folderDao().getAllFolders(account.getId());
}
public LiveData<List<FeedWithFolder>> getFeedsWithFolder() {
@ -67,7 +63,7 @@ public class ManageFeedsFoldersViewModel extends AndroidViewModel {
}
public LiveData<List<FolderWithFeedCount>> getFoldersWithFeedCount() {
return db.folderDao().getFoldersWithFeedCount(account.getId());
return database.folderDao().getFoldersWithFeedCount(account.getId());
}
public Single<Long> addFolder(Folder folder) {
@ -87,6 +83,6 @@ public class ManageFeedsFoldersViewModel extends AndroidViewModel {
}
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
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import com.readrops.db.Database
import com.readrops.db.entities.Feed
import com.readrops.db.entities.account.Account
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
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 '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;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
import androidx.room.migration.Migration;
@ -30,18 +27,7 @@ public abstract class Database extends RoomDatabase {
public abstract FolderDao folderDao();
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) {
@Override
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()
}
}