mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 20:59:54 +01:00
Merge branch '1826-settings-view-model' into 'master'
Finish migrating SettingsFragment to ViewModel Closes #1942 and #1826 See merge request briar/briar!1350
This commit is contained in:
@@ -36,6 +36,10 @@ import org.briarproject.briar.android.attachment.media.MediaModule;
|
|||||||
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
|
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
|
||||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||||
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
||||||
|
import org.briarproject.briar.android.settings.ConnectionsFragment;
|
||||||
|
import org.briarproject.briar.android.settings.NotificationsFragment;
|
||||||
|
import org.briarproject.briar.android.settings.SecurityFragment;
|
||||||
|
import org.briarproject.briar.android.settings.SettingsFragment;
|
||||||
import org.briarproject.briar.android.view.EmojiTextInputView;
|
import org.briarproject.briar.android.view.EmojiTextInputView;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.android.DozeWatchdog;
|
import org.briarproject.briar.api.android.DozeWatchdog;
|
||||||
@@ -193,4 +197,12 @@ public interface AndroidComponent
|
|||||||
void inject(EmojiTextInputView textInputView);
|
void inject(EmojiTextInputView textInputView);
|
||||||
|
|
||||||
void inject(BriarModelLoader briarModelLoader);
|
void inject(BriarModelLoader briarModelLoader);
|
||||||
|
|
||||||
|
void inject(SettingsFragment settingsFragment);
|
||||||
|
|
||||||
|
void inject(ConnectionsFragment connectionsFragment);
|
||||||
|
|
||||||
|
void inject(SecurityFragment securityFragment);
|
||||||
|
|
||||||
|
void inject(NotificationsFragment notificationsFragment);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
|
|
||||||
@@ -115,6 +116,11 @@ public class AppModule {
|
|||||||
this.application = application;
|
this.application = application;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AndroidComponent getAndroidComponent(Context ctx) {
|
||||||
|
BriarApplication app = (BriarApplication) ctx.getApplicationContext();
|
||||||
|
return app.getApplicationComponent();
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
Application providesApplication() {
|
Application providesApplication() {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import java.util.Locale;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.LANGUAGE;
|
import static org.briarproject.briar.android.settings.DisplayFragment.PREF_LANGUAGE;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class Localizer {
|
public class Localizer {
|
||||||
@@ -25,7 +25,7 @@ public class Localizer {
|
|||||||
|
|
||||||
private Localizer(SharedPreferences sharedPreferences) {
|
private Localizer(SharedPreferences sharedPreferences) {
|
||||||
this(Locale.getDefault(), getLocaleFromTag(
|
this(Locale.getDefault(), getLocaleFromTag(
|
||||||
sharedPreferences.getString(LANGUAGE, "default")));
|
sharedPreferences.getString(PREF_LANGUAGE, "default")));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Localizer(Locale systemLocale, @Nullable Locale userLocale) {
|
private Localizer(Locale systemLocale, @Nullable Locale userLocale) {
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ import static android.os.SystemClock.elapsedRealtime;
|
|||||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.PREF_SCREEN_LOCK;
|
import static org.briarproject.briar.android.settings.SecurityFragment.PREF_SCREEN_LOCK;
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.PREF_SCREEN_LOCK_TIMEOUT;
|
import static org.briarproject.briar.android.settings.SecurityFragment.PREF_SCREEN_LOCK_TIMEOUT;
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.hasScreenLock;
|
import static org.briarproject.briar.android.util.UiUtils.hasScreenLock;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.os.Bundle;
|
|||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -24,7 +25,6 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.lifecycle.ViewModelProviders;
|
|
||||||
|
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
@@ -56,14 +56,18 @@ public class ChangePasswordActivity extends BriarActivity
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
ChangePasswordViewModel viewModel;
|
ChangePasswordViewModel viewModel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectActivity(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
viewModel = new ViewModelProvider(this, viewModelFactory)
|
||||||
|
.get(ChangePasswordViewModel.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle state) {
|
public void onCreate(Bundle state) {
|
||||||
super.onCreate(state);
|
super.onCreate(state);
|
||||||
setContentView(R.layout.activity_change_password);
|
setContentView(R.layout.activity_change_password);
|
||||||
|
|
||||||
viewModel = ViewModelProviders.of(this, viewModelFactory)
|
|
||||||
.get(ChangePasswordViewModel.class);
|
|
||||||
|
|
||||||
currentPasswordEntryWrapper =
|
currentPasswordEntryWrapper =
|
||||||
findViewById(R.id.current_password_entry_wrapper);
|
findViewById(R.id.current_password_entry_wrapper);
|
||||||
newPasswordEntryWrapper = findViewById(R.id.new_password_entry_wrapper);
|
newPasswordEntryWrapper = findViewById(R.id.new_password_entry_wrapper);
|
||||||
@@ -77,7 +81,6 @@ public class ChangePasswordActivity extends BriarActivity
|
|||||||
progress = findViewById(R.id.progress_wheel);
|
progress = findViewById(R.id.progress_wheel);
|
||||||
|
|
||||||
TextWatcher tw = new TextWatcher() {
|
TextWatcher tw = new TextWatcher() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||||
int after) {
|
int after) {
|
||||||
@@ -102,8 +105,12 @@ public class ChangePasswordActivity extends BriarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectActivity(ActivityComponent component) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
component.inject(this);
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
onBackPressed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableOrDisableContinueButton() {
|
private void enableOrDisableContinueButton() {
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package org.briarproject.briar.android.login;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface ChangePasswordController {
|
|
||||||
|
|
||||||
float estimatePasswordStrength(String password);
|
|
||||||
|
|
||||||
void changePassword(String oldPassword, String newPassword,
|
|
||||||
ResultHandler<Boolean> resultHandler);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,7 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static android.content.Intent.ACTION_BOOT_COMPLETED;
|
import static android.content.Intent.ACTION_BOOT_COMPLETED;
|
||||||
import static android.content.Intent.ACTION_MY_PACKAGE_REPLACED;
|
import static android.content.Intent.ACTION_MY_PACKAGE_REPLACED;
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.NOTIFY_SIGN_IN;
|
import static org.briarproject.briar.android.settings.NotificationsFragment.PREF_NOTIFY_SIGN_IN;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ACTION_DISMISS_REMINDER;
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.ACTION_DISMISS_REMINDER;
|
||||||
|
|
||||||
public class SignInReminderReceiver extends BroadcastReceiver {
|
public class SignInReminderReceiver extends BroadcastReceiver {
|
||||||
@@ -37,7 +37,7 @@ public class SignInReminderReceiver extends BroadcastReceiver {
|
|||||||
if (accountManager.accountExists() &&
|
if (accountManager.accountExists() &&
|
||||||
!accountManager.hasDatabaseKey()) {
|
!accountManager.hasDatabaseKey()) {
|
||||||
SharedPreferences prefs = app.getDefaultSharedPreferences();
|
SharedPreferences prefs = app.getDefaultSharedPreferences();
|
||||||
if (prefs.getBoolean(NOTIFY_SIGN_IN, true)) {
|
if (prefs.getBoolean(PREF_NOTIFY_SIGN_IN, true)) {
|
||||||
notificationManager.showSignInNotification();
|
notificationManager.showSignInNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import javax.annotation.Nullable;
|
|||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
import androidx.preference.SwitchPreference;
|
import androidx.preference.SwitchPreferenceCompat;
|
||||||
import info.guardianproject.panic.PanicResponder;
|
import info.guardianproject.panic.PanicResponder;
|
||||||
|
|
||||||
import static android.app.Activity.RESULT_CANCELED;
|
import static android.app.Activity.RESULT_CANCELED;
|
||||||
@@ -40,7 +40,7 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
Logger.getLogger(PanicPreferencesFragment.class.getName());
|
Logger.getLogger(PanicPreferencesFragment.class.getName());
|
||||||
|
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
private SwitchPreference lockPref, purgePref;
|
private SwitchPreferenceCompat lockPref, purgePref;
|
||||||
private ListPreference panicAppPref;
|
private ListPreference panicAppPref;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -51,9 +51,9 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
private void updatePreferences() {
|
private void updatePreferences() {
|
||||||
pm = getActivity().getPackageManager();
|
pm = getActivity().getPackageManager();
|
||||||
|
|
||||||
lockPref = (SwitchPreference) findPreference(KEY_LOCK);
|
lockPref = (SwitchPreferenceCompat) findPreference(KEY_LOCK);
|
||||||
panicAppPref = (ListPreference) findPreference(KEY_PANIC_APP);
|
panicAppPref = (ListPreference) findPreference(KEY_PANIC_APP);
|
||||||
purgePref = (SwitchPreference) findPreference(KEY_PURGE);
|
purgePref = (SwitchPreferenceCompat) findPreference(KEY_PURGE);
|
||||||
|
|
||||||
// check for connect/disconnect intents from panic trigger apps
|
// check for connect/disconnect intents from panic trigger apps
|
||||||
if (PanicResponder.checkForDisconnectIntent(getActivity())) {
|
if (PanicResponder.checkForDisconnectIntent(getActivity())) {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package org.briarproject.briar.android.settings;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
import de.hdodenhof.circleimageview.CircleImageView;
|
||||||
|
|
||||||
|
import static org.briarproject.briar.android.view.AuthorView.setAvatar;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class AvatarPreference extends Preference {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private OwnIdentityInfo info;
|
||||||
|
|
||||||
|
public AvatarPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
setLayoutResource(R.layout.preference_avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||||
|
super.onBindViewHolder(holder);
|
||||||
|
View v = holder.itemView;
|
||||||
|
if (info != null) {
|
||||||
|
TextView textViewUserName = v.findViewById(R.id.username);
|
||||||
|
CircleImageView imageViewAvatar = v.findViewById(R.id.avatarImage);
|
||||||
|
textViewUserName.setText(info.getLocalAuthor().getName());
|
||||||
|
setAvatar(imageViewAvatar, info.getLocalAuthor().getId(),
|
||||||
|
info.getAuthorInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOwnIdentityInfo(OwnIdentityInfo info) {
|
||||||
|
this.info = info;
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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.SwitchPreferenceCompat;
|
||||||
|
|
||||||
|
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 SwitchPreferenceCompat enableBluetooth;
|
||||||
|
private SwitchPreferenceCompat enableWifi;
|
||||||
|
private SwitchPreferenceCompat enableTor;
|
||||||
|
private ListPreference torNetwork;
|
||||||
|
private SwitchPreferenceCompat torMobile;
|
||||||
|
private SwitchPreferenceCompat 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.connectionsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package org.briarproject.briar.android.settings;
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
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 androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
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 putString(String key, @Nullable String value) {
|
||||||
|
// translate between Android UI pref keys and bramble keys
|
||||||
|
if (key.equals(PREF_KEY_TOR_NETWORK)) {
|
||||||
|
super.putString(PREF_TOR_NETWORK, value);
|
||||||
|
} else {
|
||||||
|
throw new AssertionError(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
package org.briarproject.briar.android.settings;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.Localizer;
|
||||||
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import androidx.core.text.TextUtilsCompat;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.preference.ListPreference;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
||||||
|
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.SIGN_OUT_URI;
|
||||||
|
import static org.briarproject.briar.android.settings.SettingsActivity.EXTRA_THEME_CHANGE;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class DisplayFragment extends PreferenceFragmentCompat {
|
||||||
|
|
||||||
|
public static final String PREF_LANGUAGE = "pref_key_language";
|
||||||
|
private static final String PREF_THEME = "pref_key_theme";
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(DisplayFragment.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreatePreferences(Bundle bundle, String s) {
|
||||||
|
addPreferencesFromResource(R.xml.settings_display);
|
||||||
|
|
||||||
|
ListPreference language = requireNonNull(findPreference(PREF_LANGUAGE));
|
||||||
|
setLanguageEntries(language);
|
||||||
|
language.setOnPreferenceChangeListener(this::onLanguageChanged);
|
||||||
|
|
||||||
|
ListPreference theme = requireNonNull(findPreference(PREF_THEME));
|
||||||
|
setThemeEntries(theme);
|
||||||
|
theme.setOnPreferenceChangeListener(this::onThemeChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
requireActivity().setTitle(R.string.display_settings_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLanguageEntries(ListPreference language) {
|
||||||
|
CharSequence[] tags = language.getEntryValues();
|
||||||
|
List<CharSequence> entries = new ArrayList<>(tags.length);
|
||||||
|
List<CharSequence> entryValues = new ArrayList<>(tags.length);
|
||||||
|
for (CharSequence cs : tags) {
|
||||||
|
String tag = cs.toString();
|
||||||
|
if (tag.equals("default")) {
|
||||||
|
entries.add(getString(R.string.pref_language_default));
|
||||||
|
entryValues.add(tag);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Locale locale = Localizer.getLocaleFromTag(tag);
|
||||||
|
if (locale == null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
// Exclude RTL locales on API < 17, they won't be laid out correctly
|
||||||
|
if (SDK_INT < 17 && !isLeftToRight(locale)) {
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Skipping RTL locale " + tag);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String nativeName = locale.getDisplayName(locale);
|
||||||
|
// Fallback to English if the name is unknown in both native and
|
||||||
|
// current locale.
|
||||||
|
if (nativeName.equals(tag)) {
|
||||||
|
String tmp = locale.getDisplayLanguage(Locale.ENGLISH);
|
||||||
|
if (!tmp.isEmpty() && !tmp.equals(nativeName))
|
||||||
|
nativeName = tmp;
|
||||||
|
}
|
||||||
|
// Prefix with LRM marker to prevent any RTL direction
|
||||||
|
entries.add("\u200E" + nativeName.substring(0, 1).toUpperCase()
|
||||||
|
+ nativeName.substring(1));
|
||||||
|
entryValues.add(tag);
|
||||||
|
}
|
||||||
|
language.setEntries(entries.toArray(new CharSequence[0]));
|
||||||
|
language.setEntryValues(entryValues.toArray(new CharSequence[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLeftToRight(Locale locale) {
|
||||||
|
// TextUtilsCompat returns the wrong direction for Hebrew on some phones
|
||||||
|
String language = locale.getLanguage();
|
||||||
|
if (language.equals("iw") || language.equals("he")) return false;
|
||||||
|
int direction = TextUtilsCompat.getLayoutDirectionFromLocale(locale);
|
||||||
|
return direction == LAYOUT_DIRECTION_LTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setThemeEntries(ListPreference theme) {
|
||||||
|
if (SDK_INT < 27) {
|
||||||
|
// remove System Default Theme option from preference entries
|
||||||
|
// as it is not functional on this API anyway
|
||||||
|
List<CharSequence> entries =
|
||||||
|
new ArrayList<>(Arrays.asList(theme.getEntries()));
|
||||||
|
entries.remove(getString(R.string.pref_theme_system));
|
||||||
|
theme.setEntries(entries.toArray(new CharSequence[0]));
|
||||||
|
// also remove corresponding value
|
||||||
|
List<CharSequence> values =
|
||||||
|
new ArrayList<>(Arrays.asList(theme.getEntryValues()));
|
||||||
|
values.remove(getString(R.string.pref_theme_system_value));
|
||||||
|
theme.setEntryValues(values.toArray(new CharSequence[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onThemeChanged(Preference preference, Object newValue) {
|
||||||
|
// activate new theme
|
||||||
|
FragmentActivity activity = requireActivity();
|
||||||
|
UiUtils.setTheme(activity, (String) newValue);
|
||||||
|
// bring up parent activity, so it can change its theme as well
|
||||||
|
// upstream bug: https://issuetracker.google.com/issues/38352704
|
||||||
|
Intent intent = new Intent(getActivity(), ENTRY_ACTIVITY);
|
||||||
|
intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
// bring this activity back to the foreground
|
||||||
|
intent = new Intent(getActivity(), activity.getClass());
|
||||||
|
intent.putExtra(EXTRA_THEME_CHANGE, true);
|
||||||
|
startActivity(intent);
|
||||||
|
activity.finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onLanguageChanged(Preference preference, Object newValue) {
|
||||||
|
ListPreference language = (ListPreference) preference;
|
||||||
|
if (!language.getValue().equals(newValue)) {
|
||||||
|
AlertDialog.Builder builder =
|
||||||
|
new AlertDialog.Builder(getActivity());
|
||||||
|
builder.setTitle(R.string.pref_language_title);
|
||||||
|
builder.setMessage(R.string.pref_language_changed);
|
||||||
|
builder.setPositiveButton(R.string.sign_out_button, (d, i) -> {
|
||||||
|
language.setValue((String) newValue);
|
||||||
|
Intent intent = new Intent(getContext(), ENTRY_ACTIVITY);
|
||||||
|
intent.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
intent.setData(SIGN_OUT_URI);
|
||||||
|
requireActivity().startActivity(intent);
|
||||||
|
requireActivity().finish();
|
||||||
|
});
|
||||||
|
builder.setNegativeButton(R.string.cancel, null);
|
||||||
|
builder.setCancelable(false);
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
package org.briarproject.briar.android.settings;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
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.annotation.StringRes;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
import androidx.preference.SwitchPreferenceCompat;
|
||||||
|
|
||||||
|
import static android.app.Activity.RESULT_OK;
|
||||||
|
import static android.media.RingtoneManager.ACTION_RINGTONE_PICKER;
|
||||||
|
import static android.media.RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI;
|
||||||
|
import static android.media.RingtoneManager.EXTRA_RINGTONE_EXISTING_URI;
|
||||||
|
import static android.media.RingtoneManager.EXTRA_RINGTONE_PICKED_URI;
|
||||||
|
import static android.media.RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT;
|
||||||
|
import static android.media.RingtoneManager.EXTRA_RINGTONE_TITLE;
|
||||||
|
import static android.media.RingtoneManager.EXTRA_RINGTONE_TYPE;
|
||||||
|
import static android.media.RingtoneManager.TYPE_NOTIFICATION;
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.provider.Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS;
|
||||||
|
import static android.provider.Settings.EXTRA_APP_PACKAGE;
|
||||||
|
import static android.provider.Settings.EXTRA_CHANNEL_ID;
|
||||||
|
import static android.provider.Settings.System.DEFAULT_NOTIFICATION_URI;
|
||||||
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
|
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||||
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE;
|
||||||
|
import static org.briarproject.briar.android.settings.SettingsActivity.enableAndPersist;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.BLOG_CHANNEL_ID;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.CONTACT_CHANNEL_ID;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.FORUM_CHANNEL_ID;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.GROUP_CHANNEL_ID;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_BLOG;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_FORUM;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_GROUP;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_PRIVATE;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_SOUND;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_VIBRATION;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class NotificationsFragment extends PreferenceFragmentCompat {
|
||||||
|
|
||||||
|
public static final String PREF_NOTIFY_SIGN_IN = "pref_key_notify_sign_in";
|
||||||
|
private static final int NOTIFICATION_CHANNEL_API = 26;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
|
|
||||||
|
private SettingsViewModel viewModel;
|
||||||
|
private NotificationsManager nm;
|
||||||
|
|
||||||
|
private SwitchPreferenceCompat notifyPrivateMessages;
|
||||||
|
private SwitchPreferenceCompat notifyGroupMessages;
|
||||||
|
private SwitchPreferenceCompat notifyForumPosts;
|
||||||
|
private SwitchPreferenceCompat notifyBlogPosts;
|
||||||
|
private SwitchPreferenceCompat notifyVibration;
|
||||||
|
|
||||||
|
private Preference notifySound;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
getAndroidComponent(context).inject(this);
|
||||||
|
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
|
||||||
|
.get(SettingsViewModel.class);
|
||||||
|
nm = viewModel.notificationsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreatePreferences(Bundle bundle, String s) {
|
||||||
|
addPreferencesFromResource(R.xml.settings_notifications);
|
||||||
|
|
||||||
|
notifyPrivateMessages = findPreference(PREF_NOTIFY_PRIVATE);
|
||||||
|
notifyGroupMessages = findPreference(PREF_NOTIFY_GROUP);
|
||||||
|
notifyForumPosts = findPreference(PREF_NOTIFY_FORUM);
|
||||||
|
notifyBlogPosts = findPreference(PREF_NOTIFY_BLOG);
|
||||||
|
notifyVibration = findPreference(PREF_NOTIFY_VIBRATION);
|
||||||
|
notifySound = findPreference(PREF_NOTIFY_SOUND);
|
||||||
|
|
||||||
|
if (SDK_INT < NOTIFICATION_CHANNEL_API) {
|
||||||
|
// NOTIFY_SIGN_IN gets stored in Android's SharedPreferences
|
||||||
|
notifyPrivateMessages
|
||||||
|
.setPreferenceDataStore(viewModel.settingsStore);
|
||||||
|
notifyGroupMessages.setPreferenceDataStore(viewModel.settingsStore);
|
||||||
|
notifyForumPosts.setPreferenceDataStore(viewModel.settingsStore);
|
||||||
|
notifyBlogPosts.setPreferenceDataStore(viewModel.settingsStore);
|
||||||
|
notifyVibration.setPreferenceDataStore(viewModel.settingsStore);
|
||||||
|
|
||||||
|
notifySound.setOnPreferenceClickListener(pref ->
|
||||||
|
onNotificationSoundClicked()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setupNotificationPreference(notifyPrivateMessages,
|
||||||
|
CONTACT_CHANNEL_ID,
|
||||||
|
R.string.notify_private_messages_setting_summary_26);
|
||||||
|
setupNotificationPreference(notifyGroupMessages,
|
||||||
|
GROUP_CHANNEL_ID,
|
||||||
|
R.string.notify_group_messages_setting_summary_26);
|
||||||
|
setupNotificationPreference(notifyForumPosts, FORUM_CHANNEL_ID,
|
||||||
|
R.string.notify_forum_posts_setting_summary_26);
|
||||||
|
setupNotificationPreference(notifyBlogPosts, BLOG_CHANNEL_ID,
|
||||||
|
R.string.notify_blog_posts_setting_summary_26);
|
||||||
|
|
||||||
|
notifyVibration.setVisible(false);
|
||||||
|
notifySound.setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
if (SDK_INT < NOTIFICATION_CHANNEL_API) {
|
||||||
|
LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
|
||||||
|
nm.getNotifyPrivateMessages().observe(lifecycleOwner, enabled -> {
|
||||||
|
notifyPrivateMessages.setChecked(enabled);
|
||||||
|
enableAndPersist(notifyPrivateMessages);
|
||||||
|
});
|
||||||
|
nm.getNotifyGroupMessages().observe(lifecycleOwner, enabled -> {
|
||||||
|
notifyGroupMessages.setChecked(enabled);
|
||||||
|
enableAndPersist(notifyGroupMessages);
|
||||||
|
});
|
||||||
|
nm.getNotifyForumPosts().observe(lifecycleOwner, enabled -> {
|
||||||
|
notifyForumPosts.setChecked(enabled);
|
||||||
|
enableAndPersist(notifyForumPosts);
|
||||||
|
});
|
||||||
|
nm.getNotifyBlogPosts().observe(lifecycleOwner, enabled -> {
|
||||||
|
notifyBlogPosts.setChecked(enabled);
|
||||||
|
enableAndPersist(notifyBlogPosts);
|
||||||
|
});
|
||||||
|
nm.getNotifyVibration().observe(lifecycleOwner, enabled -> {
|
||||||
|
notifyVibration.setChecked(enabled);
|
||||||
|
enableAndPersist(notifyVibration);
|
||||||
|
});
|
||||||
|
nm.getNotifySound().observe(lifecycleOwner, enabled -> {
|
||||||
|
String text;
|
||||||
|
if (enabled) {
|
||||||
|
String ringtoneName = nm.getRingtoneName();
|
||||||
|
if (isNullOrEmpty(ringtoneName)) {
|
||||||
|
text = getString(R.string.notify_sound_setting_default);
|
||||||
|
} else {
|
||||||
|
text = ringtoneName;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text = getString(R.string.notify_sound_setting_disabled);
|
||||||
|
}
|
||||||
|
notifySound.setSummary(text);
|
||||||
|
notifySound.setEnabled(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
requireActivity().setTitle(R.string.notification_settings_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int request, int result,
|
||||||
|
@Nullable Intent data) {
|
||||||
|
super.onActivityResult(request, result, data);
|
||||||
|
if (request == REQUEST_RINGTONE && result == RESULT_OK &&
|
||||||
|
data != null) {
|
||||||
|
Uri uri = data.getParcelableExtra(EXTRA_RINGTONE_PICKED_URI);
|
||||||
|
nm.onRingtoneSet(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(NOTIFICATION_CHANNEL_API)
|
||||||
|
private void setupNotificationPreference(SwitchPreferenceCompat pref,
|
||||||
|
String channelId, @StringRes int summary) {
|
||||||
|
pref.setWidgetLayoutResource(0);
|
||||||
|
pref.setSummary(summary);
|
||||||
|
pref.setEnabled(true);
|
||||||
|
pref.setOnPreferenceClickListener(clickedPref -> {
|
||||||
|
String packageName = requireContext().getPackageName();
|
||||||
|
Intent intent = new Intent(ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
||||||
|
.putExtra(EXTRA_APP_PACKAGE, packageName)
|
||||||
|
.putExtra(EXTRA_CHANNEL_ID, channelId);
|
||||||
|
Context ctx = requireContext();
|
||||||
|
if (intent.resolveActivity(ctx.getPackageManager()) != null) {
|
||||||
|
startActivity(intent);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(ctx, R.string.error_start_activity, LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onNotificationSoundClicked() {
|
||||||
|
String title = getString(R.string.choose_ringtone_title);
|
||||||
|
Intent i = new Intent(ACTION_RINGTONE_PICKER);
|
||||||
|
i.putExtra(EXTRA_RINGTONE_TYPE, TYPE_NOTIFICATION);
|
||||||
|
i.putExtra(EXTRA_RINGTONE_TITLE, title);
|
||||||
|
i.putExtra(EXTRA_RINGTONE_DEFAULT_URI,
|
||||||
|
DEFAULT_NOTIFICATION_URI);
|
||||||
|
i.putExtra(EXTRA_RINGTONE_SHOW_SILENT, true);
|
||||||
|
if (requireNonNull(nm.getNotifySound().getValue())) {
|
||||||
|
Uri uri;
|
||||||
|
String ringtoneUri = nm.getRingtoneUri();
|
||||||
|
if (isNullOrEmpty(ringtoneUri))
|
||||||
|
uri = DEFAULT_NOTIFICATION_URI;
|
||||||
|
else uri = Uri.parse(ringtoneUri);
|
||||||
|
i.putExtra(EXTRA_RINGTONE_EXISTING_URI, uri);
|
||||||
|
}
|
||||||
|
if (i.resolveActivity(requireActivity().getPackageManager()) != null) {
|
||||||
|
startActivityForResult(i, REQUEST_RINGTONE);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), R.string.cannot_load_ringtone,
|
||||||
|
LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
package org.briarproject.briar.android.settings;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.Ringtone;
|
||||||
|
import android.media.RingtoneManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
|
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;
|
||||||
|
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_BLOG;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_FORUM;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_GROUP;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_PRIVATE;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_NAME;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_URI;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_SOUND;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_VIBRATION;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
class NotificationsManager {
|
||||||
|
|
||||||
|
private final static Logger LOG =
|
||||||
|
getLogger(NotificationsManager.class.getName());
|
||||||
|
|
||||||
|
private final Context ctx;
|
||||||
|
private final SettingsManager settingsManager;
|
||||||
|
private final Executor dbExecutor;
|
||||||
|
|
||||||
|
private final MutableLiveData<Boolean> notifyPrivateMessages =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<Boolean> notifyGroupMessages =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<Boolean> notifyForumPosts =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<Boolean> notifyBlogPosts =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<Boolean> notifyVibration =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<Boolean> notifySound =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
|
||||||
|
private volatile String ringtoneName, ringtoneUri;
|
||||||
|
|
||||||
|
NotificationsManager(Context ctx,
|
||||||
|
SettingsManager settingsManager,
|
||||||
|
Executor dbExecutor) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.settingsManager = settingsManager;
|
||||||
|
this.dbExecutor = dbExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSettings(Settings settings) {
|
||||||
|
notifyPrivateMessages.postValue(settings.getBoolean(
|
||||||
|
PREF_NOTIFY_PRIVATE, true));
|
||||||
|
notifyGroupMessages.postValue(settings.getBoolean(
|
||||||
|
PREF_NOTIFY_GROUP, true));
|
||||||
|
notifyForumPosts.postValue(settings.getBoolean(
|
||||||
|
PREF_NOTIFY_FORUM, true));
|
||||||
|
notifyBlogPosts.postValue(settings.getBoolean(
|
||||||
|
PREF_NOTIFY_BLOG, true));
|
||||||
|
notifyVibration.postValue(settings.getBoolean(
|
||||||
|
PREF_NOTIFY_VIBRATION, true));
|
||||||
|
ringtoneName = settings.get(PREF_NOTIFY_RINGTONE_NAME);
|
||||||
|
ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
|
||||||
|
notifySound.postValue(settings.getBoolean(PREF_NOTIFY_SOUND, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRingtoneSet(@Nullable Uri uri) {
|
||||||
|
Settings s = new Settings();
|
||||||
|
if (uri == null) {
|
||||||
|
// The user chose silence
|
||||||
|
s.putBoolean(PREF_NOTIFY_SOUND, false);
|
||||||
|
s.put(PREF_NOTIFY_RINGTONE_NAME, "");
|
||||||
|
s.put(PREF_NOTIFY_RINGTONE_URI, "");
|
||||||
|
} else if (RingtoneManager.isDefault(uri)) {
|
||||||
|
// The user chose the default
|
||||||
|
s.putBoolean(PREF_NOTIFY_SOUND, true);
|
||||||
|
s.put(PREF_NOTIFY_RINGTONE_NAME, "");
|
||||||
|
s.put(PREF_NOTIFY_RINGTONE_URI, "");
|
||||||
|
} else {
|
||||||
|
// The user chose a ringtone other than the default
|
||||||
|
Ringtone r = RingtoneManager.getRingtone(ctx, uri);
|
||||||
|
if (r == null || "file".equals(uri.getScheme())) {
|
||||||
|
Toast.makeText(ctx, R.string.cannot_load_ringtone, LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
String name = r.getTitle(ctx);
|
||||||
|
s.putBoolean(PREF_NOTIFY_SOUND, true);
|
||||||
|
s.put(PREF_NOTIFY_RINGTONE_NAME, name);
|
||||||
|
s.put(PREF_NOTIFY_RINGTONE_URI, uri.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dbExecutor.execute(() -> {
|
||||||
|
try {
|
||||||
|
long start = now();
|
||||||
|
settingsManager.mergeSettings(s, SETTINGS_NAMESPACE);
|
||||||
|
logDuration(LOG, "Merging notification settings", start);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<Boolean> getNotifyPrivateMessages() {
|
||||||
|
return notifyPrivateMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<Boolean> getNotifyGroupMessages() {
|
||||||
|
return notifyGroupMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<Boolean> getNotifyForumPosts() {
|
||||||
|
return notifyForumPosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<Boolean> getNotifyBlogPosts() {
|
||||||
|
return notifyBlogPosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<Boolean> getNotifyVibration() {
|
||||||
|
return notifyVibration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
LiveData<Boolean> getNotifySound() {
|
||||||
|
return notifySound;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getRingtoneName() {
|
||||||
|
return ringtoneName;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getRingtoneUri() {
|
||||||
|
return ringtoneUri;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
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.SwitchPreferenceCompat;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||||
|
import static org.briarproject.briar.android.settings.SettingsActivity.enableAndPersist;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.hasScreenLock;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class SecurityFragment extends PreferenceFragmentCompat {
|
||||||
|
|
||||||
|
public static final String PREF_SCREEN_LOCK = "pref_key_lock";
|
||||||
|
public static final String PREF_SCREEN_LOCK_TIMEOUT =
|
||||||
|
"pref_key_lock_timeout";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
|
|
||||||
|
private SettingsViewModel viewModel;
|
||||||
|
private SwitchPreferenceCompat screenLock;
|
||||||
|
private ListPreference screenLockTimeout;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
getAndroidComponent(context).inject(this);
|
||||||
|
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
|
||||||
|
.get(SettingsViewModel.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreatePreferences(Bundle bundle, String s) {
|
||||||
|
addPreferencesFromResource(R.xml.settings_security);
|
||||||
|
getPreferenceManager().setPreferenceDataStore(viewModel.settingsStore);
|
||||||
|
|
||||||
|
screenLock = findPreference(PREF_SCREEN_LOCK);
|
||||||
|
screenLockTimeout =
|
||||||
|
requireNonNull(findPreference(PREF_SCREEN_LOCK_TIMEOUT));
|
||||||
|
|
||||||
|
screenLockTimeout.setSummaryProvider(preference -> {
|
||||||
|
CharSequence timeout = screenLockTimeout.getValue();
|
||||||
|
String never = getString(R.string.pref_lock_timeout_value_never);
|
||||||
|
if (timeout.equals(never)) {
|
||||||
|
return getString(R.string.pref_lock_timeout_never_summary);
|
||||||
|
} else {
|
||||||
|
return getString(R.string.pref_lock_timeout_summary,
|
||||||
|
screenLockTimeout.getEntry());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
if (SDK_INT < 21) {
|
||||||
|
screenLock.setVisible(false);
|
||||||
|
screenLockTimeout.setVisible(false);
|
||||||
|
} else {
|
||||||
|
// timeout depends on screenLock and gets disabled automatically
|
||||||
|
LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
|
||||||
|
viewModel.getScreenLockTimeout().observe(lifecycleOwner, value -> {
|
||||||
|
screenLockTimeout.setValue(value);
|
||||||
|
enableAndPersist(screenLockTimeout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
requireActivity().setTitle(R.string.security_settings_title);
|
||||||
|
checkScreenLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkScreenLock() {
|
||||||
|
if (SDK_INT < 21) return;
|
||||||
|
LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
|
||||||
|
viewModel.getScreenLockEnabled().removeObservers(lifecycleOwner);
|
||||||
|
if (hasScreenLock(requireActivity())) {
|
||||||
|
viewModel.getScreenLockEnabled().observe(lifecycleOwner, on -> {
|
||||||
|
screenLock.setChecked(on);
|
||||||
|
enableAndPersist(screenLock);
|
||||||
|
});
|
||||||
|
screenLock.setSummary(R.string.pref_lock_summary);
|
||||||
|
} else {
|
||||||
|
screenLock.setEnabled(false);
|
||||||
|
screenLock.setPersistent(false);
|
||||||
|
screenLock.setChecked(false);
|
||||||
|
screenLock.setSummary(R.string.pref_lock_disabled_summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,41 +1,37 @@
|
|||||||
package org.briarproject.briar.android.settings;
|
package org.briarproject.briar.android.settings;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FeatureFlags;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
|
||||||
import org.briarproject.briar.android.view.AuthorView;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.fragment.app.Fragment;
|
||||||
import de.hdodenhof.circleimageview.CircleImageView;
|
import androidx.fragment.app.FragmentFactory;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback;
|
||||||
|
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
@MethodsNotNullByDefault
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_AVATAR_IMAGE;
|
@ParametersNotNullByDefault
|
||||||
|
public class SettingsActivity extends BriarActivity
|
||||||
|
implements OnPreferenceStartFragmentCallback {
|
||||||
|
|
||||||
public class SettingsActivity extends BriarActivity {
|
static final String EXTRA_THEME_CHANGE = "themeChange";
|
||||||
|
|
||||||
@Inject
|
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
|
||||||
private SettingsViewModel settingsViewModel;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
FeatureFlags featureFlags;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle bundle) {
|
public void injectActivity(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle bundle) {
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
@@ -44,43 +40,15 @@ public class SettingsActivity extends BriarActivity {
|
|||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentView(R.layout.activity_settings);
|
// show display fragment after theme change
|
||||||
|
Bundle extras = getIntent().getExtras();
|
||||||
if (featureFlags.shouldEnableProfilePictures()) {
|
if (bundle == null && extras != null &&
|
||||||
ViewModelProvider provider =
|
extras.getBoolean(EXTRA_THEME_CHANGE, false)) {
|
||||||
new ViewModelProvider(this, viewModelFactory);
|
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
settingsViewModel = provider.get(SettingsViewModel.class);
|
showNextFragment(fragmentManager, new DisplayFragment());
|
||||||
|
|
||||||
TextView textViewUserName = findViewById(R.id.username);
|
|
||||||
CircleImageView imageViewAvatar =
|
|
||||||
findViewById(R.id.avatarImage);
|
|
||||||
|
|
||||||
settingsViewModel.getOwnIdentityInfo().observe(this, us -> {
|
|
||||||
textViewUserName.setText(us.getLocalAuthor().getName());
|
|
||||||
AuthorView.setAvatar(imageViewAvatar,
|
|
||||||
us.getLocalAuthor().getId(), us.getAuthorInfo());
|
|
||||||
});
|
|
||||||
|
|
||||||
settingsViewModel.getSetAvatarFailed()
|
|
||||||
.observeEvent(this, failed -> {
|
|
||||||
if (failed) {
|
|
||||||
Toast.makeText(this,
|
|
||||||
R.string.change_profile_picture_failed_message,
|
|
||||||
LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
View avatarGroup = findViewById(R.id.avatarGroup);
|
|
||||||
avatarGroup.setOnClickListener(e -> selectAvatarImage());
|
|
||||||
} else {
|
|
||||||
View view = findViewById(R.id.avatarGroup);
|
|
||||||
view.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
setContentView(R.layout.activity_settings);
|
||||||
public void injectActivity(ActivityComponent component) {
|
|
||||||
component.inject(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -92,30 +60,40 @@ public class SettingsActivity extends BriarActivity {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectAvatarImage() {
|
@Override
|
||||||
Intent intent = UiUtils.createSelectImageIntent(false);
|
public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller,
|
||||||
startActivityForResult(intent, REQUEST_AVATAR_IMAGE);
|
Preference pref) {
|
||||||
|
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
|
FragmentFactory fragmentFactory = fragmentManager.getFragmentFactory();
|
||||||
|
Fragment fragment = fragmentFactory
|
||||||
|
.instantiate(getClassLoader(), pref.getFragment());
|
||||||
|
fragment.setTargetFragment(caller, 0);
|
||||||
|
// Replace the existing Fragment with the new Fragment
|
||||||
|
showNextFragment(fragmentManager, fragment);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void showNextFragment(FragmentManager fragmentManager, Fragment f) {
|
||||||
protected void onActivityResult(int request, int result,
|
fragmentManager.beginTransaction()
|
||||||
@Nullable Intent data) {
|
.setCustomAnimations(R.anim.step_next_in,
|
||||||
super.onActivityResult(request, result, data);
|
R.anim.step_previous_out, R.anim.step_previous_in,
|
||||||
|
R.anim.step_next_out)
|
||||||
|
.replace(R.id.fragmentContainer, f)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
if (request == REQUEST_AVATAR_IMAGE && result == RESULT_OK) {
|
/**
|
||||||
onAvatarImageReceived(data);
|
* 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAvatarImageReceived(@Nullable Intent resultData) {
|
|
||||||
if (resultData == null) return;
|
|
||||||
Uri uri = resultData.getData();
|
|
||||||
if (uri == null) return;
|
|
||||||
|
|
||||||
ConfirmAvatarDialogFragment dialog =
|
|
||||||
ConfirmAvatarDialogFragment.newInstance(uri);
|
|
||||||
dialog.show(getSupportFragmentManager(),
|
|
||||||
ConfirmAvatarDialogFragment.TAG);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,761 +1,120 @@
|
|||||||
package org.briarproject.briar.android.settings;
|
package org.briarproject.briar.android.settings;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
|
||||||
import android.media.Ringtone;
|
|
||||||
import android.media.RingtoneManager;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
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.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
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;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.Localizer;
|
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.core.text.TextUtilsCompat;
|
|
||||||
import androidx.preference.ListPreference;
|
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.Preference.OnPreferenceChangeListener;
|
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
import androidx.preference.PreferenceGroup;
|
import androidx.preference.PreferenceGroup;
|
||||||
import androidx.preference.SwitchPreference;
|
|
||||||
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
import static android.app.Activity.RESULT_OK;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
|
||||||
import static android.media.RingtoneManager.ACTION_RINGTONE_PICKER;
|
|
||||||
import static android.media.RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI;
|
|
||||||
import static android.media.RingtoneManager.EXTRA_RINGTONE_EXISTING_URI;
|
|
||||||
import static android.media.RingtoneManager.EXTRA_RINGTONE_PICKED_URI;
|
|
||||||
import static android.media.RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT;
|
|
||||||
import static android.media.RingtoneManager.EXTRA_RINGTONE_TITLE;
|
|
||||||
import static android.media.RingtoneManager.EXTRA_RINGTONE_TYPE;
|
|
||||||
import static android.media.RingtoneManager.TYPE_NOTIFICATION;
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.provider.Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS;
|
|
||||||
import static android.provider.Settings.EXTRA_APP_PACKAGE;
|
|
||||||
import static android.provider.Settings.EXTRA_CHANNEL_ID;
|
|
||||||
import static android.provider.Settings.System.DEFAULT_NOTIFICATION_URI;
|
|
||||||
import static android.widget.Toast.LENGTH_SHORT;
|
|
||||||
import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR;
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.INFO;
|
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||||
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.BriarApplication.ENTRY_ACTIVITY;
|
|
||||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
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.activity.RequestCodes.REQUEST_AVATAR_IMAGE;
|
||||||
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.SIGN_OUT_URI;
|
import static org.briarproject.briar.android.util.UiUtils.createSelectImageIntent;
|
||||||
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.android.util.UiUtils.triggerFeedback;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.BLOG_CHANNEL_ID;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.CONTACT_CHANNEL_ID;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.FORUM_CHANNEL_ID;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.GROUP_CHANNEL_ID;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_BLOG;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_FORUM;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_GROUP;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_PRIVATE;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_NAME;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_URI;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_SOUND;
|
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_VIBRATION;
|
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class SettingsFragment extends PreferenceFragmentCompat
|
public class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
implements EventListener, OnPreferenceChangeListener {
|
|
||||||
|
|
||||||
public static final String SETTINGS_NAMESPACE = "android-ui";
|
public static final String SETTINGS_NAMESPACE = "android-ui";
|
||||||
public static final String LANGUAGE = "pref_key_language";
|
|
||||||
public static final String PREF_SCREEN_LOCK = "pref_key_lock";
|
|
||||||
public static final String PREF_SCREEN_LOCK_TIMEOUT =
|
|
||||||
"pref_key_lock_timeout";
|
|
||||||
public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in";
|
|
||||||
|
|
||||||
private static final String BT_NAMESPACE =
|
private static final String PREF_KEY_AVATAR = "pref_key_avatar";
|
||||||
BluetoothConstants.ID.getString();
|
private static final String PREF_KEY_FEEDBACK = "pref_key_send_feedback";
|
||||||
private static final String BT_ENABLE = "pref_key_bluetooth";
|
private static final String PREF_KEY_DEV = "pref_key_dev";
|
||||||
|
private static final String PREF_KEY_EXPLODE = "pref_key_explode";
|
||||||
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 ListPreference language;
|
|
||||||
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;
|
|
||||||
private SwitchPreference notifyGroupMessages;
|
|
||||||
private SwitchPreference notifyForumPosts;
|
|
||||||
private SwitchPreference notifyBlogPosts;
|
|
||||||
private SwitchPreference notifyVibration;
|
|
||||||
|
|
||||||
private Preference notifySound;
|
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
|
||||||
private volatile Settings settings, btSettings, wifiSettings, torSettings;
|
|
||||||
private volatile boolean settingsLoaded = false;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
volatile SettingsManager settingsManager;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
@Inject
|
|
||||||
volatile EventBus eventBus;
|
private SettingsViewModel viewModel;
|
||||||
@Inject
|
private AvatarPreference prefAvatar;
|
||||||
LocationUtils locationUtils;
|
|
||||||
@Inject
|
|
||||||
CircumventionProvider circumventionProvider;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
listener = (SettingsActivity) context;
|
getAndroidComponent(context).inject(this);
|
||||||
listener.getActivityComponent().inject(this);
|
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
|
||||||
|
.get(SettingsViewModel.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreatePreferences(Bundle bundle, String s) {
|
public void onCreatePreferences(Bundle bundle, String s) {
|
||||||
addPreferencesFromResource(R.xml.settings);
|
addPreferencesFromResource(R.xml.settings);
|
||||||
|
|
||||||
language = findPreference(LANGUAGE);
|
prefAvatar = requireNonNull(findPreference(PREF_KEY_AVATAR));
|
||||||
setLanguageEntries();
|
if (viewModel.shouldEnableProfilePictures()) {
|
||||||
ListPreference theme = findPreference("pref_key_theme");
|
prefAvatar.setOnPreferenceClickListener(preference -> {
|
||||||
enableBluetooth = findPreference(BT_ENABLE);
|
Intent intent = createSelectImageIntent(false);
|
||||||
enableWifi = findPreference(WIFI_ENABLE);
|
startActivityForResult(intent, REQUEST_AVATAR_IMAGE);
|
||||||
enableTor = findPreference(TOR_ENABLE);
|
return true;
|
||||||
torNetwork = findPreference(TOR_NETWORK);
|
});
|
||||||
torMobile = findPreference(TOR_MOBILE);
|
} else {
|
||||||
torOnlyWhenCharging = findPreference(TOR_ONLY_WHEN_CHARGING);
|
prefAvatar.setVisible(false);
|
||||||
screenLock = findPreference(PREF_SCREEN_LOCK);
|
}
|
||||||
screenLockTimeout = findPreference(PREF_SCREEN_LOCK_TIMEOUT);
|
|
||||||
notifyPrivateMessages =
|
|
||||||
findPreference("pref_key_notify_private_messages");
|
|
||||||
notifyGroupMessages = findPreference("pref_key_notify_group_messages");
|
|
||||||
notifyForumPosts = findPreference("pref_key_notify_forum_posts");
|
|
||||||
notifyBlogPosts = findPreference("pref_key_notify_blog_posts");
|
|
||||||
notifyVibration = findPreference("pref_key_notify_vibration");
|
|
||||||
notifySound = findPreference("pref_key_notify_sound");
|
|
||||||
|
|
||||||
language.setOnPreferenceChangeListener(this);
|
|
||||||
theme.setOnPreferenceChangeListener((preference, newValue) -> {
|
|
||||||
if (getActivity() != null) {
|
|
||||||
// activate new theme
|
|
||||||
UiUtils.setTheme(getActivity(), (String) newValue);
|
|
||||||
// bring up parent activity, so it can change its theme as well
|
|
||||||
// upstream bug: https://issuetracker.google.com/issues/38352704
|
|
||||||
Intent intent = new Intent(getActivity(), ENTRY_ACTIVITY);
|
|
||||||
intent.setFlags(
|
|
||||||
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
startActivity(intent);
|
|
||||||
// bring this activity back to the foreground
|
|
||||||
intent = new Intent(getActivity(), getActivity().getClass());
|
|
||||||
startActivity(intent);
|
|
||||||
getActivity().finish();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
enableBluetooth.setOnPreferenceChangeListener(this);
|
|
||||||
enableWifi.setOnPreferenceChangeListener(this);
|
|
||||||
enableTor.setOnPreferenceChangeListener(this);
|
|
||||||
torNetwork.setOnPreferenceChangeListener(this);
|
|
||||||
torMobile.setOnPreferenceChangeListener(this);
|
|
||||||
torOnlyWhenCharging.setOnPreferenceChangeListener(this);
|
|
||||||
screenLock.setOnPreferenceChangeListener(this);
|
|
||||||
screenLockTimeout.setOnPreferenceChangeListener(this);
|
|
||||||
|
|
||||||
Preference prefFeedback =
|
Preference prefFeedback =
|
||||||
requireNonNull(findPreference("pref_key_send_feedback"));
|
requireNonNull(findPreference(PREF_KEY_FEEDBACK));
|
||||||
prefFeedback.setOnPreferenceClickListener(preference -> {
|
prefFeedback.setOnPreferenceClickListener(preference -> {
|
||||||
triggerFeedback(requireContext());
|
triggerFeedback(requireContext());
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (SDK_INT < 27) {
|
Preference explode = requireNonNull(findPreference(PREF_KEY_EXPLODE));
|
||||||
// remove System Default Theme option from preference entries
|
|
||||||
// as it is not functional on this API anyway
|
|
||||||
List<CharSequence> entries =
|
|
||||||
new ArrayList<>(Arrays.asList(theme.getEntries()));
|
|
||||||
entries.remove(getString(R.string.pref_theme_system));
|
|
||||||
theme.setEntries(entries.toArray(new CharSequence[0]));
|
|
||||||
// also remove corresponding value
|
|
||||||
List<CharSequence> values =
|
|
||||||
new ArrayList<>(Arrays.asList(theme.getEntryValues()));
|
|
||||||
values.remove(getString(R.string.pref_theme_system_value));
|
|
||||||
theme.setEntryValues(values.toArray(new CharSequence[0]));
|
|
||||||
}
|
|
||||||
Preference explode = requireNonNull(findPreference("pref_key_explode"));
|
|
||||||
if (IS_DEBUG_BUILD) {
|
if (IS_DEBUG_BUILD) {
|
||||||
explode.setOnPreferenceClickListener(preference -> {
|
explode.setOnPreferenceClickListener(preference -> {
|
||||||
throw new RuntimeException("Boom!");
|
throw new RuntimeException("Boom!");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
explode.setVisible(false);
|
PreferenceGroup dev = requireNonNull(findPreference(PREF_KEY_DEV));
|
||||||
findPreference("pref_key_test_data").setVisible(false);
|
dev.setVisible(false);
|
||||||
PreferenceGroup testing = explode.getParent();
|
|
||||||
if (testing == null) throw new AssertionError();
|
|
||||||
testing.setVisible(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater,
|
public void onViewCreated(@NonNull View view,
|
||||||
@Nullable ViewGroup container,
|
|
||||||
@Nullable Bundle savedInstanceState) {
|
@Nullable Bundle savedInstanceState) {
|
||||||
View view = super.onCreateView(inflater, container, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
ColorDrawable divider = new ColorDrawable(
|
|
||||||
ContextCompat.getColor(requireContext(), R.color.divider));
|
viewModel.getOwnIdentityInfo().observe(getViewLifecycleOwner(), us ->
|
||||||
setDivider(divider);
|
prefAvatar.setOwnIdentityInfo(us)
|
||||||
return view;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
eventBus.addListener(this);
|
requireActivity().setTitle(R.string.settings_button);
|
||||||
setSettingsEnabled(false);
|
|
||||||
loadSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onActivityResult(int request, int result,
|
||||||
super.onStop();
|
@Nullable Intent data) {
|
||||||
eventBus.removeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setLanguageEntries() {
|
|
||||||
CharSequence[] tags = language.getEntryValues();
|
|
||||||
List<CharSequence> entries = new ArrayList<>(tags.length);
|
|
||||||
List<CharSequence> entryValues = new ArrayList<>(tags.length);
|
|
||||||
for (CharSequence cs : tags) {
|
|
||||||
String tag = cs.toString();
|
|
||||||
if (tag.equals("default")) {
|
|
||||||
entries.add(getString(R.string.pref_language_default));
|
|
||||||
entryValues.add(tag);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Locale locale = Localizer.getLocaleFromTag(tag);
|
|
||||||
if (locale == null)
|
|
||||||
throw new IllegalStateException();
|
|
||||||
// Exclude RTL locales on API < 17, they won't be laid out correctly
|
|
||||||
if (SDK_INT < 17 && !isLeftToRight(locale)) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Skipping RTL locale " + tag);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String nativeName = locale.getDisplayName(locale);
|
|
||||||
// Fallback to English if the name is unknown in both native and
|
|
||||||
// current locale.
|
|
||||||
if (nativeName.equals(tag)) {
|
|
||||||
String tmp = locale.getDisplayLanguage(Locale.ENGLISH);
|
|
||||||
if (!tmp.isEmpty() && !tmp.equals(nativeName))
|
|
||||||
nativeName = tmp;
|
|
||||||
}
|
|
||||||
// Prefix with LRM marker to prevent any RTL direction
|
|
||||||
entries.add("\u200E" + nativeName.substring(0, 1).toUpperCase()
|
|
||||||
+ nativeName.substring(1));
|
|
||||||
entryValues.add(tag);
|
|
||||||
}
|
|
||||||
language.setEntries(entries.toArray(new CharSequence[0]));
|
|
||||||
language.setEntryValues(entryValues.toArray(new CharSequence[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isLeftToRight(Locale locale) {
|
|
||||||
// TextUtilsCompat returns the wrong direction for Hebrew on some phones
|
|
||||||
String language = locale.getLanguage();
|
|
||||||
if (language.equals("iw") || language.equals("he")) return false;
|
|
||||||
int direction = TextUtilsCompat.getLayoutDirectionFromLocale(locale);
|
|
||||||
return direction == LAYOUT_DIRECTION_LTR;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
notifyPrivateMessages.setChecked(settings.getBoolean(
|
|
||||||
PREF_NOTIFY_PRIVATE, true));
|
|
||||||
notifyGroupMessages.setChecked(settings.getBoolean(
|
|
||||||
PREF_NOTIFY_GROUP, true));
|
|
||||||
notifyForumPosts.setChecked(settings.getBoolean(
|
|
||||||
PREF_NOTIFY_FORUM, true));
|
|
||||||
notifyBlogPosts.setChecked(settings.getBoolean(
|
|
||||||
PREF_NOTIFY_BLOG, true));
|
|
||||||
notifyVibration.setChecked(settings.getBoolean(
|
|
||||||
PREF_NOTIFY_VIBRATION, true));
|
|
||||||
notifyPrivateMessages.setOnPreferenceChangeListener(this);
|
|
||||||
notifyGroupMessages.setOnPreferenceChangeListener(this);
|
|
||||||
notifyForumPosts.setOnPreferenceChangeListener(this);
|
|
||||||
notifyBlogPosts.setOnPreferenceChangeListener(this);
|
|
||||||
notifyVibration.setOnPreferenceChangeListener(this);
|
|
||||||
notifySound.setOnPreferenceClickListener(
|
|
||||||
pref -> onNotificationSoundClicked());
|
|
||||||
String text;
|
|
||||||
if (settings.getBoolean(PREF_NOTIFY_SOUND, true)) {
|
|
||||||
String ringtoneName =
|
|
||||||
settings.get(PREF_NOTIFY_RINGTONE_NAME);
|
|
||||||
if (StringUtils.isNullOrEmpty(ringtoneName)) {
|
|
||||||
text = getString(R.string.notify_sound_setting_default);
|
|
||||||
} else {
|
|
||||||
text = ringtoneName;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
text = getString(R.string.notify_sound_setting_disabled);
|
|
||||||
}
|
|
||||||
notifySound.setSummary(text);
|
|
||||||
} else {
|
|
||||||
setupNotificationPreference(notifyPrivateMessages,
|
|
||||||
CONTACT_CHANNEL_ID,
|
|
||||||
R.string.notify_private_messages_setting_summary_26);
|
|
||||||
setupNotificationPreference(notifyGroupMessages,
|
|
||||||
GROUP_CHANNEL_ID,
|
|
||||||
R.string.notify_group_messages_setting_summary_26);
|
|
||||||
setupNotificationPreference(notifyForumPosts, FORUM_CHANNEL_ID,
|
|
||||||
R.string.notify_forum_posts_setting_summary_26);
|
|
||||||
setupNotificationPreference(notifyBlogPosts, BLOG_CHANNEL_ID,
|
|
||||||
R.string.notify_blog_posts_setting_summary_26);
|
|
||||||
notifyVibration.setVisible(false);
|
|
||||||
notifySound.setVisible(false);
|
|
||||||
}
|
|
||||||
setSettingsEnabled(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSettingsEnabled(boolean enabled) {
|
|
||||||
// preferences not needed here, because handled by SharedPreferences:
|
|
||||||
// - pref_key_theme
|
|
||||||
// - pref_key_notify_sign_in
|
|
||||||
// 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);
|
|
||||||
notifyForumPosts.setEnabled(enabled);
|
|
||||||
notifyBlogPosts.setEnabled(enabled);
|
|
||||||
notifyVibration.setEnabled(enabled);
|
|
||||||
notifySound.setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayScreenLockSetting() {
|
|
||||||
if (SDK_INT < 21) {
|
|
||||||
screenLock.setVisible(false);
|
|
||||||
screenLockTimeout.setVisible(false);
|
|
||||||
} else {
|
|
||||||
if (getActivity() != null && hasScreenLock(getActivity())) {
|
|
||||||
screenLock.setEnabled(true);
|
|
||||||
screenLock.setChecked(
|
|
||||||
settings.getBoolean(PREF_SCREEN_LOCK, false));
|
|
||||||
screenLock.setSummary(R.string.pref_lock_summary);
|
|
||||||
} else {
|
|
||||||
screenLock.setEnabled(false);
|
|
||||||
screenLock.setChecked(false);
|
|
||||||
screenLock.setSummary(R.string.pref_lock_disabled_summary);
|
|
||||||
}
|
|
||||||
// timeout depends on screenLock and gets disabled automatically
|
|
||||||
int timeout = settings.getInt(PREF_SCREEN_LOCK_TIMEOUT,
|
|
||||||
Integer.valueOf(getString(
|
|
||||||
R.string.pref_lock_timeout_value_default)));
|
|
||||||
String newValue = String.valueOf(timeout);
|
|
||||||
screenLockTimeout.setValue(newValue);
|
|
||||||
setScreenLockTimeoutSummary(newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setScreenLockTimeoutSummary(String timeout) {
|
|
||||||
String never = getString(R.string.pref_lock_timeout_value_never);
|
|
||||||
if (timeout.equals(never)) {
|
|
||||||
screenLockTimeout
|
|
||||||
.setSummary(R.string.pref_lock_timeout_never_summary);
|
|
||||||
} else {
|
|
||||||
screenLockTimeout
|
|
||||||
.setSummary(R.string.pref_lock_timeout_summary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(26)
|
|
||||||
private void setupNotificationPreference(SwitchPreference pref,
|
|
||||||
String channelId, @StringRes int summary) {
|
|
||||||
pref.setWidgetLayoutResource(0);
|
|
||||||
pref.setSummary(summary);
|
|
||||||
pref.setOnPreferenceClickListener(clickedPref -> {
|
|
||||||
String packageName = requireContext().getPackageName();
|
|
||||||
Intent intent = new Intent(ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
|
||||||
.putExtra(EXTRA_APP_PACKAGE, packageName)
|
|
||||||
.putExtra(EXTRA_CHANNEL_ID, channelId);
|
|
||||||
Context ctx = requireContext();
|
|
||||||
if (intent.resolveActivity(ctx.getPackageManager()) != null) {
|
|
||||||
startActivity(intent);
|
|
||||||
} else {
|
|
||||||
Toast.makeText(ctx, R.string.error_start_activity, LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean onNotificationSoundClicked() {
|
|
||||||
String title = getString(R.string.choose_ringtone_title);
|
|
||||||
Intent i = new Intent(ACTION_RINGTONE_PICKER);
|
|
||||||
i.putExtra(EXTRA_RINGTONE_TYPE, TYPE_NOTIFICATION);
|
|
||||||
i.putExtra(EXTRA_RINGTONE_TITLE, title);
|
|
||||||
i.putExtra(EXTRA_RINGTONE_DEFAULT_URI,
|
|
||||||
DEFAULT_NOTIFICATION_URI);
|
|
||||||
i.putExtra(EXTRA_RINGTONE_SHOW_SILENT, true);
|
|
||||||
if (settings.getBoolean(PREF_NOTIFY_SOUND, true)) {
|
|
||||||
Uri uri;
|
|
||||||
String ringtoneUri =
|
|
||||||
settings.get(PREF_NOTIFY_RINGTONE_URI);
|
|
||||||
if (StringUtils.isNullOrEmpty(ringtoneUri))
|
|
||||||
uri = DEFAULT_NOTIFICATION_URI;
|
|
||||||
else uri = Uri.parse(ringtoneUri);
|
|
||||||
i.putExtra(EXTRA_RINGTONE_EXISTING_URI, uri);
|
|
||||||
}
|
|
||||||
if (i.resolveActivity(requireActivity().getPackageManager()) != null) {
|
|
||||||
startActivityForResult(i, REQUEST_RINGTONE);
|
|
||||||
} else {
|
|
||||||
Toast.makeText(getContext(), R.string.cannot_load_ringtone,
|
|
||||||
LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
||||||
if (preference == language) {
|
|
||||||
if (!language.getValue().equals(newValue))
|
|
||||||
languageChanged((String) newValue);
|
|
||||||
return false;
|
|
||||||
} else 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) {
|
|
||||||
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));
|
|
||||||
storeSettings(s);
|
|
||||||
setScreenLockTimeoutSummary(value);
|
|
||||||
} else if (preference == notifyPrivateMessages) {
|
|
||||||
Settings s = new Settings();
|
|
||||||
s.putBoolean(PREF_NOTIFY_PRIVATE, (Boolean) newValue);
|
|
||||||
storeSettings(s);
|
|
||||||
} else if (preference == notifyGroupMessages) {
|
|
||||||
Settings s = new Settings();
|
|
||||||
s.putBoolean(PREF_NOTIFY_GROUP, (Boolean) newValue);
|
|
||||||
storeSettings(s);
|
|
||||||
} else if (preference == notifyForumPosts) {
|
|
||||||
Settings s = new Settings();
|
|
||||||
s.putBoolean(PREF_NOTIFY_FORUM, (Boolean) newValue);
|
|
||||||
storeSettings(s);
|
|
||||||
} else if (preference == notifyBlogPosts) {
|
|
||||||
Settings s = new Settings();
|
|
||||||
s.putBoolean(PREF_NOTIFY_BLOG, (Boolean) newValue);
|
|
||||||
storeSettings(s);
|
|
||||||
} else if (preference == notifyVibration) {
|
|
||||||
Settings s = new Settings();
|
|
||||||
s.putBoolean(PREF_NOTIFY_VIBRATION, (Boolean) newValue);
|
|
||||||
storeSettings(s);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void languageChanged(String newValue) {
|
|
||||||
AlertDialog.Builder builder =
|
|
||||||
new AlertDialog.Builder(getActivity());
|
|
||||||
builder.setTitle(R.string.pref_language_title);
|
|
||||||
builder.setMessage(R.string.pref_language_changed);
|
|
||||||
builder.setPositiveButton(R.string.sign_out_button,
|
|
||||||
(dialogInterface, i) -> {
|
|
||||||
language.setValue(newValue);
|
|
||||||
Intent intent = new Intent(getContext(), ENTRY_ACTIVITY);
|
|
||||||
intent.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
intent.setData(SIGN_OUT_URI);
|
|
||||||
requireActivity().startActivity(intent);
|
|
||||||
requireActivity().finish();
|
|
||||||
});
|
|
||||||
builder.setNegativeButton(R.string.cancel, null);
|
|
||||||
builder.setCancelable(false);
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void mergeSettings(Settings s, String namespace) {
|
|
||||||
listener.runOnDbThread(() -> {
|
|
||||||
try {
|
|
||||||
long start = now();
|
|
||||||
settingsManager.mergeSettings(s, namespace);
|
|
||||||
logDuration(LOG, "Merging settings", start);
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityResult(int request, int result, Intent data) {
|
|
||||||
super.onActivityResult(request, result, data);
|
super.onActivityResult(request, result, data);
|
||||||
if (request == REQUEST_RINGTONE && result == RESULT_OK) {
|
if (request == REQUEST_AVATAR_IMAGE && result == RESULT_OK) {
|
||||||
Settings s = new Settings();
|
if (data == null) return;
|
||||||
Uri uri = data.getParcelableExtra(EXTRA_RINGTONE_PICKED_URI);
|
Uri uri = data.getData();
|
||||||
if (uri == null) {
|
if (uri == null) return;
|
||||||
// The user chose silence
|
|
||||||
s.putBoolean(PREF_NOTIFY_SOUND, false);
|
|
||||||
s.put(PREF_NOTIFY_RINGTONE_NAME, "");
|
|
||||||
s.put(PREF_NOTIFY_RINGTONE_URI, "");
|
|
||||||
} else if (RingtoneManager.isDefault(uri)) {
|
|
||||||
// The user chose the default
|
|
||||||
s.putBoolean(PREF_NOTIFY_SOUND, true);
|
|
||||||
s.put(PREF_NOTIFY_RINGTONE_NAME, "");
|
|
||||||
s.put(PREF_NOTIFY_RINGTONE_URI, "");
|
|
||||||
} else {
|
|
||||||
// The user chose a ringtone other than the default
|
|
||||||
Ringtone r = RingtoneManager.getRingtone(getContext(), uri);
|
|
||||||
if (r == null || "file".equals(uri.getScheme())) {
|
|
||||||
Toast.makeText(getContext(), R.string.cannot_load_ringtone,
|
|
||||||
LENGTH_SHORT).show();
|
|
||||||
} else {
|
|
||||||
String name = r.getTitle(getContext());
|
|
||||||
s.putBoolean(PREF_NOTIFY_SOUND, true);
|
|
||||||
s.put(PREF_NOTIFY_RINGTONE_NAME, name);
|
|
||||||
s.put(PREF_NOTIFY_RINGTONE_URI, uri.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
storeSettings(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
DialogFragment dialog =
|
||||||
public void eventOccurred(Event e) {
|
ConfirmAvatarDialogFragment.newInstance(uri);
|
||||||
if (e instanceof SettingsUpdatedEvent) {
|
dialog.show(getParentFragmentManager(),
|
||||||
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
ConfirmAvatarDialogFragment.TAG);
|
||||||
String namespace = s.getNamespace();
|
|
||||||
if (namespace.equals(SETTINGS_NAMESPACE)) {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
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.logging.Level.INFO;
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
@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) {
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Store bool setting: " + key + "=" + value);
|
||||||
|
Settings s = new Settings();
|
||||||
|
s.putBoolean(key, value);
|
||||||
|
storeSettings(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putInt(String key, int value) {
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Store int setting: " + key + "=" + value);
|
||||||
|
Settings s = new Settings();
|
||||||
|
s.putInt(key, value);
|
||||||
|
storeSettings(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putString(String key, @Nullable String value) {
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Store string setting: " + key + "=" + value);
|
||||||
|
Settings s = new Settings();
|
||||||
|
s.put(key, value);
|
||||||
|
storeSettings(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,17 +3,34 @@ package org.briarproject.briar.android.settings;
|
|||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FeatureFlags;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
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.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
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.AndroidExecutor;
|
||||||
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
|
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.attachment.UnsupportedMimeTypeException;
|
import org.briarproject.briar.android.attachment.UnsupportedMimeTypeException;
|
||||||
import org.briarproject.briar.android.attachment.media.ImageCompressor;
|
import org.briarproject.briar.android.attachment.media.ImageCompressor;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
|
||||||
import org.briarproject.briar.api.avatar.AvatarManager;
|
import org.briarproject.briar.api.avatar.AvatarManager;
|
||||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||||
import org.briarproject.briar.api.identity.AuthorManager;
|
import org.briarproject.briar.api.identity.AuthorManager;
|
||||||
@@ -25,66 +42,127 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
import androidx.annotation.AnyThread;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
|
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.logException;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
|
import static org.briarproject.briar.android.settings.SecurityFragment.PREF_SCREEN_LOCK;
|
||||||
|
import static org.briarproject.briar.android.settings.SecurityFragment.PREF_SCREEN_LOCK_TIMEOUT;
|
||||||
|
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
|
|
||||||
@NotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
class SettingsViewModel extends AndroidViewModel {
|
@ParametersNotNullByDefault
|
||||||
|
class SettingsViewModel extends DbViewModel implements EventListener {
|
||||||
|
|
||||||
private final static Logger LOG =
|
private final static Logger LOG =
|
||||||
getLogger(SettingsViewModel.class.getName());
|
getLogger(SettingsViewModel.class.getName());
|
||||||
|
|
||||||
|
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 IdentityManager identityManager;
|
||||||
|
private final EventBus eventBus;
|
||||||
private final AvatarManager avatarManager;
|
private final AvatarManager avatarManager;
|
||||||
private final AuthorManager authorManager;
|
private final AuthorManager authorManager;
|
||||||
private final ImageCompressor imageCompressor;
|
private final ImageCompressor imageCompressor;
|
||||||
@IoExecutor
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
@DatabaseExecutor
|
private final FeatureFlags featureFlags;
|
||||||
private final Executor dbExecutor;
|
|
||||||
|
final SettingsStore settingsStore;
|
||||||
|
final TorSummaryProvider torSummaryProvider;
|
||||||
|
final ConnectionsManager connectionsManager;
|
||||||
|
final NotificationsManager notificationsManager;
|
||||||
|
|
||||||
|
private volatile Settings settings;
|
||||||
|
|
||||||
private final MutableLiveData<OwnIdentityInfo> ownIdentityInfo =
|
private final MutableLiveData<OwnIdentityInfo> ownIdentityInfo =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<Boolean> screenLockEnabled =
|
||||||
private final MutableLiveEvent<Boolean> setAvatarFailed =
|
new MutableLiveData<>();
|
||||||
new MutableLiveEvent<>();
|
private final MutableLiveData<String> screenLockTimeout =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SettingsViewModel(Application application,
|
SettingsViewModel(Application application,
|
||||||
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
|
LifecycleManager lifecycleManager,
|
||||||
|
TransactionManager db,
|
||||||
|
AndroidExecutor androidExecutor,
|
||||||
|
SettingsManager settingsManager,
|
||||||
IdentityManager identityManager,
|
IdentityManager identityManager,
|
||||||
|
EventBus eventBus,
|
||||||
AvatarManager avatarManager,
|
AvatarManager avatarManager,
|
||||||
AuthorManager authorManager,
|
AuthorManager authorManager,
|
||||||
ImageCompressor imageCompressor,
|
ImageCompressor imageCompressor,
|
||||||
|
LocationUtils locationUtils,
|
||||||
|
CircumventionProvider circumventionProvider,
|
||||||
@IoExecutor Executor ioExecutor,
|
@IoExecutor Executor ioExecutor,
|
||||||
@DatabaseExecutor Executor dbExecutor) {
|
FeatureFlags featureFlags) {
|
||||||
super(application);
|
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
|
||||||
|
this.settingsManager = settingsManager;
|
||||||
this.identityManager = identityManager;
|
this.identityManager = identityManager;
|
||||||
|
this.eventBus = eventBus;
|
||||||
this.imageCompressor = imageCompressor;
|
this.imageCompressor = imageCompressor;
|
||||||
this.avatarManager = avatarManager;
|
this.avatarManager = avatarManager;
|
||||||
this.authorManager = authorManager;
|
this.authorManager = authorManager;
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.dbExecutor = dbExecutor;
|
this.featureFlags = featureFlags;
|
||||||
|
settingsStore = new SettingsStore(settingsManager, dbExecutor,
|
||||||
|
SETTINGS_NAMESPACE);
|
||||||
|
torSummaryProvider = new TorSummaryProvider(getApplication(),
|
||||||
|
locationUtils, circumventionProvider);
|
||||||
|
connectionsManager =
|
||||||
|
new ConnectionsManager(settingsManager, dbExecutor);
|
||||||
|
notificationsManager = new NotificationsManager(getApplication(),
|
||||||
|
settingsManager, dbExecutor);
|
||||||
|
|
||||||
loadOwnIdentityInfo();
|
eventBus.addListener(this);
|
||||||
|
loadSettings();
|
||||||
|
if (shouldEnableProfilePictures()) loadOwnIdentityInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveData<OwnIdentityInfo> getOwnIdentityInfo() {
|
@Override
|
||||||
return ownIdentityInfo;
|
protected void onCleared() {
|
||||||
|
super.onCleared();
|
||||||
|
eventBus.removeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveEvent<Boolean> getSetAvatarFailed() {
|
private void loadSettings() {
|
||||||
return setAvatarFailed;
|
runOnDbThread(() -> {
|
||||||
|
try {
|
||||||
|
long start = now();
|
||||||
|
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||||
|
updateSettings(settings);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean shouldEnableProfilePictures() {
|
||||||
|
return featureFlags.shouldEnableProfilePictures();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadOwnIdentityInfo() {
|
private void loadOwnIdentityInfo() {
|
||||||
dbExecutor.execute(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
LocalAuthor localAuthor = identityManager.getLocalAuthor();
|
LocalAuthor localAuthor = identityManager.getLocalAuthor();
|
||||||
AuthorInfo authorInfo = authorManager.getMyAuthorInfo();
|
AuthorInfo authorInfo = authorManager.getMyAuthorInfo();
|
||||||
@@ -96,13 +174,47 @@ 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(SETTINGS_NAMESPACE)) {
|
||||||
|
LOG.info("Settings updated");
|
||||||
|
settings = s.getSettings();
|
||||||
|
updateSettings(settings);
|
||||||
|
} else 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AnyThread
|
||||||
|
private void updateSettings(Settings settings) {
|
||||||
|
screenLockEnabled.postValue(settings.getBoolean(PREF_SCREEN_LOCK,
|
||||||
|
false));
|
||||||
|
int defaultTimeout = Integer.parseInt(getApplication()
|
||||||
|
.getString(R.string.pref_lock_timeout_value_default));
|
||||||
|
screenLockTimeout.postValue(String.valueOf(
|
||||||
|
settings.getInt(PREF_SCREEN_LOCK_TIMEOUT, defaultTimeout)
|
||||||
|
));
|
||||||
|
notificationsManager.updateSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
void setAvatar(Uri uri) {
|
void setAvatar(Uri uri) {
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
trySetAvatar(uri);
|
trySetAvatar(uri);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
setAvatarFailed.postEvent(true);
|
onSetAvatarFailed();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -120,15 +232,34 @@ class SettingsViewModel extends AndroidViewModel {
|
|||||||
"ContentResolver returned null when opening InputStream");
|
"ContentResolver returned null when opening InputStream");
|
||||||
InputStream compressed = imageCompressor.compressImage(is, contentType);
|
InputStream compressed = imageCompressor.compressImage(is, contentType);
|
||||||
|
|
||||||
dbExecutor.execute(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
avatarManager.addAvatar(ImageCompressor.MIME_TYPE, compressed);
|
avatarManager.addAvatar(ImageCompressor.MIME_TYPE, compressed);
|
||||||
loadOwnIdentityInfo();
|
loadOwnIdentityInfo();
|
||||||
} catch (IOException | DbException e) {
|
} catch (IOException | DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
setAvatarFailed.postEvent(true);
|
onSetAvatarFailed();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AnyThread
|
||||||
|
private void onSetAvatarFailed() {
|
||||||
|
androidExecutor.runOnUiThread(() -> Toast.makeText(getApplication(),
|
||||||
|
R.string.change_profile_picture_failed_message, LENGTH_LONG)
|
||||||
|
.show());
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<OwnIdentityInfo> getOwnIdentityInfo() {
|
||||||
|
return ownIdentityInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<Boolean> getScreenLockEnabled() {
|
||||||
|
return screenLockEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<String> getScreenLockTimeout() {
|
||||||
|
return screenLockTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?android:attr/textColorPrimary"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
tools:ignore="NewApi">
|
||||||
|
<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>
|
||||||
12
briar-android/src/main/res/drawable/ic_feedback.xml
Normal file
12
briar-android/src/main/res/drawable/ic_feedback.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?android:attr/textColorPrimary"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
tools:ignore="NewApi">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM13,14h-2v-2h2v2zM13,10h-2L11,6h2v4z" />
|
||||||
|
</vector>
|
||||||
12
briar-android/src/main/res/drawable/ic_notifications.xml
Normal file
12
briar-android/src/main/res/drawable/ic_notifications.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?android:attr/textColorPrimary"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
tools:ignore="NewApi">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?android:attr/textColorPrimary"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
tools:ignore="NewApi">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.99h18v14.02zM8,16h2.5l1.5,1.5 1.5,-1.5L16,16v-2.5l1.5,-1.5 -1.5,-1.5L16,8h-2.5L12,6.5 10.5,8L8,8v2.5L6.5,12 8,13.5L8,16zM12,9c1.66,0 3,1.34 3,3s-1.34,3 -3,3L12,9z" />
|
||||||
|
</vector>
|
||||||
12
briar-android/src/main/res/drawable/ic_settings_security.xml
Normal file
12
briar-android/src/main/res/drawable/ic_settings_security.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?android:attr/textColorPrimary"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
tools:ignore="NewApi">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z" />
|
||||||
|
</vector>
|
||||||
@@ -1,78 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
android:id="@+id/fragmentContainer"
|
||||||
|
android:name="org.briarproject.briar.android.settings.SettingsFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/avatarGroup"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_scrollFlags="scroll">
|
|
||||||
|
|
||||||
<de.hdodenhof.circleimageview.CircleImageView
|
|
||||||
android:id="@+id/avatarImage"
|
|
||||||
style="@style/BriarAvatar"
|
|
||||||
android:layout_width="@dimen/listitem_picture_size"
|
|
||||||
android:layout_height="@dimen/listitem_picture_size"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@mipmap/ic_launcher_round" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/username"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginLeft="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
android:paddingStart="@dimen/margin_medium"
|
|
||||||
android:paddingEnd="@dimen/margin_medium"
|
|
||||||
android:textColor="@color/briar_text_primary_inverse"
|
|
||||||
android:textSize="@dimen/text_size_medium"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/avatarExplanation"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/avatarImage"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:text="username" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/avatarExplanation"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginLeft="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:paddingStart="@dimen/margin_medium"
|
|
||||||
android:paddingEnd="@dimen/margin_medium"
|
|
||||||
android:text="@string/change_profile_picture"
|
|
||||||
android:textColor="@color/briar_text_secondary_inverse"
|
|
||||||
android:textSize="@dimen/text_size_small"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/avatarImage"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/username" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/fragment"
|
|
||||||
android:name="org.briarproject.briar.android.settings.SettingsFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textSize="@dimen/text_size_medium"
|
android:textSize="@dimen/text_size_medium"
|
||||||
android:widgetLayout="@layout/preference_switch_compat"
|
|
||||||
tools:checked="true"
|
tools:checked="true"
|
||||||
tools:text="@string/tor_enable_title" />
|
tools:text="@string/tor_enable_title" />
|
||||||
|
|
||||||
|
|||||||
68
briar-android/src/main/res/layout/preference_avatar.xml
Normal file
68
briar-android/src/main/res/layout/preference_avatar.xml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:layout_width="800dp"
|
||||||
|
tools:layout_height="75dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/avatarGroup"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_scrollFlags="scroll">
|
||||||
|
|
||||||
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
|
android:id="@+id/avatarImage"
|
||||||
|
style="@style/BriarAvatar"
|
||||||
|
android:layout_width="@dimen/listitem_picture_size"
|
||||||
|
android:layout_height="@dimen/listitem_picture_size"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@mipmap/ic_launcher_round" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/username"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:paddingStart="@dimen/margin_medium"
|
||||||
|
android:paddingEnd="@dimen/margin_medium"
|
||||||
|
android:textColor="@color/briar_text_primary_inverse"
|
||||||
|
android:textSize="@dimen/text_size_medium"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/avatarExplanation"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/avatarImage"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="username" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/avatarExplanation"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:paddingStart="@dimen/margin_medium"
|
||||||
|
android:paddingEnd="@dimen/margin_medium"
|
||||||
|
android:text="@string/change_profile_picture"
|
||||||
|
android:textColor="@color/briar_text_secondary_inverse"
|
||||||
|
android:textSize="@dimen/text_size_small"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/avatarImage"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/username" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!-- Needed for SwitchPreference on Android 4 (API < 21)-->
|
|
||||||
<androidx.appcompat.widget.SwitchCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@android:id/switch_widget"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@null"
|
|
||||||
android:clickable="false"
|
|
||||||
android:focusable="false"
|
|
||||||
android:focusableInTouchMode="false"
|
|
||||||
tools:targetApi="n" />
|
|
||||||
@@ -468,7 +468,7 @@
|
|||||||
<string name="pref_theme_light">Light</string>
|
<string name="pref_theme_light">Light</string>
|
||||||
<string name="pref_theme_dark">Dark</string>
|
<string name="pref_theme_dark">Dark</string>
|
||||||
<string name="pref_theme_auto">Automatic (Daytime)</string>
|
<string name="pref_theme_auto">Automatic (Daytime)</string>
|
||||||
<string name="pref_theme_system">System Default</string>
|
<string name="pref_theme_system">System default</string>
|
||||||
|
|
||||||
<!-- Settings Connections -->
|
<!-- Settings Connections -->
|
||||||
<string name="network_settings_title">Connections</string>
|
<string name="network_settings_title">Connections</string>
|
||||||
@@ -552,7 +552,6 @@
|
|||||||
<string name="cannot_load_ringtone">Cannot load ringtone</string>
|
<string name="cannot_load_ringtone">Cannot load ringtone</string>
|
||||||
|
|
||||||
<!-- Settings Feedback -->
|
<!-- Settings Feedback -->
|
||||||
<string name="feedback_settings_title">Feedback</string>
|
|
||||||
<string name="send_feedback">Send feedback</string>
|
<string name="send_feedback">Send feedback</string>
|
||||||
|
|
||||||
<!-- Link Warning -->
|
<!-- Link Warning -->
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:key="pref_key_lock"
|
android:key="pref_key_lock"
|
||||||
android:summary="@string/panic_setting_signout_summary"
|
android:summary="@string/panic_setting_signout_summary"
|
||||||
android:title="@string/panic_setting_signout_title"
|
android:title="@string/panic_setting_signout_title"
|
||||||
app:iconSpaceReserved="false"/>
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:layout="@layout/preferences_category"
|
android:layout="@layout/preferences_category"
|
||||||
@@ -18,15 +18,16 @@
|
|||||||
android:icon="@android:drawable/ic_menu_close_clear_cancel"
|
android:icon="@android:drawable/ic_menu_close_clear_cancel"
|
||||||
android:key="pref_key_panic_app"
|
android:key="pref_key_panic_app"
|
||||||
android:summary="@string/panic_app_setting_summary"
|
android:summary="@string/panic_app_setting_summary"
|
||||||
android:title="@string/panic_app_setting_title"/>
|
android:title="@string/panic_app_setting_title" />
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
android:key="pref_key_purge"
|
android:key="pref_key_purge"
|
||||||
android:summary="@string/purge_setting_summary"
|
android:summary="@string/purge_setting_summary"
|
||||||
android:title="@string/purge_setting_title"
|
android:title="@string/purge_setting_title"
|
||||||
app:iconSpaceReserved="false"/>
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
|||||||
@@ -1,236 +1,52 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<PreferenceCategory
|
|
||||||
android:layout="@layout/preferences_category"
|
|
||||||
android:title="@string/display_settings_title">
|
|
||||||
|
|
||||||
<ListPreference
|
<org.briarproject.briar.android.settings.AvatarPreference android:key="pref_key_avatar" />
|
||||||
android:defaultValue="default"
|
|
||||||
android:entryValues="@array/pref_language_values"
|
|
||||||
android:key="pref_key_language"
|
|
||||||
android:summary="%s"
|
|
||||||
android:title="@string/pref_language_title"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<ListPreference
|
<Preference
|
||||||
android:defaultValue="@string/pref_theme_light_value"
|
android:title="@string/display_settings_title"
|
||||||
android:entries="@array/pref_theme_entries"
|
app:fragment="org.briarproject.briar.android.settings.DisplayFragment"
|
||||||
android:entryValues="@array/pref_theme_values"
|
app:icon="@drawable/ic_settings_brightness" />
|
||||||
android:key="pref_key_theme"
|
|
||||||
android:summary="%s"
|
|
||||||
android:title="@string/pref_theme_title"
|
|
||||||
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" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:title="@string/security_settings_title"
|
||||||
|
app:fragment="org.briarproject.briar.android.settings.SecurityFragment"
|
||||||
|
app:icon="@drawable/ic_settings_security" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:title="@string/notification_settings_title"
|
||||||
|
app:fragment="org.briarproject.briar.android.settings.NotificationsFragment"
|
||||||
|
app:icon="@drawable/ic_notifications" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="pref_key_send_feedback"
|
||||||
|
android:title="@string/send_feedback"
|
||||||
|
app:icon="@drawable/ic_feedback" />
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
|
android:key="pref_key_dev"
|
||||||
android:layout="@layout/preferences_category"
|
android:layout="@layout/preferences_category"
|
||||||
android:title="@string/network_settings_title">
|
android:title="Developer Options"
|
||||||
|
app:allowDividerAbove="true">
|
||||||
<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:title="@string/tor_enable_title"
|
|
||||||
android:summary="@string/tor_enable_summary"
|
|
||||||
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:title="@string/tor_only_when_charging_title"
|
|
||||||
android:summary="@string/tor_only_when_charging_summary"
|
|
||||||
android:widgetLayout="@layout/preference_switch_compat"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory
|
|
||||||
android:layout="@layout/preferences_category"
|
|
||||||
android:title="@string/security_settings_title">
|
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:enabled="false"
|
|
||||||
android:key="pref_key_lock"
|
|
||||||
android:persistent="false"
|
|
||||||
android:summary="@string/pref_lock_summary"
|
|
||||||
android:title="@string/pref_lock_title"
|
|
||||||
android:widgetLayout="@layout/preference_switch_compat"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="@string/pref_lock_timeout_value_default"
|
|
||||||
android:dependency="pref_key_lock"
|
|
||||||
android:entries="@array/pref_key_lock_timeout_entries"
|
|
||||||
android:entryValues="@array/pref_key_lock_timeout_values"
|
|
||||||
android:key="pref_key_lock_timeout"
|
|
||||||
android:persistent="false"
|
|
||||||
android:summary="@string/pref_lock_timeout_summary"
|
|
||||||
android:title="@string/pref_lock_timeout_title"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="pref_key_change_password"
|
|
||||||
android:title="@string/change_password"
|
|
||||||
app:iconSpaceReserved="false">
|
|
||||||
|
|
||||||
<intent
|
|
||||||
android:targetClass="org.briarproject.briar.android.login.ChangePasswordActivity"
|
|
||||||
android:targetPackage="@string/app_package"/>
|
|
||||||
</Preference>
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory
|
|
||||||
android:layout="@layout/preferences_category"
|
|
||||||
android:title="@string/panic_setting_title">
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:summary="@string/panic_setting_hint"
|
|
||||||
android:title="@string/panic_setting"
|
|
||||||
app:iconSpaceReserved="false">
|
|
||||||
|
|
||||||
<intent
|
|
||||||
android:targetClass="org.briarproject.briar.android.panic.PanicPreferencesActivity"
|
|
||||||
android:targetPackage="@string/app_package"/>
|
|
||||||
|
|
||||||
</Preference>
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory
|
|
||||||
android:layout="@layout/preferences_category"
|
|
||||||
android:title="@string/notification_settings_title">
|
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="true"
|
|
||||||
android:key="pref_key_notify_sign_in"
|
|
||||||
android:summary="@string/notify_sign_in_summary"
|
|
||||||
android:title="@string/notify_sign_in_title"
|
|
||||||
android:widgetLayout="@layout/preference_switch_compat"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="true"
|
|
||||||
android:key="pref_key_notify_private_messages"
|
|
||||||
android:persistent="false"
|
|
||||||
android:summary="@string/notify_private_messages_setting_summary"
|
|
||||||
android:title="@string/notify_private_messages_setting_title"
|
|
||||||
android:widgetLayout="@layout/preference_switch_compat"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="true"
|
|
||||||
android:key="pref_key_notify_group_messages"
|
|
||||||
android:persistent="false"
|
|
||||||
android:summary="@string/notify_group_messages_setting_summary"
|
|
||||||
android:title="@string/notify_group_messages_setting_title"
|
|
||||||
android:widgetLayout="@layout/preference_switch_compat"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="true"
|
|
||||||
android:key="pref_key_notify_forum_posts"
|
|
||||||
android:persistent="false"
|
|
||||||
android:summary="@string/notify_forum_posts_setting_summary"
|
|
||||||
android:title="@string/notify_forum_posts_setting_title"
|
|
||||||
android:widgetLayout="@layout/preference_switch_compat"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="true"
|
|
||||||
android:key="pref_key_notify_blog_posts"
|
|
||||||
android:persistent="false"
|
|
||||||
android:summary="@string/notify_blog_posts_setting_summary"
|
|
||||||
android:title="@string/notify_blog_posts_setting_title"
|
|
||||||
android:widgetLayout="@layout/preference_switch_compat"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="true"
|
|
||||||
android:key="pref_key_notify_vibration"
|
|
||||||
android:persistent="false"
|
|
||||||
android:title="@string/notify_vibration_setting"
|
|
||||||
android:widgetLayout="@layout/preference_switch_compat"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="pref_key_notify_sound"
|
|
||||||
android:title="@string/notify_sound_setting"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory
|
|
||||||
android:layout="@layout/preferences_category"
|
|
||||||
android:title="@string/feedback_settings_title">
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="pref_key_send_feedback"
|
|
||||||
android:title="@string/send_feedback"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory
|
|
||||||
android:layout="@layout/preferences_category"
|
|
||||||
android:title="Testing">
|
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="pref_key_test_data"
|
android:key="pref_key_test_data"
|
||||||
android:title="Create Test Data"
|
android:title="Create test data">
|
||||||
app:iconSpaceReserved="false">
|
|
||||||
|
|
||||||
<intent
|
<intent
|
||||||
android:targetClass="org.briarproject.briar.android.test.TestDataActivity"
|
android:targetClass="org.briarproject.briar.android.test.TestDataActivity"
|
||||||
android:targetPackage="@string/app_package"/>
|
android:targetPackage="@string/app_package" />
|
||||||
</Preference>
|
</Preference>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="pref_key_explode"
|
android:key="pref_key_explode"
|
||||||
android:title="Crash"
|
android:title="Crash" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
|||||||
66
briar-android/src/main/res/xml/settings_connections.xml
Normal file
66
briar-android/src/main/res/xml/settings_connections.xml
Normal 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">
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:enabled="false"
|
||||||
|
android:key="pref_key_bluetooth"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/bluetooth_setting"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:enabled="false"
|
||||||
|
android:key="pref_key_wifi"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/wifi_setting"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
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"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="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" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
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"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
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"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
24
briar-android/src/main/res/xml/settings_display.xml
Normal file
24
briar-android/src/main/res/xml/settings_display.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="default"
|
||||||
|
android:entryValues="@array/pref_language_values"
|
||||||
|
android:key="pref_key_language"
|
||||||
|
android:summary="%s"
|
||||||
|
android:title="@string/pref_language_title"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
tools:summary="System default" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="@string/pref_theme_light_value"
|
||||||
|
android:entries="@array/pref_theme_entries"
|
||||||
|
android:entryValues="@array/pref_theme_values"
|
||||||
|
android:key="pref_key_theme"
|
||||||
|
android:summary="%s"
|
||||||
|
android:title="@string/pref_theme_title"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
67
briar-android/src/main/res/xml/settings_notifications.xml
Normal file
67
briar-android/src/main/res/xml/settings_notifications.xml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:key="pref_key_notify_sign_in"
|
||||||
|
android:summary="@string/notify_sign_in_summary"
|
||||||
|
android:title="@string/notify_sign_in_title"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:enabled="false"
|
||||||
|
android:key="notifyPrivateMessages"
|
||||||
|
android:persistent="false"
|
||||||
|
android:summary="@string/notify_private_messages_setting_summary"
|
||||||
|
android:title="@string/notify_private_messages_setting_title"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:enabled="false"
|
||||||
|
android:key="notifyGroupMessages"
|
||||||
|
android:persistent="false"
|
||||||
|
android:summary="@string/notify_group_messages_setting_summary"
|
||||||
|
android:title="@string/notify_group_messages_setting_title"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:enabled="false"
|
||||||
|
android:key="notifyForumPosts"
|
||||||
|
android:persistent="false"
|
||||||
|
android:summary="@string/notify_forum_posts_setting_summary"
|
||||||
|
android:title="@string/notify_forum_posts_setting_title"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:enabled="false"
|
||||||
|
android:key="notifyBlogPosts"
|
||||||
|
android:persistent="false"
|
||||||
|
android:summary="@string/notify_blog_posts_setting_summary"
|
||||||
|
android:title="@string/notify_blog_posts_setting_title"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:enabled="false"
|
||||||
|
android:key="notifyVibration"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/notify_vibration_setting"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="notifySound"
|
||||||
|
android:title="@string/notify_sound_setting"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
54
briar-android/src/main/res/xml/settings_security.xml
Normal file
54
briar-android/src/main/res/xml/settings_security.xml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:enabled="false"
|
||||||
|
android:key="pref_key_lock"
|
||||||
|
android:persistent="false"
|
||||||
|
android:summary="@string/pref_lock_summary"
|
||||||
|
android:title="@string/pref_lock_title"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="@string/pref_lock_timeout_value_default"
|
||||||
|
android:dependency="pref_key_lock"
|
||||||
|
android:enabled="false"
|
||||||
|
android:entries="@array/pref_key_lock_timeout_entries"
|
||||||
|
android:entryValues="@array/pref_key_lock_timeout_values"
|
||||||
|
android:key="pref_key_lock_timeout"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/pref_lock_timeout_title"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
tools:summary="@string/pref_lock_timeout_summary" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="pref_key_change_password"
|
||||||
|
android:title="@string/change_password"
|
||||||
|
app:iconSpaceReserved="false">
|
||||||
|
|
||||||
|
<intent
|
||||||
|
android:targetClass="org.briarproject.briar.android.login.ChangePasswordActivity"
|
||||||
|
android:targetPackage="@string/app_package" />
|
||||||
|
</Preference>
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:layout="@layout/preferences_category"
|
||||||
|
android:title="@string/panic_setting_title">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:summary="@string/panic_setting_hint"
|
||||||
|
android:title="@string/panic_setting"
|
||||||
|
app:iconSpaceReserved="false">
|
||||||
|
|
||||||
|
<intent
|
||||||
|
android:targetClass="org.briarproject.briar.android.panic.PanicPreferencesActivity"
|
||||||
|
android:targetPackage="@string/app_package" />
|
||||||
|
|
||||||
|
</Preference>
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
Reference in New Issue
Block a user