Loading src/main/java/com/nextcloud/android/sso/AccountImporter.java +53 −30 Original line number Diff line number Diff line Loading @@ -34,6 +34,10 @@ import android.os.Bundle; 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.AndroidGetAccountsPermissionNotGranted; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountPermissionNotGrantedException; Loading @@ -45,12 +49,9 @@ 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 static android.app.Activity.RESULT_CANCELED; Loading @@ -71,15 +72,19 @@ public class AccountImporter { private static SharedPreferences SHARED_PREFERENCES; private static final List<String> APPS = Arrays.asList(Constants.PACKAGE_NAME_PROD, Constants.PACKAGE_NAME_DEV); private static final String[] ACCOUNT_TYPES = {Constants.ACCOUNT_TYPE_PROD, Constants.ACCOUNT_TYPE_DEV}; public static boolean accountsToImportAvailable(Context context) { return findAccounts(context).size() > 0; } public static void pickNewAccount(Activity activity) throws NextcloudFilesAppNotInstalledException, AndroidGetAccountsPermissionNotGranted { public static void pickNewAccount(Activity activity) throws NextcloudFilesAppNotInstalledException, AndroidGetAccountsPermissionNotGranted { checkAndroidAccountPermissions(activity); if (appInstalledOrNot(activity, "com.nextcloud.client")) { Intent intent = AccountManager.newChooseAccountIntent(null, null, new String[]{"nextcloud"}, if (appInstalledOrNot(activity)) { Intent intent = AccountManager.newChooseAccountIntent(null, null, ACCOUNT_TYPES, true, null, null, null, null); activity.startActivityForResult(intent, CHOOSE_ACCOUNT_SSO); } else { Loading @@ -87,11 +92,12 @@ public class AccountImporter { } } public static void pickNewAccount(Fragment fragment) throws NextcloudFilesAppNotInstalledException, AndroidGetAccountsPermissionNotGranted { public static void pickNewAccount(Fragment fragment) throws NextcloudFilesAppNotInstalledException, AndroidGetAccountsPermissionNotGranted { checkAndroidAccountPermissions(fragment.getContext()); if (appInstalledOrNot(fragment.getContext(), "com.nextcloud.client")) { Intent intent = AccountManager.newChooseAccountIntent(null, null, new String[]{"nextcloud"}, if (appInstalledOrNot(fragment.getContext())) { Intent intent = AccountManager.newChooseAccountIntent(null, null, ACCOUNT_TYPES, true, null, null, null, null); fragment.startActivityForResult(intent, CHOOSE_ACCOUNT_SSO); } else { Loading @@ -100,13 +106,15 @@ public class AccountImporter { } public static void requestAndroidAccountPermissionsAndPickAccount(Activity activity) { ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.GET_ACCOUNTS}, REQUEST_GET_ACCOUNTS_PERMISSION); ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.GET_ACCOUNTS}, REQUEST_GET_ACCOUNTS_PERMISSION); } private static void checkAndroidAccountPermissions(Context context) throws AndroidGetAccountsPermissionNotGranted { // https://developer.android.com/reference/android/accounts/AccountManager#getAccountsByType(java.lang.String) // Caller targeting API level below Build.VERSION_CODES.O that have not been granted the Manifest.permission.GET_ACCOUNTS permission, // will only see those accounts managed by AbstractAccountAuthenticators whose signature matches the client. // Caller targeting API level below Build.VERSION_CODES.O that have not been granted the // Manifest.permission.GET_ACCOUNTS permission, will only see those accounts managed by // AbstractAccountAuthenticators whose signature matches the client. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { // Do something for lollipop and above versions if (ContextCompat.checkSelfPermission(context, Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED) { Loading @@ -118,28 +126,34 @@ public class AccountImporter { } } private static boolean appInstalledOrNot(Context context, String uri) { private static boolean appInstalledOrNot(Context context) { boolean returnValue = false; PackageManager pm = context.getPackageManager(); for (String app : APPS) { try { pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES); return true; pm.getPackageInfo(app, PackageManager.GET_ACTIVITIES); returnValue = true; break; } catch (PackageManager.NameNotFoundException e) { Log.v(TAG, e.getMessage()); } return false; } return returnValue; } // Find all currently installed nextcloud accounts on the phone public static List<Account> findAccounts(final Context context) { final AccountManager accMgr = AccountManager.get(context); final Account[] accounts = accMgr.getAccountsByType("nextcloud"); final Account[] accounts = accMgr.getAccounts(); List<Account> accountsAvailable = new ArrayList<>(); for (final Account account : accounts) { if ("nextcloud".equals(account.type)) { for (String accountType : ACCOUNT_TYPES) { if (accountType.equals(account.type)) { accountsAvailable.add(account); } } } return accountsAvailable; } Loading Loading @@ -188,10 +202,11 @@ public class AccountImporter { } String token = future.getString(Constants.SSO_TOKEN); String serverUrl = future.getString(Constants.SSO_SERVER_URL); String type = future.getString("accountType"); SharedPreferences mPrefs = getSharedPreferences(context); String prefKey = getPrefKeyForAccount(accountName); SingleSignOnAccount ssoAccount = new SingleSignOnAccount(accountName, userId, token, serverUrl); SingleSignOnAccount ssoAccount = new SingleSignOnAccount(accountName, userId, token, serverUrl, type); try { mPrefs.edit().putString(prefKey, SingleSignOnAccount.toString(ssoAccount)).apply(); } catch (IOException e) { Loading Loading @@ -351,8 +366,16 @@ public class AccountImporter { if(account == null) { throw new NextcloudFilesAppAccountPermissionNotGrantedException(); } String componentName; if (account.type.equalsIgnoreCase(Constants.ACCOUNT_TYPE_DEV)) { componentName = Constants.PACKAGE_NAME_DEV; } else { componentName = Constants.PACKAGE_NAME_PROD; } Intent authIntent = new Intent(); authIntent.setComponent(new ComponentName("com.nextcloud.client", authIntent.setComponent(new ComponentName(componentName, "com.owncloud.android.ui.activity.SsoGrantPermissionActivity")); authIntent.putExtra(NEXTCLOUD_FILES_ACCOUNT, account); return authIntent; Loading src/main/java/com/nextcloud/android/sso/Constants.java +7 −0 Original line number Diff line number Diff line Loading @@ -38,4 +38,11 @@ public class Constants { public static final String EXCEPTION_INVALID_REQUEST_URL = "CE_4"; public static final String EXCEPTION_HTTP_REQUEST_FAILED = "CE_5"; public static final String EXCEPTION_ACCOUNT_ACCESS_DECLINED = "CE_6"; // package related constans public static final String PACKAGE_NAME_PROD = "com.nextcloud.client"; public static final String PACKAGE_NAME_DEV = "com.nextcloud.android.beta"; public static final String ACCOUNT_TYPE_PROD = "nextcloud"; public static final String ACCOUNT_TYPE_DEV = "nextcloud.beta"; } src/main/java/com/nextcloud/android/sso/api/AidlNetworkRequest.java +10 −4 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; import com.nextcloud.android.sso.Constants; import com.nextcloud.android.sso.aidl.IInputStreamService; import com.nextcloud.android.sso.aidl.IThreadListener; import com.nextcloud.android.sso.aidl.NextcloudRequest; Loading Loading @@ -67,17 +68,22 @@ public class AidlNetworkRequest extends NetworkRequest { } }; public void connect() { super.connect(); public void connect(String type) { super.connect(type); // Disconnect if connected if (mBound.get()) { stop(); } String componentName = Constants.PACKAGE_NAME_PROD; if (type.equals(Constants.ACCOUNT_TYPE_DEV)) { componentName = Constants.PACKAGE_NAME_DEV; } try { Intent intentService = new Intent(); intentService.setComponent(new ComponentName("com.nextcloud.client", intentService.setComponent(new ComponentName(componentName, "com.owncloud.android.services.AccountManagerService")); if (!mContext.bindService(intentService, mConnection, Context.BIND_AUTO_CREATE)) { Log.d(TAG, "Binding to AccountManagerService returned false"); Loading src/main/java/com/nextcloud/android/sso/api/NetworkRequest.java +4 −7 Original line number Diff line number Diff line Loading @@ -27,8 +27,8 @@ public abstract class NetworkRequest { } protected void connect() { Log.v(TAG, "Nextcloud Single sign-on connect() called [" + Thread.currentThread().getName() + "]"); protected void connect(String type) { Log.v(TAG, "Nextcloud Single sign-on connect() called [" + Thread.currentThread().getName() + "] Account-Type: [" + type + "]"); if (mDestroyed) { throw new IllegalStateException("API already destroyed! You cannot reuse a stopped API instance"); } Loading @@ -37,11 +37,8 @@ public abstract class NetworkRequest { protected abstract InputStream performNetworkRequest(NextcloudRequest request, InputStream requestBodyInputStream) throws Exception; protected void connectApiWithBackoff() { new ExponentialBackoff(1000, 10000, 2, 5, Looper.getMainLooper(), new Runnable() { @Override public void run() { connect(); } new ExponentialBackoff(1000, 10000, 2, 5, Looper.getMainLooper(), () -> { connect(mAccount.type); }).start(); } Loading src/main/java/com/nextcloud/android/sso/model/SingleSignOnAccount.java +3 −1 Original line number Diff line number Diff line Loading @@ -37,12 +37,14 @@ public class SingleSignOnAccount implements Serializable { public String userId; public String token; public String url; public String type; public SingleSignOnAccount(String name, String userId, String token, String url) { public SingleSignOnAccount(String name, String userId, String token, String url, String type) { this.name = name; this.userId = userId; this.token = token; this.url = url; this.type = type; } /** Read the object from Base64 string. */ Loading Loading
src/main/java/com/nextcloud/android/sso/AccountImporter.java +53 −30 Original line number Diff line number Diff line Loading @@ -34,6 +34,10 @@ import android.os.Bundle; 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.AndroidGetAccountsPermissionNotGranted; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountPermissionNotGrantedException; Loading @@ -45,12 +49,9 @@ 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 static android.app.Activity.RESULT_CANCELED; Loading @@ -71,15 +72,19 @@ public class AccountImporter { private static SharedPreferences SHARED_PREFERENCES; private static final List<String> APPS = Arrays.asList(Constants.PACKAGE_NAME_PROD, Constants.PACKAGE_NAME_DEV); private static final String[] ACCOUNT_TYPES = {Constants.ACCOUNT_TYPE_PROD, Constants.ACCOUNT_TYPE_DEV}; public static boolean accountsToImportAvailable(Context context) { return findAccounts(context).size() > 0; } public static void pickNewAccount(Activity activity) throws NextcloudFilesAppNotInstalledException, AndroidGetAccountsPermissionNotGranted { public static void pickNewAccount(Activity activity) throws NextcloudFilesAppNotInstalledException, AndroidGetAccountsPermissionNotGranted { checkAndroidAccountPermissions(activity); if (appInstalledOrNot(activity, "com.nextcloud.client")) { Intent intent = AccountManager.newChooseAccountIntent(null, null, new String[]{"nextcloud"}, if (appInstalledOrNot(activity)) { Intent intent = AccountManager.newChooseAccountIntent(null, null, ACCOUNT_TYPES, true, null, null, null, null); activity.startActivityForResult(intent, CHOOSE_ACCOUNT_SSO); } else { Loading @@ -87,11 +92,12 @@ public class AccountImporter { } } public static void pickNewAccount(Fragment fragment) throws NextcloudFilesAppNotInstalledException, AndroidGetAccountsPermissionNotGranted { public static void pickNewAccount(Fragment fragment) throws NextcloudFilesAppNotInstalledException, AndroidGetAccountsPermissionNotGranted { checkAndroidAccountPermissions(fragment.getContext()); if (appInstalledOrNot(fragment.getContext(), "com.nextcloud.client")) { Intent intent = AccountManager.newChooseAccountIntent(null, null, new String[]{"nextcloud"}, if (appInstalledOrNot(fragment.getContext())) { Intent intent = AccountManager.newChooseAccountIntent(null, null, ACCOUNT_TYPES, true, null, null, null, null); fragment.startActivityForResult(intent, CHOOSE_ACCOUNT_SSO); } else { Loading @@ -100,13 +106,15 @@ public class AccountImporter { } public static void requestAndroidAccountPermissionsAndPickAccount(Activity activity) { ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.GET_ACCOUNTS}, REQUEST_GET_ACCOUNTS_PERMISSION); ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.GET_ACCOUNTS}, REQUEST_GET_ACCOUNTS_PERMISSION); } private static void checkAndroidAccountPermissions(Context context) throws AndroidGetAccountsPermissionNotGranted { // https://developer.android.com/reference/android/accounts/AccountManager#getAccountsByType(java.lang.String) // Caller targeting API level below Build.VERSION_CODES.O that have not been granted the Manifest.permission.GET_ACCOUNTS permission, // will only see those accounts managed by AbstractAccountAuthenticators whose signature matches the client. // Caller targeting API level below Build.VERSION_CODES.O that have not been granted the // Manifest.permission.GET_ACCOUNTS permission, will only see those accounts managed by // AbstractAccountAuthenticators whose signature matches the client. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { // Do something for lollipop and above versions if (ContextCompat.checkSelfPermission(context, Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED) { Loading @@ -118,28 +126,34 @@ public class AccountImporter { } } private static boolean appInstalledOrNot(Context context, String uri) { private static boolean appInstalledOrNot(Context context) { boolean returnValue = false; PackageManager pm = context.getPackageManager(); for (String app : APPS) { try { pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES); return true; pm.getPackageInfo(app, PackageManager.GET_ACTIVITIES); returnValue = true; break; } catch (PackageManager.NameNotFoundException e) { Log.v(TAG, e.getMessage()); } return false; } return returnValue; } // Find all currently installed nextcloud accounts on the phone public static List<Account> findAccounts(final Context context) { final AccountManager accMgr = AccountManager.get(context); final Account[] accounts = accMgr.getAccountsByType("nextcloud"); final Account[] accounts = accMgr.getAccounts(); List<Account> accountsAvailable = new ArrayList<>(); for (final Account account : accounts) { if ("nextcloud".equals(account.type)) { for (String accountType : ACCOUNT_TYPES) { if (accountType.equals(account.type)) { accountsAvailable.add(account); } } } return accountsAvailable; } Loading Loading @@ -188,10 +202,11 @@ public class AccountImporter { } String token = future.getString(Constants.SSO_TOKEN); String serverUrl = future.getString(Constants.SSO_SERVER_URL); String type = future.getString("accountType"); SharedPreferences mPrefs = getSharedPreferences(context); String prefKey = getPrefKeyForAccount(accountName); SingleSignOnAccount ssoAccount = new SingleSignOnAccount(accountName, userId, token, serverUrl); SingleSignOnAccount ssoAccount = new SingleSignOnAccount(accountName, userId, token, serverUrl, type); try { mPrefs.edit().putString(prefKey, SingleSignOnAccount.toString(ssoAccount)).apply(); } catch (IOException e) { Loading Loading @@ -351,8 +366,16 @@ public class AccountImporter { if(account == null) { throw new NextcloudFilesAppAccountPermissionNotGrantedException(); } String componentName; if (account.type.equalsIgnoreCase(Constants.ACCOUNT_TYPE_DEV)) { componentName = Constants.PACKAGE_NAME_DEV; } else { componentName = Constants.PACKAGE_NAME_PROD; } Intent authIntent = new Intent(); authIntent.setComponent(new ComponentName("com.nextcloud.client", authIntent.setComponent(new ComponentName(componentName, "com.owncloud.android.ui.activity.SsoGrantPermissionActivity")); authIntent.putExtra(NEXTCLOUD_FILES_ACCOUNT, account); return authIntent; Loading
src/main/java/com/nextcloud/android/sso/Constants.java +7 −0 Original line number Diff line number Diff line Loading @@ -38,4 +38,11 @@ public class Constants { public static final String EXCEPTION_INVALID_REQUEST_URL = "CE_4"; public static final String EXCEPTION_HTTP_REQUEST_FAILED = "CE_5"; public static final String EXCEPTION_ACCOUNT_ACCESS_DECLINED = "CE_6"; // package related constans public static final String PACKAGE_NAME_PROD = "com.nextcloud.client"; public static final String PACKAGE_NAME_DEV = "com.nextcloud.android.beta"; public static final String ACCOUNT_TYPE_PROD = "nextcloud"; public static final String ACCOUNT_TYPE_DEV = "nextcloud.beta"; }
src/main/java/com/nextcloud/android/sso/api/AidlNetworkRequest.java +10 −4 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; import com.nextcloud.android.sso.Constants; import com.nextcloud.android.sso.aidl.IInputStreamService; import com.nextcloud.android.sso.aidl.IThreadListener; import com.nextcloud.android.sso.aidl.NextcloudRequest; Loading Loading @@ -67,17 +68,22 @@ public class AidlNetworkRequest extends NetworkRequest { } }; public void connect() { super.connect(); public void connect(String type) { super.connect(type); // Disconnect if connected if (mBound.get()) { stop(); } String componentName = Constants.PACKAGE_NAME_PROD; if (type.equals(Constants.ACCOUNT_TYPE_DEV)) { componentName = Constants.PACKAGE_NAME_DEV; } try { Intent intentService = new Intent(); intentService.setComponent(new ComponentName("com.nextcloud.client", intentService.setComponent(new ComponentName(componentName, "com.owncloud.android.services.AccountManagerService")); if (!mContext.bindService(intentService, mConnection, Context.BIND_AUTO_CREATE)) { Log.d(TAG, "Binding to AccountManagerService returned false"); Loading
src/main/java/com/nextcloud/android/sso/api/NetworkRequest.java +4 −7 Original line number Diff line number Diff line Loading @@ -27,8 +27,8 @@ public abstract class NetworkRequest { } protected void connect() { Log.v(TAG, "Nextcloud Single sign-on connect() called [" + Thread.currentThread().getName() + "]"); protected void connect(String type) { Log.v(TAG, "Nextcloud Single sign-on connect() called [" + Thread.currentThread().getName() + "] Account-Type: [" + type + "]"); if (mDestroyed) { throw new IllegalStateException("API already destroyed! You cannot reuse a stopped API instance"); } Loading @@ -37,11 +37,8 @@ public abstract class NetworkRequest { protected abstract InputStream performNetworkRequest(NextcloudRequest request, InputStream requestBodyInputStream) throws Exception; protected void connectApiWithBackoff() { new ExponentialBackoff(1000, 10000, 2, 5, Looper.getMainLooper(), new Runnable() { @Override public void run() { connect(); } new ExponentialBackoff(1000, 10000, 2, 5, Looper.getMainLooper(), () -> { connect(mAccount.type); }).start(); } Loading
src/main/java/com/nextcloud/android/sso/model/SingleSignOnAccount.java +3 −1 Original line number Diff line number Diff line Loading @@ -37,12 +37,14 @@ public class SingleSignOnAccount implements Serializable { public String userId; public String token; public String url; public String type; public SingleSignOnAccount(String name, String userId, String token, String url) { public SingleSignOnAccount(String name, String userId, String token, String url, String type) { this.name = name; this.userId = userId; this.token = token; this.url = url; this.type = type; } /** Read the object from Base64 string. */ Loading