Initial support of greader API for Freshrss with working authentication

This commit is contained in:
Shinokuni 2019-08-09 21:57:11 +02:00
parent 2b08ae9c96
commit 6a5feb2d63
15 changed files with 283 additions and 31 deletions

View File

@ -70,6 +70,7 @@ public class AccountTypeListActivity extends AppCompatActivity {
accountTypes.add(Account.AccountType.LOCAL);
accountTypes.add(Account.AccountType.NEXTCLOUD_NEWS);
accountTypes.add(Account.AccountType.FRESHRSS);
return accountTypes;
}

View File

@ -49,15 +49,23 @@ public class AddAccountActivity extends AppCompatActivity {
accountToEdit = getIntent().getParcelableExtra(EDIT_ACCOUNT);
if (accountToEdit != null) {
editAccount = true;
fillFields();
} else {
binding.providerImage.setImageResource(accountType.getIconRes());
binding.providerName.setText(accountType.getName());
try {
if (accountToEdit != null) {
viewModel.setAccountType(accountToEdit.getAccountType());
editAccount = true;
fillFields();
} else {
viewModel.setAccountType(accountType);
binding.addAccountName.setText(accountType.getName());
binding.providerImage.setImageResource(accountType.getIconRes());
binding.providerName.setText(accountType.getName());
binding.addAccountName.setText(accountType.getName());
}
} catch (Exception e) {
// TODO : see how to handle this exception
e.printStackTrace();
}
}
public void createAccount(View view) {

View File

@ -37,6 +37,8 @@ public class Account implements Parcelable {
@ColumnInfo(name = "current_account")
private boolean currentAccount;
private String token;
@Ignore
private String login;
@ -157,6 +159,14 @@ public class Account implements Parcelable {
return accountType.name() + "_password_" + id;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
@Override
public int describeContents() {
return 0;

View File

@ -0,0 +1,81 @@
package com.readrops.app.repositories;
import android.app.Application;
import com.readrops.app.database.entities.Account;
import com.readrops.app.database.entities.Feed;
import com.readrops.app.database.entities.Folder;
import com.readrops.app.utils.FeedInsertionResult;
import com.readrops.app.utils.ParsingResult;
import com.readrops.readropslibrary.services.freshrss.FreshRSSAPI;
import com.readrops.readropslibrary.services.freshrss.FreshRSSCredentials;
import com.readrops.readropslibrary.services.freshrss.FreshRSSService;
import java.util.List;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.Single;
public class FreshRSSRepository extends ARepository {
public FreshRSSRepository(Application application) {
super(application);
}
@Override
public Single<Boolean> login(Account account, boolean insert) {
FreshRSSAPI api = new FreshRSSAPI(new FreshRSSCredentials(null, account.getUrl()));
return api.login(account.getLogin(), account.getPassword())
.flatMap(token -> {
account.setToken(token);
api.buildAPI(new FreshRSSCredentials(token, account.getUrl()), FreshRSSService.class, FreshRSSService.END_POINT);
return api.getUserInfo();
})
.flatMap(userInfo -> {
account.setDisplayedName(userInfo.getUserName());
if (insert)
account.setId((int) database.accountDao().insert(account));
return Single.just(true);
});
}
@Override
public Observable<Feed> sync(List<Feed> feeds, Account account) {
return null;
}
@Override
public Single<List<FeedInsertionResult>> addFeeds(List<ParsingResult> results, Account account) {
return null;
}
@Override
public Completable updateFeed(Feed feed, Account account) {
return null;
}
@Override
public Completable deleteFeed(Feed feed, Account account) {
return null;
}
@Override
public Completable addFolder(Folder folder, Account account) {
return null;
}
@Override
public Completable updateFolder(Folder folder, Account account) {
return null;
}
@Override
public Completable deleteFolder(Folder folder, Account account) {
return null;
}
}

View File

@ -46,7 +46,7 @@ public class NextNewsRepository extends ARepository {
@Override
public Single<Boolean> login(Account account, boolean insert) {
return Single.create(emitter -> {
NextNewsAPI newsAPI = new NextNewsAPI(account.toCredentials());
NextNewsAPI newsAPI = new NextNewsAPI(account.toNextNewsCredentials());
NextNewsUser user = newsAPI.login();
if (user != null) {
@ -65,7 +65,7 @@ public class NextNewsRepository extends ARepository {
public Observable<Feed> sync(List<Feed> feeds, Account account) {
return Observable.create(emitter -> {
try {
NextNewsAPI newsAPI = new NextNewsAPI(account.toCredentials());
NextNewsAPI newsAPI = new NextNewsAPI(account.toNextNewsCredentials());
long lastModified = LocalDateTime.now().toDateTime().getMillis();
NextNewsAPI.SyncType syncType;
@ -117,7 +117,7 @@ public class NextNewsRepository extends ARepository {
public Single<List<FeedInsertionResult>> addFeeds(List<ParsingResult> results, Account account) {
return Single.create(emitter -> {
List<FeedInsertionResult> feedInsertionResults = new ArrayList<>();
NextNewsAPI newsAPI = new NextNewsAPI(account.toCredentials());
NextNewsAPI newsAPI = new NextNewsAPI(account.toNextNewsCredentials());
for (ParsingResult result : results) {
FeedInsertionResult insertionResult = new FeedInsertionResult();
@ -154,7 +154,7 @@ public class NextNewsRepository extends ARepository {
@Override
public Completable updateFeed(Feed feed, Account account) {
return Completable.create(emitter -> {
NextNewsAPI api = new NextNewsAPI(account.toCredentials());
NextNewsAPI api = new NextNewsAPI(account.toNextNewsCredentials());
Folder folder = feed.getFolderId() == null ? null : database.folderDao().select(feed.getFolderId());
@ -185,7 +185,7 @@ public class NextNewsRepository extends ARepository {
@Override
public Completable deleteFeed(Feed feed, Account account) {
return Completable.create(emitter -> {
NextNewsAPI api = new NextNewsAPI(account.toCredentials());
NextNewsAPI api = new NextNewsAPI(account.toNextNewsCredentials());
try {
if (api.deleteFeed(feed.getRemoteId())) {
@ -204,7 +204,7 @@ public class NextNewsRepository extends ARepository {
@Override
public Completable addFolder(Folder folder, Account account) {
return Completable.create(emitter -> {
NextNewsAPI api = new NextNewsAPI(account.toCredentials());
NextNewsAPI api = new NextNewsAPI(account.toNextNewsCredentials());
try {
NextNewsFolders folders = api.createFolder(new NextNewsFolder(folder.getRemoteId(), folder.getName()));
@ -224,7 +224,7 @@ public class NextNewsRepository extends ARepository {
@Override
public Completable updateFolder(Folder folder, Account account) {
return Completable.create(emitter -> {
NextNewsAPI api = new NextNewsAPI(account.toCredentials());
NextNewsAPI api = new NextNewsAPI(account.toNextNewsCredentials());
try {
if (api.renameFolder(new NextNewsFolder(folder.getRemoteId(), folder.getName()))) {
@ -244,7 +244,7 @@ public class NextNewsRepository extends ARepository {
@Override
public Completable deleteFolder(Folder folder, Account account) {
return Completable.create(emitter -> {
NextNewsAPI api = new NextNewsAPI(account.toCredentials());
NextNewsAPI api = new NextNewsAPI(account.toNextNewsCredentials());
try {
if (api.deleteFolder(new NextNewsFolder(folder.getRemoteId(), folder.getName()))) {

View File

@ -8,6 +8,7 @@ import androidx.lifecycle.AndroidViewModel;
import com.readrops.app.database.Database;
import com.readrops.app.database.entities.Account;
import com.readrops.app.repositories.ARepository;
import com.readrops.app.repositories.FreshRSSRepository;
import com.readrops.app.repositories.NextNewsRepository;
import io.reactivex.Completable;
@ -21,10 +22,22 @@ public class AccountViewModel extends AndroidViewModel {
public AccountViewModel(@NonNull Application application) {
super(application);
repository = new NextNewsRepository(application);
database = Database.getInstance(application);
}
public void setAccountType(Account.AccountType accountType) throws Exception {
switch (accountType) {
case NEXTCLOUD_NEWS:
repository = new NextNewsRepository(getApplication());
break;
case FRESHRSS:
repository = new FreshRSSRepository(getApplication());
break;
default:
throw new Exception("unknown account type");
}
}
public Single<Boolean> login(Account account, boolean insert) {
return repository.login(account, insert);
}

View File

@ -17,6 +17,7 @@ import com.readrops.app.database.entities.Feed;
import com.readrops.app.database.entities.Folder;
import com.readrops.app.database.pojo.ItemWithFeed;
import com.readrops.app.repositories.ARepository;
import com.readrops.app.repositories.FreshRSSRepository;
import com.readrops.app.repositories.LocalFeedRepository;
import com.readrops.app.repositories.NextNewsRepository;
@ -67,6 +68,9 @@ public class MainViewModel extends AndroidViewModel {
case NEXTCLOUD_NEWS:
repository = new NextNewsRepository(getApplication());
break;
case FRESHRSS:
repository = new FreshRSSRepository(getApplication());
break;
}
}

View File

@ -21,7 +21,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
}
dependencies {
@ -35,6 +38,7 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.retrofit2:converter-simplexml:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.14.1'

View File

@ -1,15 +1,16 @@
package com.readrops.readropslibrary;
package com.readrops.readropslibrary.services;
import androidx.annotation.NonNull;
import com.readrops.readropslibrary.services.nextcloudnews.Credentials;
import com.readrops.readropslibrary.utils.HttpManager;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Abstraction level for services APIs
*
* @param <T> an API service interface
*/
public abstract class API<T> {
@ -17,13 +18,14 @@ public abstract class API<T> {
protected T api;
public API(Credentials credentials, @NonNull Class<T> clazz, @NonNull String endPoint) {
api = createAPI(credentials, clazz, endPoint);
buildAPI(credentials, clazz, endPoint);
}
protected Retrofit getConfiguredRetrofitInstance(@NonNull HttpManager httpManager, @NonNull String endPoint) {
return new Retrofit.Builder()
.baseUrl(httpManager.getCredentials().getUrl() + endPoint)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpManager.getOkHttpClient())
.build();
}
@ -34,4 +36,8 @@ public abstract class API<T> {
return retrofit.create(clazz);
}
public void buildAPI(@NonNull Credentials credentials, @NonNull Class<T> clazz, @NonNull String endPoint) {
api = createAPI(credentials, clazz, endPoint);
}
}

View File

@ -0,0 +1,38 @@
package com.readrops.readropslibrary.services.freshrss;
import com.readrops.readropslibrary.services.API;
import com.readrops.readropslibrary.services.freshrss.json.FreshRSSUserInfo;
import java.io.StringReader;
import java.util.Properties;
import io.reactivex.Single;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
public class FreshRSSAPI extends API<FreshRSSService> {
public FreshRSSAPI(FreshRSSCredentials credentials) {
super(credentials, FreshRSSService.class, FreshRSSService.END_POINT);
}
public Single<String> login(String login, String password) {
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("Email", login)
.addFormDataPart("Passwd", password)
.build();
return api.login(requestBody)
.flatMap(response -> {
Properties properties = new Properties();
properties.load(new StringReader(response.string()));
return Single.just(properties.getProperty("Auth"));
});
}
public Single<FreshRSSUserInfo> getUserInfo() {
return api.getUserInfo();
}
}

View File

@ -0,0 +1,13 @@
package com.readrops.readropslibrary.services.freshrss;
import com.readrops.readropslibrary.services.Credentials;
public class FreshRSSCredentials extends Credentials {
private static final String AUTH_PREFIX = "GoogleLogin auth=";
public FreshRSSCredentials(String token, String url) {
super(token != null ? AUTH_PREFIX + token : null, url);
}
}

View File

@ -0,0 +1,22 @@
package com.readrops.readropslibrary.services.freshrss;
import com.readrops.readropslibrary.services.freshrss.json.FreshRSSUserInfo;
import io.reactivex.Single;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
public interface FreshRSSService {
String END_POINT = "/api/greader.php/";
@POST("accounts/ClientLogin")
Single<ResponseBody> login(@Body RequestBody body);
@GET("reader/api/0/user-info")
Single<FreshRSSUserInfo> getUserInfo();
}

View File

@ -0,0 +1,46 @@
package com.readrops.readropslibrary.services.freshrss.json;
public class FreshRSSUserInfo {
private String userEmail;
private String userId;
private String userName;
private String userProfileId;
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserProfileId() {
return userProfileId;
}
public void setUserProfileId(String userProfileId) {
this.userProfileId = userProfileId;
}
}

View File

@ -1,7 +1,8 @@
package com.readrops.readropslibrary.utils;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
public final class HttpBuilder {
@ -15,6 +16,8 @@ public final class HttpBuilder {
}
private static OkHttpClient.Builder createOkHttpBuilder() {
return new OkHttpClient.Builder();
return new OkHttpClient.Builder()
.callTimeout(30, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.HOURS);
}
}

View File

@ -1,9 +1,8 @@
package com.readrops.readropslibrary.utils;
import com.readrops.readropslibrary.services.nextcloudnews.Credentials;
import com.readrops.readropslibrary.services.Credentials;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
@ -18,11 +17,15 @@ public class HttpManager {
public HttpManager(final Credentials credentials) {
this.credentials = credentials;
okHttpClient = HttpBuilder.getBuilder()
.callTimeout(30, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.HOURS)
.addInterceptor(new AuthInterceptor())
.build();
if (credentials.getAuthorization() != null) {
okHttpClient = HttpBuilder.getBuilder()
.addInterceptor(new AuthInterceptor())
.build();
} else {
okHttpClient = HttpBuilder.getBuilder()
.build();
}
}
public OkHttpClient getOkHttpClient() {
@ -36,7 +39,7 @@ public class HttpManager {
public class AuthInterceptor implements Interceptor {
public AuthInterceptor() {
// empty constructor
}
@Override
@ -48,7 +51,7 @@ public class HttpManager {
// So preventively, I delete the first added header
// TODO : find why AuthInterceptor is called twice
request = request.newBuilder().removeHeader("Authorization")
.addHeader("Authorization", credentials.getBase64())
.addHeader("Authorization", credentials.getAuthorization())
.build();
return chain.proceed(request);