redesign settings screen (WIP)

This commit is contained in:
Daniel Gultsch 2024-04-07 20:05:43 +02:00
parent ed332020fd
commit 85771fc26c
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
129 changed files with 1890 additions and 1248 deletions

View file

@ -47,6 +47,7 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.exifinterface:exifinterface:1.3.7'
implementation 'androidx.cardview:cardview:1.0.0'
implementation "androidx.preference:preference:1.2.1"
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'com.google.android.material:material:1.11.0'

View file

@ -147,7 +147,7 @@ public class EasyOnboardingInviteActivity extends XmppActivity
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
if (easyOnboardingInvite != null) {
return;
}

View file

@ -35,7 +35,7 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher {
protected void refreshUiReal() {}
@Override
void onBackendConnected() {}
protected void onBackendConnected() {}
@Override
protected void onCreate(final Bundle savedInstanceState) {

View file

@ -137,7 +137,7 @@ public class ManageAccountActivity extends XmppActivity
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
if (selectedAccountJid != null) {
this.selectedAccount = xmppConnectionService.findAccountByJid(selectedAccountJid);
}

View file

@ -22,7 +22,7 @@ public class PickServerActivity extends XmppActivity {
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
}

View file

@ -57,7 +57,7 @@ public class ShareViaAccountActivity extends XmppActivity {
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
final int numAccounts = xmppConnectionService.getAccounts().size();
if (numAccounts == 1) {

View file

@ -88,7 +88,7 @@ public class WelcomeActivity extends XmppActivity
protected void refreshUiReal() {}
@Override
void onBackendConnected() {}
protected void onBackendConnected() {}
@Override
public void onStart() {

View file

@ -255,7 +255,7 @@
</intent-filter>
</activity>
<activity
android:name=".ui.SettingsActivity"
android:name=".ui.activity.SettingsActivity"
android:exported="true"
android:label="@string/title_activity_settings">
<intent-filter>

View file

@ -0,0 +1,91 @@
package eu.siacs.conversations;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import androidx.annotation.BoolRes;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import com.google.common.base.Strings;
public class AppSettings {
public static final String KEEP_FOREGROUND_SERVICE = "enable_foreground_service";
public static final String AWAY_WHEN_SCREEN_IS_OFF = "away_when_screen_off";
public static final String TREAT_VIBRATE_AS_SILENT = "treat_vibrate_as_silent";
public static final String DND_ON_SILENT_MODE = "dnd_on_silent_mode";
public static final String MANUALLY_CHANGE_PRESENCE = "manually_change_presence";
public static final String BLIND_TRUST_BEFORE_VERIFICATION = "btbv";
public static final String AUTOMATIC_MESSAGE_DELETION = "automatic_message_deletion";
public static final String BROADCAST_LAST_ACTIVITY = "last_activity";
public static final String THEME = "theme";
public static final String SHOW_DYNAMIC_TAGS = "show_dynamic_tags";
public static final String OMEMO = "omemo";
public static final String ALLOW_SCREENSHOTS = "allow_screenshots";
public static final String RINGTONE = "call_ringtone";
public static final String BTBV = "btbv";
public static final String CONFIRM_MESSAGES = "confirm_messages";
public static final String ALLOW_MESSAGE_CORRECTION = "allow_message_correction";
public static final String TRUST_SYSTEM_CA_STORE = "trust_system_ca_store";
public static final String REQUIRE_CHANNEL_BINDING = "channel_binding_required";
public static final String NOTIFICATION_RINGTONE = "notification_ringtone";
public static final String NOTIFICATION_HEADS_UP = "notification_headsup";
public static final String NOTIFICATION_VIBRATE = "vibrate_on_notification";
public static final String NOTIFICATION_LED = "led";
public static final String SHOW_CONNECTION_OPTIONS = "show_connection_options";
public static final String USE_TOR = "use_tor";
public static final String CHANNEL_DISCOVERY_METHOD = "channel_discovery_method";
private final Context context;
public AppSettings(final Context context) {
this.context = context;
}
public Uri getRingtone() {
final SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
final String incomingCallRingtone =
sharedPreferences.getString(
RINGTONE, context.getString(R.string.incoming_call_ringtone));
return Strings.isNullOrEmpty(incomingCallRingtone) ? null : Uri.parse(incomingCallRingtone);
}
public void setRingtone(final Uri uri) {
final SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences.edit().putString(RINGTONE, uri == null ? null : uri.toString()).apply();
}
public boolean isBTBVEnabled() {
return getBooleanPreference(BTBV, R.bool.btbv);
}
public boolean isTrustSystemCAStore() {
return getBooleanPreference(TRUST_SYSTEM_CA_STORE, R.bool.trust_system_ca_store);
}
public boolean isAllowScreenshots() {
return getBooleanPreference(ALLOW_SCREENSHOTS, R.bool.allow_screenshots);
}
public boolean isUseTor() {
return getBooleanPreference(USE_TOR, R.bool.use_tor);
}
private boolean getBooleanPreference(@NonNull final String name, @BoolRes int res) {
final SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
return sharedPreferences.getBoolean(name, context.getResources().getBoolean(res));
}
public String getOmemo() {
final SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
return sharedPreferences.getString(OMEMO, context.getString(R.string.omemo_setting_default));
}
}

View file

@ -36,10 +36,10 @@ import android.preference.PreferenceManager;
import com.google.common.base.Strings;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.SettingsActivity;
public class OmemoSetting {
@ -54,13 +54,14 @@ public class OmemoSetting {
return encryption;
}
public static void load(final Context context, final SharedPreferences sharedPreferences) {
public static void load(final Context context) {
if (Config.omemoOnly()) {
always = true;
encryption = Message.ENCRYPTION_AXOLOTL;
return;
}
final String value = sharedPreferences.getString(SettingsActivity.OMEMO_SETTING, context.getResources().getString(R.string.omemo_setting_default));
final var appSettings = new AppSettings(context);
final var value = appSettings.getOmemo();
switch (Strings.nullToEmpty(value)) {
case "always":
always = true;
@ -77,8 +78,4 @@ public class OmemoSetting {
}
}
public static void load(final Context context) {
load(context, PreferenceManager.getDefaultSharedPreferences(context));
}
}

View file

@ -67,6 +67,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
@ -940,16 +941,15 @@ public class NotificationService {
final Resources resources = mXmppConnectionService.getResources();
final String ringtone =
preferences.getString(
"notification_ringtone",
AppSettings.NOTIFICATION_RINGTONE,
resources.getString(R.string.notification_ringtone));
final boolean vibrate =
preferences.getBoolean(
"vibrate_on_notification",
AppSettings.NOTIFICATION_VIBRATE,
resources.getBoolean(R.bool.vibrate_on_notification));
final boolean led = preferences.getBoolean("led", resources.getBoolean(R.bool.led));
final boolean led = preferences.getBoolean(AppSettings.NOTIFICATION_LED, resources.getBoolean(R.bool.led));
final boolean headsup =
preferences.getBoolean(
"notification_headsup", resources.getBoolean(R.bool.headsup_notifications));
preferences.getBoolean(AppSettings.NOTIFICATION_HEADS_UP, resources.getBoolean(R.bool.headsup_notifications));
if (notify && !quietHours) {
if (vibrate) {
final int dat = 70;

View file

@ -93,6 +93,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.android.JabberIdContact;
@ -130,7 +131,6 @@ import eu.siacs.conversations.persistance.UnifiedPushDatabase;
import eu.siacs.conversations.ui.ChooseAccountForProfilePictureActivity;
import eu.siacs.conversations.ui.ConversationsActivity;
import eu.siacs.conversations.ui.RtpSessionActivity;
import eu.siacs.conversations.ui.SettingsActivity;
import eu.siacs.conversations.ui.UiCallback;
import eu.siacs.conversations.ui.interfaces.OnAvatarPublication;
import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
@ -865,9 +865,7 @@ public class XmppConnectionService extends Service {
}
break;
case ACTION_IDLE_PING:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
scheduleNextIdlePing();
}
break;
case ACTION_FCM_MESSAGE_RECEIVED:
Log.d(Config.LOGTAG, "push message arrived in service. account");
@ -1119,19 +1117,19 @@ public class XmppConnectionService extends Service {
}
private boolean dndOnSilentMode() {
return getBooleanPreference(SettingsActivity.DND_ON_SILENT_MODE, R.bool.dnd_on_silent_mode);
return getBooleanPreference(AppSettings.DND_ON_SILENT_MODE, R.bool.dnd_on_silent_mode);
}
private boolean manuallyChangePresence() {
return getBooleanPreference(SettingsActivity.MANUALLY_CHANGE_PRESENCE, R.bool.manually_change_presence);
return getBooleanPreference(AppSettings.MANUALLY_CHANGE_PRESENCE, R.bool.manually_change_presence);
}
private boolean treatVibrateAsSilent() {
return getBooleanPreference(SettingsActivity.TREAT_VIBRATE_AS_SILENT, R.bool.treat_vibrate_as_silent);
return getBooleanPreference(AppSettings.TREAT_VIBRATE_AS_SILENT, R.bool.treat_vibrate_as_silent);
}
private boolean awayWhenScreenLocked() {
return getBooleanPreference(SettingsActivity.AWAY_WHEN_SCREEN_IS_OFF, R.bool.away_when_screen_off);
return getBooleanPreference(AppSettings.AWAY_WHEN_SCREEN_IS_OFF, R.bool.away_when_screen_off);
}
private String getCompressPicturesPreference() {
@ -1287,10 +1285,6 @@ public class XmppConnectionService extends Service {
Log.d(Config.LOGTAG, "restoring accounts...");
this.accounts = databaseBackend.getAccounts();
final SharedPreferences.Editor editor = getPreferences().edit();
if (this.accounts.size() == 0 && Arrays.asList("Sony", "Sony Ericsson").contains(Build.MANUFACTURER)) {
editor.putBoolean(SettingsActivity.KEEP_FOREGROUND_SERVICE, true);
Log.d(Config.LOGTAG, Build.MANUFACTURER + " is on blacklist. enabling foreground service");
}
final boolean hasEnabledAccounts = hasEnabledAccounts();
editor.putBoolean(EventReceiver.SETTING_ENABLED_ACCOUNTS, hasEnabledAccounts).apply();
editor.apply();
@ -1334,20 +1328,18 @@ public class XmppConnectionService extends Service {
this.pgpServiceConnection.bindToService();
}
final PowerManager pm = ContextCompat.getSystemService(this, PowerManager.class);
this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Conversations:Service");
final PowerManager powerManager = getSystemService(PowerManager.class);
this.wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Conversations:Service");
toggleForegroundService();
updateUnreadCountBadge();
toggleScreenEventReceiver();
final IntentFilter systemBroadcastFilter = new IntentFilter();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
scheduleNextIdlePing();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
systemBroadcastFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
}
systemBroadcastFilter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
}
ContextCompat.registerReceiver(
this,
this.mInternalEventReceiver,
@ -1363,6 +1355,17 @@ public class XmppConnectionService extends Service {
mForceDuringOnCreate.set(false);
toggleForegroundService();
internalPingExecutor.scheduleAtFixedRate(this::manageAccountConnectionStatesInternal,10,10,TimeUnit.SECONDS);
final SharedPreferences sharedPreferences =
androidx.preference.PreferenceManager.getDefaultSharedPreferences(this);
sharedPreferences.registerOnSharedPreferenceChangeListener(new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @Nullable String key) {
Log.d(Config.LOGTAG,"preference '"+key+"' has changed");
if (AppSettings.KEEP_FOREGROUND_SERVICE.equals(key)) {
toggleForegroundService();
}
}
});
}
@ -4421,7 +4424,7 @@ public class XmppConnectionService extends Service {
}
public long getAutomaticMessageDeletionDate() {
final long timeout = getLongPreference(SettingsActivity.AUTOMATIC_MESSAGE_DELETION, R.integer.automatic_message_deletion);
final long timeout = getLongPreference(AppSettings.AUTOMATIC_MESSAGE_DELETION, R.integer.automatic_message_deletion);
return timeout == 0 ? timeout : (System.currentTimeMillis() - (timeout * 1000));
}
@ -4459,11 +4462,11 @@ public class XmppConnectionService extends Service {
}
public boolean showExtendedConnectionOptions() {
return QuickConversationsService.isConversations() && getBooleanPreference("show_connection_options", R.bool.show_connection_options);
return QuickConversationsService.isConversations() && getBooleanPreference(AppSettings.SHOW_CONNECTION_OPTIONS, R.bool.show_connection_options);
}
public boolean broadcastLastActivity() {
return getBooleanPreference(SettingsActivity.BROADCAST_LAST_ACTIVITY, R.bool.last_activity);
return getBooleanPreference(AppSettings.BROADCAST_LAST_ACTIVITY, R.bool.last_activity);
}
public int unreadCount() {
@ -4477,7 +4480,7 @@ public class XmppConnectionService extends Service {
private <T> List<T> threadSafeList(Set<T> set) {
synchronized (LISTENER_LOCK) {
return set.size() == 0 ? Collections.emptyList() : new ArrayList<>(set);
return set.isEmpty() ? Collections.emptyList() : new ArrayList<>(set);
}
}
@ -5208,7 +5211,7 @@ public class XmppConnectionService extends Service {
}
public boolean blindTrustBeforeVerification() {
return getBooleanPreference(SettingsActivity.BLIND_TRUST_BEFORE_VERIFICATION, R.bool.btbv);
return getBooleanPreference(AppSettings.BLIND_TRUST_BEFORE_VERIFICATION, R.bool.btbv);
}
public ShortcutService getShortcutService() {

View file

@ -126,7 +126,7 @@ public abstract class AbstractSearchableListItemActivity extends XmppActivity im
protected abstract void filterContacts(final String needle);
@Override
void onBackendConnected() {
protected void onBackendConnected() {
filterContacts();
}

View file

@ -25,7 +25,7 @@ public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
SettingsUtils.applyScreenshotSetting(this);
}
public void setDynamicColors(final boolean isDynamicColors) {

View file

@ -51,7 +51,7 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
private Account mAccount;
@Override
void onBackendConnected() {
protected void onBackendConnected() {
this.mAccount = extractAccount(getIntent());
if (this.mAccount != null && this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
this.binding.currentPasswordLayout.setVisibility(View.GONE);

View file

@ -65,7 +65,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
if (optedIn || method == ChannelDiscoveryService.Method.LOCAL_SERVER) {
final String query;
if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) {

View file

@ -47,7 +47,7 @@ public class ChooseAccountForProfilePictureActivity extends XmppActivity {
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
loadEnabledAccounts();
if (accountList.size() == 1) {
goToProfilePictureActivity(accountList.get(0));

View file

@ -362,7 +362,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
filterContacts();
this.mActivatedAccounts.clear();
for (final Account account : xmppConnectionService.getAccounts()) {

View file

@ -396,7 +396,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
if (mPendingConferenceInvite != null) {
mPendingConferenceInvite.execute(this);
mPendingConferenceInvite = null;

View file

@ -39,6 +39,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@ -243,7 +244,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
public void onStart() {
super.onStart();
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
this.showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, false);
this.showDynamicTags = preferences.getBoolean(AppSettings.SHOW_DYNAMIC_TAGS, false);
this.showLastSeen = preferences.getBoolean("last_activity", false);
binding.mediaWrapper.setVisibility(Compatibility.hasStoragePermission(this) ? View.VISIBLE : View.GONE);
mMediaAdapter.setAttachments(Collections.emptyList());

View file

@ -144,7 +144,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
if (performRedirectIfNecessary(true)) {
return;
}

View file

@ -47,6 +47,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@ -938,7 +939,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
private void changePresence() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean manualStatus = sharedPreferences.getBoolean(SettingsActivity.MANUALLY_CHANGE_PRESENCE, getResources().getBoolean(R.bool.manually_change_presence));
boolean manualStatus = sharedPreferences.getBoolean(AppSettings.MANUALLY_CHANGE_PRESENCE, getResources().getBoolean(R.bool.manually_change_presence));
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
final DialogPresenceBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.dialog_presence, null, false);
String current = mAccount.getPresenceStatusMessage();

View file

@ -44,7 +44,7 @@ public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
Intent intent = getIntent();
String account = intent == null ? null : intent.getStringExtra("account");
String jid = intent == null ? null : intent.getStringExtra("jid");

View file

@ -60,7 +60,7 @@ public class MemorizingActivity extends AppCompatActivity implements OnClickList
@Override
public void onResume() {
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
SettingsUtils.applyScreenshotSetting(this);
Intent i = getIntent();
decisionId = i.getIntExtra(MemorizingTrustManager.DECISION_INTENT_ID, MTMDecision.DECISION_INVALID);

View file

@ -49,7 +49,7 @@ public class MucUsersActivity extends XmppActivity implements XmppConnectionServ
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
final Intent intent = getIntent();
final String uuid = intent == null ? null : intent.getStringExtra("uuid");
if (uuid != null) {

View file

@ -63,7 +63,7 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
String uuid = pendingConversationUuid.pop();
if (uuid != null) {
this.conversation = xmppConnectionService.findConversationByUuid(uuid);

View file

@ -475,7 +475,7 @@ public class RtpSessionActivity extends XmppActivity
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
final var intent = getIntent();
if (intent == null) {
return;

View file

@ -215,7 +215,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
final List<String> searchTerm = pendingSearch.pop();
if (searchTerm != null && currentSearch.watch(searchTerm)) {
xmppConnectionService.search(searchTerm, uuid,this);

View file

@ -1,568 +0,0 @@
package eu.siacs.conversations.ui;
import android.app.FragmentManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;
import com.google.android.material.color.DynamicColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStoreException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.Conversations;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OmemoSetting;
import eu.siacs.conversations.databinding.ActivitySettingsBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.ExportBackupService;
import eu.siacs.conversations.services.MemorizingTrustManager;
import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.services.UnifiedPushDistributor;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.xmpp.Jid;
public class SettingsActivity extends XmppActivity implements OnSharedPreferenceChangeListener {
public static final String KEEP_FOREGROUND_SERVICE = "enable_foreground_service";
public static final String AWAY_WHEN_SCREEN_IS_OFF = "away_when_screen_off";
public static final String TREAT_VIBRATE_AS_SILENT = "treat_vibrate_as_silent";
public static final String DND_ON_SILENT_MODE = "dnd_on_silent_mode";
public static final String MANUALLY_CHANGE_PRESENCE = "manually_change_presence";
public static final String BLIND_TRUST_BEFORE_VERIFICATION = "btbv";
public static final String AUTOMATIC_MESSAGE_DELETION = "automatic_message_deletion";
public static final String BROADCAST_LAST_ACTIVITY = "last_activity";
public static final String THEME = "theme";
public static final String SHOW_DYNAMIC_TAGS = "show_dynamic_tags";
public static final String OMEMO_SETTING = "omemo";
public static final String PREVENT_SCREENSHOTS = "prevent_screenshots";
public static final int REQUEST_CREATE_BACKUP = 0xbf8701;
private SettingsFragment mSettingsFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActivitySettingsBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_settings);
FragmentManager fm = getFragmentManager();
mSettingsFragment = (SettingsFragment) fm.findFragmentById(R.id.settings_content);
if (mSettingsFragment == null
|| !mSettingsFragment.getClass().equals(SettingsFragment.class)) {
mSettingsFragment = new SettingsFragment();
fm.beginTransaction().replace(R.id.settings_content, mSettingsFragment).commit();
}
mSettingsFragment.setActivityIntent(getIntent());
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar());
}
@Override
void onBackendConnected() {
final Preference accountPreference =
mSettingsFragment.findPreference(UnifiedPushDistributor.PREFERENCE_ACCOUNT);
reconfigureUpAccountPreference(accountPreference);
}
private void reconfigureUpAccountPreference(final Preference preference) {
final ListPreference listPreference;
if (preference instanceof ListPreference) {
listPreference = (ListPreference) preference;
} else {
return;
}
final List<CharSequence> accounts =
ImmutableList.copyOf(
Lists.transform(
xmppConnectionService.getAccounts(),
a -> a.getJid().asBareJid().toEscapedString()));
final ImmutableList.Builder<CharSequence> entries = new ImmutableList.Builder<>();
final ImmutableList.Builder<CharSequence> entryValues = new ImmutableList.Builder<>();
entries.add(getString(R.string.no_account_deactivated));
entryValues.add("none");
entries.addAll(accounts);
entryValues.addAll(accounts);
listPreference.setEntries(entries.build().toArray(new CharSequence[0]));
listPreference.setEntryValues(entryValues.build().toArray(new CharSequence[0]));
if (!accounts.contains(listPreference.getValue())) {
listPreference.setValue("none");
}
}
@Override
public void onStart() {
super.onStart();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
changeOmemoSettingSummary();
if (QuickConversationsService.isQuicksy()
|| QuickConversationsService.isPlayStoreFlavor()
|| Strings.isNullOrEmpty(Config.CHANNEL_DISCOVERY)) {
final PreferenceCategory groupChats =
(PreferenceCategory) mSettingsFragment.findPreference("group_chats");
final Preference channelDiscoveryMethod =
mSettingsFragment.findPreference("channel_discovery_method");
if (groupChats != null && channelDiscoveryMethod != null) {
groupChats.removePreference(channelDiscoveryMethod);
}
}
if (QuickConversationsService.isQuicksy()) {
final PreferenceCategory connectionOptions =
(PreferenceCategory) mSettingsFragment.findPreference("connection_options");
PreferenceScreen expert = (PreferenceScreen) mSettingsFragment.findPreference("expert");
if (connectionOptions != null) {
expert.removePreference(connectionOptions);
}
}
PreferenceScreen mainPreferenceScreen =
(PreferenceScreen) mSettingsFragment.findPreference("main_screen");
PreferenceCategory attachmentsCategory =
(PreferenceCategory) mSettingsFragment.findPreference("attachments");
CheckBoxPreference locationPlugin =
(CheckBoxPreference) mSettingsFragment.findPreference("use_share_location_plugin");
if (attachmentsCategory != null && locationPlugin != null) {
if (!GeoHelper.isLocationPluginInstalled(this)) {
attachmentsCategory.removePreference(locationPlugin);
}
}
// this feature is only available on Huawei Android 6.
PreferenceScreen huaweiPreferenceScreen =
(PreferenceScreen) mSettingsFragment.findPreference("huawei");
if (huaweiPreferenceScreen != null) {
Intent intent = huaweiPreferenceScreen.getIntent();
// remove when Api version is above M (Version 6.0) or if the intent is not callable
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M || !isCallable(intent)) {
PreferenceCategory generalCategory =
(PreferenceCategory) mSettingsFragment.findPreference("general");
generalCategory.removePreference(huaweiPreferenceScreen);
if (generalCategory.getPreferenceCount() == 0) {
if (mainPreferenceScreen != null) {
mainPreferenceScreen.removePreference(generalCategory);
}
}
}
}
final PreferenceCategory uiPreferenceCategory = (PreferenceCategory) mSettingsFragment.findPreference("ui");
final Preference dynamicColorsPreference = mSettingsFragment.findPreference("dynamic_colors");
if (dynamicColorsPreference != null && !DynamicColors.isDynamicColorAvailable()) {
uiPreferenceCategory.removePreference(dynamicColorsPreference);
}
ListPreference automaticMessageDeletionList =
(ListPreference) mSettingsFragment.findPreference(AUTOMATIC_MESSAGE_DELETION);
if (automaticMessageDeletionList != null) {
final int[] choices =
getResources().getIntArray(R.array.automatic_message_deletion_values);
CharSequence[] entries = new CharSequence[choices.length];
CharSequence[] entryValues = new CharSequence[choices.length];
for (int i = 0; i < choices.length; ++i) {
entryValues[i] = String.valueOf(choices[i]);
if (choices[i] == 0) {
entries[i] = getString(R.string.never);
} else {
entries[i] = TimeFrameUtils.resolve(this, 1000L * choices[i]);
}
}
automaticMessageDeletionList.setEntries(entries);
automaticMessageDeletionList.setEntryValues(entryValues);
}
final Preference removeCertsPreference =
mSettingsFragment.findPreference("remove_trusted_certificates");
if (removeCertsPreference != null) {
removeCertsPreference.setOnPreferenceClickListener(
preference -> {
final MemorizingTrustManager mtm =
xmppConnectionService.getMemorizingTrustManager();
final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
if (aliases.isEmpty()) {
displayToast(getString(R.string.toast_no_trusted_certs));
return true;
}
final ArrayList<Integer> selectedItems = new ArrayList<>();
final MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(SettingsActivity.this);
dialogBuilder.setTitle(
getResources().getString(R.string.dialog_manage_certs_title));
dialogBuilder.setMultiChoiceItems(
aliases.toArray(new CharSequence[0]),
null,
(dialog, indexSelected, isChecked) -> {
if (isChecked) {
selectedItems.add(indexSelected);
} else if (selectedItems.contains(indexSelected)) {
selectedItems.remove(Integer.valueOf(indexSelected));
}
((AlertDialog) dialog)
.getButton(DialogInterface.BUTTON_POSITIVE)
.setEnabled(!selectedItems.isEmpty());
});
dialogBuilder.setPositiveButton(
getResources()
.getString(R.string.dialog_manage_certs_positivebutton),
(dialog, which) -> {
int count = selectedItems.size();
if (count > 0) {
for (int i = 0; i < count; i++) {
try {
final int item =
Integer.parseInt(
selectedItems.get(i).toString());
String alias = aliases.get(item);
mtm.deleteCertificate(alias);
} catch (final KeyStoreException e) {
displayToast("Error: " + e.getLocalizedMessage());
}
}
if (xmppConnectionServiceBound) {
reconnectAccounts();
}
displayToast(
getResources()
.getQuantityString(
R.plurals.toast_delete_certificates,
count,
count));
}
});
dialogBuilder.setNegativeButton(
getResources()
.getString(R.string.dialog_manage_certs_negativebutton),
null);
AlertDialog removeCertsDialog = dialogBuilder.create();
removeCertsDialog.show();
removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
return true;
});
}
final Preference createBackupPreference = mSettingsFragment.findPreference("create_backup");
if (createBackupPreference != null) {
createBackupPreference.setSummary(
getString(
R.string.pref_create_backup_summary,
FileBackend.getBackupDirectory(this).getAbsolutePath()));
createBackupPreference.setOnPreferenceClickListener(
preference -> {
if (hasStoragePermission(REQUEST_CREATE_BACKUP)) {
createBackup();
}
return true;
});
}
if (Config.ONLY_INTERNAL_STORAGE) {
final Preference cleanCachePreference = mSettingsFragment.findPreference("clean_cache");
if (cleanCachePreference != null) {
cleanCachePreference.setOnPreferenceClickListener(preference -> cleanCache());
}
final Preference cleanPrivateStoragePreference =
mSettingsFragment.findPreference("clean_private_storage");
if (cleanPrivateStoragePreference != null) {
cleanPrivateStoragePreference.setOnPreferenceClickListener(
preference -> cleanPrivateStorage());
}
}
final Preference deleteOmemoPreference =
mSettingsFragment.findPreference("delete_omemo_identities");
if (deleteOmemoPreference != null) {
deleteOmemoPreference.setOnPreferenceClickListener(
preference -> deleteOmemoIdentities());
}
if (Config.omemoOnly()) {
final PreferenceCategory privacyCategory =
(PreferenceCategory) mSettingsFragment.findPreference("privacy");
final Preference omemoPreference =mSettingsFragment.findPreference(OMEMO_SETTING);
if (omemoPreference != null) {
privacyCategory.removePreference(omemoPreference);
}
}
}
private void changeOmemoSettingSummary() {
final ListPreference omemoPreference =
(ListPreference) mSettingsFragment.findPreference(OMEMO_SETTING);
if (omemoPreference == null) {
return;
}
final String value = omemoPreference.getValue();
switch (value) {
case "always":
omemoPreference.setSummary(R.string.pref_omemo_setting_summary_always);
break;
case "default_on":
omemoPreference.setSummary(R.string.pref_omemo_setting_summary_default_on);
break;
case "default_off":
omemoPreference.setSummary(R.string.pref_omemo_setting_summary_default_off);
break;
}
}
private boolean isCallable(final Intent i) {
return i != null
&& !getPackageManager()
.queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY).isEmpty();
}
private boolean cleanCache() {
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
return true;
}
private boolean cleanPrivateStorage() {
for (String type : Arrays.asList("Images", "Videos", "Files", "Recordings")) {
cleanPrivateFiles(type);
}
return true;
}
private void cleanPrivateFiles(final String type) {
try {
File dir = new File(getFilesDir().getAbsolutePath(), "/" + type + "/");
File[] array = dir.listFiles();
if (array != null) {
for (int b = 0; b < array.length; b++) {
String name = array[b].getName().toLowerCase();
if (name.equals(".nomedia")) {
continue;
}
if (array[b].isFile()) {
array[b].delete();
}
}
}
} catch (Throwable e) {
Log.e("CleanCache", e.toString());
}
}
private boolean deleteOmemoIdentities() {
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.pref_delete_omemo_identities);
final List<CharSequence> accounts = new ArrayList<>();
for (Account account : xmppConnectionService.getAccounts()) {
if (account.isEnabled()) {
accounts.add(account.getJid().asBareJid().toString());
}
}
final boolean[] checkedItems = new boolean[accounts.size()];
builder.setMultiChoiceItems(
accounts.toArray(new CharSequence[accounts.size()]),
checkedItems,
(dialog, which, isChecked) -> {
checkedItems[which] = isChecked;
final AlertDialog alertDialog = (AlertDialog) dialog;
for (boolean item : checkedItems) {
if (item) {
alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
return;
}
}
alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
});
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(
R.string.delete_selected_keys,
(dialog, which) -> {
for (int i = 0; i < checkedItems.length; ++i) {
if (checkedItems[i]) {
try {
Jid jid = Jid.of(accounts.get(i).toString());
Account account = xmppConnectionService.findAccountByJid(jid);
if (account != null) {
account.getAxolotlService().regenerateKeys(true);
}
} catch (IllegalArgumentException e) {
//
}
}
}
});
AlertDialog dialog = builder.create();
dialog.show();
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
return true;
}
@Override
public void onStop() {
super.onStop();
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String name) {
final List<String> resendPresence =
Arrays.asList(
"confirm_messages",
DND_ON_SILENT_MODE,
AWAY_WHEN_SCREEN_IS_OFF,
"allow_message_correction",
TREAT_VIBRATE_AS_SILENT,
MANUALLY_CHANGE_PRESENCE,
BROADCAST_LAST_ACTIVITY);
if (name.equals(OMEMO_SETTING)) {
OmemoSetting.load(this, preferences);
changeOmemoSettingSummary();
} else if (name.equals(KEEP_FOREGROUND_SERVICE)) {
xmppConnectionService.toggleForegroundService();
} else if (resendPresence.contains(name)) {
if (xmppConnectionServiceBound) {
if (name.equals(AWAY_WHEN_SCREEN_IS_OFF) || name.equals(MANUALLY_CHANGE_PRESENCE)) {
xmppConnectionService.toggleScreenEventReceiver();
}
xmppConnectionService.refreshAllPresences();
}
} else if (name.equals("dont_trust_system_cas")) {
xmppConnectionService.updateMemorizingTrustmanager();
reconnectAccounts();
} else if (name.equals("use_tor")) {
if (preferences.getBoolean(name, false)) {
displayToast(getString(R.string.audio_video_disabled_tor));
}
reconnectAccounts();
xmppConnectionService.reinitializeMuclumbusService();
} else if (name.equals(AUTOMATIC_MESSAGE_DELETION)) {
xmppConnectionService.expireOldMessages(true);
} else if (name.equals(THEME)) {
final var value = preferences.getString(THEME,getString(R.string.theme));
final int desiredNightMode = Conversations.getDesiredNightMode(value);
setDesiredNightMode(desiredNightMode);
} else if (name.equals("dynamic_colors")) {
final var value = preferences.getBoolean("dynamic_colors",false);
setDynamicColors(value);
} else if (name.equals(PREVENT_SCREENSHOTS)) {
SettingsUtils.applyScreenshotPreventionSetting(this);
} else if (UnifiedPushDistributor.PREFERENCES.contains(name)) {
final String pushServerPreference =
Strings.nullToEmpty(preferences.getString(
UnifiedPushDistributor.PREFERENCE_PUSH_SERVER,
getString(R.string.default_push_server))).trim();
if (isJidInvalid(pushServerPreference) || isHttpUri(pushServerPreference)) {
Toast.makeText(this,R.string.invalid_jid,Toast.LENGTH_LONG).show();
}
if (xmppConnectionService.reconfigurePushDistributor()) {
xmppConnectionService.renewUnifiedPushEndpoints();
}
}
}
private static boolean isJidInvalid(final String input) {
if (Strings.isNullOrEmpty(input)) {
return true;
}
try {
Jid.ofEscaped(input);
return false;
} catch (final IllegalArgumentException e) {
return true;
}
}
private static boolean isHttpUri(final String input) {
final URI uri;
try {
uri = new URI(input);
} catch (final URISyntaxException e) {
return false;
}
return Arrays.asList("http","https").contains(uri.getScheme());
}
@Override
public void onResume() {
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
@Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (requestCode == REQUEST_CREATE_BACKUP) {
createBackup();
}
} else {
Toast.makeText(
this,
getString(
R.string.no_storage_permission,
getString(R.string.app_name)),
Toast.LENGTH_SHORT)
.show();
}
}
private void createBackup() {
ContextCompat.startForegroundService(this, new Intent(this, ExportBackupService.class));
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setMessage(R.string.backup_started_message);
builder.setPositiveButton(R.string.ok, null);
builder.create().show();
}
private void displayToast(final String msg) {
runOnUiThread(() -> Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show());
}
private void reconnectAccounts() {
for (Account account : xmppConnectionService.getAccounts()) {
if (account.isEnabled()) {
xmppConnectionService.reconnectAccountInBackground(account);
}
}
}
public void refreshUiReal() {
// nothing to do. This Activity doesn't implement any listeners
}
}

View file

@ -1,77 +0,0 @@
package eu.siacs.conversations.ui;
import android.content.Intent;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.text.TextUtils;
import android.widget.ListView;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.utils.Compatibility;
public class SettingsFragment extends PreferenceFragment {
private String page = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
// Remove from standard preferences if the flag ONLY_INTERNAL_STORAGE is false
if (!Config.ONLY_INTERNAL_STORAGE) {
PreferenceCategory mCategory = (PreferenceCategory) findPreference("security_options");
if (mCategory != null) {
Preference cleanCache = findPreference("clean_cache");
Preference cleanPrivateStorage = findPreference("clean_private_storage");
mCategory.removePreference(cleanCache);
mCategory.removePreference(cleanPrivateStorage);
}
}
Compatibility.removeUnusedPreferences(this);
if (!TextUtils.isEmpty(page)) {
openPreferenceScreen(page);
}
}
@Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
final ListView listView = getActivity().findViewById(android.R.id.list);
if (listView != null) {
listView.setDivider(null);
}
}
public void setActivityIntent(final Intent intent) {
boolean wasEmpty = TextUtils.isEmpty(page);
if (intent != null) {
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
if (intent.getExtras() != null) {
this.page = intent.getExtras().getString("page");
if (wasEmpty) {
openPreferenceScreen(page);
}
}
}
}
}
private void openPreferenceScreen(final String screenName) {
final Preference pref = findPreference(screenName);
if (pref instanceof PreferenceScreen) {
final PreferenceScreen preferenceScreen = (PreferenceScreen) pref;
getActivity().setTitle(preferenceScreen.getTitle());
preferenceScreen.setDependency("");
setPreferenceScreen((PreferenceScreen) pref);
}
}
}

View file

@ -172,7 +172,7 @@ public class ShareWithActivity extends XmppActivity
}
@Override
void onBackendConnected() {
protected void onBackendConnected() {
if (xmppConnectionServiceBound
&& share != null
&& ((share.contact != null && share.account != null))) {

View file

@ -18,7 +18,6 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
@ -56,6 +55,7 @@ import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.common.base.Strings;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.BuildConfig;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@ -348,7 +348,7 @@ public abstract class XmppActivity extends ActionBarActivity {
dialog.show();
}
abstract void onBackendConnected();
protected abstract void onBackendConnected();
protected void registerListeners() {
if (this instanceof XmppConnectionService.OnConversationUpdate) {
@ -414,7 +414,7 @@ public abstract class XmppActivity extends ActionBarActivity {
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_settings:
startActivity(new Intent(this, SettingsActivity.class));
startActivity(new Intent(this, eu.siacs.conversations.ui.activity.SettingsActivity.class));
break;
case R.id.action_privacy_policy:
openPrivacyPolicy();
@ -832,7 +832,7 @@ public abstract class XmppActivity extends ActionBarActivity {
}
protected boolean manuallyChangePresence() {
return getBooleanPreference(SettingsActivity.MANUALLY_CHANGE_PRESENCE, R.bool.manually_change_presence);
return getBooleanPreference(AppSettings.MANUALLY_CHANGE_PRESENCE, R.bool.manually_change_presence);
}
protected String getShareableUri() {
@ -873,7 +873,7 @@ public abstract class XmppActivity extends ActionBarActivity {
@Override
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
SettingsUtils.applyScreenshotSetting(this);
}
@Override

View file

@ -0,0 +1,70 @@
package eu.siacs.conversations.ui.activity;
import android.app.Notification;
import android.os.Bundle;
import androidx.databinding.DataBindingUtil;
import androidx.preference.PreferenceFragmentCompat;
import com.google.common.collect.ImmutableSet;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivitySettingsBinding;
import eu.siacs.conversations.ui.Activities;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.fragment.settings.MainSettingsFragment;
import eu.siacs.conversations.ui.fragment.settings.NotificationsSettingsFragment;
import eu.siacs.conversations.ui.fragment.settings.XmppPreferenceFragment;
import java.util.Collections;
public class SettingsActivity extends XmppActivity {
@Override
protected void refreshUiReal() {}
@Override
protected void onBackendConnected() {
final var fragmentManager = getSupportFragmentManager();
final var currentFragment = fragmentManager.findFragmentById(R.id.fragment_container);
if (currentFragment instanceof XmppPreferenceFragment xmppPreferenceFragment) {
xmppPreferenceFragment.onBackendConnected();
}
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActivitySettingsBinding binding =
DataBindingUtil.setContentView(this, R.layout.activity_settings);
setSupportActionBar(binding.materialToolbar);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
final var intent = getIntent();
final var categories = intent == null ? Collections.emptySet() : intent.getCategories();
final PreferenceFragmentCompat preferenceFragment;
if (ImmutableSet.of(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
.equals(categories)) {
preferenceFragment = new NotificationsSettingsFragment();
} else {
preferenceFragment = new MainSettingsFragment();
}
final var fragmentManager = getSupportFragmentManager();
final var currentFragment = fragmentManager.findFragmentById(R.id.fragment_container);
if (currentFragment == null) {
fragmentManager
.beginTransaction()
.replace(R.id.fragment_container, preferenceFragment)
.commit();
}
binding.materialToolbar.setNavigationOnClickListener(
view -> {
if (fragmentManager.getBackStackEntryCount() == 0) {
finish();
} else {
fragmentManager.popBackStack();
}
});
}
}

View file

@ -0,0 +1,47 @@
package eu.siacs.conversations.ui.activity.result;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class PickRingtone extends ActivityResultContract<Uri, Uri> {
private static final Uri NONE = Uri.parse("about:blank");
private final int ringToneType;
public PickRingtone(final int ringToneType) {
this.ringToneType = ringToneType;
}
@NonNull
@Override
public Intent createIntent(@NonNull final Context context, final Uri existing) {
final Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, ringToneType);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
if (existing != null) {
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, existing);
}
return intent;
}
@Override
public Uri parseResult(int resultCode, @Nullable Intent data) {
if (resultCode != Activity.RESULT_OK || data == null) {
return null;
}
final Uri pickedUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
return pickedUri == null ? NONE : pickedUri;
}
public static Uri noneToNull(final Uri uri) {
return uri == null || NONE.equals(uri) ? null : uri;
}
}

View file

@ -15,11 +15,11 @@ import androidx.databinding.DataBindingUtil;
import com.wefika.flowlayout.FlowLayout;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ItemContactBinding;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.ui.SettingsActivity;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.utils.IrregularUnicodeDetector;
@ -47,7 +47,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
public void refreshSettings() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
this.showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, false);
this.showDynamicTags = preferences.getBoolean(AppSettings.SHOW_DYNAMIC_TAGS, false);
}
@NonNull

View file

@ -0,0 +1,22 @@
package eu.siacs.conversations.ui.fragment.settings;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceFragmentCompat;
import eu.siacs.conversations.R;
public class AttachmentsSettingsFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.preferences_attachments, rootKey);
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(R.string.pref_attachments);
}
}

View file

@ -0,0 +1,69 @@
package eu.siacs.conversations.ui.fragment.settings;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.common.base.Strings;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.services.QuickConversationsService;
public class ConnectionSettingsFragment extends XmppPreferenceFragment {
private static final String GROUPS_AND_CONFERENCES = "groups_and_conferences";
public static boolean hideChannelDiscovery() {
return QuickConversationsService.isQuicksy()
|| QuickConversationsService.isPlayStoreFlavor()
|| Strings.isNullOrEmpty(Config.CHANNEL_DISCOVERY);
}
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.preferences_connection, rootKey);
final var channelDiscovery = findPreference(AppSettings.CHANNEL_DISCOVERY_METHOD);
final var groupsAndConferences = findPreference(GROUPS_AND_CONFERENCES);
if (channelDiscovery == null || groupsAndConferences == null) {
throw new IllegalStateException();
}
if (hideChannelDiscovery()) {
groupsAndConferences.setVisible(false);
channelDiscovery.setVisible(false);
}
}
@Override
protected void onSharedPreferenceChanged(@NonNull String key) {
super.onSharedPreferenceChanged(key);
switch (key) {
case AppSettings.USE_TOR -> {
final var appSettings = new AppSettings(requireContext());
if (appSettings.isUseTor()) {
runOnUiThread(
() ->
Toast.makeText(
requireActivity(),
R.string.audio_video_disabled_tor,
Toast.LENGTH_LONG)
.show());
}
reconnectAccounts();
requireService().reinitializeMuclumbusService();
}
case AppSettings.SHOW_CONNECTION_OPTIONS -> {
reconnectAccounts();
}
}
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(R.string.pref_connection_options);
}
}

View file

@ -0,0 +1,67 @@
package eu.siacs.conversations.ui.fragment.settings;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.color.DynamicColors;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Conversations;
import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.activity.SettingsActivity;
import eu.siacs.conversations.ui.util.SettingsUtils;
public class InterfaceSettingsFragment extends XmppPreferenceFragment {
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.preferences_interface, rootKey);
final var themePreference = findPreference("theme");
final var dynamicColors = findPreference("dynamic_colors");
if (themePreference == null || dynamicColors == null) {
throw new IllegalStateException(
"The preference resource file did not contain theme or color preferences");
}
themePreference.setOnPreferenceChangeListener(
(preference, newValue) -> {
if (newValue instanceof final String theme) {
final int desiredNightMode = Conversations.getDesiredNightMode(theme);
requireSettingsActivity().setDesiredNightMode(desiredNightMode);
}
return true;
});
dynamicColors.setVisible(DynamicColors.isDynamicColorAvailable());
dynamicColors.setOnPreferenceChangeListener(
(preference, newValue) -> {
requireSettingsActivity().setDynamicColors(Boolean.TRUE.equals(newValue));
return true;
});
}
@Override
protected void onSharedPreferenceChanged(@NonNull String key) {
super.onSharedPreferenceChanged(key);
if (key.equals(AppSettings.ALLOW_SCREENSHOTS)) {
SettingsUtils.applyScreenshotSetting(requireActivity());
}
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(R.string.pref_title_interface);
}
public SettingsActivity requireSettingsActivity() {
final var activity = requireActivity();
if (activity instanceof SettingsActivity settingsActivity) {
return settingsActivity;
}
throw new IllegalStateException(
String.format(
"%s is not %s",
activity.getClass().getName(), SettingsActivity.class.getName()));
}
}

View file

@ -0,0 +1,36 @@
package eu.siacs.conversations.ui.fragment.settings;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceFragmentCompat;
import com.google.common.base.Strings;
import eu.siacs.conversations.BuildConfig;
import eu.siacs.conversations.R;
public class MainSettingsFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.preferences_main, rootKey);
final var about = findPreference("about");
final var connection = findPreference("connection");
if (about == null || connection == null) {
throw new IllegalStateException("The preference resource file is missing some preferences");
}
about.setTitle(getString(R.string.title_activity_about_x, BuildConfig.APP_NAME));
about.setSummary(String.format("%s %s %s (%s)", BuildConfig.APP_NAME, BuildConfig.VERSION_NAME, im.conversations.webrtc.BuildConfig.WEBRTC_VERSION, Strings.nullToEmpty(Build.DEVICE)));
if (ConnectionSettingsFragment.hideChannelDiscovery()) {
connection.setSummary(R.string.pref_connection_summary);
}
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(R.string.title_activity_settings);
}
}

View file

@ -0,0 +1,102 @@
package eu.siacs.conversations.ui.fragment.settings;
import android.content.SharedPreferences;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.activity.result.PickRingtone;
import eu.siacs.conversations.utils.Compatibility;
public class NotificationsSettingsFragment extends XmppPreferenceFragment {
private final ActivityResultLauncher<Uri> pickRingtoneLauncher =
registerForActivityResult(
new PickRingtone(RingtoneManager.TYPE_RINGTONE),
result -> {
if (result == null) {
// do nothing. user aborted
return;
}
final Uri uri = PickRingtone.noneToNull(result);
setRingtone(uri);
Log.i(Config.LOGTAG, "User set ringtone to " + uri);
});
@Override
public void onCreatePreferences(
@Nullable final Bundle savedInstanceState, final @Nullable String rootKey) {
setPreferencesFromResource(R.xml.preferences_notifications, rootKey);
final var messageNotificationSettings = findPreference("message_notification_settings");
final var notificationRingtone = findPreference(AppSettings.NOTIFICATION_RINGTONE);
final var notificationHeadsUp = findPreference(AppSettings.NOTIFICATION_HEADS_UP);
final var notificationVibrate = findPreference(AppSettings.NOTIFICATION_VIBRATE);
final var notificationLed = findPreference(AppSettings.NOTIFICATION_LED);
final var foregroundService = findPreference(AppSettings.KEEP_FOREGROUND_SERVICE);
if (messageNotificationSettings == null
|| notificationRingtone == null
|| notificationHeadsUp == null
|| notificationVibrate == null
|| notificationLed == null
|| foregroundService == null) {
throw new IllegalStateException("The preference resource file is missing preferences");
}
if (Compatibility.runsTwentySix()) {
notificationRingtone.setVisible(false);
notificationHeadsUp.setVisible(false);
notificationVibrate.setVisible(false);
notificationLed.setVisible(false);
foregroundService.setVisible(false);
} else {
messageNotificationSettings.setVisible(false);
}
}
@Override
protected void onSharedPreferenceChanged(@NonNull String key) {
super.onSharedPreferenceChanged(key);
if (key.equals(AppSettings.KEEP_FOREGROUND_SERVICE)) {
requireService().toggleForegroundService();
}
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(R.string.notifications);
}
@Override
public boolean onPreferenceTreeClick(final Preference preference) {
if (AppSettings.RINGTONE.equals(preference.getKey())) {
pickRingtone();
return true;
}
return super.onPreferenceTreeClick(preference);
}
private void pickRingtone() {
final Uri uri = appSettings().getRingtone();
Log.i(Config.LOGTAG, "current ringtone: " + uri);
this.pickRingtoneLauncher.launch(uri);
}
private void setRingtone(final Uri uri) {
appSettings().setRingtone(uri);
}
private AppSettings appSettings() {
return new AppSettings(requireContext());
}
}

View file

@ -0,0 +1,41 @@
package eu.siacs.conversations.ui.fragment.settings;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.R;
public class PrivacySettingsFragment extends XmppPreferenceFragment {
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.preferences_privacy, rootKey);
}
@Override
protected void onSharedPreferenceChanged(@NonNull String key) {
super.onSharedPreferenceChanged(key);
switch (key) {
case AppSettings.AWAY_WHEN_SCREEN_IS_OFF, AppSettings.MANUALLY_CHANGE_PRESENCE -> {
requireService().toggleScreenEventReceiver();
requireService().refreshAllPresences();
}
case AppSettings.CONFIRM_MESSAGES,
AppSettings.BROADCAST_LAST_ACTIVITY,
AppSettings.ALLOW_MESSAGE_CORRECTION,
AppSettings.DND_ON_SILENT_MODE,
AppSettings.TREAT_VIBRATE_AS_SILENT -> {
requireService().refreshAllPresences();
}
}
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(R.string.pref_privacy);
}
}

View file

@ -0,0 +1,195 @@
package eu.siacs.conversations.ui.fragment.settings;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.common.base.Strings;
import com.google.common.primitives.Ints;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OmemoSetting;
import eu.siacs.conversations.services.MemorizingTrustManager;
import eu.siacs.conversations.utils.TimeFrameUtils;
import java.security.KeyStoreException;
import java.util.ArrayList;
import java.util.Collections;
public class SecuritySettingsFragment extends XmppPreferenceFragment {
private static final String REMOVE_TRUSTED_CERTIFICATES = "remove_trusted_certificates";
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.preferences_security, rootKey);
final ListPreference omemo = findPreference(AppSettings.OMEMO);
final ListPreference automaticMessageDeletion =
findPreference(AppSettings.AUTOMATIC_MESSAGE_DELETION);
if (omemo == null || automaticMessageDeletion == null) {
throw new IllegalStateException("The preference resource file is missing preferences");
}
omemo.setSummaryProvider(new OmemoSummaryProvider());
final int[] choices = getResources().getIntArray(R.array.automatic_message_deletion_values);
final CharSequence[] entries = new CharSequence[choices.length];
final CharSequence[] entryValues = new CharSequence[choices.length];
for (int i = 0; i < choices.length; ++i) {
entryValues[i] = String.valueOf(choices[i]);
entries[i] = messageDeletionValueToName(requireContext(), choices[i]);
}
automaticMessageDeletion.setEntries(entries);
automaticMessageDeletion.setEntryValues(entryValues);
automaticMessageDeletion.setSummaryProvider(new MessageDeletionSummaryProvider());
}
private static String messageDeletionValueToName(final Context context, final int value) {
if (value == 0) {
return context.getString(R.string.never);
} else {
return TimeFrameUtils.resolve(context, 1000L * value);
}
}
@Override
protected void onSharedPreferenceChanged(@NonNull String key) {
super.onSharedPreferenceChanged(key);
switch (key) {
case AppSettings.OMEMO -> {
OmemoSetting.load(requireContext());
}
case AppSettings.TRUST_SYSTEM_CA_STORE -> {
requireService().updateMemorizingTrustmanager();
reconnectAccounts();
}
case AppSettings.REQUIRE_CHANNEL_BINDING -> {}
}
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(R.string.pref_title_security);
}
@Override
public boolean onPreferenceTreeClick(final Preference preference) {
if (REMOVE_TRUSTED_CERTIFICATES.equals(preference.getKey())) {
showRemoveCertificatesDialog();
return true;
}
return super.onPreferenceTreeClick(preference);
}
private void showRemoveCertificatesDialog() {
final MemorizingTrustManager mtm = requireService().getMemorizingTrustManager();
final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
if (aliases.isEmpty()) {
Toast.makeText(requireActivity(), R.string.toast_no_trusted_certs, Toast.LENGTH_LONG)
.show();
return;
}
final ArrayList<Integer> selectedItems = new ArrayList<>();
final MaterialAlertDialogBuilder dialogBuilder =
new MaterialAlertDialogBuilder(requireActivity());
dialogBuilder.setTitle(getString(R.string.dialog_manage_certs_title));
dialogBuilder.setMultiChoiceItems(
aliases.toArray(new CharSequence[0]),
null,
(dialog, indexSelected, isChecked) -> {
if (isChecked) {
selectedItems.add(indexSelected);
} else if (selectedItems.contains(indexSelected)) {
selectedItems.remove(Integer.valueOf(indexSelected));
}
if (dialog instanceof AlertDialog alertDialog) {
alertDialog
.getButton(DialogInterface.BUTTON_POSITIVE)
.setEnabled(!selectedItems.isEmpty());
}
});
dialogBuilder.setPositiveButton(
getString(R.string.dialog_manage_certs_positivebutton),
(dialog, which) -> confirmCertificateDeletion(aliases, selectedItems));
dialogBuilder.setNegativeButton(R.string.cancel, null);
final AlertDialog removeCertsDialog = dialogBuilder.create();
removeCertsDialog.show();
removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
}
private void confirmCertificateDeletion(
final ArrayList<String> aliases, final ArrayList<Integer> selectedItems) {
final int count = selectedItems.size();
if (count == 0) {
return;
}
final MemorizingTrustManager mtm = requireService().getMemorizingTrustManager();
for (int i = 0; i < count; i++) {
try {
final int item = Integer.parseInt(selectedItems.get(i).toString());
final String alias = aliases.get(item);
mtm.deleteCertificate(alias);
} catch (final KeyStoreException e) {
Toast.makeText(
requireActivity(),
"Error: " + e.getLocalizedMessage(),
Toast.LENGTH_LONG)
.show();
}
}
reconnectAccounts();
Toast.makeText(
requireActivity(),
getResources()
.getQuantityString(
R.plurals.toast_delete_certificates, count, count),
Toast.LENGTH_LONG)
.show();
}
private static class MessageDeletionSummaryProvider
implements Preference.SummaryProvider<ListPreference> {
@Nullable
@Override
public CharSequence provideSummary(@NonNull ListPreference preference) {
final Integer value = Ints.tryParse(Strings.nullToEmpty(preference.getValue()));
return messageDeletionValueToName(preference.getContext(), value == null ? 0 : value);
}
}
private static class OmemoSummaryProvider
implements Preference.SummaryProvider<ListPreference> {
@Nullable
@Override
public CharSequence provideSummary(@NonNull ListPreference preference) {
final var context = preference.getContext();
final var sharedPreferences = preference.getSharedPreferences();
final String value;
if (sharedPreferences == null) {
value = null;
} else {
value =
sharedPreferences.getString(
preference.getKey(),
context.getString(R.string.omemo_setting_default));
}
return switch (Strings.nullToEmpty(value)) {
case "always" -> context.getString(R.string.pref_omemo_setting_summary_always);
case "default_off" -> context.getString(
R.string.pref_omemo_setting_summary_default_off);
default -> context.getString(R.string.pref_omemo_setting_summary_default_on);
};
}
}
}

View file

@ -0,0 +1,99 @@
package eu.siacs.conversations.ui.fragment.settings;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.EditTextPreference;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import eu.siacs.conversations.R;
import eu.siacs.conversations.services.UnifiedPushDistributor;
import eu.siacs.conversations.xmpp.Jid;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
public class UpSettingsFragment extends XmppPreferenceFragment {
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.preferences_up, rootKey);
}
@Override
public void onBackendConnected() {
final ListPreference upAccounts = findPreference(UnifiedPushDistributor.PREFERENCE_ACCOUNT);
final EditTextPreference pushServer = findPreference(UnifiedPushDistributor.PREFERENCE_PUSH_SERVER);
if (upAccounts == null || pushServer == null) {
throw new IllegalStateException();
}
pushServer.setOnPreferenceChangeListener((preference, newValue) -> {
if (newValue instanceof String string) {
if (Strings.isNullOrEmpty(string) || isJidInvalid(string) || isHttpUri(string)) {
Toast.makeText(requireActivity(),R.string.invalid_jid,Toast.LENGTH_LONG).show();
return false;
} else {
return true;
}
} else {
Toast.makeText(requireActivity(),R.string.invalid_jid,Toast.LENGTH_LONG).show();
return false;
}
});
reconfigureUpAccountPreference(upAccounts);
}
private static boolean isJidInvalid(final String input) {
try {
final var jid = Jid.ofEscaped(input);
return !jid.isBareJid();
} catch (final IllegalArgumentException e) {
return true;
}
}
private static boolean isHttpUri(final String input) {
final URI uri;
try {
uri = new URI(input);
} catch (final URISyntaxException e) {
return false;
}
return Arrays.asList("http","https").contains(uri.getScheme());
}
private void reconfigureUpAccountPreference(final ListPreference listPreference) {
final List<CharSequence> accounts =
ImmutableList.copyOf(
Lists.transform(
requireService().getAccounts(),
a -> a.getJid().asBareJid().toEscapedString()));
final ImmutableList.Builder<CharSequence> entries = new ImmutableList.Builder<>();
final ImmutableList.Builder<CharSequence> entryValues = new ImmutableList.Builder<>();
entries.add(getString(R.string.no_account_deactivated));
entryValues.add("none");
entries.addAll(accounts);
entryValues.addAll(accounts);
listPreference.setEntries(entries.build().toArray(new CharSequence[0]));
listPreference.setEntryValues(entryValues.build().toArray(new CharSequence[0]));
if (!accounts.contains(listPreference.getValue())) {
listPreference.setValue("none");
}
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(R.string.unified_push_distributor);
}
}

View file

@ -0,0 +1,80 @@
package eu.siacs.conversations.ui.fragment.settings;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceFragmentCompat;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.XmppActivity;
public abstract class XmppPreferenceFragment extends PreferenceFragmentCompat {
private final SharedPreferences.OnSharedPreferenceChangeListener
sharedPreferenceChangeListener =
(sharedPreferences, key) -> {
if (key == null) {
return;
}
onSharedPreferenceChanged(key);
};
protected void onSharedPreferenceChanged(@NonNull String key) {}
public void onBackendConnected() {}
@Override
public void onResume() {
super.onResume();
final var sharedPreferences = getPreferenceManager().getSharedPreferences();
if (sharedPreferences != null) {
sharedPreferences.registerOnSharedPreferenceChangeListener(
this.sharedPreferenceChangeListener);
}
final var xmppActivity = requireXmppActivity();
if (xmppActivity.xmppConnectionService != null) {
this.onBackendConnected();
}
}
@Override
public void onPause() {
super.onPause();
final var sharedPreferences = getPreferenceManager().getSharedPreferences();
if (sharedPreferences != null) {
sharedPreferences.registerOnSharedPreferenceChangeListener(
this.sharedPreferenceChangeListener);
}
}
protected void reconnectAccounts() {
final var service = requireService();
for (final Account account : service.getAccounts()) {
if (account.isEnabled()) {
service.reconnectAccountInBackground(account);
}
}
}
protected XmppActivity requireXmppActivity() {
final var activity = requireActivity();
if (activity instanceof XmppActivity xmppActivity) {
return xmppActivity;
}
throw new IllegalStateException();
}
protected XmppConnectionService requireService() {
final var xmppActivity = requireXmppActivity();
final var service = xmppActivity.xmppConnectionService;
if (service != null) {
return service;
}
throw new IllegalStateException();
}
protected void runOnUiThread(final Runnable runnable) {
requireActivity().runOnUiThread(runnable);
}
}

View file

@ -1,20 +1,19 @@
package eu.siacs.conversations.ui.util;
import android.app.Activity;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.view.Window;
import android.view.WindowManager;
import eu.siacs.conversations.AppSettings;
public class SettingsUtils {
public static void applyScreenshotPreventionSetting(Activity activity){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
boolean preventScreenshots = preferences.getBoolean("prevent_screenshots", false);
Window activityWindow = activity.getWindow();
if(preventScreenshots){
activityWindow.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
public static void applyScreenshotSetting(final Activity activity) {
final var appSettings = new AppSettings(activity);
final Window activityWindow = activity.getWindow();
if (appSettings.isAllowScreenshots()) {
activityWindow.clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
activityWindow.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
}
}

View file

@ -22,10 +22,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.SettingsActivity;
import eu.siacs.conversations.ui.SettingsFragment;
import java.util.Arrays;
import java.util.Collections;
@ -33,15 +32,6 @@ import java.util.List;
public class Compatibility {
private static final List<String> UNUSED_SETTINGS_POST_TWENTYSIX =
Arrays.asList(
"led",
"notification_ringtone",
"notification_headsup",
"vibrate_on_notification");
private static final List<String> UNUSED_SETTINGS_PRE_TWENTYSIX =
Collections.singletonList("message_notification_settings");
public static boolean hasStoragePermission(final Context context) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU || ContextCompat.checkSelfPermission(
context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
@ -76,7 +66,7 @@ public class Compatibility {
final PackageManager packageManager = context.getPackageManager();
final ApplicationInfo applicationInfo =
packageManager.getApplicationInfo(context.getPackageName(), 0);
return applicationInfo == null || applicationInfo.targetSdkVersion >= 26;
return applicationInfo.targetSdkVersion >= 26;
} catch (PackageManager.NameNotFoundException | RuntimeException e) {
return true; // when in doubt
}
@ -87,7 +77,7 @@ public class Compatibility {
final PackageManager packageManager = context.getPackageManager();
final ApplicationInfo applicationInfo =
packageManager.getApplicationInfo(context.getPackageName(), 0);
return applicationInfo == null || applicationInfo.targetSdkVersion >= 24;
return applicationInfo.targetSdkVersion >= 24;
} catch (PackageManager.NameNotFoundException | RuntimeException e) {
return true; // when in doubt
}
@ -105,43 +95,10 @@ public class Compatibility {
return runsAndTargetsTwentySix(context)
|| getBooleanPreference(
context,
SettingsActivity.KEEP_FOREGROUND_SERVICE,
AppSettings.KEEP_FOREGROUND_SERVICE,
R.bool.enable_foreground_service);
}
public static void removeUnusedPreferences(SettingsFragment settingsFragment) {
List<PreferenceCategory> categories =
Arrays.asList(
(PreferenceCategory)
settingsFragment.findPreference("notification_category"),
(PreferenceCategory) settingsFragment.findPreference("advanced"));
for (String key :
(runsTwentySix()
? UNUSED_SETTINGS_POST_TWENTYSIX
: UNUSED_SETTINGS_PRE_TWENTYSIX)) {
Preference preference = settingsFragment.findPreference(key);
if (preference != null) {
for (PreferenceCategory category : categories) {
if (category != null) {
category.removePreference(preference);
}
}
}
}
if (Compatibility.runsTwentySix()) {
if (targetsTwentySix(settingsFragment.getContext())) {
Preference preference =
settingsFragment.findPreference(SettingsActivity.KEEP_FOREGROUND_SERVICE);
if (preference != null) {
for (PreferenceCategory category : categories) {
if (category != null) {
category.removePreference(preference);
}
}
}
}
}
}
public static void startService(Context context, Intent intent) {
try {

View file

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
</vector>

View file

@ -0,0 +1,32 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M5,10h2v7h-2z" />
<path
android:fillColor="@android:color/white"
android:pathData="M11,10h2v7h-2z" />
<path
android:fillColor="@android:color/white"
android:pathData="M22,6l-10,-5l-10,5l0,2l20,0z" />
<path
android:fillColor="@android:color/white"
android:pathData="M2,19v2h12.4c-0.21,-0.64 -0.32,-1.31 -0.36,-2H2z" />
<path
android:fillColor="@android:color/white"
android:pathData="M19,12.26l0,-2.26l-2,0l0,3.26z" />
<path
android:fillColor="@android:color/white"
android:pathData="M20,14l-4,2v2.55c0,2.52 1.71,4.88 4,5.45c2.29,-0.57 4,-2.93 4,-5.45V16L20,14zM19.28,21l-2.03,-2.03l1.06,-1.06l0.97,0.97l2.41,-2.38l1.06,1.06L19.28,21z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M2,12.5C2,9.46 4.46,7 7.5,7H18c2.21,0 4,1.79 4,4s-1.79,4 -4,4H9.5C8.12,15 7,13.88 7,12.5S8.12,10 9.5,10H17v2H9.41c-0.55,0 -0.55,1 0,1H18c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2H7.5C5.57,9 4,10.57 4,12.5S5.57,16 7.5,16H17v2H7.5C4.46,18 2,15.54 2,12.5z" />
</vector>

View file

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M15,2l-3.5,0l-1,-1l-5,0l-1,1l-3.5,0l0,2l14,0z" />
<path
android:fillColor="@android:color/white"
android:pathData="M16,9c-0.7,0 -1.37,0.1 -2,0.29V5H2v12c0,1.1 0.9,2 2,2h5.68c1.12,2.36 3.53,4 6.32,4c3.87,0 7,-3.13 7,-7C23,12.13 19.87,9 16,9zM16,21c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5s5,2.24 5,5S18.76,21 16,21z" />
<path
android:fillColor="@android:color/white"
android:pathData="M16.5,12l-1.5,0l0,5l3.6,2.1l0.8,-1.2l-2.9,-1.7z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M21.5,14.98c-0.02,0 -0.03,0 -0.05,0.01C21.2,13.3 19.76,12 18,12c-1.4,0 -2.6,0.83 -3.16,2.02C13.26,14.1 12,15.4 12,17c0,1.66 1.34,3 3,3l6.5,-0.02c1.38,0 2.5,-1.12 2.5,-2.5S22.88,14.98 21.5,14.98zM10,4.26v2.09C7.67,7.18 6,9.39 6,12c0,1.77 0.78,3.34 2,4.44V14h2v6H4v-2h2.73C5.06,16.54 4,14.4 4,12C4,8.27 6.55,5.15 10,4.26zM20,6h-2.73c1.43,1.26 2.41,3.01 2.66,5l-2.02,0C17.68,9.64 16.98,8.45 16,7.56V10h-2V4h6V6z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9s9,-4.03 9,-9c0,-0.46 -0.04,-0.92 -0.1,-1.36c-0.98,1.37 -2.58,2.26 -4.4,2.26c-2.98,0 -5.4,-2.42 -5.4,-5.4c0,-1.81 0.89,-3.42 2.26,-4.4C12.92,3.04 12.46,3 12,3L12,3z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13L7,13v-2h10v2z" />
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M312,720Q261,720 214.5,702Q168,684 131,649Q83,604 61.5,542.5Q40,481 40,415Q40,337 78,288.5Q116,240 189,240Q203,240 215.5,242.5Q228,245 241,250L480,339L719,250Q732,245 744.5,242.5Q757,240 771,240Q844,240 882,288.5Q920,337 920,415Q920,481 898.5,542.5Q877,604 829,649Q792,684 745.5,702Q699,720 648,720Q582,720 536,690Q490,660 490,660L470,660Q470,660 424,690Q378,720 312,720ZM312,640Q349,640 381,622.5Q413,605 440,580L520,580Q547,605 579,622.5Q611,640 648,640Q684,640 717.5,627.5Q751,615 777,589Q811,555 825.5,509Q840,463 840,415Q840,374 823,346.5Q806,319 769,320Q766,320 747,324L480,424L213,324Q208,322 202.5,321Q197,320 191,320Q154,320 137,347Q120,374 120,415Q120,464 134.5,510Q149,556 184,590Q210,615 243,627.5Q276,640 312,640ZM361,580Q398,580 419,563.5Q440,547 440,518Q440,469 375.5,424.5Q311,380 239,380Q202,380 181,396.5Q160,413 160,442Q160,491 224.5,535.5Q289,580 361,580ZM355,520Q317,520 272.5,495Q228,470 220,444Q225,442 231.5,440.5Q238,439 245,439Q283,439 327.5,464.5Q372,490 380,516Q375,518 368.5,519Q362,520 355,520ZM599,581Q671,581 735.5,536Q800,491 800,442Q800,413 779.5,396Q759,379 721,379Q649,379 584.5,424Q520,469 520,518Q520,547 541,564Q562,581 599,581ZM605,520Q598,520 592,519Q586,518 581,516Q589,490 633.5,465Q678,440 716,440Q723,440 729,441Q735,442 740,444Q732,470 687.5,495Q643,520 605,520ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zM22.24,5.59L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z" />
</vector>

View file

@ -1,5 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@android:color/white" android:pathData="M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z" />
</vector>

View file

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19,7v4H5.83l3.58,-3.59L8,6l-6,6 6,6 1.41,-1.41L5.83,13H21V7z" />
</vector>

View file

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M22,12l-4,4 -1.41,-1.41L18.17,13h-5.23c-0.34,3.1 -2.26,5.72 -4.94,7.05C7.96,21.69 6.64,23 5,23c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3c0.95,0 1.78,0.45 2.33,1.14 1.9,-1.03 3.26,-2.91 3.58,-5.14h-3.1C7.4,14.16 6.3,15 5,15c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3c1.3,0 2.4,0.84 2.82,2h3.1c-0.32,-2.23 -1.69,-4.1 -3.59,-5.14C6.78,6.55 5.95,7 5,7 3.34,7 2,5.66 2,4s1.34,-3 3,-3c1.64,0 2.96,1.31 2.99,2.95 2.68,1.33 4.6,3.95 4.94,7.05h5.23l-1.58,-1.59L18,8l4,4z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M18,4l2,4h-3l-2,-4h-2l2,4h-3l-2,-4H8l2,4H7L5,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4h-4z" />
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M220,880Q162,880 121,839Q80,798 80,740Q80,682 121,641Q162,600 220,600Q238,600 255,604.5Q272,609 287,617L440,464L440,354Q396,341 368,304.5Q340,268 340,220Q340,162 381,121Q422,80 480,80Q538,80 579,121Q620,162 620,220Q620,268 592,304.5Q564,341 520,354L520,464L674,617Q689,609 705.5,604.5Q722,600 740,600Q798,600 839,641Q880,682 880,740Q880,798 839,839Q798,880 740,880Q682,880 641,839Q600,798 600,740Q600,722 604.5,705Q609,688 617,673L480,536L343,673Q351,688 355.5,705Q360,722 360,740Q360,798 319,839Q278,880 220,880ZM740,800Q765,800 782.5,782.5Q800,765 800,740Q800,715 782.5,697.5Q765,680 740,680Q715,680 697.5,697.5Q680,715 680,740Q680,765 697.5,782.5Q715,800 740,800ZM480,280Q505,280 522.5,262.5Q540,245 540,220Q540,195 522.5,177.5Q505,160 480,160Q455,160 437.5,177.5Q420,195 420,220Q420,245 437.5,262.5Q455,280 480,280ZM220,800Q245,800 262.5,782.5Q280,765 280,740Q280,715 262.5,697.5Q245,680 220,680Q195,680 177.5,697.5Q160,715 160,740Q160,765 177.5,782.5Q195,800 220,800Z"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.49,2 2,6.49 2,12s4.49,10 10,10c1.38,0 2.5,-1.12 2.5,-2.5c0,-0.61 -0.23,-1.2 -0.64,-1.67c-0.08,-0.1 -0.13,-0.21 -0.13,-0.33c0,-0.28 0.22,-0.5 0.5,-0.5H16c3.31,0 6,-2.69 6,-6C22,6.04 17.51,2 12,2zM17.5,13c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5C19,12.33 18.33,13 17.5,13zM14.5,9C13.67,9 13,8.33 13,7.5C13,6.67 13.67,6 14.5,6S16,6.67 16,7.5C16,8.33 15.33,9 14.5,9zM5,11.5C5,10.67 5.67,10 6.5,10S8,10.67 8,11.5C8,12.33 7.33,13 6.5,13S5,12.33 5,11.5zM11,7.5C11,8.33 10.33,9 9.5,9S8,8.33 8,7.5C8,6.67 8.67,6 9.5,6S11,6.67 11,7.5z" />
</vector>

View file

@ -1,5 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@android:color/white" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12c5.16,-1.26 9,-6.45 9,-12V5L12,1L12,1zM11,7h2v2h-2V7zM11,11h2v6h-2V11z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M18.93,11c-0.49,-3.39 -3.4,-6 -6.93,-6s-6.44,2.61 -6.93,6H2v2h3.07c0.49,3.39 3.4,6 6.93,6s6.44,-2.61 6.93,-6H22v-2H18.93zM15,14.5c0,0.55 -0.45,1 -1,1h-4c-0.55,0 -1,-0.45 -1,-1v-3c0,-0.55 0.45,-1 1,-1v-1c0,-1.21 1.08,-2.18 2.34,-1.97C13.32,7.69 14,8.61 14,9.61v0.89c0.55,0 1,0.45 1,1V14.5zM12.75,13c0,0.41 -0.34,0.75 -0.75,0.75s-0.75,-0.34 -0.75,-0.75c0,-0.41 0.34,-0.75 0.75,-0.75S12.75,12.59 12.75,13zM13,9.5v1h-2v-1c0,-0.55 0.45,-1 1,-1S13,8.95 13,9.5z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M15.73,3L8.27,3L3,8.27v7.46L8.27,21h7.46L21,15.73L21,8.27L15.73,3zM12,17.3c-0.72,0 -1.3,-0.58 -1.3,-1.3 0,-0.72 0.58,-1.3 1.3,-1.3 0.72,0 1.3,0.58 1.3,1.3 0,0.72 -0.58,1.3 -1.3,1.3zM13,13h-2L11,7h2v6z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M10,16h4c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1v-1c0,-1.11 -0.9,-2 -2,-2 -1.11,0 -2,0.9 -2,2v1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1zM10.8,10c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2v1h-2.4v-1zM17,1L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-2 -2,-2zM17,19L7,19L7,5h10v14z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M17,1.01L7,1C5.9,1 5,1.9 5,3v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3C19,1.9 18.1,1.01 17,1.01zM17,18H7V6h10V18zM9.5,8.5H12V7H8v4h1.5V8.5zM12,17h4v-4h-1.5v2.5H12V17z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12L21,5l-9,-4zM12,11.99h7c-0.53,4.12 -3.28,7.79 -7,8.94L12,12L5,12L5,6.3l7,-3.11v8.8z" />
</vector>

View file

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20,6c0,-1.1 -0.9,-2 -2,-2h-4c0,-1.38 -1.12,-2.5 -2.5,-2.5S9,2.62 9,4H5.01c-1.1,0 -2,0.9 -2,2v3.8C5.7,9.8 6,11.96 6,12.5c0,0.54 -0.29,2.7 -3,2.7V19c0,1.1 0.9,2 2,2h3.8c0,-2.16 1.37,-2.78 2.2,-2.94v-9.3l9,4.5V6z" />
<path
android:fillColor="@android:color/white"
android:pathData="M13,12l0,4l4,1l-4,1l0,4l10,-5z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M7.77,6.76L6.23,5.48 0.82,12l5.41,6.52 1.54,-1.28L3.42,12l4.35,-5.24zM7,13h2v-2L7,11v2zM17,11h-2v2h2v-2zM11,13h2v-2h-2v2zM17.77,5.48l-1.54,1.28L20.58,12l-4.35,5.24 1.54,1.28L23.18,12l-5.41,-6.52z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M9,11.24V7.5C9,6.12 10.12,5 11.5,5S14,6.12 14,7.5v3.74c1.21,-0.81 2,-2.18 2,-3.74C16,5.01 13.99,3 11.5,3S7,5.01 7,7.5C7,9.06 7.79,10.43 9,11.24zM18.84,15.87l-4.54,-2.26c-0.17,-0.07 -0.35,-0.11 -0.54,-0.11H13v-6C13,6.67 12.33,6 11.5,6S10,6.67 10,7.5v10.74c-3.6,-0.76 -3.54,-0.75 -3.67,-0.75c-0.31,0 -0.59,0.13 -0.79,0.33l-0.79,0.8l4.94,4.94C9.96,23.83 10.34,24 10.75,24h6.79c0.75,0 1.33,-0.55 1.44,-1.28l0.75,-5.27c0.01,-0.07 0.02,-0.14 0.02,-0.2C19.75,16.63 19.37,16.09 18.84,15.87z" />
</vector>

View file

@ -1,5 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@android:color/white" android:pathData="M19.3,16.9c0.4,-0.7 0.7,-1.5 0.7,-2.4c0,-2.5 -2,-4.5 -4.5,-4.5S11,12 11,14.5s2,4.5 4.5,4.5c0.9,0 1.7,-0.3 2.4,-0.7l3.2,3.2l1.4,-1.4L19.3,16.9zM15.5,17c-1.4,0 -2.5,-1.1 -2.5,-2.5s1.1,-2.5 2.5,-2.5s2.5,1.1 2.5,2.5S16.9,17 15.5,17zM12,20v2C6.48,22 2,17.52 2,12C2,6.48 6.48,2 12,2c4.84,0 8.87,3.44 9.8,8h-2.07c-0.64,-2.46 -2.4,-4.47 -4.73,-5.41V5c0,1.1 -0.9,2 -2,2h-2v2c0,0.55 -0.45,1 -1,1H8v2h2v3H9l-4.79,-4.79C4.08,10.79 4,11.38 4,12C4,16.41 7.59,20 12,20z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M19.3,16.9c0.4,-0.7 0.7,-1.5 0.7,-2.4c0,-2.5 -2,-4.5 -4.5,-4.5S11,12 11,14.5s2,4.5 4.5,4.5c0.9,0 1.7,-0.3 2.4,-0.7l3.2,3.2l1.4,-1.4L19.3,16.9zM15.5,17c-1.4,0 -2.5,-1.1 -2.5,-2.5s1.1,-2.5 2.5,-2.5s2.5,1.1 2.5,2.5S16.9,17 15.5,17zM12,20v2C6.48,22 2,17.52 2,12C2,6.48 6.48,2 12,2c4.84,0 8.87,3.44 9.8,8h-2.07c-0.64,-2.46 -2.4,-4.47 -4.73,-5.41V5c0,1.1 -0.9,2 -2,2h-2v2c0,0.55 -0.45,1 -1,1H8v2h2v3H9l-4.79,-4.79C4.08,10.79 4,11.38 4,12C4,16.41 7.59,20 12,20z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M16,13h-3V3h-2v10H8l4,4 4,-4zM4,19v2h16v-2H4z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M0,15h2L2,9L0,9v6zM3,17h2L5,7L3,7v10zM22,9v6h2L24,9h-2zM19,17h2L21,7h-2v10zM16.5,3h-9C6.67,3 6,3.67 6,4.5v15c0,0.83 0.67,1.5 1.5,1.5h9c0.83,0 1.5,-0.67 1.5,-1.5v-15c0,-0.83 -0.67,-1.5 -1.5,-1.5zM16,19L8,19L8,5h8v14z" />
</vector>

View file

@ -1,9 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:fillColor="@android:color/white"
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" />
</vector>

View file

@ -1,26 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize" />
app:liftOnScroll="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/material_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_arrow_back_24dp" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/settings_content"
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

View file

@ -344,7 +344,6 @@
<string name="pref_remove_trusted_certificates_summary">إحذف الشهادات التي تقبلتها يدويا</string>
<string name="dialog_manage_certs_title">إحذف الشهادات</string>
<string name="dialog_manage_certs_positivebutton">قم بحذف المختارة</string>
<string name="dialog_manage_certs_negativebutton">الغاء</string>
<string name="pref_quick_action">حركة سريعة</string>
<string name="none">لا شيء</string>
<string name="recently_used">التي تم استعمالها كثيرا مؤخرا</string>

View file

@ -430,7 +430,6 @@
<string name="toast_no_trusted_certs">Няма сертификати, одобрени на ръка</string>
<string name="dialog_manage_certs_title">Премахване на сертификатите</string>
<string name="dialog_manage_certs_positivebutton">Изтриване на избраните</string>
<string name="dialog_manage_certs_negativebutton">Отказ</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d сертификат е изтрит</item>
<item quantity="other">%d сертификата са изтрити</item>

View file

@ -99,7 +99,6 @@
<string name="block_contact">এই ব্যক্তিকে ব্লক্ করা যাক</string>
<string name="unblock_contact">ব্লকটা সরিয়ে ফেলা যাক</string>
<string name="vcard">পরিচিত ব্যক্তি</string>
<string name="dialog_manage_certs_negativebutton">না, থাক।</string>
<string name="search_contacts">পরিচিত ব্যক্তিদের মধ্যে খোঁজা যাক</string>
<string name="search_messages">বার্তাগুলির মধ্যে খোঁজা যাক</string>
<string name="pref_start_search">সরাসরিভাবেই খোঁজা যাক</string>

View file

@ -429,7 +429,6 @@
<string name="toast_no_trusted_certs">No hi ha certificats aprovats manualment</string>
<string name="dialog_manage_certs_title">Esborrar certificats</string>
<string name="dialog_manage_certs_positivebutton">Esborrar selecció</string>
<string name="dialog_manage_certs_negativebutton">Cancel·lar</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d certificat esborrat</item>
<item quantity="other">%d certificats esborrats</item>

View file

@ -432,7 +432,6 @@
<string name="toast_no_trusted_certs">Žádné ručně povolené certifikáty</string>
<string name="dialog_manage_certs_title">Odstranit certifikáty</string>
<string name="dialog_manage_certs_positivebutton">Smazat výběr</string>
<string name="dialog_manage_certs_negativebutton">Zrušit</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d certifikát smazán</item>
<item quantity="few">%d certifikáty smazány</item>

View file

@ -438,7 +438,6 @@
<string name="toast_no_trusted_certs">Ingen manuelt godkendt certifikater</string>
<string name="dialog_manage_certs_title">Fjern certifikater</string>
<string name="dialog_manage_certs_positivebutton">Slet valgt</string>
<string name="dialog_manage_certs_negativebutton">Annuller</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d certifikat slettet</item>
<item quantity="other">%d certifikater slettet</item>
@ -990,5 +989,4 @@
<string name="outgoing_call_duration_timestamp">Udgående opkald (%s) · %s</string>
<string name="incoming_call_duration_timestamp">Indkommende opkald (%s) · %s</string>
<string name="delete_from_server">Fjern konto fra server</string>
<string name="pref_up_push_server_summary">En brugervalgt push-server til at videresende push-meddelelser via XMPP til din enhed.</string>
</resources>

View file

@ -440,7 +440,6 @@
<string name="toast_no_trusted_certs">Keine manuell bestätigten Zertifikate</string>
<string name="dialog_manage_certs_title">Zertifikate löschen</string>
<string name="dialog_manage_certs_positivebutton">Auswahl löschen</string>
<string name="dialog_manage_certs_negativebutton">Abbrechen</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d Zertifikat gelöscht</item>
<item quantity="other">%d Zertifikate gelöscht</item>
@ -982,7 +981,6 @@
<string name="reject_switch_to_video">Umschalten auf Video ablehnen</string>
<string name="pref_up_push_account_title">XMPP-Konto</string>
<string name="pref_up_push_server_title">Push-Server</string>
<string name="pref_up_push_server_summary">Ein selbst gewählter Push-Server, der Push-Nachrichten über XMPP an dein Gerät weiterleitet.</string>
<string name="no_account_deactivated">Kein (deaktiviert)</string>
<string name="unified_push_distributor">UnifiedPush Verteiler</string>
<string name="pref_up_push_account_summary">Das Konto, über das Push-Nachrichten empfangen werden sollen.</string>
@ -1018,7 +1016,6 @@
<string name="remove_bookmark_and_close">Möchtest du das Lesezeichen für %s entfernen und die Unterhaltung schließen?</string>
<string name="delete_and_close">Löschen &amp; Schließen</string>
<string name="remove_bookmark">Willst du das Lesezeichen für %s entfernen?</string>
<string name="channel_discover_opt_in_message">Die Channelsuche verwendet einen Drittanbieterservice namens &lt;a href=https://search.jabber.network&gt;search.jabber.network&lt;/a&gt;.&lt;br&gt;&lt;br&gt;Wenn du diese Funktion verwendest, werden deine IP-Adresse und deine Suchbegriffe an diesen Dienst übertragen. Weitere Informationen findest du in der &lt;a href=https://search.jabber.network/privacy&gt;Datenschutzerklärung&lt;/a&gt;.</string>
<string name="title_activity_share_with">Teilen mit…</string>
<string name="pref_use_colorful_bubbles_summary">Farbige Chatblasen helfen, gesendete und empfangene Nachrichten zu unterscheiden</string>
<string name="pref_use_colorful_bubbles">Farbige Chatblasen</string>

View file

@ -431,7 +431,6 @@
<string name="toast_no_trusted_certs">Δεν υπάρχουν χειροκίνητα επιβεβαιωμένα πιστοποιητικα</string>
<string name="dialog_manage_certs_title">Αφαίρεση πιστοποιητικών</string>
<string name="dialog_manage_certs_positivebutton">Διαγραφή επιλογής</string>
<string name="dialog_manage_certs_negativebutton">Ακύρωση</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d πιστοποιητικό διαγράφηκε</item>
<item quantity="other">%d πιστοποιητικά διαγράφηκαν</item>

View file

@ -441,7 +441,6 @@
<string name="toast_no_trusted_certs">No aceptar certificados manualmente</string>
<string name="dialog_manage_certs_title">Eliminar certificados</string>
<string name="dialog_manage_certs_positivebutton">Eliminar seleccionados</string>
<string name="dialog_manage_certs_negativebutton">Cancelar</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d certificado eliminado</item>
<item quantity="many">%d certificados eliminados</item>
@ -998,7 +997,6 @@
<string name="pref_up_push_account_title">Cuenta XMPP</string>
<string name="pref_up_push_account_summary">La cuenta a través de la cual se recibirán los mensajes push.</string>
<string name="pref_up_push_server_title">Servidor push</string>
<string name="pref_up_push_server_summary">Un servidor push elegido por el usuario para transmitir mensajes push a través de XMPP a su dispositivo.</string>
<string name="no_account_deactivated">Ninguno (desactivado)</string>
<string name="incoming_call_duration_timestamp">Llamada entrante (%s) · %s</string>
<string name="outgoing_call_duration_timestamp">Llamada saliente (%s) · %s</string>

View file

@ -362,7 +362,6 @@
<string name="toast_no_trusted_certs">Ez dago eskuz onartutako ziurtagiririk</string>
<string name="dialog_manage_certs_title">Ziurtagiriak kendu</string>
<string name="dialog_manage_certs_positivebutton">Aukeratutakoak ezabatu</string>
<string name="dialog_manage_certs_negativebutton">Utzi</string>
<plurals name="toast_delete_certificates">
<item quantity="one">Ziurtagiri %d ezabatua</item>
<item quantity="other">%d ziurtagiri ezabatuak</item>

View file

@ -139,7 +139,6 @@
<string name="no_role">آفلاین</string>
<string name="vcard">مخاطب</string>
<string name="show_location">نمایش موقعیت مکانی</string>
<string name="dialog_manage_certs_negativebutton">لغو</string>
<string name="presence_online">آنلاین</string>
<string name="gp_medium">متوسط</string>
<string name="mtm_accept_cert">تأییدیهٔ امنیتی ناشناخته پذیرفته شود؟</string>
@ -157,7 +156,6 @@
<string name="view_conversation">دیدن گفتگو</string>
<string name="toast_message_omemo_fingerprint">اثر انگشت OMEMO کپی شد</string>
<string name="show_block_list">نمایش مسدودسازی‌ها</string>
<string name="pref_up_push_server_summary">سرور push دلخواه کاربر برای رساندن اعلان‌های push از راه XMPP به دستگاه شما.</string>
<string name="connect">وصل شدن</string>
<string name="log_in">ورود</string>
<string name="not_connected_try_again">شما وصل نیستید. بعداً دوباره تلاش کنید</string>

View file

@ -423,7 +423,6 @@
<string name="pref_remove_trusted_certificates_summary">Poista käsin hyväksytyt varmenteet</string>
<string name="toast_no_trusted_certs">Ei käsin hyväksyttyjä varmenteita</string>
<string name="dialog_manage_certs_title">Poista varmenteet</string>
<string name="dialog_manage_certs_negativebutton">Peruuta</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d varmenne poistettiin</item>
<item quantity="other">%d varmennetta poistettiin</item>

View file

@ -437,7 +437,6 @@
<string name="toast_no_trusted_certs">Aucun certificat approuvé manuellement</string>
<string name="dialog_manage_certs_title">Retirer les certificats</string>
<string name="dialog_manage_certs_positivebutton">Supprimer la sélection</string>
<string name="dialog_manage_certs_negativebutton">Annuler</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d certificat supprimé</item>
<item quantity="many">%d certificats supprimés</item>
@ -990,7 +989,6 @@
<string name="pref_up_push_account_title">Compte XMPP</string>
<string name="pref_up_push_account_summary">Le compte par lequel les messages push seront reçus.</string>
<string name="pref_up_push_server_title">Serveur Push</string>
<string name="pref_up_push_server_summary">Un serveur push choisi par l\'utilisateur pour relayer les messages push via XMPP vers votre appareil.</string>
<string name="no_account_deactivated">Aucun (désactivé)</string>
<plurals name="n_missed_calls_from_m_contacts">
<item quantity="one">%1$d appels manqués de %2$d contact</item>

View file

@ -440,7 +440,6 @@
<string name="toast_no_trusted_certs">Sen certificados aprobados manualmente</string>
<string name="dialog_manage_certs_title">Eliminar certificados</string>
<string name="dialog_manage_certs_positivebutton">Borrar seleción</string>
<string name="dialog_manage_certs_negativebutton">Cancelar</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d certificado eliminado</item>
<item quantity="other">%d certificados eleminados</item>
@ -984,7 +983,6 @@
<string name="pref_up_push_account_title">Conta XMPP</string>
<string name="pref_up_push_account_summary">A conta a través da cal se recibirán as mensaxes push.</string>
<string name="pref_up_push_server_title">Servidor Push</string>
<string name="pref_up_push_server_summary">O servidor elexido pola usuaria para obter as mensaxes push a través de XMPP.</string>
<string name="no_account_deactivated">Ningún (desactivado)</string>
<string name="decline">Rexeitar</string>
<string name="incoming_call_duration_timestamp">Chamada entrante (%s) · %s</string>

View file

@ -422,7 +422,6 @@
<string name="toast_no_trusted_certs">Nincsenek kézzel jóváhagyott tanúsítványok</string>
<string name="dialog_manage_certs_title">Tanúsítványok eltávolítása</string>
<string name="dialog_manage_certs_positivebutton">Kijelölés törlése</string>
<string name="dialog_manage_certs_negativebutton">Mégse</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d tanúsítvány törölve</item>
<item quantity="other">%d tanúsítvány törölve</item>

View file

@ -357,7 +357,6 @@
<string name="toast_no_trusted_certs">Tidak ada sertifikat yang disahkan secara manual</string>
<string name="dialog_manage_certs_title">Hapus sertifikat</string>
<string name="dialog_manage_certs_positivebutton">Hapus seleksi</string>
<string name="dialog_manage_certs_negativebutton">Batal</string>
<plurals name="toast_delete_certificates">
<item quantity="other">%d sertifikat dihapus</item>
</plurals>

View file

@ -441,7 +441,6 @@
<string name="toast_no_trusted_certs">Non sono presenti certificati accettati manualmente</string>
<string name="dialog_manage_certs_title">Elimina i certificati</string>
<string name="dialog_manage_certs_positivebutton">Cancella la selezione</string>
<string name="dialog_manage_certs_negativebutton">Annulla</string>
<plurals name="toast_delete_certificates">
<item quantity="one">Cancellato il %d certificato</item>
<item quantity="many">Cancellati %d certificati</item>
@ -999,7 +998,6 @@
<string name="pref_up_push_account_title">Profilo XMPP</string>
<string name="pref_up_push_account_summary">Il profilo attraverso cui verranno ricevuti i messaggi push.</string>
<string name="pref_up_push_server_title">Server push</string>
<string name="pref_up_push_server_summary">Un server scelto dall\'utente per inoltrare i messaggi push via XMPP al tuo dispositivo.</string>
<string name="no_account_deactivated">Nessuno (disattivato)</string>
<string name="decline">Rifiuta</string>
<string name="incoming_call_duration_timestamp">Chiamata in arrivo (%s) · %s</string>

View file

@ -240,7 +240,6 @@
<string name="toast_no_trusted_certs">אין חתימות דיגטליות שאושרו ידנית</string>
<string name="dialog_manage_certs_title">מחק חתימות דיגטליות</string>
<string name="dialog_manage_certs_positivebutton">מחק פריטים שנבחרו</string>
<string name="dialog_manage_certs_negativebutton">ביטול</string>
<plurals name="toast_delete_certificates">
<item quantity="one">%d חתימה נמחקה</item>
<item quantity="two">%d חתימות נמחקו</item>

View file

@ -436,7 +436,6 @@
<string name="toast_no_trusted_certs">手動で承認した証明書がありません</string>
<string name="dialog_manage_certs_title">証明書を削除</string>
<string name="dialog_manage_certs_positivebutton">選択したものを削除</string>
<string name="dialog_manage_certs_negativebutton">中止</string>
<plurals name="toast_delete_certificates">
<item quantity="other">%d個の証明書を削除しました</item>
</plurals>
@ -973,7 +972,6 @@
<string name="outgoing_call_timestamp">発信通話 · %s</string>
<string name="audiobook">オーディオブック</string>
<string name="restore_warning_continued">自分で保存したバックアップしか復元しないでください!</string>
<string name="pref_up_push_server_summary">XMPP経由でPushメッセージを端末に転送するユーザー指定のPushサーバー。</string>
<string name="log_in">ログイン</string>
<string name="hide_notification">通知を表示しない</string>
<string name="delete_from_server">アカウントをサーバーから削除</string>

View file

@ -279,7 +279,6 @@
<string name="toast_no_trusted_certs">수동으로 승인된 인증서 없음 </string>
<string name="dialog_manage_certs_title">인증서 삭제 </string>
<string name="dialog_manage_certs_positivebutton">선택 삭제 </string>
<string name="dialog_manage_certs_negativebutton">취소 </string>
<plurals name="toast_delete_certificates">
<item quantity="other">%d 인증서 삭제됨 </item>
</plurals>

Some files were not shown because too many files have changed in this diff Show more