Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 73127c79 authored by Tobias Kaminsky's avatar Tobias Kaminsky Committed by GitHub
Browse files

Merge pull request #21 from nextcloud/dev

Refactoring / Update Readme
parents 29838236 0e8a2693
Loading
Loading
Loading
Loading
+134 −110
Original line number Diff line number Diff line
Nextcloud Single Sign On
=========================
# Nextcloud Single Sign On

This library allows you to use the network stack provided by the nextcloud app. Therefore you don't need to ask for the users credentials anymore as well as you don't need to worry about self-signed ssl certificates, two factor authentication, etc.
This library allows you to use accounts as well as the network stack provided by the [nextcloud files app](https://play.google.com/store/apps/details?id=com.nextcloud.client). Therefore you as a developer don't need to worry about asking the user for credentials as well as you don't need to worry about self-signed ssl certificates, two factor authentication, save credential storage etc.

*Please note that the user needs to install the [nextcloud files app](https://play.google.com/store/apps/details?id=com.nextcloud.client) in order to use those features.* While this might seem like a "no-go" for some developers, we still think that using this library is worth consideration as it makes the account handling much faster and safer.

How to use it?
--------------

>**IMPORTANT NOTE**: As this library is under heavy development right now you'll need to install the nextcloud files app manually on your device. Checkout the `sso` branch of the files app (`git clone -b sso https://github.com/nextcloud/android.git`) and install the app using Android Studio. We would love to get feedback!

## How to use this library

1) Add this library to your project

@@ -23,11 +25,12 @@ dependencies {
3) To choose an account, include the following code in your login dialog:

```java
final int CHOOSE_ACCOUNT = 12;

private void showAccountChooserLogin() {
    Intent intent = AccountManager.newChooseAccountIntent(null, null, new String[] {"nextcloud"}, true, null, null, null, null);
    startActivityForResult(intent, CHOOSE_ACCOUNT);
private void openAccountChooser() {
    try {
        AccountImporter.PickNewAccount(LoginDialogFragment.this);
    } catch (NextcloudFilesAppNotInstalledException e) {
        UiExceptionManager.ShowDialogForException(getActivity(), e);
    }
}

@Override
@@ -35,17 +38,21 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_OK) {
        if (requestCode == CHOOSE_ACCOUNT) {
        if (requestCode == CHOOSE_ACCOUNT_SSO) {
            String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
            Account account = AccountImporter.GetAccountForName(getActivity(), accountName);
            if(account != null) {
                AccountImporter.SetCurrentAccount(getActivity(), account);
                SingleAccountHelper.SetCurrentAccount(getActivity(), account);
            }
        }
    } else if (resultCode == RESULT_CANCELED) {
        if (requestCode == CHOOSE_ACCOUNT_SSO) {
            // Something went wrong..
        }
    }
}

// Hint: Checkout the LoginDialogFragment.java from the nextcloud-news app (sso-branch) to see a fully working example
// Complete example: https://github.com/nextcloud/news-android/blob/master/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/LoginDialogFragment.java
```

3) How to get account information?
@@ -62,7 +69,7 @@ SingleSignOnAccount ssoAccount = AccountImporter.GetAuthTokenInSeparateThread(ge

4) How to make a network request?

4.1.1) Retrofit:
    4.1.1) **Using Retrofit** (see below for instructions without retrofit)

    If you have an interface such as the following:

@@ -72,11 +79,11 @@ public interface API {
        @GET("user")
        Observable<UserInfo> user();

    @GET("status")
    Observable<NextcloudStatus> status();
        @POST("feeds")
        Call<List<Feed>> createFeed(@Body Map<String, Object> feedMap);

    @GET("version")
    Observable<NextcloudNewsVersion> version();
        @DELETE("feeds/{feedId}")
        Completable deleteFeed(@Path("feedId") long feedId);


    }
@@ -119,23 +126,24 @@ public class API_SSO implements API {
        }

        @Override
    public Observable<NextcloudStatus> status() {
        Type type = NextcloudStatus.class;
        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("GET")
                .setUrl(mApiEndpoint + "status")
                    .setMethod("POST")
                    .setUrl(mApiEndpoint + "feeds")
                    .setRequestBody(body)
                    .build();
        return nextcloudAPI.performRequestObservable(type, request);
            return Retrofit2Helper.WrapInCall(nextcloudAPI, request, feedListType);
        }

        @Override
    public Observable<NextcloudNewsVersion> version() {
        Type type = NextcloudNewsVersion.class;
        NextcloudRequest request = new NextcloudRequest.Builder()
                .setMethod("GET")
                .setUrl(mApiEndpoint + "version")
        public Completable deleteFeed(long feedId) {
            final NextcloudRequest request = new NextcloudRequest.Builder()
                    .setMethod("DELETE")
                    .setUrl(mApiEndpoint + "feeds/" + feedId)
                    .build();
        return nextcloudAPI.performRequestObservable(type, request);
            return ReactivexHelper.WrapInCompletable(nextcloudAPI, request);
        }


@@ -150,6 +158,7 @@ public class ApiProvider {
        private API mApi;

        public ApiProvider(NextcloudAPI.ApiConnectedListener callback) {
            Account account = SingleAccountHelper.GetCurrentAccount(context);
            SingleSignOnAccount ssoAccount =
                AccountImporter.GetAuthTokenInSeparateThread(context, account);
            NextcloudAPI nextcloudAPI = new NextcloudAPI(ssoAccount, GsonConfig.GetGson());
@@ -160,9 +169,9 @@ public class ApiProvider {
    ```
    Enjoy! If you're already using retrofit, you don't need to modify your application logic. Just exchange the API and you're good to go!

4.2) Without Retrofit
    4.2) **Without Retrofit**

NextcloudAPI provides a method called `performNetworkRequest(NextcloudRequest request)` that allows you to handle the server response yourself.
    `NextcloudAPI` provides a method called `performNetworkRequest(NextcloudRequest request)` that allows you to handle the server response yourself.

    ```java
    // create nextcloudAPI instance
@@ -189,7 +198,22 @@ private void downloadFile() {
    }
    ```

# Flow Diagram
## Video

![](https://user-images.githubusercontent.com/4489723/41563281-75cbc196-734f-11e8-8b22-7b906363e34a.gif)


## Examples

- [Nextcloud news app](https://github.com/nextcloud/news-android)
    - [API](https://github.com/nextcloud/news-android/blob/master/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/API_SSO.java)
    - [API-Provider (Dagger)](https://github.com/nextcloud/news-android/blob/master/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java#L98)
    - [Login Fragment](https://github.com/nextcloud/news-android/blob/master/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/LoginDialogFragment.java)




## Flow Diagram

Note that the "Make network request" section in the diagram only shows the workflow if you use the "retrofit" api.

+1 −1
Original line number Diff line number Diff line
package de.luhmer.owncloud.accountimporter.helper;
package com.nextcloud.android.sso.helper;

import android.support.annotation.Nullable;

+41 −0
Original line number Diff line number Diff line
package com.nextcloud.android.sso.interfaces;
package com.nextcloud.android.sso.helper;

import android.accounts.Account;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;

/**
 *  Nextcloud SingleSignOn
@@ -21,6 +23,19 @@ import android.accounts.Account;
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

public interface IAccountImport {
    void accountAccessGranted(Account account);
public class FilesAppNotInstalledHelper {

    public static void RequestInstallNextcloudFilesApp(Context context) {
        // Nextcloud app not installed
        Intent installIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.nextcloud.client"));

        // launch market(s)
        if (installIntent.resolveActivity(context.getPackageManager()) != null) {
            context.startActivity(installIntent);
        } else {
            // no f-droid market app or Play store installed --> launch browser for f-droid url
            Intent downloadIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://f-droid.org/repository/browse/?fdid=com.nextcloud.client"));
            context.startActivity(downloadIntent);
        }
    }
}
+4 −3
Original line number Diff line number Diff line
package de.luhmer.owncloud.accountimporter.helper;
package com.nextcloud.android.sso.helper;

import com.nextcloud.android.sso.aidl.NextcloudRequest;
import com.nextcloud.android.sso.api.NextcloudAPI;

import java.io.InputStream;

import de.luhmer.owncloud.accountimporter.aidl.NextcloudRequest;
import de.luhmer.owncloud.accountimporter.api.NextcloudAPI;
import okhttp3.ResponseBody;

/**
+4 −3
Original line number Diff line number Diff line
package de.luhmer.owncloud.accountimporter.helper;
package com.nextcloud.android.sso.helper;

import com.nextcloud.android.sso.aidl.NextcloudRequest;
import com.nextcloud.android.sso.api.NextcloudAPI;

import de.luhmer.owncloud.accountimporter.aidl.NextcloudRequest;
import de.luhmer.owncloud.accountimporter.api.NextcloudAPI;
import io.reactivex.Completable;
import io.reactivex.functions.Action;

Loading