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

Unverified Commit 58ae5acc authored by Tobias Kaminsky's avatar Tobias Kaminsky Committed by GitHub
Browse files

Merge pull request #415 from nextcloud/26-current-account-livedata

#26 Implement getCurrentSingleSignOnAccount() livecycle aware
parents 3bb6efb5 e8485704
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
            // As this library supports multiple accounts we created some helper methods if you only want to use one.
            // The following line stores the selected account as the "default" account which can be queried by using
            // the SingleAccountHelper.getCurrentSingleSignOnAccount(context) method
            SingleAccountHelper.setCurrentAccount(context, account.name);
            SingleAccountHelper.commitCurrentAccount(context, account.name);

            // Get the "default" account
            SingleSignOnAccount ssoAccount = null;
@@ -108,6 +108,9 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
// If you stored the "default" account using setCurrentAccount(…) you can get the account by using the following line:
final var ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context);

// It is also possible to get the "default" account as a LiveData object:
final var ssoAccount$ = SingleAccountHelper.getCurrentSingleSignOnAccount$(context);

// Otherwise (for multi-account support you'll have to keep track of the account names yourself. Note: this has to be the name of SingleSignOnAccount.name)
AccountImporter.getSingleSignOnAccount(context, accountName);

+36 −7
Original line number Diff line number Diff line
@@ -24,7 +24,10 @@ import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;

import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.LiveData;

import com.nextcloud.android.sso.AccountImporter;
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
@@ -40,6 +43,7 @@ public final class SingleAccountHelper {
    private SingleAccountHelper() {
    }

    @WorkerThread
    private static String getCurrentAccountName(Context context) throws NoCurrentAccountSelectedException {
        SharedPreferences mPrefs = AccountImporter.getSharedPreferences(context);
        String accountName = mPrefs.getString(PREF_CURRENT_ACCOUNT_STRING, null);
@@ -49,24 +53,41 @@ public final class SingleAccountHelper {
        return accountName;
    }

    @WorkerThread
    public static SingleSignOnAccount getCurrentSingleSignOnAccount(Context context)
            throws NextcloudFilesAppAccountNotFoundException, NoCurrentAccountSelectedException {
        return AccountImporter.getSingleSignOnAccount(context, getCurrentAccountName(context));
    }

    /**
     * Warning: This call is writing synchronously to the disk.
     * You should use {@link #setCurrentAccountAsync(Context, String)} if possible.
     * Emits the currently set {@link SingleSignOnAccount} or <code>null</code> if an error occurred.
     */
    @SuppressLint("ApplySharedPref")
    public static LiveData<SingleSignOnAccount> getCurrentSingleSignOnAccount$(@NonNull Context context) {
        return new SingleSignOnAccountLiveData(context, AccountImporter.getSharedPreferences(context), PREF_CURRENT_ACCOUNT_STRING);
    }

    /**
     * @deprecated Replaced by {@link #commitCurrentAccount(Context, String)}
     */
    @Deprecated(forRemoval = true)
    public static void setCurrentAccount(Context context, String accountName) {
        commitCurrentAccount(context, accountName);
    }

    /**
     * Warning: This call is <em>synchronous</em>.
     * Consider using {@link #applyCurrentAccount(Context, String)} if possible.
     */
    @SuppressLint("ApplySharedPref")
    @WorkerThread
    public static void commitCurrentAccount(Context context, String accountName) {
        AccountImporter.getSharedPreferences(context)
                .edit()
                .putString(PREF_CURRENT_ACCOUNT_STRING, accountName)
                .commit();
    }

    public static void setCurrentAccountAsync(Context context, String accountName) {
    public static void applyCurrentAccount(Context context, String accountName) {
        AccountImporter.getSharedPreferences(context)
                .edit()
                .putString(PREF_CURRENT_ACCOUNT_STRING, accountName)
@@ -81,12 +102,20 @@ public final class SingleAccountHelper {
        AccountImporter.authenticateSingleSignAccount(activity, getCurrentSingleSignOnAccount(activity));
    }

    /**
     * @deprecated Use {@link #getCurrentSingleSignOnAccount$(Context)} which is lifecycle aware
     */
    @Deprecated(forRemoval = true)
    public static void registerSharedPreferenceChangeListener(Context context, 
                                                              SharedPreferences.OnSharedPreferenceChangeListener listener) {
        AccountImporter.getSharedPreferences(context)
                .registerOnSharedPreferenceChangeListener(listener);
    }

    /**
     * @deprecated Use {@link #getCurrentSingleSignOnAccount$(Context)} which is lifecycle aware
     */
    @Deprecated(forRemoval = true)
    public static void unregisterSharedPreferenceChangeListener(Context context,
                                                                SharedPreferences.OnSharedPreferenceChangeListener listener) {
        AccountImporter.getSharedPreferences(context)
+87 −0
Original line number Diff line number Diff line
/*
 * Nextcloud SingleSignOn
 *
 * @author Stefan Niedermann
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.nextcloud.android.sso.helper;

import android.content.Context;
import android.content.SharedPreferences;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LiveData;

import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException;
import com.nextcloud.android.sso.model.SingleSignOnAccount;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleSignOnAccountLiveData extends LiveData<SingleSignOnAccount> {

    private final Context context;
    private final SharedPreferences sharedPrefs;
    private final SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
    private final ExecutorService executor;

    SingleSignOnAccountLiveData(@NonNull Context context,
                                @NonNull SharedPreferences sharedPrefs,
                                @NonNull String key) {
        this(context, sharedPrefs, key, Executors.newSingleThreadExecutor());
    }

    @VisibleForTesting
    SingleSignOnAccountLiveData(@NonNull Context context,
                                @NonNull SharedPreferences sharedPrefs,
                                @NonNull String key,
                                @NonNull ExecutorService executor) {
        this.context = context;
        this.sharedPrefs = sharedPrefs;
        this.executor = executor;
        this.preferenceChangeListener = (changedPrefs, changedKey) -> {
            if (key.equals(changedKey)) {
                postValueFromPreferences();
            }
        };
    }

    @Override
    protected void onActive() {
        super.onActive();
        postValueFromPreferences();
        sharedPrefs.registerOnSharedPreferenceChangeListener(preferenceChangeListener);
    }

    @Override
    protected void onInactive() {
        sharedPrefs.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener);
        super.onInactive();
    }

    private void postValueFromPreferences() {
        executor.submit(() -> {
            try {
                postValue(SingleAccountHelper.getCurrentSingleSignOnAccount(context));
            } catch (NoCurrentAccountSelectedException | NextcloudFilesAppAccountNotFoundException e) {
                postValue(null);
                e.printStackTrace();
            }
        });
    }
}
+18 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ 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 java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -61,6 +62,17 @@ public class MainActivity extends AppCompatActivity {
                e.printStackTrace();
            }
        });

        /*
         * We can also observe the current SingleSignOnAccount (set via SingleAccountHelper) with LiveData
         */
        SingleAccountHelper.getCurrentSingleSignOnAccount$(this).observe(this, ssoAccount -> {
            if (ssoAccount == null) {
                Log.i(TAG, "Currently no SingleSignOnAccount selected.");
            } else {
                Log.i(TAG, "New SingleSignOnAccount set: " + ssoAccount.name);
            }
        });
    }

    @Override
@@ -71,6 +83,12 @@ public class MainActivity extends AppCompatActivity {
            AccountImporter.onActivityResult(requestCode, resultCode, data, this, ssoAccount -> {
                Log.i(TAG, "Imported account: " + ssoAccount.name);

                /*
                 * A little helper to store the currently selected account.
                 * We can query this later if we want to keep working with it.
                 */
                SingleAccountHelper.commitCurrentAccount(this, ssoAccount.name);

                /* Network requests need to be performed on a background thread */
                executor.submit(() -> {
                    runOnUiThread(() -> ((TextView) findViewById(R.id.result)).setText(R.string.loading));