SettingsActivity.java

  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}