Commit fdf52003 authored by Bartek Fabiszewski's avatar Bartek Fabiszewski
Browse files

Refactor settings to use permission helper

parent 34dde14d
Loading
Loading
Loading
Loading
+0 −10
Original line number Diff line number Diff line
@@ -104,16 +104,6 @@ class LocationHelper {
        return permissionHelper.hasForegroundLocationPermission();
    }

    /**
     * Check if user granted permission to access background location.
     *
     * @return True if permission granted, false otherwise
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    boolean canAccessBackgroundLocation() {
        return permissionHelper.hasBackgroundLocationPermission();
    }

    /**
     * Check if given provider exists on device
     * @param provider Provider
+69 −182
Original line number Diff line number Diff line
@@ -9,7 +9,6 @@

package net.fabiszewski.ulogger;

import static androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions;
import static net.fabiszewski.ulogger.SettingsActivity.KEY_ALLOW_EXTERNAL;
import static net.fabiszewski.ulogger.SettingsActivity.KEY_AUTO_NAME;
import static net.fabiszewski.ulogger.SettingsActivity.KEY_AUTO_START;
@@ -19,20 +18,15 @@ import static net.fabiszewski.ulogger.SettingsActivity.KEY_PASS;
import static net.fabiszewski.ulogger.SettingsActivity.KEY_PROVIDER;
import static net.fabiszewski.ulogger.SettingsActivity.KEY_USERNAME;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import androidx.annotation.Nullable;
import androidx.preference.EditTextPreference;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
@@ -40,17 +34,17 @@ import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import androidx.preference.TwoStatePreference;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;

@SuppressWarnings("WeakerAccess")
public class SettingsFragment extends PreferenceFragmentCompat {
public class SettingsFragment extends PreferenceFragmentCompat implements PermissionHelper.PermissionRequester {

    private static final String TAG = SettingsFragment.class.getSimpleName();

    final PermissionHelper permissionHelper;

    public SettingsFragment() {
        permissionHelper = new PermissionHelper(this, this);
    }

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.preferences, rootKey);
@@ -89,56 +83,9 @@ public class SettingsFragment extends PreferenceFragmentCompat {
        final Preference prefUsername = findPreference(KEY_USERNAME);
        final Preference prefPass = findPreference(KEY_PASS);
        final Preference prefHost = findPreference(KEY_HOST);
        // on change listeners
        if (prefLiveSync != null) {
            prefLiveSync.setOnPreferenceChangeListener(liveSyncChanged);
        }
        if (prefUsername != null) {
            prefUsername.setOnPreferenceChangeListener(serverSetupChanged);
        }
        if (prefPass != null) {
            prefPass.setOnPreferenceChangeListener(serverSetupChanged);
        }
        if (prefHost != null) {
            prefHost.setOnPreferenceChangeListener(serverSetupChanged);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            final Preference prefAutoStart = findPreference(KEY_AUTO_START);
            final Preference prefAllowExternal = findPreference(KEY_ALLOW_EXTERNAL);
            if (prefAutoStart != null) {
                prefAutoStart.setOnPreferenceChangeListener(permissionLevelChanged);
            }
            if (prefAllowExternal != null) {
                prefAllowExternal.setOnPreferenceChangeListener(permissionLevelChanged);
            }
        }
        // on click listeners
        if (prefUsername != null) {
            prefUsername.setOnPreferenceClickListener(serverSetupClicked);
        }
        if (prefHost != null) {
            prefHost.setOnPreferenceClickListener(serverSetupClicked);
        }
    }

    /**
     * On change listener to validate whether live synchronization is allowed
     */
    private final Preference.OnPreferenceChangeListener liveSyncChanged = (preference, newValue) -> {
        final Context context = preference.getContext();
        if (Boolean.parseBoolean(newValue.toString())) {
            if (!isValidServerSetup(context)) {
                Toast.makeText(context, R.string.provide_user_pass_url, Toast.LENGTH_LONG).show();
                return false;
            }
        }
        return true;
    };

    /**
     * On change listener to destroy session cookies if server setup has changed
     */
    private final Preference.OnPreferenceChangeListener serverSetupChanged = (preference, newValue) -> {
        // On change listener to destroy session cookies if server setup has changed
        Preference.OnPreferenceChangeListener serverSetupChanged = (preference, newValue) -> {
            // update web helper settings, remove session cookies
            WebHelper.updatePreferences(preference.getContext());
            // disable live synchronization if any server preference is removed
@@ -148,26 +95,20 @@ public class SettingsFragment extends PreferenceFragmentCompat {
            return true;
        };

    /**
     * On change listener to check permission for background location
     */
    @RequiresApi(api = Build.VERSION_CODES.R)
    private final Preference.OnPreferenceChangeListener permissionLevelChanged = (preference, newValue) -> {
        // On change listener to validate whether live synchronization is allowed
        Preference.OnPreferenceChangeListener liveSyncChanged = (preference, newValue) -> {
            final Context context = preference.getContext();
            if (Boolean.parseBoolean(newValue.toString())) {
            LocationHelper locationHelper = LocationHelper.getInstance(context);
            if (!locationHelper.canAccessBackgroundLocation()) {
                requestBackgroundLocationPermission(context, preference.getKey());
                if (!isValidServerSetup(context)) {
                    Toast.makeText(context, R.string.provide_user_pass_url, Toast.LENGTH_LONG).show();
                    return false;
                }
            }
            return true;
        };

    /**
     * On click listener to warn if server setup has changed
     */
    private final Preference.OnPreferenceClickListener serverSetupClicked = preference -> {
        // On click listener to warn if server setup has changed
        Preference.OnPreferenceClickListener serverSetupClicked = preference -> {
            final Context context = preference.getContext();
            DbAccess db = DbAccess.getInstance();
            db.open(context);
@@ -182,6 +123,50 @@ public class SettingsFragment extends PreferenceFragmentCompat {
            return true;
        };

        // on change listeners
        if (prefLiveSync != null) {
            prefLiveSync.setOnPreferenceChangeListener(liveSyncChanged);
        }
        if (prefUsername != null) {
            prefUsername.setOnPreferenceChangeListener(serverSetupChanged);
        }
        if (prefPass != null) {
            prefPass.setOnPreferenceChangeListener(serverSetupChanged);
        }
        if (prefHost != null) {
            prefHost.setOnPreferenceChangeListener(serverSetupChanged);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // On change listener to check permission for background location
            Preference.OnPreferenceChangeListener permissionLevelChanged = (preference, newValue) -> {
//                final Context context = preference.getContext();
                if (Boolean.parseBoolean(newValue.toString())) {
                    if (!permissionHelper.hasBackgroundLocationPermission()) {
                        permissionHelper.requestBackgroundLocationPermission(preference.getKey());
//                        requestBackgroundLocationPermission(context, preference.getKey());
                        return false;
                    }
                }
                return true;
            };
            final Preference prefAutoStart = findPreference(KEY_AUTO_START);
            final Preference prefAllowExternal = findPreference(KEY_ALLOW_EXTERNAL);
            if (prefAutoStart != null) {
                prefAutoStart.setOnPreferenceChangeListener(permissionLevelChanged);
            }
            if (prefAllowExternal != null) {
                prefAllowExternal.setOnPreferenceChangeListener(permissionLevelChanged);
            }
        }
        // on click listeners
        if (prefUsername != null) {
            prefUsername.setOnPreferenceClickListener(serverSetupClicked);
        }
        if (prefHost != null) {
            prefHost.setOnPreferenceClickListener(serverSetupClicked);
        }
    }

    /**
     * Disable live sync preference, reset checkbox
     * @param context Context
@@ -223,116 +208,18 @@ public class SettingsFragment extends PreferenceFragmentCompat {
                && (pass != null && !pass.isEmpty()));
    }

    /**
     * Request permission when given preference key changed
     * @param context Context
     * @param key Key
     */
    @RequiresApi(api = Build.VERSION_CODES.R)
    private void requestBackgroundLocationPermission(@NonNull Context context, @NonNull String key) {
        List<String> permissions = new ArrayList<>();
        // Background location permission can only be granted when forward location is permitted
        LocationHelper locationHelper = LocationHelper.getInstance(context);
        if (locationHelper.canAccessLocation()) {
            permissions.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
        } else {
            if (Logger.DEBUG) { Log.d(TAG, "[forward location permission denied]"); }
            permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                // On Android 12+ coarse location permission must be also requested
                permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
            }
        }

        if (permissions.contains(Manifest.permission.ACCESS_BACKGROUND_LOCATION) &&
                ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(), Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
            final CharSequence label = getBackgroundPermissionOptionLabel(context);
            Alert.showConfirm(
                    context,
                    getString(R.string.background_location_required),
                    getString(R.string.background_location_rationale, label),
                    (dialog, which) -> {
                        dialog.dismiss();
                        setPreferenceOnLocationPermissionGranted(key, new String[]{ Manifest.permission.ACCESS_BACKGROUND_LOCATION });
                    }
            );
        } else {
            setPreferenceOnLocationPermissionGranted(key, permissions.toArray(new String[0]));
        }

    }

    @RequiresApi(api = Build.VERSION_CODES.R)
    void setPreferenceOnLocationPermissionGranted(@NonNull String key, @NonNull String[] permissions) {
        if (Logger.DEBUG) { Log.d(TAG, "[request permission " + Arrays.toString(permissions) + "]"); }
        if (key.equals(KEY_ALLOW_EXTERNAL)) {
            setAllowExternalOnLocationPermissionGranted.launch(permissions);
        } else if (key.equals(KEY_AUTO_START)) {
            setAutoStartOnLocationPermissionGranted.launch(permissions);
        }
    }

    /**
     * Wrapper for getBackgroundPermissionOptionLabel() method
     * Will return translated label only when context string was also translated
     * @param context Context
     * @return Localized label
     */
    @SuppressLint("AppBundleLocaleChanges")
    @RequiresApi(api = Build.VERSION_CODES.R)
    private CharSequence getBackgroundPermissionOptionLabel(Context context) {
        CharSequence label = context.getPackageManager().getBackgroundPermissionOptionLabel();
        CharSequence defaultLabel = "Allow all the time";

        if (Locale.getDefault().getLanguage().equals("en")) {
            return label.length() > 0 ? label : defaultLabel;
        }

        CharSequence translated = context.getString(R.string.background_location_rationale);
        Configuration config = new Configuration(context.getResources().getConfiguration());
        config.setLocale(Locale.ENGLISH);
        CharSequence defaultText = context.createConfigurationContext(config).getText(R.string.background_location_rationale);

        return translated.equals(defaultText) ? defaultLabel : label;
    }

    @RequiresApi(api = Build.VERSION_CODES.R)
    final ActivityResultLauncher<String[]> setAllowExternalOnLocationPermissionGranted = getResultLauncher(KEY_ALLOW_EXTERNAL);

    @RequiresApi(api = Build.VERSION_CODES.R)
    final ActivityResultLauncher<String[]> setAutoStartOnLocationPermissionGranted = getResultLauncher(KEY_AUTO_START);
    @Override
    public void onPermissionGranted(@Nullable String requestCode) {
        if (Logger.DEBUG) { Log.d(TAG, "[onPermissionGranted: " + requestCode + "]"); }

    /**
     * Get ActivityResultLauncher for requesting location permission profiled for given preference key
     * @param key Preference key
     * @return ActivityResultLauncher
     */
    @RequiresApi(api = Build.VERSION_CODES.R)
    private ActivityResultLauncher<String[]> getResultLauncher(@NonNull String key) {
        return registerForActivityResult(new RequestMultiplePermissions(), results -> {
            if (Logger.DEBUG) { Log.d(TAG, "[RequestMultiplePermissions: " + key + ", permissions: " + results.entrySet() + "]"); }
            boolean isGranted = false;
            for (Map.Entry<String, Boolean> result : results.entrySet()) {
                if (result.getValue()) {
                    isGranted = true;
                    break;
        Context context = getContext();
        if (context != null && requestCode != null) {
            setBooleanPreference(context, requestCode, true);
        }
    }
            if (isGranted) {
                if (Logger.DEBUG) { Log.d(TAG, "[RequestPermission: granted]"); }

                Context context = SettingsFragment.this.getContext();
                if (context != null) {
                    if (results.containsKey(Manifest.permission.ACCESS_FINE_LOCATION)) {
                        SettingsFragment.this.requestBackgroundLocationPermission(context, key);
                    } else if (results.containsKey(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
                        SettingsFragment.this.setBooleanPreference(context, key, true);
                    }
                }
            } else {
                if (Logger.DEBUG) { Log.d(TAG, "[RequestPermission: refused]"); }
            }
        });
    @Override
    public void onPermissionDenied(@Nullable String requestCode) {
        if (Logger.DEBUG) { Log.d(TAG, "[onPermissionGranted: " + requestCode + "]"); }
    }

}