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