Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/news-android.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Luhmer <david-dev@live.de>2018-06-12 16:56:47 +0300
committerGitHub <noreply@github.com>2018-06-12 16:56:47 +0300
commit05055a82d933d1147eacdb52ce5067426566368e (patch)
tree591978c2af1aafc189eeb376927f3f283357a573 /News-Android-App
parent96efaa7d7eaca737d42c6340f7e7cffd0bba8f84 (diff)
parente48dfe87fd0e11fc561cc38f189108e515555cd0 (diff)
Merge pull request #639 from nextcloud/sso
Proof of concept for SingSignOn
Diffstat (limited to 'News-Android-App')
-rw-r--r--News-Android-App/build.gradle5
-rw-r--r--News-Android-App/src/main/AndroidManifest.xml1
-rw-r--r--News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/LoginDialogFragment.java318
-rw-r--r--News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java38
-rw-r--r--News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/SettingsActivity.java1
-rw-r--r--News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java101
-rw-r--r--News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/GsonConfig.java67
-rw-r--r--News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/API_SSO.java216
-rw-r--r--News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/API_SSO_Helper.java138
-rw-r--r--News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/BufferedSourceSSO.java249
-rw-r--r--News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/RssItemObservable.java1
-rw-r--r--News-Android-App/src/main/res/layout/dialog_signin.xml23
12 files changed, 972 insertions, 186 deletions
diff --git a/News-Android-App/build.gradle b/News-Android-App/build.gradle
index 86275f5c..abb4a14f 100644
--- a/News-Android-App/build.gradle
+++ b/News-Android-App/build.gradle
@@ -13,6 +13,7 @@ android {
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
defaultConfig {
+ multiDexEnabled true
minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION)
targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION)
@@ -89,6 +90,7 @@ repositories {
jcenter()
maven { url 'http://guardian.github.com/maven/repo-releases' } //needed for com.gu:option:1.3 in Android-DirectoryChooser
maven { url "http://dl.bintray.com/lukaville/maven" } //Needed for com.nbsp:library:1.02 in Material File Picker
+ maven { url "https://jitpack.io" }
}
@@ -100,7 +102,7 @@ dependencies {
// You must install or update the Google Repository through the SDK manager to use this dependency.
// The Google Repository (separate from the corresponding library) can be found in the Extras category.
// implementation 'com.google.android.gms:play-services:4.2.42'
- implementation project(path: ':ownCloud-Account-Importer', configuration: 'default')
+ implementation "com.github.nextcloud:android-SingleSignOn:sso-SNAPSHOT"
implementation "com.android.support:support-v4:${SUPPORT_VERSION}"
implementation "com.android.support:support-compat:${SUPPORT_VERSION}"
implementation "com.android.support:appcompat-v7:${SUPPORT_VERSION}"
@@ -113,6 +115,7 @@ dependencies {
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.4'
implementation 'com.google.code.gson:gson:2.8.0'
implementation 'com.jakewharton:butterknife:8.8.1'
+ implementation 'com.android.support:multidex:1.0.3'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
compileOnly 'com.google.auto.value:auto-value:1.5.2'
diff --git a/News-Android-App/src/main/AndroidManifest.xml b/News-Android-App/src/main/AndroidManifest.xml
index 3bc1a936..f6394e9a 100644
--- a/News-Android-App/src/main/AndroidManifest.xml
+++ b/News-Android-App/src/main/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
+ <uses-permission android:name="com.owncloud.android.sso"/>
<!--
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
-->
diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/LoginDialogFragment.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/LoginDialogFragment.java
index 35c07cd9..c4b08055 100644
--- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/LoginDialogFragment.java
+++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/LoginDialogFragment.java
@@ -21,14 +21,20 @@
package de.luhmer.owncloudnewsreader;
+import android.Manifest;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.preference.PreferenceManager;
+import android.support.design.widget.TextInputLayout;
+import android.support.v4.app.ActivityCompat;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
@@ -48,7 +54,9 @@ import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.Switch;
import android.widget.TextView;
+import android.widget.Toast;
import java.net.MalformedURLException;
import java.net.URL;
@@ -57,9 +65,9 @@ import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
-import de.luhmer.owncloud.accountimporter.ImportAccountsDialogFragment;
import de.luhmer.owncloud.accountimporter.helper.AccountImporter;
-import de.luhmer.owncloud.accountimporter.helper.OwnCloudAccount;
+import de.luhmer.owncloud.accountimporter.helper.NextcloudAPI;
+import de.luhmer.owncloud.accountimporter.helper.SingleSignOnAccount;
import de.luhmer.owncloud.accountimporter.interfaces.IAccountImport;
import de.luhmer.owncloudnewsreader.authentication.AuthenticatorActivity;
import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm;
@@ -72,12 +80,18 @@ import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import static android.app.Activity.RESULT_OK;
+
/**
* Activity which displays a login screen to the user, offering registration as
* well.
*/
public class LoginDialogFragment extends DialogFragment implements IAccountImport {
+ final String TAG = LoginDialogFragment.class.getCanonicalName();
+ final int CHOOSE_ACCOUNT = 12;
+
+
static LoginDialogFragment instance;
public static LoginDialogFragment getInstance() {
if(instance == null)
@@ -104,23 +118,39 @@ public class LoginDialogFragment extends DialogFragment implements IAccountImpor
// UI references.
@BindView(R.id.username) EditText mUsernameView;
@BindView(R.id.password) EditText mPasswordView;
+ @BindView(R.id.password_container) TextInputLayout mPasswordContainerView;
@BindView(R.id.edt_owncloudRootPath) EditText mOc_root_path_View;
@BindView(R.id.cb_AllowAllSSLCertificates) CheckBox mCbDisableHostnameVerificationView;
@BindView(R.id.imgView_ShowPassword) ImageView mImageViewShowPwd;
+ @BindView(R.id.swSingleSignOn) Switch mSwSingleSignOn;
- boolean mPasswordVisible = false;
+ private Account importedAccount = null;
+ private boolean mPasswordVisible = false;
+ private LoginSuccessfulListener listener;
- @Override
- public void accountAccessGranted(OwnCloudAccount account) {
- mUsernameView.setText(account.getUsername());
- mPasswordView.setText(account.getPassword());
- mOc_root_path_View.setText(account.getUrl());
- }
+ @Override
+ public void accountAccessGranted(final Account account) {
+ try {
+ SingleSignOnAccount singleAccount = AccountImporter.BlockingGetAuthToken(getActivity(), account);
+ mUsernameView.setText(singleAccount.username);
+ mPasswordView.setText("");
+ mOc_root_path_View.setText(singleAccount.url);
+
+ mPasswordContainerView.setVisibility(View.GONE);
+ mImageViewShowPwd.setVisibility(View.GONE);
+ mCbDisableHostnameVerificationView.setVisibility(View.GONE);
- public interface LoginSuccessfullListener {
+ this.importedAccount = account;
+ } catch (Exception e) {
+ e.printStackTrace();
+ Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public interface LoginSuccessfulListener {
void LoginSucceeded();
}
- LoginSuccessfullListener listener;
+
public LoginDialogFragment() {
@@ -134,7 +164,7 @@ public class LoginDialogFragment extends DialogFragment implements IAccountImpor
/**
* @param listener the listener to set
*/
- public void setListener(LoginSuccessfullListener listener) {
+ public void setListener(LoginSuccessfulListener listener) {
this.listener = listener;
}
@@ -153,7 +183,11 @@ public class LoginDialogFragment extends DialogFragment implements IAccountImpor
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
- showImportAccountButton = AccountImporter.findAccounts(getActivity()).size() > 0;
+ ActivityCompat.requestPermissions(getActivity(),
+ new String[]{Manifest.permission.GET_ACCOUNTS}, 0);
+
+ //accountImporter = new AccountImporter();
+ showImportAccountButton = AccountImporter.AccountsToImportAvailable(getActivity());
//setRetainInstance(true);
@@ -179,6 +213,7 @@ public class LoginDialogFragment extends DialogFragment implements IAccountImpor
mUsername = mPrefs.getString(SettingsActivity.EDT_USERNAME_STRING, "");
mPassword = mPrefs.getString(SettingsActivity.EDT_PASSWORD_STRING, "");
mOc_root_path = mPrefs.getString(SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING, "");
+ boolean useSSO = mPrefs.getBoolean(SettingsActivity.SW_USE_SINGLE_SIGN_ON, false);
mCbDisableHostnameVerification = mPrefs.getBoolean(SettingsActivity.CB_DISABLE_HOSTNAME_VERIFICATION_STRING, false);
if(!mPassword.isEmpty()) {
@@ -201,11 +236,52 @@ public class LoginDialogFragment extends DialogFragment implements IAccountImpor
}
});
+ if(useSSO) {
+ mSwSingleSignOn.setChecked(true);
+ syncUiElementState();
+ }
+
+ mSwSingleSignOn.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ syncUiElementState();
+
+ mUsernameView.setText("");
+ mPasswordView.setText("");
+ mOc_root_path_View.setText("");
+ mCbDisableHostnameVerificationView.setChecked(false);
+
+ mPasswordContainerView.setVisibility(View.VISIBLE);
+ mImageViewShowPwd.setVisibility(View.VISIBLE);
+ mCbDisableHostnameVerificationView.setVisibility(View.VISIBLE);
+
+ if(isChecked) {
+ Intent intent = AccountManager.newChooseAccountIntent(null, null, new String[] {"nextcloud"},
+ true, null, null, null, null);
+ startActivityForResult(intent, CHOOSE_ACCOUNT);
+ } else {
+ importedAccount = null;
+ }
+ }
+ });
+
+
+
AlertDialog dialog = builder.create();
// Set dialog to resize when soft keyboard pops up
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
- return dialog;
+
+
+ return dialog;
+ }
+
+ private void syncUiElementState() {
+ boolean useSSO = mSwSingleSignOn.isChecked();
+ mUsernameView.setEnabled(!useSSO);
+ mPasswordView.setEnabled(!useSSO);
+ mOc_root_path_View.setEnabled(!useSSO);
+ mCbDisableHostnameVerificationView.setEnabled(!useSSO);
}
@Override
@@ -219,7 +295,9 @@ public class LoginDialogFragment extends DialogFragment implements IAccountImpor
neutralButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- ImportAccountsDialogFragment.show(getActivity(), LoginDialogFragment.this);
+ mSwSingleSignOn.setChecked(false);
+ mSwSingleSignOn.setChecked(true);
+ //ImportAccountsDialogFragment.show(getActivity(), LoginDialogFragment.this);
}
});
// Limit button width to not push positive button out of view
@@ -235,6 +313,7 @@ public class LoginDialogFragment extends DialogFragment implements IAccountImpor
}
}
+
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
@@ -304,36 +383,39 @@ public class LoginDialogFragment extends DialogFragment implements IAccountImpor
boolean cancel = false;
View focusView = null;
- // Check for a valid password.
- if (TextUtils.isEmpty(mPassword)) {
- mPasswordView.setError(getString(R.string.error_field_required));
- focusView = mPasswordView;
- cancel = true;
- }
- // Check for a valid email address.
- if (TextUtils.isEmpty(mUsername)) {
- mUsernameView.setError(getString(R.string.error_field_required));
- focusView = mUsernameView;
- cancel = true;
- }
+ // Only run checks if we don't use sso
+ if(!mSwSingleSignOn.isChecked()) {
+ // Check for a valid password.
+ if (TextUtils.isEmpty(mPassword)) {
+ mPasswordView.setError(getString(R.string.error_field_required));
+ focusView = mPasswordView;
+ cancel = true;
+ }
+ // Check for a valid email address.
+ if (TextUtils.isEmpty(mUsername)) {
+ mUsernameView.setError(getString(R.string.error_field_required));
+ focusView = mUsernameView;
+ cancel = true;
+ }
- if (TextUtils.isEmpty(mOc_root_path)) {
- mOc_root_path_View.setError(getString(R.string.error_field_required));
- focusView = mOc_root_path_View;
- cancel = true;
- } else {
- try {
- URL url = new URL(mOc_root_path);
- if(!url.getProtocol().equals("https"))
- ShowAlertDialog(getString(R.string.login_dialog_title_security_warning),
- getString(R.string.login_dialog_text_security_warning), getActivity());
- } catch (MalformedURLException e) {
- mOc_root_path_View.setError(getString(R.string.error_invalid_url));
- focusView = mOc_root_path_View;
- cancel = true;
- //e.printStackTrace();
- }
- }
+ if (TextUtils.isEmpty(mOc_root_path)) {
+ mOc_root_path_View.setError(getString(R.string.error_field_required));
+ focusView = mOc_root_path_View;
+ cancel = true;
+ } else {
+ try {
+ URL url = new URL(mOc_root_path);
+ if (!url.getProtocol().equals("https"))
+ ShowAlertDialog(getString(R.string.login_dialog_title_security_warning),
+ getString(R.string.login_dialog_text_security_warning), getActivity());
+ } catch (MalformedURLException e) {
+ mOc_root_path_View.setError(getString(R.string.error_invalid_url));
+ focusView = mOc_root_path_View;
+ cancel = true;
+ //e.printStackTrace();
+ }
+ }
+ }
if (cancel) {
// There was an error; don't attempt login and focus the first
@@ -345,74 +427,87 @@ public class LoginDialogFragment extends DialogFragment implements IAccountImpor
editor.putString(SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING, mOc_root_path);
editor.putString(SettingsActivity.EDT_PASSWORD_STRING, mPassword);
editor.putString(SettingsActivity.EDT_USERNAME_STRING, mUsername);
+ editor.putBoolean(SettingsActivity.SW_USE_SINGLE_SIGN_ON, importedAccount != null);
editor.commit();
- // Re-init API
- mApi.initApi();
-
- //((NewsReaderApplication) getActivity().getApplication()).initDaggerAppComponent();
- //((NewsReaderApplication) getActivity().getApplication()).getAppComponent().injectFragment(this);
-
-
- final String TAG = LoginDialogFragment.class.getCanonicalName();
- final ProgressDialog mDialogLogin = BuildPendingDialogWhileLoggingIn();
- mDialogLogin.show();
-
-
- mApi.getAPI().version()
- .subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Observer<NextcloudNewsVersion>() {
- boolean loginSuccessful = false;
-
- @Override
- public void onSubscribe(@NonNull Disposable d) {
- Log.v(TAG, "onSubscribe() called with: d = [" + d + "]");
- }
+ final ProgressDialog dialogLogin = BuildPendingDialogWhileLoggingIn();
+ dialogLogin.show();
- @Override
- public void onNext(@NonNull NextcloudNewsVersion version) {
- Log.v(TAG, "onNext() called with: status = [" + version + "]");
-
- loginSuccessful = true;
- mPrefs.edit().putString(Constants.NEWS_WEB_VERSION_NUMBER_STRING, version.version).apply();
-
- if(version.version.equals("0")) {
- ShowAlertDialog(getString(R.string.login_dialog_title_error), getString(R.string.login_dialog_text_zero_version_code), getActivity());
- loginSuccessful = false;
- }
- }
-
- @Override
- public void onError(@NonNull Throwable e) {
- mDialogLogin.dismiss();
- Log.v(TAG, "onError() called with: e = [" + e + "]");
-
- Throwable t = OkHttpSSLClient.HandleExceptions(e);
- ShowAlertDialog(getString(R.string.login_dialog_title_error), t.getMessage(), getActivity());
- }
-
- @Override
- public void onComplete() {
- mDialogLogin.dismiss();
+ if(mSwSingleSignOn.isChecked()) {
+ AccountImporter.SetCurrentAccount(getActivity(), importedAccount);
+ }
- Log.v(TAG, "onComplete() called");
- if(loginSuccessful) {
- //Reset Database
- DatabaseConnectionOrm dbConn = new DatabaseConnectionOrm(getActivity());
- dbConn.resetDatabase();
+ mApi.initApi(new NextcloudAPI.ApiConnectedListener() {
+ @Override
+ public void onConnected() {
+ Log.d(TAG, "onConnected() called");
+ finishLogin(dialogLogin);
+ }
- listener.LoginSucceeded();
- LoginDialogFragment.this.getDialog().cancel();
- if(mActivity instanceof AuthenticatorActivity)
- mActivity.finish();
- }
- }
- });
+ @Override
+ public void onError(Exception ex) {
+ Log.d(TAG, "onError() called with: ex = [" + ex + "]");
+ ShowAlertDialog(getString(R.string.login_dialog_title_error), ex.getMessage(), getActivity());
+ }
+ });
}
}
+ private void finishLogin(final ProgressDialog dialogLogin) {
+ mApi.getAPI().version()
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Observer<NextcloudNewsVersion>() {
+ boolean loginSuccessful = false;
+
+ @Override
+ public void onSubscribe(@NonNull Disposable d) {
+ Log.v(TAG, "onSubscribe() called with: d = [" + d + "]");
+ }
+
+ @Override
+ public void onNext(@NonNull NextcloudNewsVersion version) {
+ Log.v(TAG, "onNext() called with: status = [" + version + "]");
+
+ loginSuccessful = true;
+ mPrefs.edit().putString(Constants.NEWS_WEB_VERSION_NUMBER_STRING, version.version).apply();
+
+ if(version.version.equals("0")) {
+ ShowAlertDialog(getString(R.string.login_dialog_title_error), getString(R.string.login_dialog_text_zero_version_code), getActivity());
+ loginSuccessful = false;
+ }
+ }
+
+ @Override
+ public void onError(@NonNull Throwable e) {
+ dialogLogin.dismiss();
+ Log.v(TAG, "onError() called with: e = [" + e + "]");
+
+ Throwable t = OkHttpSSLClient.HandleExceptions(e);
+ ShowAlertDialog(getString(R.string.login_dialog_title_error), t.getMessage(), getActivity());
+ }
+
+ @Override
+ public void onComplete() {
+ dialogLogin.dismiss();
+
+ Log.v(TAG, "onComplete() called");
+
+ if(loginSuccessful) {
+ //Reset Database
+ DatabaseConnectionOrm dbConn = new DatabaseConnectionOrm(getActivity());
+ dbConn.resetDatabase();
+
+ listener.LoginSucceeded();
+ LoginDialogFragment.this.getDialog().cancel();
+ if(mActivity instanceof AuthenticatorActivity)
+ mActivity.finish();
+ }
+ }
+ });
+ }
+
public static void ShowAlertDialog(String title, String text, Activity activity)
{
// Linkify the message
@@ -429,4 +524,21 @@ public class LoginDialogFragment extends DialogFragment implements IAccountImpor
// Make the textview clickable. Must be called after show()
((TextView)aDialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
}
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (resultCode == RESULT_OK) {
+ if (requestCode == CHOOSE_ACCOUNT) {
+ importedAccount = null;
+ String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
+
+ Account account = AccountImporter.GetAccountForName(getActivity(), accountName);
+ if(account != null) {
+ accountAccessGranted(account);
+ }
+ }
+ }
+ }
}
diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java
index 2ada5c97..3c0a38dd 100644
--- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java
+++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java
@@ -69,8 +69,9 @@ import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
+import de.luhmer.owncloud.accountimporter.helper.NextcloudAPI;
import de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter;
-import de.luhmer.owncloudnewsreader.LoginDialogFragment.LoginSuccessfullListener;
+import de.luhmer.owncloudnewsreader.LoginDialogFragment.LoginSuccessfulListener;
import de.luhmer.owncloudnewsreader.adapter.NewsListRecyclerAdapter;
import de.luhmer.owncloudnewsreader.adapter.RecyclerItemClickListener;
import de.luhmer.owncloudnewsreader.adapter.ViewHolder;
@@ -824,7 +825,9 @@ public class NewsReaderListActivity extends PodcastFragmentActivity implements
}
@Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
if(resultCode == RESULT_OK) {
UpdateListView();
getSlidingListFragment().ListViewNotifyDataSetChanged();
@@ -833,17 +836,26 @@ public class NewsReaderListActivity extends PodcastFragmentActivity implements
if(requestCode == RESULT_SETTINGS)
{
//Update settings of image Loader
- mApi.initApi();
-
- String oldLayout = data.getStringExtra(SettingsActivity.SP_FEED_LIST_LAYOUT);
- String newLayout = PreferenceManager.getDefaultSharedPreferences(this).getString(SettingsActivity.SP_FEED_LIST_LAYOUT,"0");
+ mApi.initApi(new NextcloudAPI.ApiConnectedListener() {
+ @Override
+ public void onConnected() {
+ String oldLayout = data.getStringExtra(SettingsActivity.SP_FEED_LIST_LAYOUT);
+ String newLayout = PreferenceManager.getDefaultSharedPreferences(NewsReaderListActivity.this).getString(SettingsActivity.SP_FEED_LIST_LAYOUT,"0");
+
+ if(ThemeChooser.getInstance(NewsReaderListActivity.this).themeRequiresRestartOfUI(NewsReaderListActivity.this) || !newLayout.equals(oldLayout)) {
+ finish();
+ startActivity(getIntent());
+ } else if(data.hasExtra(SettingsActivity.CACHE_CLEARED) && ownCloudSyncService != null) {
+ resetUiAndStartSync();
+ }
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ ex.printStackTrace();
+ }
+ });
- if(ThemeChooser.getInstance(this).themeRequiresRestartOfUI(this) || !newLayout.equals(oldLayout)) {
- finish();
- startActivity(getIntent());
- } else if(data.hasExtra(SettingsActivity.CACHE_CLEARED) && ownCloudSyncService != null) {
- resetUiAndStartSync();
- }
} else if(requestCode == RESULT_ADD_NEW_FEED) {
if(data != null) {
boolean val = data.getBooleanExtra(NewFeedActivity.ADD_NEW_SUCCESS, false);
@@ -867,7 +879,7 @@ public class NewsReaderListActivity extends PodcastFragmentActivity implements
{
LoginDialogFragment dialog = LoginDialogFragment.getInstance();
dialog.setActivity(activity);
- dialog.setListener(new LoginSuccessfullListener() {
+ dialog.setListener(new LoginSuccessfulListener() {
@Override
public void LoginSucceeded() {
((NewsReaderListActivity) activity).resetUiAndStartSync();
diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/SettingsActivity.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/SettingsActivity.java
index 20e31b65..8e5b8ebf 100644
--- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/SettingsActivity.java
+++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/SettingsActivity.java
@@ -90,6 +90,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
public static final String EDT_USERNAME_STRING = "edt_username";
public static final String EDT_PASSWORD_STRING = "edt_password";
public static final String EDT_OWNCLOUDROOTPATH_STRING = "edt_owncloudRootPath";
+ public static final String SW_USE_SINGLE_SIGN_ON = "sw_use_single_sign_on";
public static final String EDT_CLEAR_CACHE = "edt_clearCache";
//public static final String CB_ALLOWALLSSLCERTIFICATES_STRING = "cb_AllowAllSSLCertificates";
diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java
index a008cdc9..da24281a 100644
--- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java
+++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java
@@ -1,36 +1,24 @@
package de.luhmer.owncloudnewsreader.di;
+import android.accounts.Account;
import android.content.Context;
import android.content.SharedPreferences;
-import android.graphics.BitmapFactory;
-import android.util.Base64;
+import android.support.annotation.NonNull;
import android.util.Log;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.reflect.TypeToken;
import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
-import java.lang.reflect.Type;
-import java.util.List;
-
+import de.luhmer.owncloud.accountimporter.helper.AccountImporter;
+import de.luhmer.owncloud.accountimporter.helper.NextcloudAPI;
+import de.luhmer.owncloud.accountimporter.helper.SingleSignOnAccount;
import de.luhmer.owncloudnewsreader.SettingsActivity;
-import de.luhmer.owncloudnewsreader.database.model.Feed;
-import de.luhmer.owncloudnewsreader.database.model.Folder;
-import de.luhmer.owncloudnewsreader.database.model.RssItem;
-import de.luhmer.owncloudnewsreader.model.UserInfo;
+import de.luhmer.owncloudnewsreader.helper.GsonConfig;
import de.luhmer.owncloudnewsreader.reader.OkHttpImageDownloader;
import de.luhmer.owncloudnewsreader.reader.nextcloud.API;
-import de.luhmer.owncloudnewsreader.reader.nextcloud.NextcloudDeserializer;
-import de.luhmer.owncloudnewsreader.reader.nextcloud.Types;
+import de.luhmer.owncloudnewsreader.reader.nextcloud.API_SSO;
import de.luhmer.owncloudnewsreader.ssl.MemorizingTrustManager;
import de.luhmer.owncloudnewsreader.ssl.OkHttpSSLClient;
import okhttp3.HttpUrl;
@@ -45,74 +33,71 @@ import retrofit2.converter.gson.GsonConverterFactory;
public class ApiProvider {
+ private static final String TAG = ApiProvider.class.getCanonicalName();
private final MemorizingTrustManager mMemorizingTrustManager;
private final SharedPreferences mPrefs;
private API mApi;
private Context context;
+
public ApiProvider(MemorizingTrustManager mtm, SharedPreferences sp, Context context) {
this.mMemorizingTrustManager = mtm;
this.mPrefs = sp;
this.context = context;
- initApi();
+ initApi(new NextcloudAPI.ApiConnectedListener() {
+ @Override
+ public void onConnected() {
+
+ }
+
+ @Override
+ public void onError(Exception ex) {
+
+ }
+ });
}
- public void initApi() {
+ public void initApi(@NonNull NextcloudAPI.ApiConnectedListener apiConnectedListener) {
String username = mPrefs.getString(SettingsActivity.EDT_USERNAME_STRING, "");
String password = mPrefs.getString(SettingsActivity.EDT_PASSWORD_STRING, "");
String baseUrlStr = mPrefs.getString(SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING, "https://luhmer.de"); // We need to provide some sort of default URL here..
+ Boolean useSSO = mPrefs.getBoolean(SettingsActivity.SW_USE_SINGLE_SIGN_ON, false);
HttpUrl baseUrl = HttpUrl.parse(baseUrlStr).newBuilder()
.addPathSegments("index.php/apps/news/api/v1-2/")
.build();
-
Log.d("ApiModule", "HttpUrl: " + baseUrl.toString());
+ OkHttpClient client = OkHttpSSLClient.GetSslClient(baseUrl, username, password, mPrefs, mMemorizingTrustManager);
+ initImageLoader(mPrefs, client, context);
- Type feedList = new TypeToken<List<Feed>>() {}.getType();
- Type folderList = new TypeToken<List<Folder>>() {}.getType();
- Type rssItemsList = new TypeToken<List<RssItem>>() {}.getType();
-
- // Info: RssItems are handled as a stream (to be more memory efficient - see @OwnCloudSyncService and @RssItemObservable)
- Gson gson = new GsonBuilder()
- .setLenient()
- .registerTypeAdapter(folderList, new NextcloudDeserializer<>(Types.FOLDERS.toString(), Folder.class))
- .registerTypeAdapter(feedList, new NextcloudDeserializer<>(Types.FEEDS.toString(), Feed.class))
- .registerTypeAdapter(rssItemsList, new NextcloudDeserializer<>(Types.ITEMS.toString(), RssItem.class))
- .registerTypeAdapter(UserInfo.class, new JsonDeserializer<UserInfo>() {
- @Override
- public UserInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
- try {
- JsonObject jObj = json.getAsJsonObject();
- JsonElement avatar = jObj.get("avatar");
- byte[] decodedString = {};
- if (!avatar.isJsonNull()) {
- decodedString = Base64.decode(avatar.getAsJsonObject().get("data").getAsString(), Base64.DEFAULT);
- }
- return new UserInfo.Builder()
- .setDisplayName(jObj.get("displayName").getAsString())
- .setUserId(jObj.get("userId").getAsString())
- .setAvatar(BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length))
- .setLastLoginTimestamp(jObj.get("lastLoginTimestamp").getAsLong())
- .build();
- } catch(IllegalStateException ex) {
- throw OkHttpSSLClient.HandleExceptions(ex);
- }
- }
- })
- .create();
+ if(useSSO) {
+ Account account = AccountImporter.GetCurrentAccount(context);
+ initSsoApi(account, apiConnectedListener);
+ } else {
+ initRetrofitApi(baseUrl, client);
+ apiConnectedListener.onConnected();
+ }
+ }
- OkHttpClient client = OkHttpSSLClient.GetSslClient(baseUrl, username, password, mPrefs, mMemorizingTrustManager);
+ private void initRetrofitApi(HttpUrl baseUrl, OkHttpClient client) {
Retrofit retrofit = new Retrofit.Builder()
- .addConverterFactory(GsonConverterFactory.create(gson))
+ .addConverterFactory(GsonConverterFactory.create(GsonConfig.GetGson()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(baseUrl)
.client(client)
.build();
- initImageLoader(mPrefs, client, context);
-
mApi = retrofit.create(API.class);
}
+ private void initSsoApi(final Account account, final NextcloudAPI.ApiConnectedListener callback) {
+ SingleSignOnAccount ssoAccount = AccountImporter.GetAuthTokenInSeparateThread(context, account);
+ NextcloudAPI nextcloudAPI = new NextcloudAPI(ssoAccount, GsonConfig.GetGson());
+ nextcloudAPI.start(context, callback);
+ mApi = new API_SSO(nextcloudAPI);
+ }
+
+
+
private void initImageLoader(SharedPreferences mPrefs, OkHttpClient okHttpClient, Context context) {
int diskCacheSize = Integer.parseInt(mPrefs.getString(SettingsActivity.SP_MAX_CACHE_SIZE,"500"))*1024*1024;
if(ImageLoader.getInstance().isInited()) {
diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/GsonConfig.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/GsonConfig.java
new file mode 100644
index 00000000..12e533bd
--- /dev/null
+++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/GsonConfig.java
@@ -0,0 +1,67 @@
+package de.luhmer.owncloudnewsreader.helper;
+
+import android.graphics.BitmapFactory;
+import android.util.Base64;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.reflect.TypeToken;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+import de.luhmer.owncloudnewsreader.database.model.Feed;
+import de.luhmer.owncloudnewsreader.database.model.Folder;
+import de.luhmer.owncloudnewsreader.database.model.RssItem;
+import de.luhmer.owncloudnewsreader.model.UserInfo;
+import de.luhmer.owncloudnewsreader.reader.nextcloud.NextcloudDeserializer;
+import de.luhmer.owncloudnewsreader.reader.nextcloud.Types;
+import de.luhmer.owncloudnewsreader.ssl.OkHttpSSLClient;
+
+/**
+ * Created by david on 27.06.17.
+ */
+
+public class GsonConfig {
+
+ public static Gson GetGson() {
+ Type feedList = new TypeToken<List<Feed>>() {}.getType();
+ Type folderList = new TypeToken<List<Folder>>() {}.getType();
+ Type rssItemsList = new TypeToken<List<RssItem>>() {}.getType();
+
+ // Info: RssItems are handled as a stream (to be more memory efficient - see @OwnCloudSyncService and @RssItemObservable)
+ return new GsonBuilder()
+ .setLenient()
+ .registerTypeAdapter(folderList, new NextcloudDeserializer<>(Types.FOLDERS.toString(), Folder.class))
+ .registerTypeAdapter(feedList, new NextcloudDeserializer<>(Types.FEEDS.toString(), Feed.class))
+ .registerTypeAdapter(rssItemsList, new NextcloudDeserializer<>(Types.ITEMS.toString(), RssItem.class))
+ .registerTypeAdapter(UserInfo.class, new JsonDeserializer<UserInfo>() {
+ @Override
+ public UserInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ try {
+ JsonObject jObj = json.getAsJsonObject();
+ JsonElement avatar = jObj.get("avatar");
+ byte[] decodedString = {};
+ if (!avatar.isJsonNull()) {
+ decodedString = Base64.decode(avatar.getAsJsonObject().get("data").getAsString(), Base64.DEFAULT);
+ }
+ return new UserInfo.Builder()
+ .setDisplayName(jObj.get("displayName").getAsString())
+ .setUserId(jObj.get("userId").getAsString())
+ .setAvatar(BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length))
+ .setLastLoginTimestamp(jObj.get("lastLoginTimestamp").getAsLong())
+ .build();
+ } catch(IllegalStateException ex) {
+ throw OkHttpSSLClient.HandleExceptions(ex);
+ }
+ }
+ })
+ .create();
+ }
+
+} \ No newline at end of file
diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/API_SSO.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/API_SSO.java
new file mode 100644
index 00000000..373fa275
--- /dev/null
+++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/API_SSO.java
@@ -0,0 +1,216 @@
+package de.luhmer.owncloudnewsreader.reader.nextcloud;
+
+import com.google.gson.reflect.TypeToken;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.luhmer.owncloud.accountimporter.helper.NextcloudAPI;
+import de.luhmer.owncloud.accountimporter.helper.NextcloudRequest;
+import de.luhmer.owncloudnewsreader.database.model.Feed;
+import de.luhmer.owncloudnewsreader.database.model.Folder;
+import de.luhmer.owncloudnewsreader.database.model.RssItem;
+import de.luhmer.owncloudnewsreader.helper.GsonConfig;
+import de.luhmer.owncloudnewsreader.model.NextcloudNewsVersion;
+import de.luhmer.owncloudnewsreader.model.NextcloudStatus;
+import de.luhmer.owncloudnewsreader.model.UserInfo;
+import io.reactivex.Completable;
+import io.reactivex.Observable;
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+
+
+public class API_SSO implements API {
+
+ private static final String mApiEndpoint = "/index.php/apps/news/api/v1-2/";
+ private NextcloudAPI nextcloudAPI;
+
+ public API_SSO(NextcloudAPI nextcloudAPI) {
+ this.nextcloudAPI = nextcloudAPI;
+ }
+
+ public NextcloudAPI getNextcloudAPI() {
+ return nextcloudAPI;
+ }
+
+
+
+
+ @Override
+ public Observable<UserInfo> user() {
+ final Type type = UserInfo.class;
+ NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("GET")
+ .setUrl(mApiEndpoint + "user")
+ .build();
+ return nextcloudAPI.performRequestObservable(type, request);
+ }
+
+ @Override
+ public Observable<NextcloudStatus> status() {
+ Type type = NextcloudStatus.class;
+ NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("GET")
+ .setUrl(mApiEndpoint + "status")
+ .build();
+ return nextcloudAPI.performRequestObservable(type, request);
+ }
+
+ @Override
+ public Observable<NextcloudNewsVersion> version() {
+ Type type = NextcloudNewsVersion.class;
+ NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("GET")
+ .setUrl(mApiEndpoint + "version")
+ .build();
+ return nextcloudAPI.performRequestObservable(type, request);
+ }
+
+ @Override
+ public Observable<List<Folder>> folders() {
+ Type type = new TypeToken<List<Folder>>() {}.getType();
+ NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("GET")
+ .setUrl(mApiEndpoint + "folders")
+ .build();
+ return nextcloudAPI.performRequestObservable(type, request);
+ }
+
+ @Override
+ public Observable<List<Feed>> feeds() {
+ Type type = new TypeToken<List<Feed>>() {}.getType();
+ NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("GET")
+ .setUrl(mApiEndpoint + "feeds")
+ .build();
+
+ return nextcloudAPI.performRequestObservable(type, request);
+ }
+
+ @Override
+ public Call<List<Folder>> createFolder(Map<String, Object> folderMap) {
+ String body = GsonConfig.GetGson().toJson(folderMap);
+ NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("POST")
+ .setUrl(mApiEndpoint + "folders")
+ .setRequestBody(body)
+ .build();
+ return API_SSO_Helper.WrapInCall(nextcloudAPI, request, Folder.class);
+ }
+
+
+ @Override
+ public Call<List<Feed>> createFeed(Map<String, Object> feedMap) {
+ Type feedListType = new TypeToken<List<Feed>>() {}.getType();
+ String body = GsonConfig.GetGson().toJson(feedMap);
+ NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("POST")
+ .setUrl(mApiEndpoint + "feeds")
+ .setRequestBody(body)
+ .build();
+ return API_SSO_Helper.WrapInCall(nextcloudAPI, request, feedListType);
+ }
+
+ @Override
+ public Completable renameFeed(long feedId, Map<String, String> feedTitleMap) {
+ String body = GsonConfig.GetGson().toJson(feedTitleMap);
+ final NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("PUT")
+ .setUrl(mApiEndpoint + "feeds/" + feedId + "/rename")
+ .setRequestBody(body)
+ .build();
+ return API_SSO_Helper.WrapInCompletable(nextcloudAPI, request);
+ }
+
+ @Override
+ public Completable deleteFeed(long feedId) {
+ final NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("DELETE")
+ .setUrl(mApiEndpoint + "feeds/" + feedId)
+ .build();
+ return API_SSO_Helper.WrapInCompletable(nextcloudAPI, request);
+ }
+
+
+ @Override
+ public Call<List<RssItem>> items(long batchSize, long offset, int type, long id, boolean getRead, boolean oldestFirst) {
+ HashMap<String, String> parameters = new HashMap<>();
+ parameters.put("batchSize", String.valueOf(batchSize));
+ parameters.put("offset", String.valueOf(offset));
+ parameters.put("type", String.valueOf(type));
+ parameters.put("id", String.valueOf(id));
+ parameters.put("getRead", String.valueOf(getRead));
+ parameters.put("oldestFirst", String.valueOf(oldestFirst));
+
+ Type resType = new TypeToken<List<RssItem>>() {}.getType();
+ NextcloudRequest request = new NextcloudRequest.Builder()
+ .setParameter(parameters)
+ .setMethod("GET")
+ .setUrl(mApiEndpoint + "items")
+ .build();
+
+ return API_SSO_Helper.WrapInCall(nextcloudAPI, request, resType);
+ }
+
+ @Override
+ public Observable<ResponseBody> updatedItems(long lastModified, int type, long id) {
+ HashMap<String, String> parameters = new HashMap<>();
+ parameters.put("lastModified", String.valueOf(lastModified));
+ parameters.put("type", String.valueOf(type));
+ parameters.put("id", String.valueOf(id));
+
+ final NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("GET")
+ .setUrl(mApiEndpoint + "items/updated")
+ .setParameter(parameters)
+ .build();
+ return Observable.just(API_SSO_Helper.getResponseBodyFromRequest(nextcloudAPI, request));
+ }
+
+ // https://github.com/owncloud/news/wiki/Items-1.2#mark-multiple-items-as-read
+ @Override
+ public Call<Void> markItemsRead(ItemIds items) {
+ String body = GsonConfig.GetGson().toJson(items);
+ return markItems("items/read/multiple", body);
+ }
+
+ // https://github.com/owncloud/news/wiki/Items-1.2#mark-multiple-items-as-read
+ @Override
+ public Call<Void> markItemsUnread(ItemIds items) {
+ String body = GsonConfig.GetGson().toJson(items);
+ return markItems("items/unread/multiple", body);
+ }
+
+ // https://github.com/owncloud/news/wiki/Items-1.2#mark-multiple-items-as-read
+ @Override
+ public Call<Void> markItemsStarred(ItemMap itemMap) {
+ String body = GsonConfig.GetGson().toJson(itemMap);
+ return markItems("items/star/multiple", body);
+ }
+
+ // https://github.com/owncloud/news/wiki/Items-1.2#mark-multiple-items-as-read
+ @Override
+ public Call<Void> markItemsUnstarred(ItemMap itemMap) {
+ String body = GsonConfig.GetGson().toJson(itemMap);
+ return markItems("items/unstar/multiple", body);
+ }
+
+
+ private Call<Void> markItems(String endpoint, String body) {
+ NextcloudRequest request = new NextcloudRequest.Builder()
+ .setMethod("PUT")
+ .setUrl(mApiEndpoint + endpoint)
+ .setRequestBody(body)
+ .build();
+ try {
+ nextcloudAPI.performRequest(Void.class, request);
+ return API_SSO_Helper.WrapVoidCall(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return API_SSO_Helper.WrapVoidCall(false);
+ }
+}
+
diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/API_SSO_Helper.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/API_SSO_Helper.java
new file mode 100644
index 00000000..db3a2fbf
--- /dev/null
+++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/API_SSO_Helper.java
@@ -0,0 +1,138 @@
+package de.luhmer.owncloudnewsreader.reader.nextcloud;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Type;
+
+import de.luhmer.owncloud.accountimporter.helper.NextcloudAPI;
+import de.luhmer.owncloud.accountimporter.helper.NextcloudRequest;
+import io.reactivex.Completable;
+import io.reactivex.functions.Action;
+import okhttp3.Request;
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class API_SSO_Helper {
+
+ public static ResponseBody getResponseBodyFromRequest(NextcloudAPI nextcloudAPI, NextcloudRequest request) {
+ try {
+ InputStream os = nextcloudAPI.performNetworkRequest(request);
+ return ResponseBody.create(null, 0, new BufferedSourceSSO(os));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return ResponseBody.create(null, "");
+ }
+
+ public static Completable WrapInCompletable(final NextcloudAPI nextcloudAPI, final NextcloudRequest request) {
+ return Completable.fromAction(new Action() {
+ @Override
+ public void run() throws Exception {
+ nextcloudAPI.performRequest(Void.class, request);
+ }
+ });
+ }
+
+ public static <T> Call<T> WrapInCall(final NextcloudAPI nextcloudAPI, final NextcloudRequest nextcloudRequest, final Type resType) {
+ return new Call<T>() {
+ @Override
+ public Response<T> execute() throws IOException {
+ try {
+ T body = nextcloudAPI.performRequest(resType, nextcloudRequest);
+ return Response.success(body);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ public void enqueue(Callback<T> callback) {
+ try {
+ T body = nextcloudAPI.performRequest(resType, nextcloudRequest);
+ callback.onResponse(null, Response.success(body));
+ } catch (Exception e) {
+ callback.onResponse(null, Response.<T>error(520, ResponseBody.create(null, e.toString())));
+ }
+ }
+
+ @Override
+ public boolean isExecuted() {
+ return false;
+ }
+
+ @Override
+ public void cancel() {
+
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return false;
+ }
+
+ @Override
+ public Call<T> clone() {
+ return null;
+ }
+
+ @Override
+ public Request request() {
+ return null;
+ }
+ };
+ }
+
+ public static Call<Void> WrapVoidCall(final boolean success) {
+ return new Call<Void>() {
+ @Override
+ public Response<Void> execute() {
+ if(success) {
+ return Response.success(null);
+ } else {
+ return Response.error(520, null);
+ }
+ }
+
+ @Override
+ public void enqueue(Callback callback) {
+ if(success) {
+ callback.onResponse(null, Response.success(null));
+ } else {
+ callback.onResponse(null, Response.error(520, null));
+ }
+ }
+
+ @Override
+ public boolean isExecuted() {
+ return false;
+ }
+
+ @Override
+ public void cancel() {
+
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return false;
+ }
+
+ @Override
+ public Call<Void> clone() {
+ return null;
+ }
+
+ @Override
+ public Request request() {
+ return null;
+ }
+ };
+
+ }
+}
diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/BufferedSourceSSO.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/BufferedSourceSSO.java
new file mode 100644
index 00000000..4ab782bf
--- /dev/null
+++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/BufferedSourceSSO.java
@@ -0,0 +1,249 @@
+package de.luhmer.owncloudnewsreader.reader.nextcloud;
+
+import android.support.annotation.Nullable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+import okio.Buffer;
+import okio.BufferedSource;
+import okio.ByteString;
+import okio.Options;
+import okio.Sink;
+import okio.Timeout;
+
+public class BufferedSourceSSO implements BufferedSource {
+
+ private InputStream mInputStream;
+
+ public BufferedSourceSSO(InputStream inputStream) {
+ this.mInputStream = inputStream;
+ }
+
+ @Override
+ public Buffer buffer() {
+ return null;
+ }
+
+ @Override
+ public boolean exhausted() throws IOException {
+ return false;
+ }
+
+ @Override
+ public void require(long byteCount) throws IOException {
+
+ }
+
+ @Override
+ public boolean request(long byteCount) throws IOException {
+ return false;
+ }
+
+ @Override
+ public byte readByte() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public short readShort() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public short readShortLe() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public int readInt() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public int readIntLe() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long readLong() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long readLongLe() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long readDecimalLong() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long readHexadecimalUnsignedLong() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public void skip(long byteCount) throws IOException {
+
+ }
+
+ @Override
+ public ByteString readByteString() throws IOException {
+ return null;
+ }
+
+ @Override
+ public ByteString readByteString(long byteCount) throws IOException {
+ return null;
+ }
+
+ @Override
+ public int select(Options options) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public byte[] readByteArray() throws IOException {
+ return new byte[0];
+ }
+
+ @Override
+ public byte[] readByteArray(long byteCount) throws IOException {
+ return new byte[0];
+ }
+
+ @Override
+ public int read(byte[] sink) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public void readFully(byte[] sink) throws IOException {
+
+ }
+
+ @Override
+ public int read(byte[] sink, int offset, int byteCount) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public void readFully(Buffer sink, long byteCount) throws IOException {
+
+ }
+
+ @Override
+ public long readAll(Sink sink) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public String readUtf8() throws IOException {
+ return null;
+ }
+
+ @Override
+ public String readUtf8(long byteCount) throws IOException {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String readUtf8Line() throws IOException {
+ return null;
+ }
+
+ @Override
+ public String readUtf8LineStrict() throws IOException {
+ return null;
+ }
+
+ @Override
+ public String readUtf8LineStrict(long limit) throws IOException {
+ return null;
+ }
+
+ @Override
+ public int readUtf8CodePoint() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public String readString(Charset charset) throws IOException {
+ return null;
+ }
+
+ @Override
+ public String readString(long byteCount, Charset charset) throws IOException {
+ return null;
+ }
+
+ @Override
+ public long indexOf(byte b) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long indexOf(byte b, long fromIndex) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long indexOf(byte b, long fromIndex, long toIndex) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long indexOf(ByteString bytes) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long indexOf(ByteString bytes, long fromIndex) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long indexOfElement(ByteString targetBytes) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long indexOfElement(ByteString targetBytes, long fromIndex) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public boolean rangeEquals(long offset, ByteString bytes) throws IOException {
+ return false;
+ }
+
+ @Override
+ public boolean rangeEquals(long offset, ByteString bytes, int bytesOffset, int byteCount) throws IOException {
+ return false;
+ }
+
+ @Override
+ public InputStream inputStream() {
+ return mInputStream;
+ }
+
+ @Override
+ public long read(Buffer sink, long byteCount) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public Timeout timeout() {
+ return null;
+ }
+
+ @Override
+ public void close() throws IOException {
+
+ }
+}
diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/RssItemObservable.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/RssItemObservable.java
index 67602f8e..259ac15f 100644
--- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/RssItemObservable.java
+++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/RssItemObservable.java
@@ -51,7 +51,6 @@ public class RssItemObservable implements Publisher<Integer> {
@Override
public void subscribe(Subscriber<? super Integer> s) {
try {
- //throw new RuntimeException("");
sync(s);
s.onComplete();
} catch (Exception ex) {
diff --git a/News-Android-App/src/main/res/layout/dialog_signin.xml b/News-Android-App/src/main/res/layout/dialog_signin.xml
index f061a621..e3d66280 100644
--- a/News-Android-App/src/main/res/layout/dialog_signin.xml
+++ b/News-Android-App/src/main/res/layout/dialog_signin.xml
@@ -1,11 +1,10 @@
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- tools:context=".LoginActivity"
android:id="@+id/login_form"
android:layout_width="match_parent"
android:layout_height="match_parent">
+
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -13,11 +12,20 @@
android:paddingRight="@dimen/abc_dialog_padding_material"
android:paddingTop="@dimen/abc_dialog_padding_material">
+ <Switch
+ android:id="@+id/swSingleSignOn"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:text="Use single sign on" />
+
+
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:id="@+id/username_container">
+ android:id="@+id/username_container"
+ android:layout_marginTop="8dp"
+ android:layout_below="@id/swSingleSignOn">
<EditText
android:id="@+id/username"
@@ -34,7 +42,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/username_container"
- android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:id="@+id/password_container">
@@ -56,7 +63,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/password_container"
- android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<EditText
@@ -84,7 +90,6 @@
android:src="@drawable/ic_action_visibility"
android:layout_alignTop="@+id/password_container"
android:layout_alignBottom="@+id/password_container"
- android:layout_alignRight="@+id/password_container"
android:layout_alignEnd="@+id/password_container"
android:contentDescription="@string/content_desc_show_password"/>
@@ -93,9 +98,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pref_title_DisableHostnameVerification"
- android:layout_below="@+id/url_container"
- android:layout_alignRight="@+id/imgView_ShowPassword"
- android:layout_alignEnd="@+id/imgView_ShowPassword" />
+ android:layout_below="@+id/url_container" />
</RelativeLayout>