Move connections settings into own screen

This commit is contained in:
Torsten Grote
2021-01-22 13:07:48 -03:00
parent ec910cb80f
commit 6df1e0fd77
13 changed files with 636 additions and 272 deletions

View File

@@ -36,6 +36,7 @@ import org.briarproject.briar.android.attachment.media.MediaModule;
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
import org.briarproject.briar.android.logging.CachingLogHandler;
import org.briarproject.briar.android.login.SignInReminderReceiver;
import org.briarproject.briar.android.settings.ConnectionsFragment;
import org.briarproject.briar.android.view.EmojiTextInputView;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.DozeWatchdog;
@@ -193,4 +194,6 @@ public interface AndroidComponent
void inject(EmojiTextInputView textInputView);
void inject(BriarModelLoader briarModelLoader);
void inject(ConnectionsFragment connectionsFragment);
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.StrictMode;
@@ -115,6 +116,11 @@ public class AppModule {
this.application = application;
}
public static AndroidComponent getAndroidComponent(Context ctx) {
BriarApplication app = (BriarApplication) ctx.getApplicationContext();
return app.getApplicationComponent();
}
@Provides
@Singleton
Application providesApplication() {

View File

@@ -0,0 +1,118 @@
package org.briarproject.briar.android.settings;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.ListPreference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreference;
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
import static org.briarproject.briar.android.settings.SettingsActivity.enableAndPersist;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ConnectionsFragment extends PreferenceFragmentCompat {
static final String PREF_KEY_BLUETOOTH = "pref_key_bluetooth";
static final String PREF_KEY_WIFI = "pref_key_wifi";
static final String PREF_KEY_TOR_ENABLE = "pref_key_tor_enable";
static final String PREF_KEY_TOR_NETWORK = "pref_key_tor_network";
static final String PREF_KEY_TOR_MOBILE_DATA =
"pref_key_tor_mobile_data";
static final String PREF_KEY_TOR_ONLY_WHEN_CHARGING =
"pref_key_tor_only_when_charging";
@Inject
ViewModelProvider.Factory viewModelFactory;
private SettingsViewModel viewModel;
private ConnectionsManager connectionsManager;
private SwitchPreference enableBluetooth;
private SwitchPreference enableWifi;
private SwitchPreference enableTor;
private ListPreference torNetwork;
private SwitchPreference torMobile;
private SwitchPreference torOnlyWhenCharging;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
getAndroidComponent(context).inject(this);
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
.get(SettingsViewModel.class);
connectionsManager = viewModel.getConnectionsManager();
}
@Override
public void onCreatePreferences(Bundle bundle, String s) {
addPreferencesFromResource(R.xml.settings_connections);
enableBluetooth = findPreference(PREF_KEY_BLUETOOTH);
enableWifi = findPreference(PREF_KEY_WIFI);
enableTor = findPreference(PREF_KEY_TOR_ENABLE);
torNetwork = findPreference(PREF_KEY_TOR_NETWORK);
torMobile = findPreference(PREF_KEY_TOR_MOBILE_DATA);
torOnlyWhenCharging = findPreference(PREF_KEY_TOR_ONLY_WHEN_CHARGING);
torNetwork.setSummaryProvider(viewModel.torSummaryProvider);
enableBluetooth.setPreferenceDataStore(connectionsManager.btStore);
enableWifi.setPreferenceDataStore(connectionsManager.wifiStore);
enableTor.setPreferenceDataStore(connectionsManager.torStore);
torNetwork.setPreferenceDataStore(connectionsManager.torStore);
torMobile.setPreferenceDataStore(connectionsManager.torStore);
torOnlyWhenCharging.setPreferenceDataStore(connectionsManager.torStore);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// persist changes after setting initial value and enabling
LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
connectionsManager.btEnabled().observe(lifecycleOwner, enabled -> {
enableBluetooth.setChecked(enabled);
enableAndPersist(enableBluetooth);
});
connectionsManager.wifiEnabled().observe(lifecycleOwner, enabled -> {
enableWifi.setChecked(enabled);
enableAndPersist(enableWifi);
});
connectionsManager.torEnabled().observe(lifecycleOwner, enabled -> {
enableTor.setChecked(enabled);
enableAndPersist(enableTor);
});
connectionsManager.torNetwork().observe(lifecycleOwner, value -> {
torNetwork.setValue(value);
enableAndPersist(torNetwork);
});
connectionsManager.torMobile().observe(lifecycleOwner, enabled -> {
torMobile.setChecked(enabled);
enableAndPersist(torMobile);
});
connectionsManager.torCharging().observe(lifecycleOwner, enabled -> {
torOnlyWhenCharging.setChecked(enabled);
enableAndPersist(torOnlyWhenCharging);
});
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(R.string.network_settings_title);
}
}

View File

@@ -0,0 +1,114 @@
package org.briarproject.briar.android.settings;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import java.util.concurrent.Executor;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.briar.android.settings.SettingsViewModel.BT_NAMESPACE;
import static org.briarproject.briar.android.settings.SettingsViewModel.TOR_NAMESPACE;
import static org.briarproject.briar.android.settings.SettingsViewModel.WIFI_NAMESPACE;
class ConnectionsManager {
final ConnectionsStore btStore;
final ConnectionsStore wifiStore;
final ConnectionsStore torStore;
private final MutableLiveData<Boolean> btEnabled = new MutableLiveData<>();
private final MutableLiveData<Boolean> wifiEnabled =
new MutableLiveData<>();
private final MutableLiveData<Boolean> torEnabled = new MutableLiveData<>();
private final MutableLiveData<String> torNetwork = new MutableLiveData<>();
private final MutableLiveData<Boolean> torMobile = new MutableLiveData<>();
private final MutableLiveData<Boolean> torCharging =
new MutableLiveData<>();
ConnectionsManager(SettingsManager settingsManager,
Executor dbExecutor) {
btStore =
new ConnectionsStore(settingsManager, dbExecutor, BT_NAMESPACE);
wifiStore = new ConnectionsStore(settingsManager, dbExecutor,
WIFI_NAMESPACE);
torStore = new ConnectionsStore(settingsManager, dbExecutor,
TOR_NAMESPACE);
}
void updateBtSetting(Settings btSettings) {
btEnabled.postValue(btSettings.getBoolean(PREF_PLUGIN_ENABLE,
BluetoothConstants.DEFAULT_PREF_PLUGIN_ENABLE));
}
void updateWifiSettings(Settings wifiSettings) {
wifiEnabled.postValue(wifiSettings.getBoolean(PREF_PLUGIN_ENABLE,
LanTcpConstants.DEFAULT_PREF_PLUGIN_ENABLE));
}
void updateTorSettings(Settings settings) {
Settings torSettings = migrateTorSettings(settings);
torEnabled.postValue(torSettings.getBoolean(PREF_PLUGIN_ENABLE,
TorConstants.DEFAULT_PREF_PLUGIN_ENABLE));
int torNetworkSetting = torSettings.getInt(PREF_TOR_NETWORK,
DEFAULT_PREF_TOR_NETWORK);
torNetwork.postValue(Integer.toString(torNetworkSetting));
torMobile.postValue(torSettings.getBoolean(PREF_TOR_MOBILE,
DEFAULT_PREF_TOR_MOBILE));
torCharging
.postValue(torSettings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING,
DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING));
}
// TODO: Remove after a reasonable migration period (added 2020-06-25)
private Settings migrateTorSettings(Settings s) {
int network = s.getInt(PREF_TOR_NETWORK, DEFAULT_PREF_TOR_NETWORK);
if (network == PREF_TOR_NETWORK_NEVER) {
s.putInt(PREF_TOR_NETWORK, DEFAULT_PREF_TOR_NETWORK);
s.putBoolean(PREF_PLUGIN_ENABLE, false);
// We don't need to save the migrated settings - the Tor plugin is
// responsible for that. This code just handles the case where the
// settings are loaded before the plugin migrates them.
}
return s;
}
LiveData<Boolean> btEnabled() {
return btEnabled;
}
LiveData<Boolean> wifiEnabled() {
return wifiEnabled;
}
LiveData<Boolean> torEnabled() {
return torEnabled;
}
LiveData<String> torNetwork() {
return torNetwork;
}
LiveData<Boolean> torMobile() {
return torMobile;
}
LiveData<Boolean> torCharging() {
return torCharging;
}
}

View File

@@ -0,0 +1,61 @@
package org.briarproject.briar.android.settings;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.settings.SettingsManager;
import java.util.concurrent.Executor;
import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.briar.android.settings.ConnectionsFragment.PREF_KEY_BLUETOOTH;
import static org.briarproject.briar.android.settings.ConnectionsFragment.PREF_KEY_TOR_ENABLE;
import static org.briarproject.briar.android.settings.ConnectionsFragment.PREF_KEY_TOR_MOBILE_DATA;
import static org.briarproject.briar.android.settings.ConnectionsFragment.PREF_KEY_TOR_NETWORK;
import static org.briarproject.briar.android.settings.ConnectionsFragment.PREF_KEY_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.briar.android.settings.ConnectionsFragment.PREF_KEY_WIFI;
@NotNullByDefault
class ConnectionsStore extends SettingsStore {
ConnectionsStore(
SettingsManager settingsManager,
Executor dbExecutor,
String namespace) {
super(settingsManager, dbExecutor, namespace);
}
@Override
public void putBoolean(String key, boolean value) {
String newKey;
// translate between Android UI pref keys and bramble keys
switch (key) {
case PREF_KEY_BLUETOOTH:
case PREF_KEY_WIFI:
case PREF_KEY_TOR_ENABLE:
newKey = PREF_PLUGIN_ENABLE;
break;
case PREF_KEY_TOR_MOBILE_DATA:
newKey = PREF_TOR_MOBILE;
break;
case PREF_KEY_TOR_ONLY_WHEN_CHARGING:
newKey = PREF_TOR_ONLY_WHEN_CHARGING;
break;
default:
throw new AssertionError();
}
super.putBoolean(newKey, value);
}
@Override
public void putInt(String key, int value) {
// translate between Android UI pref keys and bramble keys
if (key.equals(PREF_KEY_TOR_NETWORK)) {
super.putInt(PREF_TOR_NETWORK, value);
} else {
throw new AssertionError();
}
}
}

View File

@@ -146,4 +146,17 @@ public class SettingsActivity extends BriarActivity
return true;
}
/**
* If the preference is not yet enabled, this enables the preference
* and makes it persist changed values.
* Call this after setting the initial value
* to prevent this change from getting persisted in the DB unnecessarily.
*/
static void enableAndPersist(Preference pref) {
if (!pref.isEnabled()) {
pref.setEnabled(true);
pref.setPersistent(true);
}
}
}

View File

@@ -19,14 +19,9 @@ import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R;
@@ -61,21 +56,11 @@ import static android.provider.Settings.System.DEFAULT_NOTIFICATION_URI;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE;
import static org.briarproject.briar.android.util.UiUtils.getCountryDisplayName;
import static org.briarproject.briar.android.util.UiUtils.hasScreenLock;
import static org.briarproject.briar.android.util.UiUtils.triggerFeedback;
import static org.briarproject.briar.api.android.AndroidNotificationManager.BLOG_CHANNEL_ID;
@@ -102,30 +87,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
"pref_key_lock_timeout";
public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in";
private static final String BT_NAMESPACE =
BluetoothConstants.ID.getString();
private static final String BT_ENABLE = "pref_key_bluetooth";
private static final String WIFI_NAMESPACE = LanTcpConstants.ID.getString();
private static final String WIFI_ENABLE = "pref_key_wifi";
private static final String TOR_NAMESPACE = TorConstants.ID.getString();
private static final String TOR_ENABLE = "pref_key_tor_enable";
private static final String TOR_NETWORK = "pref_key_tor_network";
private static final String TOR_MOBILE = "pref_key_tor_mobile_data";
private static final String TOR_ONLY_WHEN_CHARGING =
"pref_key_tor_only_when_charging";
private static final Logger LOG =
Logger.getLogger(SettingsFragment.class.getName());
private SettingsActivity listener;
private SwitchPreference enableBluetooth;
private SwitchPreference enableWifi;
private SwitchPreference enableTor;
private ListPreference torNetwork;
private SwitchPreference torMobile;
private SwitchPreference torOnlyWhenCharging;
private SwitchPreference screenLock;
private ListPreference screenLockTimeout;
private SwitchPreference notifyPrivateMessages;
@@ -137,17 +102,13 @@ public class SettingsFragment extends PreferenceFragmentCompat
private Preference notifySound;
// Fields that are accessed from background threads must be volatile
private volatile Settings settings, btSettings, wifiSettings, torSettings;
private volatile Settings settings;
private volatile boolean settingsLoaded = false;
@Inject
volatile SettingsManager settingsManager;
@Inject
volatile EventBus eventBus;
@Inject
LocationUtils locationUtils;
@Inject
CircumventionProvider circumventionProvider;
@Override
public void onAttach(Context context) {
@@ -160,12 +121,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
public void onCreatePreferences(Bundle bundle, String s) {
addPreferencesFromResource(R.xml.settings);
enableBluetooth = findPreference(BT_ENABLE);
enableWifi = findPreference(WIFI_ENABLE);
enableTor = findPreference(TOR_ENABLE);
torNetwork = findPreference(TOR_NETWORK);
torMobile = findPreference(TOR_MOBILE);
torOnlyWhenCharging = findPreference(TOR_ONLY_WHEN_CHARGING);
screenLock = findPreference(PREF_SCREEN_LOCK);
screenLockTimeout = findPreference(PREF_SCREEN_LOCK_TIMEOUT);
notifyPrivateMessages =
@@ -176,12 +131,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
notifyVibration = findPreference("pref_key_notify_vibration");
notifySound = findPreference("pref_key_notify_sound");
enableBluetooth.setOnPreferenceChangeListener(this);
enableWifi.setOnPreferenceChangeListener(this);
enableTor.setOnPreferenceChangeListener(this);
torNetwork.setOnPreferenceChangeListener(this);
torMobile.setOnPreferenceChangeListener(this);
torOnlyWhenCharging.setOnPreferenceChangeListener(this);
screenLock.setOnPreferenceChangeListener(this);
screenLockTimeout.setOnPreferenceChangeListener(this);
@@ -232,39 +181,11 @@ public class SettingsFragment extends PreferenceFragmentCompat
eventBus.removeListener(this);
}
private void setTorNetworkSummary(int torNetworkSetting) {
if (torNetworkSetting != PREF_TOR_NETWORK_AUTOMATIC) {
torNetwork.setSummary("%s"); // use setting value
return;
}
// Look up country name in the user's chosen language if available
String country = locationUtils.getCurrentCountry();
String countryName = getCountryDisplayName(country);
boolean blocked =
circumventionProvider.isTorProbablyBlocked(country);
boolean useBridges = circumventionProvider.doBridgesWork(country);
String setting =
getString(R.string.tor_network_setting_without_bridges);
if (blocked && useBridges) {
setting = getString(R.string.tor_network_setting_with_bridges);
} else if (blocked) {
setting = getString(R.string.tor_network_setting_never);
}
torNetwork.setSummary(
getString(R.string.tor_network_setting_summary, setting,
countryName));
}
private void loadSettings() {
listener.runOnDbThread(() -> {
try {
long start = now();
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
btSettings = settingsManager.getSettings(BT_NAMESPACE);
wifiSettings = settingsManager.getSettings(WIFI_NAMESPACE);
torSettings = settingsManager.getSettings(TOR_NAMESPACE);
settingsLoaded = true;
logDuration(LOG, "Loading settings", start);
displaySettings();
@@ -274,52 +195,11 @@ public class SettingsFragment extends PreferenceFragmentCompat
});
}
// TODO: Remove after a reasonable migration period (added 2020-06-25)
private Settings migrateTorSettings(Settings s) {
int network = s.getInt(PREF_TOR_NETWORK, DEFAULT_PREF_TOR_NETWORK);
if (network == PREF_TOR_NETWORK_NEVER) {
s.putInt(PREF_TOR_NETWORK, DEFAULT_PREF_TOR_NETWORK);
s.putBoolean(PREF_PLUGIN_ENABLE, false);
// We don't need to save the migrated settings - the Tor plugin is
// responsible for that. This code just handles the case where the
// settings are loaded before the plugin migrates them.
}
return s;
}
private void displaySettings() {
listener.runOnUiThreadUnlessDestroyed(() -> {
// due to events, we might try to display before a load completed
if (!settingsLoaded) return;
boolean btEnabledSetting = btSettings.getBoolean(PREF_PLUGIN_ENABLE,
BluetoothConstants.DEFAULT_PREF_PLUGIN_ENABLE);
enableBluetooth.setChecked(btEnabledSetting);
boolean wifiEnabledSetting =
wifiSettings.getBoolean(PREF_PLUGIN_ENABLE,
LanTcpConstants.DEFAULT_PREF_PLUGIN_ENABLE);
enableWifi.setChecked(wifiEnabledSetting);
boolean torEnabledSetting =
torSettings.getBoolean(PREF_PLUGIN_ENABLE,
TorConstants.DEFAULT_PREF_PLUGIN_ENABLE);
enableTor.setChecked(torEnabledSetting);
int torNetworkSetting = torSettings.getInt(PREF_TOR_NETWORK,
DEFAULT_PREF_TOR_NETWORK);
torNetwork.setValue(Integer.toString(torNetworkSetting));
setTorNetworkSummary(torNetworkSetting);
boolean torMobileSetting = torSettings.getBoolean(PREF_TOR_MOBILE,
DEFAULT_PREF_TOR_MOBILE);
torMobile.setChecked(torMobileSetting);
boolean torChargingSetting =
torSettings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING,
DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING);
torOnlyWhenCharging.setChecked(torChargingSetting);
displayScreenLockSetting();
if (SDK_INT < 26) {
@@ -378,12 +258,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
// preferences partly needed here, because they have their own logic
// - pref_key_lock (screenLock -> displayScreenLockSetting())
// - pref_key_lock_timeout (screenLockTimeout)
enableBluetooth.setEnabled(enabled);
enableWifi.setEnabled(enabled);
enableTor.setEnabled(enabled);
torNetwork.setEnabled(enabled);
torMobile.setEnabled(enabled);
torOnlyWhenCharging.setEnabled(enabled);
if (!enabled) screenLock.setEnabled(false);
notifyPrivateMessages.setEnabled(enabled);
notifyGroupMessages.setEnabled(enabled);
@@ -478,33 +352,14 @@ public class SettingsFragment extends PreferenceFragmentCompat
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == enableBluetooth) {
boolean btSetting = (Boolean) newValue;
storeBluetoothSetting(btSetting);
} else if (preference == enableWifi) {
boolean wifiSetting = (Boolean) newValue;
storeWifiSetting(wifiSetting);
} else if (preference == enableTor) {
boolean torEnabledSetting = (Boolean) newValue;
storeTorEnabledSetting(torEnabledSetting);
} else if (preference == torNetwork) {
int torNetworkSetting = Integer.valueOf((String) newValue);
storeTorNetworkSetting(torNetworkSetting);
setTorNetworkSummary(torNetworkSetting);
} else if (preference == torMobile) {
boolean torMobileSetting = (Boolean) newValue;
storeTorMobileSetting(torMobileSetting);
} else if (preference == torOnlyWhenCharging) {
boolean torChargingSetting = (Boolean) newValue;
storeTorChargingSetting(torChargingSetting);
} else if (preference == screenLock) {
if (preference == screenLock) {
Settings s = new Settings();
s.putBoolean(PREF_SCREEN_LOCK, (Boolean) newValue);
storeSettings(s);
} else if (preference == screenLockTimeout) {
Settings s = new Settings();
String value = (String) newValue;
s.putInt(PREF_SCREEN_LOCK_TIMEOUT, Integer.valueOf(value));
s.putInt(PREF_SCREEN_LOCK_TIMEOUT, Integer.parseInt(value));
storeSettings(s);
setScreenLockTimeoutSummary(value);
} else if (preference == notifyPrivateMessages) {
@@ -531,41 +386,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
return true;
}
private void storeTorEnabledSetting(boolean torEnabledSetting) {
Settings s = new Settings();
s.putBoolean(PREF_PLUGIN_ENABLE, torEnabledSetting);
mergeSettings(s, TOR_NAMESPACE);
}
private void storeTorNetworkSetting(int torNetworkSetting) {
Settings s = new Settings();
s.putInt(PREF_TOR_NETWORK, torNetworkSetting);
mergeSettings(s, TOR_NAMESPACE);
}
private void storeTorMobileSetting(boolean torMobileSetting) {
Settings s = new Settings();
s.putBoolean(PREF_TOR_MOBILE, torMobileSetting);
mergeSettings(s, TOR_NAMESPACE);
}
private void storeTorChargingSetting(boolean torChargingSetting) {
Settings s = new Settings();
s.putBoolean(PREF_TOR_ONLY_WHEN_CHARGING, torChargingSetting);
mergeSettings(s, TOR_NAMESPACE);
}
private void storeBluetoothSetting(boolean btSetting) {
Settings s = new Settings();
s.putBoolean(PREF_PLUGIN_ENABLE, btSetting);
mergeSettings(s, BT_NAMESPACE);
}
private void storeWifiSetting(boolean wifiSetting) {
Settings s = new Settings();
s.putBoolean(PREF_PLUGIN_ENABLE, wifiSetting);
mergeSettings(s, WIFI_NAMESPACE);
}
private void storeSettings(Settings s) {
mergeSettings(s, SETTINGS_NAMESPACE);
@@ -584,7 +404,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
@Override
public void onActivityResult(int request, int result, Intent data) {
public void onActivityResult(int request, int result,
@Nullable Intent data) {
super.onActivityResult(request, result, data);
if (request == REQUEST_RINGTONE && result == RESULT_OK) {
Settings s = new Settings();
@@ -625,18 +446,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
LOG.info("Settings updated");
settings = s.getSettings();
displaySettings();
} else if (namespace.equals(BT_NAMESPACE)) {
LOG.info("Bluetooth settings updated");
btSettings = s.getSettings();
displaySettings();
} else if (namespace.equals(WIFI_NAMESPACE)) {
LOG.info("Wifi settings updated");
wifiSettings = s.getSettings();
displaySettings();
} else if (namespace.equals(TOR_NAMESPACE)) {
LOG.info("Tor settings updated");
torSettings = migrateTorSettings(s.getSettings());
displaySettings();
}
}
}

View File

@@ -0,0 +1,75 @@
package org.briarproject.briar.android.settings;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceDataStore;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
/**
* A custom PreferenceDataStore that stores settings in Briar's encrypted DB.
* <p>
* Warning: This expects all strings to be integers and stores them as such.
*/
@NotNullByDefault
class SettingsStore extends PreferenceDataStore {
private final static Logger LOG = getLogger(SettingsStore.class.getName());
private final SettingsManager settingsManager;
private final Executor dbExecutor;
private final String namespace;
SettingsStore(SettingsManager settingsManager,
Executor dbExecutor,
String namespace) {
this.settingsManager = settingsManager;
this.dbExecutor = dbExecutor;
this.namespace = namespace;
}
@Override
public void putBoolean(String key, boolean value) {
Settings s = new Settings();
s.putBoolean(key, value);
storeSettings(s);
}
@Override
public void putInt(String key, int value) {
Settings s = new Settings();
s.putInt(key, value);
storeSettings(s);
}
@Override
public void putString(String key, @Nullable String value) {
int integer = Integer.parseInt(requireNonNull(value));
putInt(key, integer);
}
private void storeSettings(Settings s) {
dbExecutor.execute(() -> {
try {
long start = now();
settingsManager.mergeSettings(s, namespace);
logDuration(LOG, "Merging " + namespace + " settings", start);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
}

View File

@@ -6,12 +6,27 @@ import android.net.Uri;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.briar.android.attachment.UnsupportedMimeTypeException;
import org.briarproject.briar.android.attachment.media.ImageCompressor;
import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.avatar.AvatarManager;
@@ -25,7 +40,6 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
@@ -33,58 +47,102 @@ import static java.util.Arrays.asList;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
@NotNullByDefault
class SettingsViewModel extends AndroidViewModel {
class SettingsViewModel extends DbViewModel implements EventListener {
private final static Logger LOG =
getLogger(SettingsViewModel.class.getName());
public static final String SETTINGS_NAMESPACE = "android-ui";
static final String BT_NAMESPACE =
BluetoothConstants.ID.getString();
static final String WIFI_NAMESPACE = LanTcpConstants.ID.getString();
static final String TOR_NAMESPACE = TorConstants.ID.getString();
private final SettingsManager settingsManager;
private final IdentityManager identityManager;
private final EventBus eventBus;
private final AvatarManager avatarManager;
private final AuthorManager authorManager;
private final ImageCompressor imageCompressor;
@IoExecutor
private final Executor ioExecutor;
@DatabaseExecutor
private final Executor dbExecutor;
private final ConnectionsManager connectionsManager;
final SettingsStore settingsStore;
final TorSummaryProvider torSummaryProvider;
private volatile Settings settings;
private final MutableLiveData<OwnIdentityInfo> ownIdentityInfo =
new MutableLiveData<>();
private final MutableLiveEvent<Boolean> setAvatarFailed =
new MutableLiveEvent<>();
@Inject
SettingsViewModel(Application application,
@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager,
TransactionManager db,
AndroidExecutor androidExecutor,
SettingsManager settingsManager,
IdentityManager identityManager,
EventBus eventBus,
AvatarManager avatarManager,
AuthorManager authorManager,
ImageCompressor imageCompressor,
@IoExecutor Executor ioExecutor,
@DatabaseExecutor Executor dbExecutor) {
super(application);
LocationUtils locationUtils,
CircumventionProvider circumventionProvider,
@IoExecutor Executor ioExecutor) {
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
this.settingsManager = settingsManager;
this.identityManager = identityManager;
this.eventBus = eventBus;
this.imageCompressor = imageCompressor;
this.avatarManager = avatarManager;
this.authorManager = authorManager;
this.ioExecutor = ioExecutor;
this.dbExecutor = dbExecutor;
this.settingsStore = new SettingsStore(settingsManager, dbExecutor,
SETTINGS_NAMESPACE);
this.torSummaryProvider = new TorSummaryProvider(getApplication(),
locationUtils, circumventionProvider);
this.connectionsManager =
new ConnectionsManager(settingsManager, dbExecutor);
eventBus.addListener(this);
loadSettings();
loadOwnIdentityInfo();
}
LiveData<OwnIdentityInfo> getOwnIdentityInfo() {
return ownIdentityInfo;
@Override
protected void onCleared() {
super.onCleared();
eventBus.removeListener(this);
}
LiveEvent<Boolean> getSetAvatarFailed() {
return setAvatarFailed;
private void loadSettings() {
runOnDbThread(() -> {
try {
long start = now();
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
connectionsManager.updateBtSetting(
settingsManager.getSettings(BT_NAMESPACE));
connectionsManager.updateWifiSettings(
settingsManager.getSettings(WIFI_NAMESPACE));
connectionsManager.updateTorSettings(
settingsManager.getSettings(TOR_NAMESPACE));
logDuration(LOG, "Loading settings", start);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void loadOwnIdentityInfo() {
dbExecutor.execute(() -> {
runOnDbThread(() -> {
try {
LocalAuthor localAuthor = identityManager.getLocalAuthor();
AuthorInfo authorInfo = authorManager.getMyAuthorInfo();
@@ -96,6 +154,24 @@ class SettingsViewModel extends AndroidViewModel {
});
}
@Override
public void eventOccurred(Event e) {
if (e instanceof SettingsUpdatedEvent) {
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
String namespace = s.getNamespace();
if (namespace.equals(BT_NAMESPACE)) {
LOG.info("Bluetooth settings updated");
connectionsManager.updateBtSetting(s.getSettings());
} else if (namespace.equals(WIFI_NAMESPACE)) {
LOG.info("Wifi settings updated");
connectionsManager.updateWifiSettings(s.getSettings());
} else if (namespace.equals(TOR_NAMESPACE)) {
LOG.info("Tor settings updated");
connectionsManager.updateTorSettings(s.getSettings());
}
}
}
void setAvatar(Uri uri) {
ioExecutor.execute(() -> {
try {
@@ -120,7 +196,7 @@ class SettingsViewModel extends AndroidViewModel {
"ContentResolver returned null when opening InputStream");
InputStream compressed = imageCompressor.compressImage(is, contentType);
dbExecutor.execute(() -> {
runOnDbThread(() -> {
try {
avatarManager.addAvatar(ImageCompressor.MIME_TYPE, compressed);
loadOwnIdentityInfo();
@@ -131,4 +207,16 @@ class SettingsViewModel extends AndroidViewModel {
});
}
LiveData<OwnIdentityInfo> getOwnIdentityInfo() {
return ownIdentityInfo;
}
LiveEvent<Boolean> getSetAvatarFailed() {
return setAvatarFailed;
}
ConnectionsManager getConnectionsManager() {
return connectionsManager;
}
}

View File

@@ -0,0 +1,57 @@
package org.briarproject.briar.android.settings;
import android.content.Context;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.briar.R;
import androidx.preference.ListPreference;
import androidx.preference.Preference.SummaryProvider;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC;
import static org.briarproject.briar.android.util.UiUtils.getCountryDisplayName;
@NotNullByDefault
class TorSummaryProvider implements SummaryProvider<ListPreference> {
private final Context ctx;
private final LocationUtils locationUtils;
private final CircumventionProvider circumventionProvider;
public TorSummaryProvider(Context ctx,
LocationUtils locationUtils,
CircumventionProvider circumventionProvider) {
this.ctx = ctx;
this.locationUtils = locationUtils;
this.circumventionProvider = circumventionProvider;
}
@Override
public CharSequence provideSummary(ListPreference preference) {
int torNetworkSetting = Integer.parseInt(preference.getValue());
if (torNetworkSetting != PREF_TOR_NETWORK_AUTOMATIC) {
return preference.getEntry(); // use setting value
}
// Look up country name in the user's chosen language if available
String country = locationUtils.getCurrentCountry();
String countryName = getCountryDisplayName(country);
boolean blocked =
circumventionProvider.isTorProbablyBlocked(country);
boolean useBridges = circumventionProvider.doBridgesWork(country);
String setting =
ctx.getString(R.string.tor_network_setting_without_bridges);
if (blocked && useBridges) {
setting = ctx.getString(R.string.tor_network_setting_with_bridges);
} else if (blocked) {
setting = ctx.getString(R.string.tor_network_setting_never);
}
return ctx.getString(R.string.tor_network_setting_summary, setting,
countryName);
}
}

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/textColorPrimary"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M11,14H9c0,-4.97 4.03,-9 9,-9v2C14.13,7 11,10.13 11,14zM18,11V9c-2.76,0 -5,2.24 -5,5h2C15,12.34 16.34,11 18,11zM7,4c0,-1.11 -0.89,-2 -2,-2S3,2.89 3,4s0.89,2 2,2S7,5.11 7,4zM11.45,4.5h-2C9.21,5.92 7.99,7 6.5,7h-3C2.67,7 2,7.67 2,8.5V11h6V8.74C9.86,8.15 11.25,6.51 11.45,4.5zM19,17c1.11,0 2,-0.89 2,-2s-0.89,-2 -2,-2s-2,0.89 -2,2S17.89,17 19,17zM20.5,18h-3c-1.49,0 -2.71,-1.08 -2.95,-2.5h-2c0.2,2.01 1.59,3.65 3.45,4.24V22h6v-2.5C22,18.67 21.33,18 20.5,18z" />
</vector>

View File

@@ -7,66 +7,10 @@
app:fragment="org.briarproject.briar.android.settings.DisplayFragment"
app:icon="@drawable/ic_settings_brightness" />
<PreferenceCategory
android:layout="@layout/preferences_category"
android:title="@string/network_settings_title">
<SwitchPreference
android:defaultValue="false"
android:key="pref_key_bluetooth"
android:persistent="false"
android:title="@string/bluetooth_setting"
android:widgetLayout="@layout/preference_switch_compat"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="false"
android:key="pref_key_wifi"
android:persistent="false"
android:title="@string/wifi_setting"
android:widgetLayout="@layout/preference_switch_compat"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="true"
android:key="pref_key_tor_enable"
android:persistent="false"
android:summary="@string/tor_enable_summary"
android:title="@string/tor_enable_title"
android:widgetLayout="@layout/preference_switch_compat"
app:iconSpaceReserved="false" />
<ListPreference
android:defaultValue="0"
android:dependency="pref_key_tor_enable"
android:entries="@array/tor_network_setting_names"
android:entryValues="@array/tor_network_setting_values"
android:key="pref_key_tor_network"
android:persistent="false"
android:summary="%s"
android:title="@string/tor_network_setting"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="true"
android:dependency="pref_key_tor_enable"
android:key="pref_key_tor_mobile_data"
android:persistent="false"
android:title="@string/tor_mobile_data_title"
android:widgetLayout="@layout/preference_switch_compat"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="false"
android:dependency="pref_key_tor_enable"
android:key="pref_key_tor_only_when_charging"
android:persistent="false"
android:summary="@string/tor_only_when_charging_summary"
android:title="@string/tor_only_when_charging_title"
android:widgetLayout="@layout/preference_switch_compat"
app:iconSpaceReserved="false" />
</PreferenceCategory>
<Preference
android:title="@string/network_settings_title"
app:fragment="org.briarproject.briar.android.settings.ConnectionsFragment"
app:icon="@drawable/ic_connect_without_contact" />
<PreferenceCategory
android:layout="@layout/preferences_category"

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreference
android:defaultValue="false"
android:enabled="false"
android:key="pref_key_bluetooth"
android:persistent="false"
android:title="@string/bluetooth_setting"
android:widgetLayout="@layout/preference_switch_compat"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="false"
android:enabled="false"
android:key="pref_key_wifi"
android:persistent="false"
android:title="@string/wifi_setting"
android:widgetLayout="@layout/preference_switch_compat"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="true"
android:enabled="false"
android:key="pref_key_tor_enable"
android:persistent="false"
android:summary="@string/tor_enable_summary"
android:title="@string/tor_enable_title"
android:widgetLayout="@layout/preference_switch_compat"
app:iconSpaceReserved="false" />
<ListPreference
android:defaultValue="0"
android:dependency="pref_key_tor_enable"
android:enabled="false"
android:entries="@array/tor_network_setting_names"
android:entryValues="@array/tor_network_setting_values"
android:key="pref_key_tor_network"
android:persistent="false"
android:summary="%s"
android:title="@string/tor_network_setting"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="true"
android:dependency="pref_key_tor_enable"
android:enabled="false"
android:key="pref_key_tor_mobile_data"
android:persistent="false"
android:title="@string/tor_mobile_data_title"
android:widgetLayout="@layout/preference_switch_compat"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="false"
android:dependency="pref_key_tor_enable"
android:enabled="false"
android:key="pref_key_tor_only_when_charging"
android:persistent="false"
android:summary="@string/tor_only_when_charging_summary"
android:title="@string/tor_only_when_charging_title"
android:widgetLayout="@layout/preference_switch_compat"
app:iconSpaceReserved="false" />
</PreferenceScreen>