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