diff --git a/sample/build.gradle b/sample/build.gradle index 564166f6..21f70cda 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -29,11 +29,15 @@ android { dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' + implementation project(path: ':lib') implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.1' - testImplementation 'junit:junit:4.+' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } \ No newline at end of file diff --git a/sample/src/main/java/com/nextcloud/android/sso/sample/MainActivity.java b/sample/src/main/java/com/nextcloud/android/sso/sample/MainActivity.java index 379102bd..b939861a 100644 --- a/sample/src/main/java/com/nextcloud/android/sso/sample/MainActivity.java +++ b/sample/src/main/java/com/nextcloud/android/sso/sample/MainActivity.java @@ -1,20 +1,36 @@ package com.nextcloud.android.sso.sample; +import android.annotation.SuppressLint; import android.content.Intent; import android.os.Bundle; import android.util.Log; +import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import com.google.gson.GsonBuilder; import com.nextcloud.android.sso.AccountImporter; +import com.nextcloud.android.sso.api.NextcloudAPI; import com.nextcloud.android.sso.exceptions.AccountImportCancelledException; import com.nextcloud.android.sso.exceptions.AndroidGetAccountsPermissionNotGranted; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotInstalledException; +import com.nextcloud.android.sso.helper.SingleAccountHelper; +import com.nextcloud.android.sso.model.SingleSignOnAccount; +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import retrofit2.NextcloudRetrofitApiBuilder; + +@SuppressLint("SetTextI18n") +@SuppressWarnings("ConstantConditions") public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); + private final ExecutorService executor = Executors.newSingleThreadExecutor(); @Override protected void onCreate(Bundle savedInstanceState) { @@ -22,6 +38,10 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); findViewById(R.id.chooseAccountBtn).setOnClickListener(v -> { try { + /* + * Prompt dialog to select existing or create a new account + * As soon as an account has been imported, we will continue in #onActivityResult() + */ AccountImporter.pickNewAccount(this); } catch (NextcloudFilesAppNotInstalledException | AndroidGetAccountsPermissionNotGranted e) { e.printStackTrace(); @@ -35,9 +55,48 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten try { AccountImporter.onActivityResult(requestCode, resultCode, data, this, ssoAccount -> { Log.i(TAG, "Imported account: " + ssoAccount.name); + SingleAccountHelper.setCurrentAccount(this, ssoAccount.name); + executor.submit(() -> { + + // Create local bridge API to the Nextcloud Files Android app + final var nextcloudAPI = createNextcloudAPI(ssoAccount); + + // Create the Ocs API to talk to the server + final var ocsAPI = new NextcloudRetrofitApiBuilder(nextcloudAPI, "ocs/v2.php/cloud/").create(OcsAPI.class); + + try { + // Perform actual requests + final var userName = ocsAPI.getUser(ssoAccount.userId).execute().body().ocs.data.displayName; + final var instanceName = ocsAPI.getCapabilities().execute().body().ocs.data.theming.name; + + ((TextView) findViewById(R.id.result)).setText(userName + " on " + instanceName); + } catch (IOException e) { + e.printStackTrace(); + } + + nextcloudAPI.stop(); + }); }); } catch (AccountImportCancelledException e) { Log.i(TAG, "Account import cancelled."); } } + + private NextcloudAPI createNextcloudAPI(@NonNull SingleSignOnAccount ssoAccount) { + return new NextcloudAPI(this, ssoAccount, new GsonBuilder().create(), new NextcloudAPI.ApiConnectedListener() { + @Override + public void onConnected() { + /* + * We don't have to wait for this callback because requests are queued and executed + * automatically as soon as the connection has been established. + */ + Log.i(TAG, "SSO API connected for " + ssoAccount); + } + + @Override + public void onError(Exception e) { + e.printStackTrace(); + } + }); + } } \ No newline at end of file diff --git a/sample/src/main/java/com/nextcloud/android/sso/sample/OcsAPI.java b/sample/src/main/java/com/nextcloud/android/sso/sample/OcsAPI.java new file mode 100644 index 00000000..865aad86 --- /dev/null +++ b/sample/src/main/java/com/nextcloud/android/sso/sample/OcsAPI.java @@ -0,0 +1,71 @@ +package com.nextcloud.android.sso.sample; + + +import com.google.gson.annotations.SerializedName; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; + + +/** + * @see Nextcloud REST API + */ +public interface OcsAPI { + + @GET("capabilities?format=json") + Call> getCapabilities(); + + @GET("users/{search}?format=json") + Call> getUser(@Path("search") String userId); + + /** + *

A generic wrapper for OpenCollaborationServices calls.

+ *

This is needed for API endpoints located at /ocs/.... It is usually not used for APIs of 3rd party server apps like Deck or Notes

+ * + * @param defines the payload of this {@link OcsResponse}. + */ + class OcsResponse { + public OcsWrapper ocs; + + public static class OcsWrapper { + public OcsMeta meta; + public T data; + } + + public static class OcsMeta { + public String status; + public int statuscode; + public String message; + } + } + + /* + * Extend the classes by the attributes you are actually using, for example:

+ *
    + *
  • version
  • + *
  • theming
  • + *
  • server_status
  • + *
  • deck
  • + *
  • + *
+ */ + class OcsCapabilities { + public Theming theming; + + static class Theming { + public String name; + } + } + + /** + * You can map the node names to other variable names using {@link SerializedName}. + * See Gson- and Retrofit-Documentation for all possibilities. + */ + class OcsUser { + @SerializedName("id") + public String userId; + @SerializedName("displayname") + public String displayName; + } +} diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index ea3c6afa..bcf6288d 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -12,8 +12,18 @@ android:layout_height="wrap_content" android:text="Choose account" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toRightOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + \ No newline at end of file