1package eu.siacs.conversations.ui;
2
3import android.app.AlertDialog;
4import android.app.FragmentManager;
5import android.content.DialogInterface;
6import android.content.Intent;
7import android.content.SharedPreferences;
8import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
9import android.content.pm.PackageManager;
10import android.os.Build;
11import android.os.Bundle;
12import android.preference.ListPreference;
13import android.preference.Preference;
14import android.preference.PreferenceCategory;
15import android.preference.PreferenceManager;
16import android.preference.PreferenceScreen;
17import android.widget.Toast;
18
19import java.security.KeyStoreException;
20import java.util.ArrayList;
21import java.util.Arrays;
22import java.util.Collections;
23import java.util.List;
24import java.util.Locale;
25
26import de.duenndns.ssl.MemorizingTrustManager;
27import eu.siacs.conversations.Config;
28import eu.siacs.conversations.R;
29import eu.siacs.conversations.entities.Account;
30import eu.siacs.conversations.services.ExportLogsService;
31import eu.siacs.conversations.xmpp.XmppConnection;
32import eu.siacs.conversations.xmpp.jid.InvalidJidException;
33import eu.siacs.conversations.xmpp.jid.Jid;
34
35public class SettingsActivity extends XmppActivity implements
36 OnSharedPreferenceChangeListener {
37
38 public static final String KEEP_FOREGROUND_SERVICE = "enable_foreground_service";
39 public static final String AWAY_WHEN_SCREEN_IS_OFF = "away_when_screen_off";
40 public static final String TREAT_VIBRATE_AS_SILENT = "treat_vibrate_as_silent";
41 public static final String MANUALLY_CHANGE_PRESENCE = "manually_change_presence";
42 public static final String BLIND_TRUST_BEFORE_VERIFICATION = "btbv";
43
44 public static final int REQUEST_WRITE_LOGS = 0xbf8701;
45 private SettingsFragment mSettingsFragment;
46
47 @Override
48 protected void onCreate(Bundle savedInstanceState) {
49 super.onCreate(savedInstanceState);
50 FragmentManager fm = getFragmentManager();
51 mSettingsFragment = (SettingsFragment) fm.findFragmentById(android.R.id.content);
52 if (mSettingsFragment == null || !mSettingsFragment.getClass().equals(SettingsFragment.class)) {
53 mSettingsFragment = new SettingsFragment();
54 fm.beginTransaction().replace(android.R.id.content, mSettingsFragment).commit();
55 }
56
57 this.mTheme = findTheme();
58 setTheme(this.mTheme);
59
60 int bgcolor = getPrimaryBackgroundColor();
61 getWindow().getDecorView().setBackgroundColor(bgcolor);
62
63 }
64
65 @Override
66 void onBackendConnected() {
67
68 }
69
70 @Override
71 public void onStart() {
72 super.onStart();
73 PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
74 ListPreference resources = (ListPreference) mSettingsFragment.findPreference("resource");
75 if (resources != null) {
76 ArrayList<CharSequence> entries = new ArrayList<>(Arrays.asList(resources.getEntries()));
77 if (!entries.contains(Build.MODEL)) {
78 entries.add(0, Build.MODEL);
79 resources.setEntries(entries.toArray(new CharSequence[entries.size()]));
80 resources.setEntryValues(entries.toArray(new CharSequence[entries.size()]));
81 }
82 }
83
84 if (Config.FORCE_ORBOT) {
85 PreferenceCategory connectionOptions = (PreferenceCategory) mSettingsFragment.findPreference("connection_options");
86 PreferenceScreen expert = (PreferenceScreen) mSettingsFragment.findPreference("expert");
87 if (connectionOptions != null) {
88 expert.removePreference(connectionOptions);
89 }
90 }
91
92 final Preference removeCertsPreference = mSettingsFragment.findPreference("remove_trusted_certificates");
93 removeCertsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
94 @Override
95 public boolean onPreferenceClick(Preference preference) {
96 final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager();
97 final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
98 if (aliases.size() == 0) {
99 displayToast(getString(R.string.toast_no_trusted_certs));
100 return true;
101 }
102 final ArrayList selectedItems = new ArrayList<>();
103 final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this);
104 dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title));
105 dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null,
106 new DialogInterface.OnMultiChoiceClickListener() {
107 @Override
108 public void onClick(DialogInterface dialog, int indexSelected,
109 boolean isChecked) {
110 if (isChecked) {
111 selectedItems.add(indexSelected);
112 } else if (selectedItems.contains(indexSelected)) {
113 selectedItems.remove(Integer.valueOf(indexSelected));
114 }
115 if (selectedItems.size() > 0)
116 ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
117 else {
118 ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
119 }
120 }
121 });
122
123 dialogBuilder.setPositiveButton(
124 getResources().getString(R.string.dialog_manage_certs_positivebutton), new DialogInterface.OnClickListener() {
125 @Override
126 public void onClick(DialogInterface dialog, int which) {
127 int count = selectedItems.size();
128 if (count > 0) {
129 for (int i = 0; i < count; i++) {
130 try {
131 Integer item = Integer.valueOf(selectedItems.get(i).toString());
132 String alias = aliases.get(item);
133 mtm.deleteCertificate(alias);
134 } catch (KeyStoreException e) {
135 e.printStackTrace();
136 displayToast("Error: " + e.getLocalizedMessage());
137 }
138 }
139 if (xmppConnectionServiceBound) {
140 reconnectAccounts();
141 }
142 displayToast(getResources().getQuantityString(R.plurals.toast_delete_certificates, count, count));
143 }
144 }
145 });
146 dialogBuilder.setNegativeButton(getResources().getString(R.string.dialog_manage_certs_negativebutton), null);
147 AlertDialog removeCertsDialog = dialogBuilder.create();
148 removeCertsDialog.show();
149 removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
150 return true;
151 }
152 });
153
154 final Preference exportLogsPreference = mSettingsFragment.findPreference("export_logs");
155 exportLogsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
156 @Override
157 public boolean onPreferenceClick(Preference preference) {
158 if (hasStoragePermission(REQUEST_WRITE_LOGS)) {
159 startExport();
160 }
161 return true;
162 }
163 });
164
165 final Preference deleteOmemoPreference = mSettingsFragment.findPreference("delete_omemo_identities");
166 deleteOmemoPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
167 @Override
168 public boolean onPreferenceClick(Preference preference) {
169 deleteOmemoIdentities();
170 return true;
171 }
172 });
173 }
174
175 private void deleteOmemoIdentities() {
176 AlertDialog.Builder builder = new AlertDialog.Builder(this);
177 builder.setTitle(R.string.pref_delete_omemo_identities);
178 final List<CharSequence> accounts = new ArrayList<>();
179 for(Account account : xmppConnectionService.getAccounts()) {
180 if (!account.isOptionSet(Account.OPTION_DISABLED)) {
181 accounts.add(account.getJid().toBareJid().toString());
182 }
183 }
184 final boolean[] checkedItems = new boolean[accounts.size()];
185 builder.setMultiChoiceItems(accounts.toArray(new CharSequence[accounts.size()]), checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
186 @Override
187 public void onClick(DialogInterface dialog, int which, boolean isChecked) {
188 checkedItems[which] = isChecked;
189 final AlertDialog alertDialog = (AlertDialog) dialog;
190 for(boolean item : checkedItems) {
191 if (item) {
192 alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
193 return;
194 }
195 }
196 alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
197 }
198 });
199 builder.setNegativeButton(R.string.cancel,null);
200 builder.setPositiveButton(R.string.delete_selected_keys, new DialogInterface.OnClickListener() {
201 @Override
202 public void onClick(DialogInterface dialog, int which) {
203 for(int i = 0; i < checkedItems.length; ++i) {
204 if (checkedItems[i]) {
205 try {
206 Jid jid = Jid.fromString(accounts.get(i).toString());
207 Account account = xmppConnectionService.findAccountByJid(jid);
208 if (account != null) {
209 account.getAxolotlService().regenerateKeys(true);
210 }
211 } catch (InvalidJidException e) {
212 //
213 }
214
215 }
216 }
217 }
218 });
219 AlertDialog dialog = builder.create();
220 dialog.show();
221 dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
222 }
223
224 @Override
225 public void onStop() {
226 super.onStop();
227 PreferenceManager.getDefaultSharedPreferences(this)
228 .unregisterOnSharedPreferenceChangeListener(this);
229 }
230
231 @Override
232 public void onSharedPreferenceChanged(SharedPreferences preferences, String name) {
233 final List<String> resendPresence = Arrays.asList(
234 "confirm_messages",
235 "xa_on_silent_mode",
236 AWAY_WHEN_SCREEN_IS_OFF,
237 "allow_message_correction",
238 TREAT_VIBRATE_AS_SILENT,
239 MANUALLY_CHANGE_PRESENCE,
240 "last_activity");
241 if (name.equals("resource")) {
242 String resource = preferences.getString("resource", "mobile")
243 .toLowerCase(Locale.US);
244 if (xmppConnectionServiceBound) {
245 for (Account account : xmppConnectionService.getAccounts()) {
246 if (account.setResource(resource)) {
247 if (!account.isOptionSet(Account.OPTION_DISABLED)) {
248 XmppConnection connection = account.getXmppConnection();
249 if (connection != null) {
250 connection.resetStreamId();
251 }
252 xmppConnectionService.reconnectAccountInBackground(account);
253 }
254 }
255 }
256 }
257 } else if (name.equals(KEEP_FOREGROUND_SERVICE)) {
258 boolean foreground_service = preferences.getBoolean(KEEP_FOREGROUND_SERVICE,false);
259 if (!foreground_service) {
260 xmppConnectionService.clearStartTimeCounter();
261 }
262 xmppConnectionService.toggleForegroundService();
263 } else if (resendPresence.contains(name)) {
264 if (xmppConnectionServiceBound) {
265 if (name.equals(AWAY_WHEN_SCREEN_IS_OFF) || name.equals(MANUALLY_CHANGE_PRESENCE)) {
266 xmppConnectionService.toggleScreenEventReceiver();
267 }
268 if (name.equals(MANUALLY_CHANGE_PRESENCE) && !noAccountUsesPgp()) {
269 Toast.makeText(this, R.string.republish_pgp_keys, Toast.LENGTH_LONG).show();
270 }
271 xmppConnectionService.refreshAllPresences();
272 }
273 } else if (name.equals("dont_trust_system_cas")) {
274 xmppConnectionService.updateMemorizingTrustmanager();
275 reconnectAccounts();
276 } else if (name.equals("use_tor")) {
277 reconnectAccounts();
278 }
279
280 }
281
282 @Override
283 public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
284 if (grantResults.length > 0)
285 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
286 if (requestCode == REQUEST_WRITE_LOGS) {
287 startExport();
288 }
289 } else {
290 Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
291 }
292 }
293
294 private void startExport() {
295 startService(new Intent(getApplicationContext(), ExportLogsService.class));
296 }
297
298 private void displayToast(final String msg) {
299 runOnUiThread(new Runnable() {
300 @Override
301 public void run() {
302 Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show();
303 }
304 });
305 }
306
307 private void reconnectAccounts() {
308 for (Account account : xmppConnectionService.getAccounts()) {
309 if (!account.isOptionSet(Account.OPTION_DISABLED)) {
310 xmppConnectionService.reconnectAccountInBackground(account);
311 }
312 }
313 }
314
315 public void refreshUiReal() {
316 //nothing to do. This Activity doesn't implement any listeners
317 }
318
319}