Move notifications settings into own screen

This commit is contained in:
Torsten Grote
2021-01-22 15:39:46 -03:00
parent 670bf15d31
commit 484817db08
12 changed files with 497 additions and 426 deletions

View File

@@ -37,6 +37,7 @@ import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
import org.briarproject.briar.android.logging.CachingLogHandler;
import org.briarproject.briar.android.login.SignInReminderReceiver;
import org.briarproject.briar.android.settings.ConnectionsFragment;
import org.briarproject.briar.android.settings.NotificationsFragment;
import org.briarproject.briar.android.settings.SecurityFragment;
import org.briarproject.briar.android.view.EmojiTextInputView;
import org.briarproject.briar.api.android.AndroidNotificationManager;
@@ -199,4 +200,6 @@ public interface AndroidComponent
void inject(ConnectionsFragment connectionsFragment);
void inject(SecurityFragment securityFragment);
void inject(NotificationsFragment notificationsFragment);
}

View File

@@ -14,7 +14,7 @@ import javax.inject.Inject;
import static android.content.Intent.ACTION_BOOT_COMPLETED;
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;
public class SignInReminderReceiver extends BroadcastReceiver {
@@ -37,7 +37,7 @@ public class SignInReminderReceiver extends BroadcastReceiver {
if (accountManager.accountExists() &&
!accountManager.hasDatabaseKey()) {
SharedPreferences prefs = app.getDefaultSharedPreferences();
if (prefs.getBoolean(NOTIFY_SIGN_IN, true)) {
if (prefs.getBoolean(PREF_NOTIFY_SIGN_IN, true)) {
notificationManager.showSignInNotification();
}
}

View File

@@ -53,7 +53,7 @@ public class ConnectionsFragment extends PreferenceFragmentCompat {
getAndroidComponent(context).inject(this);
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
.get(SettingsViewModel.class);
connectionsManager = viewModel.getConnectionsManager();
connectionsManager = viewModel.connectionsManager;
}
@Override

View File

@@ -1,5 +1,6 @@
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;
@@ -23,6 +24,7 @@ import static org.briarproject.briar.android.settings.SettingsViewModel.BT_NAMES
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;

View File

@@ -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;
}
}

View File

@@ -0,0 +1,159 @@
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;
public 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;
}
public String getRingtoneUri() {
return ringtoneUri;
}
}

View File

@@ -1,127 +1,29 @@
package org.briarproject.briar.android.settings;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
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.ParametersNotNullByDefault;
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.util.StringUtils;
import org.briarproject.briar.R;
import java.util.logging.Logger;
import javax.inject.Inject;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup;
import androidx.preference.SwitchPreference;
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 java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE;
import static org.briarproject.briar.android.util.UiUtils.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
@ParametersNotNullByDefault
public class SettingsFragment extends PreferenceFragmentCompat
implements EventListener, OnPreferenceChangeListener {
public class SettingsFragment extends PreferenceFragmentCompat {
public static final String SETTINGS_NAMESPACE = "android-ui";
public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in";
private static final Logger LOG =
Logger.getLogger(SettingsFragment.class.getName());
private SettingsActivity listener;
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;
private volatile boolean settingsLoaded = false;
@Inject
volatile SettingsManager settingsManager;
@Inject
volatile EventBus eventBus;
@Override
public void onAttach(Context context) {
super.onAttach(context);
listener = (SettingsActivity) context;
listener.getActivityComponent().inject(this);
}
@Override
public void onCreatePreferences(Bundle bundle, String s) {
addPreferencesFromResource(R.xml.settings);
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");
Preference prefFeedback =
requireNonNull(findPreference("pref_key_send_feedback"));
prefFeedback.setOnPreferenceClickListener(preference -> {
@@ -143,246 +45,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
ColorDrawable divider = new ColorDrawable(
ContextCompat.getColor(requireContext(), R.color.divider));
setDivider(divider);
return view;
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(R.string.settings_button);
eventBus.addListener(this);
setSettingsEnabled(false);
loadSettings();
}
@Override
public void onStop() {
super.onStop();
eventBus.removeListener(this);
}
private void loadSettings() {
listener.runOnDbThread(() -> {
try {
long start = now();
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
settingsLoaded = true;
logDuration(LOG, "Loading settings", start);
displaySettings();
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void displaySettings() {
listener.runOnUiThreadUnlessDestroyed(() -> {
// due to events, we might try to display before a load completed
if (!settingsLoaded) return;
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_notify_sign_in
notifyPrivateMessages.setEnabled(enabled);
notifyGroupMessages.setEnabled(enabled);
notifyForumPosts.setEnabled(enabled);
notifyBlogPosts.setEnabled(enabled);
notifyVibration.setEnabled(enabled);
notifySound.setEnabled(enabled);
}
@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 == 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 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,
@Nullable Intent data) {
super.onActivityResult(request, result, data);
if (request == REQUEST_RINGTONE && result == RESULT_OK) {
Settings s = new Settings();
Uri uri = data.getParcelableExtra(EXTRA_RINGTONE_PICKED_URI);
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(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
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();
displaySettings();
}
}
}
}

View File

@@ -14,7 +14,8 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.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;
@@ -53,14 +54,15 @@ 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
@ParametersNotNullByDefault
class SettingsViewModel extends DbViewModel implements EventListener {
private final static Logger LOG =
getLogger(SettingsViewModel.class.getName());
public static final String SETTINGS_NAMESPACE = "android-ui";
static final String BT_NAMESPACE =
BluetoothConstants.ID.getString();
static final String WIFI_NAMESPACE = LanTcpConstants.ID.getString();
@@ -73,10 +75,11 @@ class SettingsViewModel extends DbViewModel implements EventListener {
private final AuthorManager authorManager;
private final ImageCompressor imageCompressor;
private final Executor ioExecutor;
private final ConnectionsManager connectionsManager;
final SettingsStore settingsStore;
final TorSummaryProvider torSummaryProvider;
final ConnectionsManager connectionsManager;
final NotificationsManager notificationsManager;
private volatile Settings settings;
@@ -114,10 +117,12 @@ class SettingsViewModel extends DbViewModel implements EventListener {
this.ioExecutor = ioExecutor;
this.settingsStore = new SettingsStore(settingsManager, dbExecutor,
SETTINGS_NAMESPACE);
this.torSummaryProvider = new TorSummaryProvider(getApplication(),
torSummaryProvider = new TorSummaryProvider(getApplication(),
locationUtils, circumventionProvider);
this.connectionsManager =
connectionsManager =
new ConnectionsManager(settingsManager, dbExecutor);
notificationsManager = new NotificationsManager(getApplication(),
settingsManager, dbExecutor);
eventBus.addListener(this);
loadSettings();
@@ -192,6 +197,7 @@ class SettingsViewModel extends DbViewModel implements EventListener {
screenLockTimeout.postValue(String.valueOf(
settings.getInt(PREF_SCREEN_LOCK_TIMEOUT, defaultTimeout)
));
notificationsManager.updateSettings(settings);
}
void setAvatar(Uri uri) {
@@ -245,8 +251,4 @@ class SettingsViewModel extends DbViewModel implements EventListener {
return screenLockTimeout;
}
ConnectionsManager getConnectionsManager() {
return connectionsManager;
}
}