Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 15 additions & 20 deletions lib/src/main/java/com/nextcloud/android/sso/AccountImporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@
*/
package com.nextcloud.android.sso;

import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
import static com.nextcloud.android.sso.Constants.NEXTCLOUD_FILES_ACCOUNT;
import static com.nextcloud.android.sso.Constants.NEXTCLOUD_SSO;
import static com.nextcloud.android.sso.Constants.NEXTCLOUD_SSO_EXCEPTION;
import static com.nextcloud.android.sso.Constants.SSO_SHARED_PREFERENCE;

import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
Expand All @@ -31,10 +24,6 @@
import android.util.Log;
import android.widget.Toast;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;

import com.nextcloud.android.sso.exceptions.AccountImportCancelledException;
import com.nextcloud.android.sso.exceptions.AndroidGetAccountsPermissionNotGranted;
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
Expand All @@ -43,18 +32,26 @@
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotSupportedException;
import com.nextcloud.android.sso.exceptions.SSOException;
import com.nextcloud.android.sso.exceptions.UnknownErrorException;
import com.nextcloud.android.sso.model.FilesAppType;
import com.nextcloud.android.sso.model.SingleSignOnAccount;
import com.nextcloud.android.sso.ui.UiExceptionManager;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import io.reactivex.annotations.NonNull;
import io.reactivex.annotations.Nullable;

import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
import static com.nextcloud.android.sso.Constants.NEXTCLOUD_FILES_ACCOUNT;
import static com.nextcloud.android.sso.Constants.NEXTCLOUD_SSO;
import static com.nextcloud.android.sso.Constants.NEXTCLOUD_SSO_EXCEPTION;
import static com.nextcloud.android.sso.Constants.SSO_SHARED_PREFERENCE;

public class AccountImporter {

private static final String TAG = AccountImporter.class.getCanonicalName();
Expand All @@ -67,8 +64,6 @@ public class AccountImporter {

private static SharedPreferences SHARED_PREFERENCES;

private static final String[] ACCOUNT_TYPES = Arrays.stream(FilesAppType.values()).map(a -> a.accountType).toArray(String[]::new);

public static boolean accountsToImportAvailable(Context context) {
return findAccounts(context).size() > 0;
}
Expand All @@ -78,7 +73,7 @@ public static void pickNewAccount(Activity activity) throws NextcloudFilesAppNot
checkAndroidAccountPermissions(activity);

if (appInstalledOrNot(activity)) {
Intent intent = AccountManager.newChooseAccountIntent(null, null, ACCOUNT_TYPES,
Intent intent = AccountManager.newChooseAccountIntent(null, null, FilesAppTypeRegistry.getInstance().getAccountTypes(),
true, null, AUTH_TOKEN_SSO, null, null);
activity.startActivityForResult(intent, CHOOSE_ACCOUNT_SSO);
} else {
Expand All @@ -91,7 +86,7 @@ public static void pickNewAccount(Fragment fragment) throws NextcloudFilesAppNot
checkAndroidAccountPermissions(fragment.getContext());

if (appInstalledOrNot(fragment.requireContext())) {
Intent intent = AccountManager.newChooseAccountIntent(null, null, ACCOUNT_TYPES,
Intent intent = AccountManager.newChooseAccountIntent(null, null, FilesAppTypeRegistry.getInstance().getAccountTypes(),
true, null, AUTH_TOKEN_SSO, null, null);
fragment.startActivityForResult(intent, CHOOSE_ACCOUNT_SSO);
} else {
Expand Down Expand Up @@ -123,7 +118,7 @@ private static void checkAndroidAccountPermissions(Context context) throws Andro
private static boolean appInstalledOrNot(Context context) {
boolean returnValue = false;
PackageManager pm = context.getPackageManager();
for (final var appType : FilesAppType.values()) {
for (final var appType : FilesAppTypeRegistry.getInstance().getTypes()) {
try {
pm.getPackageInfo(appType.packageId, PackageManager.GET_ACTIVITIES);
returnValue = true;
Expand All @@ -142,7 +137,7 @@ public static List<Account> findAccounts(final Context context) {

List<Account> accountsAvailable = new ArrayList<>();
for (final Account account : accounts) {
for (String accountType : ACCOUNT_TYPES) {
for (String accountType : FilesAppTypeRegistry.getInstance().getAccountTypes()) {
if (accountType.equals(account.type)) {
accountsAvailable.add(account);
}
Expand Down Expand Up @@ -370,7 +365,7 @@ private static Intent buildRequestAuthTokenIntent(Context context, Intent intent
throw new NextcloudFilesAppAccountPermissionNotGrantedException(context);
}

String componentName = FilesAppType.findByAccountType(account.type).packageId;
String componentName = FilesAppTypeRegistry.getInstance().findByAccountType(account.type).packageId;

Intent authIntent = new Intent();
authIntent.setComponent(new ComponentName(componentName,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Nextcloud Android SingleSignOn Library
*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.android.sso;

import com.nextcloud.android.sso.model.FilesAppType;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class FilesAppTypeRegistry {
private static final FilesAppTypeRegistry FILES_APP_TYPE_REGISTRY = new FilesAppTypeRegistry();
private final Set<FilesAppType> types = new HashSet<>();

public FilesAppTypeRegistry() {
types.add(new FilesAppType("com.nextcloud.client", "nextcloud", FilesAppType.Type.PROD));
types.add(new FilesAppType("com.nextcloud.android.qa", "nextcloud.qa", FilesAppType.Type.QA));
types.add(new FilesAppType("com.nextcloud.android.beta", "nextcloud.beta", FilesAppType.Type.DEV));
}

public static FilesAppTypeRegistry getInstance() {
return FILES_APP_TYPE_REGISTRY;
}

public synchronized void init(FilesAppType type) {
types.clear();

if (type.type != FilesAppType.Type.PROD) {
throw new IllegalArgumentException("If only one FilesAppType added, this must be PROD!");
}

types.add(type);
}

public synchronized void init(List<FilesAppType> types) {
this.types.clear();

Optional<FilesAppType> prod = types.stream().filter(t -> t.type == FilesAppType.Type.PROD).findFirst();
if (prod.isEmpty()) {
throw new IllegalArgumentException("One provided FilesAppType must be PROD!");
}

this.types.addAll(types);
}

public Set<FilesAppType> getTypes() {
return types;
}

public String[] getAccountTypes() {
return types.stream().map(a -> a.accountType).toArray(String[]::new);
}


/**
* @return {@link FilesAppType.Type#PROD}, {@link FilesAppType.Type#QA}
* or {@link FilesAppType.Type#DEV} depending on {@param accountType}.
* Uses {@link FilesAppType.Type#PROD} as fallback.
*/
@NonNull
public FilesAppType findByAccountType(@Nullable String accountType) {
for (final var type : types) {
if (type.accountType.equalsIgnoreCase(accountType)) {
return type;
}
}
return types.stream().filter(t -> t.type == FilesAppType.Type.PROD).findFirst().get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
*/
package com.nextcloud.android.sso.api;

import static com.nextcloud.android.sso.aidl.ParcelFileDescriptorUtil.pipeFrom;
import static com.nextcloud.android.sso.exceptions.SSOException.parseNextcloudCustomException;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
Expand All @@ -23,15 +20,12 @@
import android.os.RemoteException;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import com.nextcloud.android.sso.FilesAppTypeRegistry;
import com.nextcloud.android.sso.aidl.IInputStreamService;
import com.nextcloud.android.sso.aidl.NextcloudRequest;
import com.nextcloud.android.sso.exceptions.NextcloudApiNotRespondingException;
import com.nextcloud.android.sso.model.FilesAppType;
import com.nextcloud.android.sso.model.SingleSignOnAccount;

import java.io.ByteArrayInputStream;
Expand All @@ -44,6 +38,12 @@
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import static com.nextcloud.android.sso.aidl.ParcelFileDescriptorUtil.pipeFrom;
import static com.nextcloud.android.sso.exceptions.SSOException.parseNextcloudCustomException;

public class AidlNetworkRequest extends NetworkRequest {
private static final String TAG = AidlNetworkRequest.class.getCanonicalName();

Expand Down Expand Up @@ -91,7 +91,7 @@ public void connect(String type) {
Log.d(TAG, "[connect] Binding to AccountManagerService for type [" + type + "]");
super.connect(type);

final String componentName = FilesAppType.findByAccountType(type).packageId;
final String componentName = FilesAppTypeRegistry.getInstance().findByAccountType(type).packageId;

Log.d(TAG, "[connect] Component name is: [" + componentName + "]");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
import android.content.pm.PackageManager;
import android.util.Log;

import androidx.annotation.NonNull;

import com.nextcloud.android.sso.FilesAppTypeRegistry;
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotInstalledException;
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotSupportedException;
import com.nextcloud.android.sso.model.FilesAppType;
import com.nextcloud.android.sso.ui.UiExceptionManager;

import java.util.Optional;

import androidx.annotation.NonNull;

public final class VersionCheckHelper {

private static final String TAG = VersionCheckHelper.class.getCanonicalName();
Expand All @@ -38,12 +41,22 @@ public static boolean verifyMinVersion(@NonNull Context context, int minVersion,

// Stable Files App is not installed at all. Therefore we need to run the test on the dev app
try {
final int verCode = getNextcloudFilesVersionCode(context, FilesAppType.DEV);
// The dev app follows a different versioning schema.. therefore we can't do our normal checks
Optional<FilesAppType> dev = FilesAppTypeRegistry
.getInstance()
.getTypes()
.stream()
.filter(t -> t.type == FilesAppType.Type.DEV)
.findFirst();
if (dev.isPresent()) {
final int verCode = getNextcloudFilesVersionCode(context, dev.get());
// The dev app follows a different versioning schema.. therefore we can't do our normal checks

// However beta users are probably always up to date so we will just ignore it for now
Log.d(TAG, "Dev files app version is: " + verCode);
return true;
// However beta users are probably always up to date so we will just ignore it for now
Log.d(TAG, "Dev files app version is: " + verCode);
return true;
} else {
UiExceptionManager.showDialogForException(context, new NextcloudFilesAppNotInstalledException(context));
}
} catch (PackageManager.NameNotFoundException ex) {
Log.e(TAG, "PackageManager.NameNotFoundException (dev files app not found): " + e.getMessage());
UiExceptionManager.showDialogForException(context, new NextcloudFilesAppNotInstalledException(context));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,19 @@
package com.nextcloud.android.sso.model;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public enum FilesAppType {

PROD("com.nextcloud.client", "nextcloud"),
QA("com.nextcloud.android.qa", "nextcloud.qa"),
DEV("com.nextcloud.android.beta", "nextcloud.beta");

public class FilesAppType {
public final String packageId;
public final String accountType;
public final Type type;

FilesAppType(@NonNull String packageId, @NonNull String accountType) {
public FilesAppType(@NonNull String packageId, @NonNull String accountType, Type type) {
this.packageId = packageId;
this.accountType = accountType;
this.type = type;
}

/**
* @return {@link #PROD}, {@link #QA} or {@link #DEV} depending on {@param accountType}.
* Uses {@link #PROD} as fallback.
*/
@NonNull
public static FilesAppType findByAccountType(@Nullable String accountType) {
for (final var appType : FilesAppType.values()) {
if (appType.accountType.equalsIgnoreCase(accountType)) {
return appType;
}
}
return PROD;
public enum Type {
PROD, QA, DEV
}
}