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