1package eu.siacs.conversations;
2
3import android.content.Context;
4import android.content.SharedPreferences;
5import android.net.Uri;
6import android.os.Environment;
7import androidx.annotation.BoolRes;
8import androidx.annotation.IntegerRes;
9import androidx.annotation.NonNull;
10import androidx.preference.PreferenceManager;
11import com.google.common.base.Joiner;
12import com.google.common.base.Splitter;
13import com.google.common.base.Strings;
14import eu.siacs.conversations.persistance.FileBackend;
15import eu.siacs.conversations.services.QuickConversationsService;
16import eu.siacs.conversations.utils.Compatibility;
17import java.security.SecureRandom;
18import java.util.Optional;
19
20public class AppSettings {
21
22 public static final String KEEP_FOREGROUND_SERVICE = "enable_foreground_service";
23 public static final String AWAY_WHEN_SCREEN_IS_OFF = "away_when_screen_off";
24 public static final String TREAT_VIBRATE_AS_SILENT = "treat_vibrate_as_silent";
25 public static final String DND_ON_SILENT_MODE = "dnd_on_silent_mode";
26 public static final String MANUALLY_CHANGE_PRESENCE = "manually_change_presence";
27 public static final String BLIND_TRUST_BEFORE_VERIFICATION = "btbv";
28 public static final String AUTOMATIC_MESSAGE_DELETION = "automatic_message_deletion";
29 public static final String BROADCAST_LAST_ACTIVITY = "last_activity";
30 public static final String THEME = "theme";
31 public static final String DYNAMIC_COLORS = "dynamic_colors";
32 public static final String SHOW_DYNAMIC_TAGS = "show_dynamic_tags";
33 public static final String OMEMO = "omemo";
34 public static final String ALLOW_SCREENSHOTS = "allow_screenshots";
35 public static final String RINGTONE = "call_ringtone";
36 public static final String BTBV = "btbv";
37
38 public static final String CONFIRM_MESSAGES = "confirm_messages";
39 public static final String ALLOW_MESSAGE_CORRECTION = "allow_message_correction";
40
41 public static final String TRUST_SYSTEM_CA_STORE = "trust_system_ca_store";
42 public static final String REQUIRE_CHANNEL_BINDING = "channel_binding_required";
43 public static final String NOTIFICATION_RINGTONE = "notification_ringtone";
44 public static final String NOTIFICATION_HEADS_UP = "notification_headsup";
45 public static final String NOTIFICATION_VIBRATE = "vibrate_on_notification";
46 public static final String NOTIFICATION_LED = "led";
47 public static final String SHOW_CONNECTION_OPTIONS = "show_connection_options";
48 public static final String USE_TOR = "use_tor";
49 public static final String CHANNEL_DISCOVERY_METHOD = "channel_discovery_method";
50 public static final String SEND_CRASH_REPORTS = "send_crash_reports";
51 public static final String COLORFUL_CHAT_BUBBLES = "use_green_background";
52 public static final String LARGE_FONT = "large_font";
53 public static final String SHOW_AVATARS = "show_avatars";
54 public static final String CALL_INTEGRATION = "call_integration";
55 public static final String ALIGN_START = "align_start";
56 public static final String BACKUP_LOCATION = "backup_location";
57 public static final String AUTO_ACCEPT_FILE_SIZE = "auto_accept_file_size";
58
59 private static final String ACCEPT_INVITES_FROM_STRANGERS = "accept_invites_from_strangers";
60 private static final String INSTALLATION_ID = "im.conversations.android.install_id";
61
62 private static final String EXTERNAL_STORAGE_AUTHORITY =
63 "com.android.externalstorage.documents";
64
65 private final Context context;
66
67 public AppSettings(final Context context) {
68 this.context = context;
69 }
70
71 public Uri getRingtone() {
72 final SharedPreferences sharedPreferences =
73 PreferenceManager.getDefaultSharedPreferences(context);
74 final String incomingCallRingtone =
75 sharedPreferences.getString(
76 RINGTONE, context.getString(R.string.incoming_call_ringtone));
77 return Strings.isNullOrEmpty(incomingCallRingtone) ? null : Uri.parse(incomingCallRingtone);
78 }
79
80 public void setRingtone(final Uri uri) {
81 final SharedPreferences sharedPreferences =
82 PreferenceManager.getDefaultSharedPreferences(context);
83 sharedPreferences.edit().putString(RINGTONE, uri == null ? null : uri.toString()).apply();
84 }
85
86 public Uri getNotificationTone() {
87 final SharedPreferences sharedPreferences =
88 PreferenceManager.getDefaultSharedPreferences(context);
89 final String incomingCallRingtone =
90 sharedPreferences.getString(
91 NOTIFICATION_RINGTONE, context.getString(R.string.notification_ringtone));
92 return Strings.isNullOrEmpty(incomingCallRingtone) ? null : Uri.parse(incomingCallRingtone);
93 }
94
95 public void setNotificationTone(final Uri uri) {
96 final SharedPreferences sharedPreferences =
97 PreferenceManager.getDefaultSharedPreferences(context);
98 sharedPreferences
99 .edit()
100 .putString(NOTIFICATION_RINGTONE, uri == null ? null : uri.toString())
101 .apply();
102 }
103
104 public boolean isBTBVEnabled() {
105 return getBooleanPreference(BTBV, R.bool.btbv);
106 }
107
108 public boolean isTrustSystemCAStore() {
109 return getBooleanPreference(TRUST_SYSTEM_CA_STORE, R.bool.trust_system_ca_store);
110 }
111
112 public boolean isAllowScreenshots() {
113 return getBooleanPreference(ALLOW_SCREENSHOTS, R.bool.allow_screenshots);
114 }
115
116 public boolean isColorfulChatBubbles() {
117 return getBooleanPreference(COLORFUL_CHAT_BUBBLES, R.bool.use_green_background);
118 }
119
120 public boolean isLargeFont() {
121 return getBooleanPreference(LARGE_FONT, R.bool.large_font);
122 }
123
124 public boolean isShowAvatars() {
125 return getBooleanPreference(SHOW_AVATARS, R.bool.show_avatars);
126 }
127
128 public boolean isCallIntegration() {
129 return getBooleanPreference(CALL_INTEGRATION, R.bool.call_integration);
130 }
131
132 public boolean isAlignStart() {
133 return getBooleanPreference(ALIGN_START, R.bool.align_start);
134 }
135
136 public boolean isConfirmMessages() {
137 return getBooleanPreference(CONFIRM_MESSAGES, R.bool.confirm_messages);
138 }
139
140 public boolean isAllowMessageCorrection() {
141 return getBooleanPreference(ALLOW_MESSAGE_CORRECTION, R.bool.allow_message_correction);
142 }
143
144 public boolean isBroadcastLastActivity() {
145 return getBooleanPreference(BROADCAST_LAST_ACTIVITY, R.bool.last_activity);
146 }
147
148 public boolean isUseTor() {
149 return QuickConversationsService.isConversations()
150 && getBooleanPreference(USE_TOR, R.bool.use_tor);
151 }
152
153 public boolean isExtendedConnectionOptions() {
154 return QuickConversationsService.isConversations()
155 && getBooleanPreference(
156 AppSettings.SHOW_CONNECTION_OPTIONS, R.bool.show_connection_options);
157 }
158
159 public boolean isAcceptInvitesFromStrangers() {
160 return getBooleanPreference(
161 ACCEPT_INVITES_FROM_STRANGERS, R.bool.accept_invites_from_strangers);
162 }
163
164 public boolean isKeepForegroundService() {
165 return Compatibility.twentySix()
166 || getBooleanPreference(KEEP_FOREGROUND_SERVICE, R.bool.enable_foreground_service);
167 }
168
169 private boolean getBooleanPreference(@NonNull final String name, @BoolRes final int res) {
170 final SharedPreferences sharedPreferences =
171 PreferenceManager.getDefaultSharedPreferences(context);
172 return sharedPreferences.getBoolean(name, context.getResources().getBoolean(res));
173 }
174
175 private long getLongPreference(final String name, @IntegerRes final int res) {
176 final long defaultValue = context.getResources().getInteger(res);
177 final SharedPreferences sharedPreferences =
178 PreferenceManager.getDefaultSharedPreferences(context);
179 try {
180 return Long.parseLong(sharedPreferences.getString(name, String.valueOf(defaultValue)));
181 } catch (final NumberFormatException e) {
182 return defaultValue;
183 }
184 }
185
186 public String getOmemo() {
187 final SharedPreferences sharedPreferences =
188 PreferenceManager.getDefaultSharedPreferences(context);
189 return sharedPreferences.getString(
190 OMEMO, context.getString(R.string.omemo_setting_default));
191 }
192
193 public Uri getBackupLocation() {
194 final SharedPreferences sharedPreferences =
195 PreferenceManager.getDefaultSharedPreferences(context);
196 final String location = sharedPreferences.getString(BACKUP_LOCATION, null);
197 if (Strings.isNullOrEmpty(location)) {
198 final var directory = FileBackend.getBackupDirectory(context);
199 return Uri.fromFile(directory);
200 }
201 return Uri.parse(location);
202 }
203
204 public String getBackupLocationAsPath() {
205 return asPath(getBackupLocation());
206 }
207
208 public static String asPath(final Uri uri) {
209 final var scheme = uri.getScheme();
210 final var path = uri.getPath();
211 if (path == null) {
212 return uri.toString();
213 }
214 if ("file".equalsIgnoreCase(scheme)) {
215 return path;
216 } else if ("content".equalsIgnoreCase(scheme)) {
217 if (EXTERNAL_STORAGE_AUTHORITY.equalsIgnoreCase(uri.getAuthority())) {
218 final var parts = Splitter.on(':').limit(2).splitToList(path);
219 if (parts.size() == 2 && "/tree/primary".equals(parts.get(0))) {
220 return Joiner.on('/')
221 .join(Environment.getExternalStorageDirectory(), parts.get(1));
222 }
223 }
224 }
225 return uri.toString();
226 }
227
228 public void setBackupLocation(final Uri uri) {
229 final SharedPreferences sharedPreferences =
230 PreferenceManager.getDefaultSharedPreferences(context);
231 sharedPreferences
232 .edit()
233 .putString(BACKUP_LOCATION, uri == null ? "" : uri.toString())
234 .apply();
235 }
236
237 public boolean isSendCrashReports() {
238 return getBooleanPreference(SEND_CRASH_REPORTS, R.bool.send_crash_reports);
239 }
240
241 public void setSendCrashReports(boolean value) {
242 final SharedPreferences sharedPreferences =
243 PreferenceManager.getDefaultSharedPreferences(context);
244 sharedPreferences.edit().putBoolean(SEND_CRASH_REPORTS, value).apply();
245 }
246
247 public boolean isRequireChannelBinding() {
248 return getBooleanPreference(REQUIRE_CHANNEL_BINDING, R.bool.require_channel_binding);
249 }
250
251 public synchronized long getInstallationId() {
252 final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
253 final long existing = sharedPreferences.getLong(INSTALLATION_ID, 0);
254 if (existing != 0) {
255 return existing;
256 }
257 final var secureRandom = new SecureRandom();
258 final var installationId = secureRandom.nextLong();
259 sharedPreferences.edit().putLong(INSTALLATION_ID, installationId).apply();
260 return installationId;
261 }
262
263 public Optional<Long> getAutoAcceptFileSize() {
264 final long autoAcceptFileSize =
265 getLongPreference(AUTO_ACCEPT_FILE_SIZE, R.integer.auto_accept_filesize);
266 return autoAcceptFileSize <= 0 ? Optional.empty() : Optional.of(autoAcceptFileSize);
267 }
268
269 public synchronized void resetInstallationId() {
270 final var secureRandom = new SecureRandom();
271 final var installationId = secureRandom.nextLong();
272 PreferenceManager.getDefaultSharedPreferences(context)
273 .edit()
274 .putLong(INSTALLATION_ID, installationId)
275 .apply();
276 }
277}