diff options
Diffstat (limited to '')
220 files changed, 5146 insertions, 3686 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 4eeb4c367..d458673da 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -95,7 +95,7 @@ android:configChanges="orientation|screenSize" android:label="@string/app_name" android:launchMode="singleTask" - android:theme="@style/SplashTheme"> + android:theme="@style/SplashTheme" android:windowSoftInputMode="stateHidden"> <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -108,7 +108,7 @@ android:launchMode="singleTask" android:minWidth="300dp" android:minHeight="300dp" - android:windowSoftInputMode="stateHidden"></activity> + android:windowSoftInputMode="stateHidden" /> <activity android:name=".ui.ScanActivity" android:screenOrientation="portrait" @@ -163,6 +163,10 @@ android:label="@string/create_account" android:launchMode="singleTask" /> <activity + android:name=".ui.ImportBackupActivity" + android:label="@string/restore_backup" + android:launchMode="singleTask" /> + <activity android:name=".ui.SettingsActivity" android:label="@string/title_activity_settings"> <intent-filter> @@ -180,6 +184,17 @@ android:name=".ui.ChangePasswordActivity" android:label="@string/change_password_on_server" /> <activity + android:name=".ui.ChooseAccountForProfilePictureActivity" + android:enabled="false" + android:label="@string/choose_account"> + <intent-filter android:label="@string/set_profile_picture"> + <action android:name="android.intent.action.ATTACH_DATA" /> + <category android:name="android.intent.category.DEFAULT" /> + + <data android:mimeType="image/*" /> + </intent-filter> + </activity> + <activity android:name=".ui.ManageAccountActivity" android:label="@string/title_activity_manage_accounts" android:launchMode="singleTask" /> @@ -193,7 +208,7 @@ android:windowSoftInputMode="stateHidden|adjustResize" /> <activity android:name=".ui.ConferenceDetailsActivity" - android:label="@string/title_activity_conference_details" + android:label="@string/conference_details" android:windowSoftInputMode="stateHidden" /> <activity android:name=".ui.ContactDetailsActivity" @@ -219,6 +234,13 @@ <action android:name="android.intent.action.SEND_MULTIPLE" /> <category android:name="android.intent.category.DEFAULT" /> + <data android:mimeType="text/plain" /> + </intent-filter> + <intent-filter> + <action android:name="android.intent.action.SEND" /> + <action android:name="android.intent.action.SEND_MULTIPLE" /> + + <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="*/*" /> </intent-filter> @@ -283,8 +305,12 @@ android:name=".ui.EnterNameActivity" android:label="@string/enter_your_name" android:launchMode="singleTask" /> + <activity + android:name=".ui.MucUsersActivity" + android:label="@string/group_chat_members" /> - <service android:name=".services.ExportLogsService" /> + <service android:name=".services.ExportBackupService" /> + <service android:name=".services.ImportBackupService" /> <service android:name=".services.ContactChooserTargetService" android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE"> diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java index ce65587b7..b2581f8dc 100644 --- a/src/main/java/de/pixart/messenger/Config.java +++ b/src/main/java/de/pixart/messenger/Config.java @@ -18,6 +18,7 @@ public final class Config { private static final int OMEMO = 8; private static final int ENCRYPTION_MASK = UNENCRYPTED | OPENPGP | OTR | OMEMO; + public static boolean supportUnencrypted() { return (ENCRYPTION_MASK & UNENCRYPTED) != 0; } @@ -63,8 +64,6 @@ public final class Config { public static final boolean HIDE_MESSAGE_TEXT_IN_NOTIFICATION = false; - public static final boolean SHOW_CONNECTED_ACCOUNTS = false; //show number of connected accounts in foreground notification - public static final boolean ALWAYS_NOTIFY_BY_DEFAULT = false; public static final boolean SUPPRESS_ERROR_NOTIFICATION = false; @@ -77,6 +76,7 @@ public final class Config { public static final int PING_TIMEOUT = 15; public static final int SOCKET_TIMEOUT = 15; public static final int CONNECT_TIMEOUT = 60; + public static final int POST_CONNECTIVITY_CHANGE_PING_INTERVAL = 30; public static final int CONNECT_DISCO_TIMEOUT = 30; public static final int MINI_GRACE_PERIOD = 750; @@ -85,6 +85,7 @@ public final class Config { public static final int FILE_SIZE = 1048576; // 1 MiB public static final int AVATAR_SIZE = 480; + public static final int SYSTEM_UI_AVATAR_SIZE = 48; public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.JPEG; public static final int AVATAR_CHAR_LIMIT = 9400; diff --git a/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java b/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java index 1c1a995a9..cc5e3fe70 100644 --- a/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java +++ b/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java @@ -135,6 +135,7 @@ public class PgpDecryptionService { } private void executeApi(Message message) { + boolean skipNotificationPush = false; synchronized (message) { Intent params = userInteractionResult != null ? userInteractionResult : new Intent(); params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); @@ -176,7 +177,7 @@ public class PgpDecryptionService { mXmppConnectionService.updateMessage(message); break; } - } else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { + } else if (message.isFileOrImage()) { try { final DownloadableFile inputFile = mXmppConnectionService.getFileBackend().getFile(message, false); final DownloadableFile outputFile = mXmppConnectionService.getFileBackend().getFile(message, true); @@ -208,9 +209,12 @@ public class PgpDecryptionService { URL url = message.getFileParams().url; mXmppConnectionService.getFileBackend().updateFileParams(message, url); message.setEncryption(Message.ENCRYPTION_DECRYPTED); - inputFile.delete(); - mXmppConnectionService.getFileBackend().updateMediaScanner(outputFile); mXmppConnectionService.updateMessage(message); + if (!inputFile.delete()) { + Log.w(Config.LOGTAG, "unable to delete pgp encrypted source file " + inputFile.getAbsolutePath()); + } + skipNotificationPush = true; + mXmppConnectionService.getFileBackend().updateMediaScanner(outputFile, () -> notifyIfPending(message)); break; case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: synchronized (PgpDecryptionService.this) { @@ -231,7 +235,9 @@ public class PgpDecryptionService { } } } - notifyIfPending(message); + if (!skipNotificationPush) { + notifyIfPending(message); + } } private synchronized void notifyIfPending(Message message) { diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java index 09b3b1320..1a3c4436e 100644 --- a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java +++ b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java @@ -46,7 +46,6 @@ import de.pixart.messenger.entities.Message; import de.pixart.messenger.parser.IqParser; import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.utils.CryptoHelper; -import de.pixart.messenger.utils.Namespace; import de.pixart.messenger.utils.SerialSingleThreadExecutor; import de.pixart.messenger.xml.Element; import de.pixart.messenger.xmpp.OnAdvancedStreamFeaturesLoaded; @@ -840,40 +839,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { }); } - public enum AxolotlCapability { - FULL, - MISSING_PRESENCE, - MISSING_KEYS, - WRONG_CONFIGURATION, - NO_MEMBERS - } - - public boolean isConversationAxolotlCapable(Conversation conversation) { - return conversation.isSingleOrPrivateAndNonAnonymous(); - } - - public Pair<AxolotlCapability, Jid> isConversationAxolotlCapableDetailed(Conversation conversation) { - if (conversation.isSingleOrPrivateAndNonAnonymous()) { - final List<Jid> jids = getCryptoTargets(conversation); - for (Jid jid : jids) { - if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) { - if (conversation.getAccount().getRoster().getContact(jid).mutualPresenceSubscription()) { - return new Pair<>(AxolotlCapability.MISSING_KEYS, jid); - } else { - return new Pair<>(AxolotlCapability.MISSING_PRESENCE, jid); - } - } - } - if (jids.size() > 0) { - return new Pair<>(AxolotlCapability.FULL, null); - } else { - return new Pair<>(AxolotlCapability.NO_MEMBERS, null); - } - } else { - return new Pair<>(AxolotlCapability.WRONG_CONFIGURATION, null); - } - } - public List<Jid> getCryptoTargets(Conversation conversation) { final List<Jid> jids; if (conversation.getMode() == Conversation.MODE_SINGLE) { diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java index 100ad1c43..60aa1e357 100644 --- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java @@ -2,7 +2,6 @@ package de.pixart.messenger.crypto.axolotl; import android.util.Base64; import android.util.Log; -import android.util.SparseArray; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; diff --git a/src/main/java/de/pixart/messenger/entities/Account.java b/src/main/java/de/pixart/messenger/entities/Account.java index 2c7dbfbdc..3ebb1fafa 100644 --- a/src/main/java/de/pixart/messenger/entities/Account.java +++ b/src/main/java/de/pixart/messenger/entities/Account.java @@ -29,12 +29,14 @@ import de.pixart.messenger.crypto.OtrService; import de.pixart.messenger.crypto.PgpDecryptionService; import de.pixart.messenger.crypto.axolotl.AxolotlService; import de.pixart.messenger.crypto.axolotl.XmppAxolotlSession; +import de.pixart.messenger.services.AvatarService; import de.pixart.messenger.services.XmppConnectionService; +import de.pixart.messenger.utils.UIHelper; import de.pixart.messenger.utils.XmppUri; import de.pixart.messenger.xmpp.XmppConnection; import rocks.xmpp.addr.Jid; -public class Account extends AbstractEntity { +public class Account extends AbstractEntity implements AvatarService.Avatarable { public static final String TABLENAME = "accounts"; @@ -616,6 +618,11 @@ public class Account extends AbstractEntity { return this.getStatus() == State.ONLINE && this.getXmppConnection() != null; } + @Override + public int getAvatarBackgroundColor() { + return UIHelper.getColorForName(jid.asBareJid().toString()); + } + public enum State { DISABLED(false, false), OFFLINE(false), diff --git a/src/main/java/de/pixart/messenger/entities/Bookmark.java b/src/main/java/de/pixart/messenger/entities/Bookmark.java index e670673dd..0d1f5781d 100644 --- a/src/main/java/de/pixart/messenger/entities/Bookmark.java +++ b/src/main/java/de/pixart/messenger/entities/Bookmark.java @@ -186,4 +186,9 @@ public class Bookmark extends Element implements ListItem { } return StringUtils.changed(before, name); } + + @Override + public int getAvatarBackgroundColor() { + return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName()); + } } diff --git a/src/main/java/de/pixart/messenger/entities/Contact.java b/src/main/java/de/pixart/messenger/entities/Contact.java index aa7319ad9..8768191ad 100644 --- a/src/main/java/de/pixart/messenger/entities/Contact.java +++ b/src/main/java/de/pixart/messenger/entities/Contact.java @@ -4,7 +4,6 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; -import android.provider.ContactsContract; import android.support.annotation.NonNull; import android.text.TextUtils; @@ -463,10 +462,14 @@ public class Contact implements ListItem, Blockable { } public boolean setAvatar(Avatar avatar) { + return setAvatar(avatar, false); + } + + public boolean setAvatar(Avatar avatar, boolean previouslyOmittedPepFetch) { if (this.avatar != null && this.avatar.equals(avatar)) { return false; } else { - if (this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) { + if (!previouslyOmittedPepFetch && this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) { return false; } this.avatar = avatar; @@ -599,6 +602,11 @@ public class Contact implements ListItem, Blockable { return Options.SYNCED_VIA_OTHER; } + @Override + public int getAvatarBackgroundColor() { + return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName()); + } + public final class Options { public static final int TO = 0; public static final int FROM = 1; diff --git a/src/main/java/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java index 502f2638d..121e9ed06 100644 --- a/src/main/java/de/pixart/messenger/entities/Conversation.java +++ b/src/main/java/de/pixart/messenger/entities/Conversation.java @@ -28,9 +28,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import de.pixart.messenger.Config; import de.pixart.messenger.crypto.OmemoSetting; import de.pixart.messenger.crypto.PgpDecryptionService; -import de.pixart.messenger.crypto.axolotl.AxolotlService; +import de.pixart.messenger.persistance.DatabaseBackend; +import de.pixart.messenger.services.AvatarService; import de.pixart.messenger.services.QuickConversationsService; import de.pixart.messenger.utils.JidHelper; +import de.pixart.messenger.utils.UIHelper; import de.pixart.messenger.xmpp.chatstate.ChatState; import de.pixart.messenger.xmpp.mam.MamReference; import rocks.xmpp.addr.Jid; @@ -38,7 +40,7 @@ import rocks.xmpp.addr.Jid; import static de.pixart.messenger.entities.Bookmark.printableValue; -public class Conversation extends AbstractEntity implements Blockable, Comparable<Conversation>, Conversational { +public class Conversation extends AbstractEntity implements Blockable, Comparable<Conversation>, Conversational, AvatarService.Avatarable { public static final String TABLENAME = "conversations"; public static final int STATUS_AVAILABLE = 0; @@ -61,9 +63,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl private static final String ATTRIBUTE_NEXT_MESSAGE_TIMESTAMP = "next_message_timestamp"; private static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets"; private static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption"; - public static final String ATTRIBUTE_MEMBERS_ONLY = "members_only"; - public static final String ATTRIBUTE_MODERATED = "moderated"; - public static final String ATTRIBUTE_NON_ANONYMOUS = "non_anonymous"; + static final String ATTRIBUTE_MEMBERS_ONLY = "members_only"; + static final String ATTRIBUTE_MODERATED = "moderated"; + static final String ATTRIBUTE_NON_ANONYMOUS = "non_anonymous"; + public static final String ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS = "formerly_private_non_anonymous"; protected final ArrayList<Message> messages = new ArrayList<>(); public AtomicBoolean messagesLoaded = new AtomicBoolean(true); protected Account account = null; @@ -75,7 +78,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl private int status; private long created; private int mode; - private JSONObject attributes = new JSONObject(); + private JSONObject attributes; private Jid nextCounterpart; private transient SessionImpl otrSession; private transient String otrFingerprint = null; @@ -193,26 +196,12 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } } - public void findMessagesWithFiles(final OnMessageFound onMessageFound) { - final ArrayList<Message> results = new ArrayList<>(); - synchronized (this.messages) { - for (final Message m : this.messages) { - if (m.isFileOrImage() && m.getEncryption() != Message.ENCRYPTION_PGP) { - results.add(m); - } - } - } - for (Message result : results) { - onMessageFound.onMessageFound(result); - } - } - public void findFailedMessagesWithFiles(final OnMessageFound onMessageFound) { final ArrayList<Message> results = new ArrayList<>(); synchronized (this.messages) { for (final Message m : this.messages) { if (m.isFileOrImage() && m.getEncryption() != Message.ENCRYPTION_PGP) { - if (m.getStatus() == Message.STATUS_SEND_FAILED && !m.isDeleted() && m.needsUploading()) { + if (m.getStatus() == Message.STATUS_SEND_FAILED && !m.isFileDeleted() && m.needsUploading()) { results.add(m); } } @@ -227,7 +216,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl final ArrayList<Message> results = new ArrayList<>(); synchronized (this.messages) { for (final Message m : this.messages) { - if (m.isDeleted()) { + if (m.isFileDeleted()) { results.add(m); } } @@ -242,7 +231,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl for (final Message message : this.messages) { if (message.getUuid().equals(uuid) && message.getEncryption() != Message.ENCRYPTION_PGP - && (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.treatAsDownloadable())) { + && (message.isFileOrImage() || message.treatAsDownloadable())) { return message; } } @@ -250,6 +239,42 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return null; } + public boolean markAsDeleted(final List<String> uuids) { + boolean deleted = false; + final PgpDecryptionService pgpDecryptionService = account.getPgpDecryptionService(); + synchronized (this.messages) { + for (Message message : this.messages) { + if (uuids.contains(message.getUuid())) { + message.setFileDeleted(true); + deleted = true; + if (message.getEncryption() == Message.ENCRYPTION_PGP && pgpDecryptionService != null) { + pgpDecryptionService.discard(message); + } + } + } + } + return deleted; + } + + + public boolean markAsChanged(final List<DatabaseBackend.FilePathInfo> files) { + boolean changed = false; + final PgpDecryptionService pgpDecryptionService = account.getPgpDecryptionService(); + synchronized (this.messages) { + for (Message message : this.messages) { + for (final DatabaseBackend.FilePathInfo file : files) + if (file.uuid.toString().equals(message.getUuid())) { + message.setFileDeleted(file.FileDeleted); + changed = true; + if (file.FileDeleted && message.getEncryption() == Message.ENCRYPTION_PGP && pgpDecryptionService != null) { + pgpDecryptionService.discard(message); + } + } + } + } + return changed; + } + public void clearMessages() { synchronized (this.messages) { this.messages.clear(); @@ -569,19 +594,6 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } } - public String getParticipants() { - if (getMode() == MODE_MULTI) { - String generatedName = getMucOptions().createNameFromParticipants(); - if (generatedName != null) { - return generatedName; - } else { - return null; - } - } else { - return null; - } - } - public String getAccountUuid() { return this.accountUuid; } @@ -799,7 +811,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } - private static boolean suitableForOmemoByDefault(final Conversation conversation) { + public static boolean suitableForOmemoByDefault(final Conversation conversation) { if (conversation.getJid().asBareJid().equals(Config.BUG_REPORTS)) { return false; } @@ -811,12 +823,11 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl if (Config.OMEMO_EXCEPTIONS.CONTACT_DOMAINS.contains(contact) || Config.OMEMO_EXCEPTIONS.ACCOUNT_DOMAINS.contains(account)) { return false; } - final AxolotlService axolotlService = conversation.getAccount().getAxolotlService(); - return axolotlService != null && axolotlService.isConversationAxolotlCapable(conversation); + return conversation.isSingleOrPrivateAndNonAnonymous() || conversation.getBooleanAttribute(ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS, false); } - public void setNextEncryption(int encryption) { - this.setAttribute(ATTRIBUTE_NEXT_ENCRYPTION, String.valueOf(encryption)); + public boolean setNextEncryption(int encryption) { + return this.setAttribute(ATTRIBUTE_NEXT_ENCRYPTION, encryption); } public String getNextMessage() { @@ -942,15 +953,17 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } public boolean setAttribute(String key, boolean value) { - boolean prev = getBooleanAttribute(key, false); - setAttribute(key, Boolean.toString(value)); - return prev != value; + return setAttribute(key, String.valueOf(value)); } private boolean setAttribute(String key, long value) { return setAttribute(key, Long.toString(value)); } + private boolean setAttribute(String key, int value) { + return setAttribute(key, String.valueOf(value)); + } + public boolean setAttribute(String key, String value) { synchronized (this.attributes) { try { @@ -1117,7 +1130,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl for (int i = this.messages.size() - 1; i >= 0; --i) { Message message = this.messages.get(i); if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) && message.getEncryption() != Message.ENCRYPTION_PGP) { - if (message.getStatus() == Message.STATUS_SEND_FAILED && !message.isDeleted() && message.needsUploading()){ + if (message.getStatus() == Message.STATUS_SEND_FAILED && !message.isFileDeleted() && message.needsUploading()){ ++count; } } @@ -1179,6 +1192,11 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return 0; } + @Override + public int getAvatarBackgroundColor() { + return UIHelper.getColorForName(getName().toString()); + } + public interface OnMessageFound { void onMessageFound(final Message message); } diff --git a/src/main/java/de/pixart/messenger/entities/IndividualMessage.java b/src/main/java/de/pixart/messenger/entities/IndividualMessage.java index 2a7b4c8da..195ea82c1 100644 --- a/src/main/java/de/pixart/messenger/entities/IndividualMessage.java +++ b/src/main/java/de/pixart/messenger/entities/IndividualMessage.java @@ -41,8 +41,8 @@ public class IndividualMessage extends Message { super(conversation); } - private IndividualMessage(Conversational conversation, String uuid, String conversationUUid, Jid counterpart, Jid trueCounterpart, String body, long timeSent, int encryption, int status, int type, boolean carbon, String remoteMsgId, String relativeFilePath, String serverMsgId, String fingerprint, boolean read, boolean deleted, String edited, boolean oob, String errorMessage, Set<ReadByMarker> readByMarkers, boolean markable) { - super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, deleted, edited, oob, errorMessage, readByMarkers, markable); + private IndividualMessage(Conversational conversation, String uuid, String conversationUUid, Jid counterpart, Jid trueCounterpart, String body, long timeSent, int encryption, int status, int type, boolean carbon, String remoteMsgId, String relativeFilePath, String serverMsgId, String fingerprint, boolean read, boolean deleted, String edited, boolean oob, String errorMessage, Set<ReadByMarker> readByMarkers, boolean markable, boolean file_deleted) { + super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, deleted, edited, oob, errorMessage, readByMarkers, markable, file_deleted); } public static Message createDateSeparator(Message message) { @@ -99,7 +99,8 @@ public class IndividualMessage extends Message { cursor.getInt(cursor.getColumnIndex(OOB)) > 0, cursor.getString(cursor.getColumnIndex(ERROR_MESSAGE)), ReadByMarker.fromJsonString(cursor.getString(cursor.getColumnIndex(READ_BY_MARKERS))), - cursor.getInt(cursor.getColumnIndex(MARKABLE)) > 0); + cursor.getInt(cursor.getColumnIndex(MARKABLE)) > 0, + cursor.getInt(cursor.getColumnIndex(FILE_DELETED)) > 0); } @Override diff --git a/src/main/java/de/pixart/messenger/entities/ListItem.java b/src/main/java/de/pixart/messenger/entities/ListItem.java index 21fb6c2b6..fba37cc4c 100644 --- a/src/main/java/de/pixart/messenger/entities/ListItem.java +++ b/src/main/java/de/pixart/messenger/entities/ListItem.java @@ -4,9 +4,10 @@ import android.content.Context; import java.util.List; +import de.pixart.messenger.services.AvatarService; import rocks.xmpp.addr.Jid; -public interface ListItem extends Comparable<ListItem> { +public interface ListItem extends Comparable<ListItem>, AvatarService.Avatarable { String getDisplayName(); int getOffline(); diff --git a/src/main/java/de/pixart/messenger/entities/Message.java b/src/main/java/de/pixart/messenger/entities/Message.java index abeda3ad3..ba44a10ae 100644 --- a/src/main/java/de/pixart/messenger/entities/Message.java +++ b/src/main/java/de/pixart/messenger/entities/Message.java @@ -2,6 +2,7 @@ package de.pixart.messenger.entities; import android.content.ContentValues; import android.database.Cursor; +import android.graphics.Color; import android.text.SpannableStringBuilder; import android.util.Log; @@ -16,6 +17,7 @@ import java.util.Set; import de.pixart.messenger.Config; import de.pixart.messenger.crypto.axolotl.FingerprintStatus; +import de.pixart.messenger.services.AvatarService; import de.pixart.messenger.utils.CryptoHelper; import de.pixart.messenger.utils.Emoticons; import de.pixart.messenger.utils.GeoHelper; @@ -25,7 +27,7 @@ import de.pixart.messenger.utils.UIHelper; import de.pixart.messenger.utils.XmppUri; import rocks.xmpp.addr.Jid; -public class Message extends AbstractEntity { +public class Message extends AbstractEntity implements AvatarService.Avatarable { public static final String TABLENAME = "messages"; @@ -73,8 +75,9 @@ public class Message extends AbstractEntity { public static final String ERROR_MESSAGE = "errorMsg"; public static final String READ_BY_MARKERS = "readByMarkers"; public static final String MARKABLE = "markable"; + public static final String FILE_DELETED = "file_deleted"; public static final String ME_COMMAND = "/me"; - public static final String ERROR_MESSAGE_CANCELLED = "eu.siacs.conversations.cancelled"; + public static final String ERROR_MESSAGE_CANCELLED = "de.pixart.messenger.cancelled"; public boolean markable = false; protected String conversationUuid; @@ -86,6 +89,7 @@ public class Message extends AbstractEntity { protected int encryption; protected int status; protected int type; + protected boolean file_deleted = false; protected boolean carbon = false; protected boolean oob = false; protected String edited = null; @@ -139,6 +143,7 @@ public class Message extends AbstractEntity { false, null, null, + false, false); } @@ -148,7 +153,7 @@ public class Message extends AbstractEntity { final String remoteMsgId, final String relativeFilePath, final String serverMsgId, final String fingerprint, final boolean read, final boolean deleted, final String edited, final boolean oob, final String errorMessage, final Set<ReadByMarker> readByMarkers, - final boolean markable) { + final boolean markable, final boolean file_deleted) { this.conversation = conversation; this.uuid = uuid; this.conversationUuid = conversationUUid; @@ -171,6 +176,7 @@ public class Message extends AbstractEntity { this.errorMessage = errorMessage; this.readByMarkers = readByMarkers == null ? new HashSet<ReadByMarker>() : readByMarkers; this.markable = markable; + this.file_deleted = file_deleted; } public static Message fromCursor(Cursor cursor, Conversation conversation) { @@ -219,7 +225,8 @@ public class Message extends AbstractEntity { cursor.getInt(cursor.getColumnIndex(OOB)) > 0, cursor.getString(cursor.getColumnIndex(ERROR_MESSAGE)), ReadByMarker.fromJsonString(cursor.getString(cursor.getColumnIndex(READ_BY_MARKERS))), - cursor.getInt(cursor.getColumnIndex(MARKABLE)) > 0); + cursor.getInt(cursor.getColumnIndex(MARKABLE)) > 0, + cursor.getInt(cursor.getColumnIndex(FILE_DELETED)) > 0); } public static Message createStatusMessage(Conversation conversation, String body) { @@ -269,6 +276,7 @@ public class Message extends AbstractEntity { values.put(ERROR_MESSAGE, errorMessage); values.put(READ_BY_MARKERS, ReadByMarker.toJson(readByMarkers).toString()); values.put(MARKABLE, markable ? 1 : 0); + values.put(FILE_DELETED, file_deleted ? 1 : 0); return values; } @@ -390,6 +398,14 @@ public class Message extends AbstractEntity { return this.deleted; } + public boolean isFileDeleted() { + return this.file_deleted; + } + + public void setFileDeleted(boolean file_deleted) { + this.file_deleted = file_deleted; + } + public void markRead() { this.read = true; } @@ -619,6 +635,15 @@ public class Message extends AbstractEntity { return this.counterparts; } + @Override + public int getAvatarBackgroundColor() { + if (type == Message.TYPE_STATUS && getCounterparts() != null && getCounterparts().size() > 1) { + return Color.TRANSPARENT; + } else { + return UIHelper.getColorForName(UIHelper.getMessageDisplayName(this)); + } + } + public static class MergeSeparator { } @@ -840,10 +865,6 @@ public class Message extends AbstractEntity { return type == TYPE_FILE || type == TYPE_IMAGE; } - public boolean isDeleted() { - return (type == TYPE_FILE || type == TYPE_IMAGE) && getTransferable() != null && getTransferable().getFileSize() == 0; - } - public boolean hasFileOnRemoteHost() { return isFileOrImage() && getFileParams().url != null; } diff --git a/src/main/java/de/pixart/messenger/entities/MucOptions.java b/src/main/java/de/pixart/messenger/entities/MucOptions.java index 6954995d9..c3050f3fa 100644 --- a/src/main/java/de/pixart/messenger/entities/MucOptions.java +++ b/src/main/java/de/pixart/messenger/entities/MucOptions.java @@ -14,6 +14,7 @@ import java.util.Set; import de.pixart.messenger.Config; import de.pixart.messenger.R; +import de.pixart.messenger.services.AvatarService; import de.pixart.messenger.services.MessageArchiveService; import de.pixart.messenger.utils.JidHelper; import de.pixart.messenger.utils.UIHelper; @@ -45,6 +46,7 @@ public class MucOptions { private User self; private String password = null; private boolean tookProposedNickFromBookmark = false; + public MucOptions(Conversation conversation) { this.account = conversation.getAccount(); this.conversation = conversation; @@ -101,8 +103,9 @@ public class MucOptions { return tookProposedNickFromBookmark; } - void notifyOfBookmarkNick(String nick) { - if (nick != null && nick.trim().equals(getSelf().getFullJid().getResource())) { + void notifyOfBookmarkNick(final String nick) { + final String normalized = normalize(account.getJid(), nick); + if (normalized != null && normalized.equals(getSelf().getFullJid().getResource())) { this.tookProposedNickFromBookmark = true; } } @@ -152,13 +155,21 @@ public class MucOptions { } public boolean canInvite() { - Field field = getRoomInfoForm().getFieldByName("muc#roomconfig_allowinvites"); - return !membersOnly() || self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue())); + return !membersOnly() || self.getRole().ranks(Role.MODERATOR) || allowInvites(); + } + + public boolean allowInvites() { + final Field field = getRoomInfoForm().getFieldByName("muc#roomconfig_allowinvites"); + return field != null && "1".equals(field.getValue()); } public boolean canChangeSubject() { - Field field = getRoomInfoForm().getFieldByName("muc#roominfo_changesubject"); - return self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue())); + return self.getRole().ranks(Role.MODERATOR) || participantsCanChangeSubject(); + } + + public boolean participantsCanChangeSubject() { + final Field field = getRoomInfoForm().getFieldByName("muc#roominfo_changesubject"); + return field != null && "1".equals(field.getValue()); } public boolean allowPm() { @@ -380,6 +391,21 @@ public class MucOptions { return subset; } + public static List<User> sub(List<User> users, int max) { + ArrayList<User> subset = new ArrayList<>(); + HashSet<Jid> jids = new HashSet<>(); + for (User user : users) { + jids.add(user.getAccount().getJid().asBareJid()); + if (user.getRealJid() == null || (user.getRealJid().getLocal() != null && jids.add(user.getRealJid()))) { + subset.add(user); + } + if (subset.size() >= max) { + break; + } + } + return subset; + } + public int getUserCount() { synchronized (users) { return users.size(); @@ -388,15 +414,15 @@ public class MucOptions { private String getProposedNick() { final Bookmark bookmark = this.conversation.getBookmark(); - final String bookmarkedNick = bookmark == null ? null : bookmark.getNick(); - if (bookmarkedNick != null && !bookmarkedNick.trim().isEmpty()) { + final String bookmarkedNick = normalize(account.getJid(), bookmark == null ? null : bookmark.getNick()); + if (bookmarkedNick != null) { this.tookProposedNickFromBookmark = true; - return bookmarkedNick.trim(); + return bookmarkedNick; } else if (!conversation.getJid().isBareJid()) { return conversation.getJid().getResource(); } else { - final String displayName = account.getDisplayName(); - if (TextUtils.isEmpty(displayName)) { + final String displayName = normalize(account.getJid(), account.getDisplayName()); + if (displayName == null) { return JidHelper.localPartOrFallback(account.getJid()); } else { return displayName; @@ -404,6 +430,18 @@ public class MucOptions { } } + private static String normalize(Jid account, String nick) { + if (account == null || TextUtils.isEmpty(nick)) { + return null; + } + try { + return account.withResource(nick).getResource(); + } catch (IllegalArgumentException e) { + return null; + } + + } + public String getActualNick() { if (this.self.getName() != null) { return this.self.getName(); @@ -473,7 +511,7 @@ public class MucOptions { return users; } - public String createNameFromParticipants() { + String createNameFromParticipants() { List<User> users = getUsersRelevantForNameAndAvatar(); if (users.size() >= 2) { StringBuilder builder = new StringBuilder(); @@ -531,7 +569,7 @@ public class MucOptions { public Jid createJoinJid(String nick) { try { - return Jid.of(this.conversation.getJid().asBareJid().toString() + "/" + nick); + return conversation.getJid().withResource(nick); } catch (final IllegalArgumentException e) { return null; } @@ -589,6 +627,7 @@ public class MucOptions { private int resId; private int rank; + Affiliation(int rank, int resId) { this.resId = resId; this.rank = rank; @@ -631,6 +670,7 @@ public class MucOptions { private int resId; private int rank; + Role(int resId, int rank) { this.resId = resId; this.rank = rank; @@ -689,7 +729,7 @@ public class MucOptions { } - public static class User implements Comparable<User> { + public static class User implements Comparable<User>, AvatarService.Avatarable { private Role role = Role.NONE; private Affiliation affiliation = Affiliation.NONE; private Jid realJid; @@ -825,7 +865,7 @@ public class MucOptions { } } - private String getComparableName() { + public String getComparableName() { Contact contact = getContact(); if (contact != null) { return contact.getDisplayName(); @@ -850,5 +890,11 @@ public class MucOptions { this.chatState = chatState; return true; } + + @Override + public int getAvatarBackgroundColor() { + final String seed = realJid != null ? realJid.asBareJid().toString() : null; + return UIHelper.getColorForName(seed == null ? getName() : seed); + } } } diff --git a/src/main/java/de/pixart/messenger/entities/Transferable.java b/src/main/java/de/pixart/messenger/entities/Transferable.java index c07ec750d..e1deb744d 100644 --- a/src/main/java/de/pixart/messenger/entities/Transferable.java +++ b/src/main/java/de/pixart/messenger/entities/Transferable.java @@ -18,50 +18,12 @@ public interface Transferable { "gpg", "otr" ); - List<String> WELL_KNOWN_EXTENSIONS = Arrays.asList( - //documents - "pdf", - "doc", - "docx", - "txt", - //audio - "m4a", - "m4b", - "mp3", - "mp2", - "wav", - "aac", - "aif", - "aiff", - "aifc", - "mid", - "midi", - "3gpp", - //video - "avi", - "mp4", - "mpeg", - "mpg", - "mpe", - "mov", - "3gp", - //applications - "apk", - //contact - "vcf", - //calendar - "ics", - //compressed - "zip", - "rar" - ); int STATUS_UNKNOWN = 0x200; int STATUS_CHECKING = 0x201; int STATUS_FAILED = 0x202; int STATUS_OFFER = 0x203; int STATUS_DOWNLOADING = 0x204; - int STATUS_DELETED = 0x205; int STATUS_OFFER_CHECK_FILESIZE = 0x206; int STATUS_UPLOADING = 0x207; diff --git a/src/main/java/de/pixart/messenger/generator/IqGenerator.java b/src/main/java/de/pixart/messenger/generator/IqGenerator.java index b2f43cbcf..024e7ab43 100644 --- a/src/main/java/de/pixart/messenger/generator/IqGenerator.java +++ b/src/main/java/de/pixart/messenger/generator/IqGenerator.java @@ -466,18 +466,34 @@ public class IqGenerator extends AbstractGenerator { return packet; } - public static Bundle defaultRoomConfiguration() { + public static Bundle defaultGroupChatConfiguration() { Bundle options = new Bundle(); options.putString("muc#roomconfig_persistentroom", "1"); options.putString("muc#roomconfig_membersonly", "1"); options.putString("muc#roomconfig_publicroom", "0"); options.putString("muc#roomconfig_whois", "anyone"); + options.putString("muc#roomconfig_changesubject", "0"); + options.putString("muc#roomconfig_allowinvites", "0"); options.putString("muc#roomconfig_enablearchiving", "1"); //prosody options.putString("mam", "1"); //ejabberd community options.putString("muc#roomconfig_mam", "1"); //ejabberd saas return options; } + public static Bundle defaultChannelConfiguration() { + Bundle options = new Bundle(); + options.putString("muc#roomconfig_persistentroom", "1"); + options.putString("muc#roomconfig_membersonly", "0"); + options.putString("muc#roomconfig_publicroom", "1"); + options.putString("muc#roomconfig_whois", "moderators"); + options.putString("muc#roomconfig_changesubject", "0"); + options.putString("muc#roomconfig_enablearchiving", "1"); //prosody + options.putString("mam", "1"); //ejabberd community + options.putString("muc#roomconfig_mam", "1"); //ejabberd saas + return options; + } + + public IqPacket requestPubsubConfiguration(Jid jid, String node) { return pubsubConfiguration(jid, node, null); } diff --git a/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java b/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java index fbb93d916..84c4aa1bb 100644 --- a/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java +++ b/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java @@ -1,5 +1,7 @@ package de.pixart.messenger.http; +import android.util.Log; + import org.apache.http.conn.ssl.StrictHostnameVerifier; import java.io.IOException; @@ -9,14 +11,15 @@ import java.net.Proxy; import java.net.URL; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; +import de.pixart.messenger.Config; import de.pixart.messenger.entities.Account; import de.pixart.messenger.entities.Message; import de.pixart.messenger.services.AbstractConnectionManager; @@ -25,28 +28,47 @@ import de.pixart.messenger.utils.TLSSocketFactory; public class HttpConnectionManager extends AbstractConnectionManager { + private final List<HttpDownloadConnection> downloadConnections = new ArrayList<>(); + private final List<HttpUploadConnection> uploadConnections = new ArrayList<>(); + public HttpConnectionManager(XmppConnectionService service) { super(service); } - private List<HttpDownloadConnection> downloadConnections = new CopyOnWriteArrayList<>(); - private List<HttpUploadConnection> uploadConnections = new CopyOnWriteArrayList<>(); + public static Proxy getProxy() throws IOException { + return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 8118)); + } - public HttpDownloadConnection createNewDownloadConnection(Message message) { - return this.createNewDownloadConnection(message, false); + public void createNewDownloadConnection(Message message) { + this.createNewDownloadConnection(message, false); } - public HttpDownloadConnection createNewDownloadConnection(Message message, boolean interactive) { - HttpDownloadConnection connection = new HttpDownloadConnection(this); - connection.init(message, interactive); - this.downloadConnections.add(connection); - return connection; + public void createNewDownloadConnection(final Message message, boolean interactive) { + synchronized (this.downloadConnections) { + for (HttpDownloadConnection connection : this.downloadConnections) { + if (connection.getMessage() == message) { + Log.d(Config.LOGTAG, message.getConversation().getAccount().getJid().asBareJid() + ": download already in progress"); + return; + } + } + final HttpDownloadConnection connection = new HttpDownloadConnection(message, this); + connection.init(interactive); + this.downloadConnections.add(connection); + } } - public void createNewUploadConnection(Message message, boolean delay) { - HttpUploadConnection connection = new HttpUploadConnection(Method.determine(message.getConversation().getAccount()), this); - connection.init(message, delay); - this.uploadConnections.add(connection); + public void createNewUploadConnection(final Message message, boolean delay) { + synchronized (this.uploadConnections) { + for (HttpUploadConnection connection : this.uploadConnections) { + if (connection.getMessage() == message) { + Log.d(Config.LOGTAG, message.getConversation().getAccount().getJid().asBareJid() + ": upload already in progress"); + return; + } + } + HttpUploadConnection connection = new HttpUploadConnection(message, Method.determine(message.getConversation().getAccount()), this); + connection.init(delay); + this.uploadConnections.add(connection); + } } public boolean checkConnection(Message message) { @@ -58,15 +80,19 @@ public class HttpConnectionManager extends AbstractConnectionManager { return mXmppConnectionService.hasInternetConnection(); } - public void finishConnection(HttpDownloadConnection connection) { - this.downloadConnections.remove(connection); + void finishConnection(HttpDownloadConnection connection) { + synchronized (this.downloadConnections) { + this.downloadConnections.remove(connection); + } } - public void finishUploadConnection(HttpUploadConnection httpUploadConnection) { - this.uploadConnections.remove(httpUploadConnection); + void finishUploadConnection(HttpUploadConnection httpUploadConnection) { + synchronized (this.uploadConnections) { + this.uploadConnections.remove(httpUploadConnection); + } } - public void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) { + void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) { final X509TrustManager trustManager; final HostnameVerifier hostnameVerifier = mXmppConnectionService.getMemorizingTrustManager().wrapHostnameVerifier(new StrictHostnameVerifier(), interactive); if (interactive) { @@ -81,8 +107,4 @@ public class HttpConnectionManager extends AbstractConnectionManager { } catch (final KeyManagementException | NoSuchAlgorithmException ignored) { } } - - public static Proxy getProxy() throws IOException { - return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 8118)); - } -} +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java b/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java index cf867d796..457292e27 100644 --- a/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java +++ b/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java @@ -25,7 +25,6 @@ import de.pixart.messenger.entities.Account; import de.pixart.messenger.entities.DownloadableFile; import de.pixart.messenger.entities.Message; import de.pixart.messenger.entities.Transferable; -import de.pixart.messenger.entities.TransferablePlaceholder; import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.services.AbstractConnectionManager; import de.pixart.messenger.services.XmppConnectionService; @@ -41,7 +40,7 @@ public class HttpDownloadConnection implements Transferable { private XmppConnectionService mXmppConnectionService; private URL mUrl; - private Message message; + private final Message message; private DownloadableFile file; private int mStatus = Transferable.STATUS_UNKNOWN; private boolean acceptedAutomatically = false; @@ -51,8 +50,8 @@ public class HttpDownloadConnection implements Transferable { private Method method = Method.HTTP_UPLOAD; private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US); - - HttpDownloadConnection(HttpConnectionManager manager) { + HttpDownloadConnection(Message message, HttpConnectionManager manager) { + this.message = message; this.mHttpConnectionManager = manager; this.mXmppConnectionService = manager.getXmppConnectionService(); this.mUseTor = mXmppConnectionService.useTorToConnect(); @@ -72,12 +71,7 @@ public class HttpDownloadConnection implements Transferable { } } - public void init(Message message) { - init(message, false); - } - - public void init(Message message, boolean interactive) { - this.message = message; + public void init(boolean interactive) { this.message.setTransferable(this); try { if (message.hasFileOnRemoteHost()) { @@ -137,16 +131,14 @@ public class HttpDownloadConnection implements Transferable { public void cancel() { this.canceled = true; mHttpConnectionManager.finishConnection(this); + message.setTransferable(null); if (message.isFileOrImage()) { - message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); - } else { - message.setTransferable(null); + message.setFileDeleted(true); } mHttpConnectionManager.updateConversationUi(true); } private void finish() { - mXmppConnectionService.getFileBackend().updateMediaScanner(file); message.setTransferable(null); mHttpConnectionManager.finishConnection(this); boolean notify = acceptedAutomatically && !message.isRead(); @@ -154,9 +146,12 @@ public class HttpDownloadConnection implements Transferable { notify = message.getConversation().getAccount().getPgpDecryptionService().decrypt(message, notify); } mHttpConnectionManager.updateConversationUi(true); - if (notify) { - mXmppConnectionService.getNotificationService().push(message); - } + final boolean notifyAfterScan = notify; + mXmppConnectionService.getFileBackend().updateMediaScanner(file, () -> { + if (notifyAfterScan) { + mXmppConnectionService.getNotificationService().push(message); + } + }); } private void changeStatus(int status) { @@ -200,6 +195,10 @@ public class HttpDownloadConnection implements Transferable { return this.mProgress; } + public Message getMessage() { + return message; + } + private class FileSizeChecker implements Runnable { private final boolean interactive; @@ -379,9 +378,9 @@ public class HttpDownloadConnection implements Transferable { connection.setUseCaches(false); connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getUserAgent()); connection.setRequestProperty("Accept-Encoding", "identity"); - final boolean tryResume = file.exists() && file.getKey() == null && file.getSize() > 0; + final long expected = file.getExpectedSize(); + final boolean tryResume = file.exists() && file.getKey() == null && file.getSize() > 0 && file.getSize() < expected; long resumeSize = 0; - long expected = file.getExpectedSize(); if (tryResume) { resumeSize = file.getSize(); Log.d(Config.LOGTAG, "http download trying resume after" + resumeSize + " of " + expected); @@ -466,4 +465,4 @@ public class HttpDownloadConnection implements Transferable { } } -} +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java index 49debcb78..92ccf86e1 100644 --- a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java +++ b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java @@ -43,14 +43,15 @@ public class HttpUploadConnection implements Transferable { private boolean cancelled = false; private boolean delayed = false; private DownloadableFile file; - private Message message; + private final Message message; private String mime; private SlotRequester.Slot slot; private byte[] key = null; private long transmitted = 0; - public HttpUploadConnection(Method method, HttpConnectionManager httpConnectionManager) { + public HttpUploadConnection(Message message, Method method, HttpConnectionManager httpConnectionManager) { + this.message = message; this.method = method; this.mHttpConnectionManager = httpConnectionManager; this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService(); @@ -87,13 +88,16 @@ public class HttpUploadConnection implements Transferable { } private void fail(String errorMessage) { + finish(); + mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : errorMessage); + } + + private void finish() { mHttpConnectionManager.finishUploadConnection(this); message.setTransferable(null); - mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : errorMessage); } - public void init(Message message, boolean delay) { - this.message = message; + public void init(boolean delay) { final Account account = message.getConversation().getAccount(); this.file = mXmppConnectionService.getFileBackend().getFile(message, false); if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { @@ -123,6 +127,7 @@ public class HttpUploadConnection implements Transferable { } else { md5 = null; } + this.file.setExpectedSize(file.getSize() + (file.getKey() != null ? 16 : 0)); message.resetFileParams(); this.mSlotRequester.request(method, account, file, mime, md5, new SlotRequester.OnSlotRequested() { @@ -210,7 +215,7 @@ public class HttpUploadConnection implements Transferable { } mXmppConnectionService.getFileBackend().updateFileParams(message, get); mXmppConnectionService.getFileBackend().updateMediaScanner(file); - message.setTransferable(null); + finish(); message.setCounterpart(message.getConversation().getJid().asBareJid()); mXmppConnectionService.resendMessage(message, delayed); } else { @@ -230,4 +235,8 @@ public class HttpUploadConnection implements Transferable { WakeLockHelper.release(wakeLock); } } -} + + public Message getMessage() { + return message; + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/parser/MessageParser.java b/src/main/java/de/pixart/messenger/parser/MessageParser.java index d8461d940..a1b6f498a 100644 --- a/src/main/java/de/pixart/messenger/parser/MessageParser.java +++ b/src/main/java/de/pixart/messenger/parser/MessageParser.java @@ -278,6 +278,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece if (account.getJid().asBareJid().equals(from)) { if (account.setAvatar(avatar.getFilename())) { mXmppConnectionService.databaseBackend.updateAccount(account); + mXmppConnectionService.notifyAccountAvatarHasChanged(account); } mXmppConnectionService.getAvatarService().clear(account); mXmppConnectionService.updateConversationUi(); @@ -650,7 +651,6 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet); mXmppConnectionService.updateMessage(replacedMessage, uuid); - mXmppConnectionService.getNotificationService().updateNotification(false); if (mXmppConnectionService.confirmMessages() && replacedMessage.getStatus() == Message.STATUS_RECEIVED && (replacedMessage.trusted() || replacedMessage.getType() == Message.TYPE_PRIVATE) @@ -664,6 +664,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece conversation.getAccount().getPgpDecryptionService().decrypt(replacedMessage, false); } } + mXmppConnectionService.getNotificationService().updateNotification(); return; } else { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received message correction but verification didn't check out"); diff --git a/src/main/java/de/pixart/messenger/parser/PresenceParser.java b/src/main/java/de/pixart/messenger/parser/PresenceParser.java index 0e0b67382..0c90dd4ac 100644 --- a/src/main/java/de/pixart/messenger/parser/PresenceParser.java +++ b/src/main/java/de/pixart/messenger/parser/PresenceParser.java @@ -95,7 +95,7 @@ public class PresenceParser extends AbstractParser implements + mucOptions.getConversation().getJid().asBareJid() + "' created. pushing default configuration"); mXmppConnectionService.pushConferenceConfiguration(mucOptions.getConversation(), - IqGenerator.defaultRoomConfiguration(), + IqGenerator.defaultChannelConfiguration(), null); } if (mXmppConnectionService.getPgpEngine() != null) { diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java index 85d32d55f..8336a4ed9 100644 --- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java +++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java @@ -60,7 +60,7 @@ import rocks.xmpp.addr.Jid; public class DatabaseBackend extends SQLiteOpenHelper { public static final String DATABASE_NAME = "history"; - public static final int DATABASE_VERSION = 44; // = Conversations DATABASE_VERSION + 2 + public static final int DATABASE_VERSION = 46; // = Conversations DATABASE_VERSION + 2 private static DatabaseBackend instance = null; private static String CREATE_CONTATCS_STATEMENT = "create table " @@ -136,6 +136,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { + SQLiteAxolotlStore.OWN + " INTEGER, " + SQLiteAxolotlStore.FINGERPRINT + " TEXT, " + SQLiteAxolotlStore.CERTIFICATE + " BLOB, " + + SQLiteAxolotlStore.TRUSTED + " TEXT, " + SQLiteAxolotlStore.TRUST + " TEXT, " + SQLiteAxolotlStore.ACTIVE + " NUMBER, " + SQLiteAxolotlStore.LAST_ACTIVATION + " NUMBER," @@ -161,6 +162,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { + ");"; private static String CREATE_MESSAGE_TIME_INDEX = "create INDEX message_time_index ON " + Message.TABLENAME + "(" + Message.TIME_SENT + ")"; private static String CREATE_MESSAGE_CONVERSATION_INDEX = "create INDEX message_conversation_index ON " + Message.TABLENAME + "(" + Message.CONVERSATION + ")"; + private static String CREATE_MESSAGE_DELETED_INDEX = "create index message_deleted_index ON " + Message.TABLENAME + "(" + Message.DELETED + ")"; + private static String CREATE_MESSAGE_FILE_DELETED_INDEX = "create index message_file_deleted_index ON " + Message.TABLENAME + "(" + Message.FILE_DELETED + ")"; + private static String CREATE_MESSAGE_RELATIVE_FILE_PATH_INDEX = "create INDEX message_file_path_index ON " + Message.TABLENAME + "(" + Message.RELATIVE_FILE_PATH + ")"; + private static String CREATE_MESSAGE_TYPE_INDEX = "create INDEX message_type_index ON " + Message.TABLENAME + "(" + Message.TYPE + ")"; private static String CREATE_MESSAGE_INDEX_TABLE = "CREATE VIRTUAL TABLE messages_index USING FTS4(uuid TEXT PRIMARY KEY, body TEXT)"; private static String CREATE_MESSAGE_INSERT_TRIGGER = "CREATE TRIGGER after_message_insert AFTER INSERT ON " + Message.TABLENAME + " BEGIN INSERT INTO messages_index (uuid,body) VALUES (new.uuid,new.body); END;"; @@ -233,6 +238,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { + Message.ERROR_MESSAGE + " TEXT," + Message.READ_BY_MARKERS + " TEXT," + Message.MARKABLE + " NUMBER DEFAULT 0," + + Message.FILE_DELETED + " NUMBER DEFAULT 0," + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY(" + Message.CONVERSATION + ") REFERENCES " + Conversation.TABLENAME + "(" + Conversation.UUID @@ -240,6 +246,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL(CREATE_MESSAGE_TIME_INDEX); db.execSQL(CREATE_MESSAGE_CONVERSATION_INDEX); + db.execSQL(CREATE_MESSAGE_DELETED_INDEX); + db.execSQL(CREATE_MESSAGE_FILE_DELETED_INDEX); + db.execSQL(CREATE_MESSAGE_RELATIVE_FILE_PATH_INDEX); + db.execSQL(CREATE_MESSAGE_TYPE_INDEX); db.execSQL(CREATE_CONTATCS_STATEMENT); db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT); db.execSQL(CREATE_SESSIONS_STATEMENT); @@ -337,7 +347,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { } /* Any migrations that alter the Account table need to happen BEFORE this migration, as it * depends on account de-serialization. - */ + */ if (oldVersion < 17 && newVersion >= 17 && newVersion < 31) { List<Account> accounts = getAccounts(db); for (Account account : accounts) { @@ -535,6 +545,34 @@ public class DatabaseBackend extends SQLiteOpenHelper { if (oldVersion < 44 && newVersion >= 44) { db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.DELETED + " NUMBER DEFAULT 0"); } + + if (oldVersion < 45 && newVersion >= 45) { + db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.FILE_DELETED + " NUMBER DEFAULT 0"); + db.execSQL(CREATE_MESSAGE_DELETED_INDEX); + db.execSQL(CREATE_MESSAGE_FILE_DELETED_INDEX); + db.execSQL(CREATE_MESSAGE_RELATIVE_FILE_PATH_INDEX); + db.execSQL(CREATE_MESSAGE_TYPE_INDEX); + } + + if (oldVersion < 46 && newVersion >= 46) { + if (!isColumnExisting(db, SQLiteAxolotlStore.IDENTITIES_TABLENAME, SQLiteAxolotlStore.TRUSTED)) { + db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.TRUSTED); // TODO - just to make old databases importable, column isn't needed at all + } + } + } + + private boolean isColumnExisting(SQLiteDatabase db, String TableName, String ColumnName) { + boolean isExist = false; + Cursor cursor = db.rawQuery("PRAGMA table_info(" + TableName + ")", null); + cursor.moveToFirst(); + do { + String currentColumn = cursor.getString(1); + if (currentColumn.equals(ColumnName)) { + isExist = true; + } + } while (cursor.moveToNext()); + cursor.close(); + return isExist; } private void canonicalizeJids(SQLiteDatabase db) { @@ -797,9 +835,77 @@ public class DatabaseBackend extends SQLiteOpenHelper { }; } + public List<String> markFileAsDeleted(final File file, final boolean internal) { + SQLiteDatabase db = this.getReadableDatabase(); + String selection; + String[] selectionArgs; + if (internal) { + final String name = file.getName(); + if (name.endsWith(".pgp")) { + selection = "(" + Message.RELATIVE_FILE_PATH + " IN(?,?) OR (" + Message.RELATIVE_FILE_PATH + "=? and encryption in(1,4))) and type in (1,2)"; + selectionArgs = new String[]{file.getAbsolutePath(), name, name.substring(0, name.length() - 4)}; + } else { + selection = Message.RELATIVE_FILE_PATH + " IN(?,?) and type in (1,2)"; + selectionArgs = new String[]{file.getAbsolutePath(), name}; + } + } else { + selection = Message.RELATIVE_FILE_PATH + "=? and type in (1,2)"; + selectionArgs = new String[]{file.getAbsolutePath()}; + } + final List<String> uuids = new ArrayList<>(); + Cursor cursor = db.query(Message.TABLENAME, new String[]{Message.UUID}, selection, selectionArgs, null, null, null); + while (cursor != null && cursor.moveToNext()) { + uuids.add(cursor.getString(0)); + } + if (cursor != null) { + cursor.close(); + } + markFileAsDeleted(uuids); + return uuids; + } + + public void markFileAsDeleted(List<String> uuids) { + SQLiteDatabase db = this.getReadableDatabase(); + final ContentValues contentValues = new ContentValues(); + final String where = Message.UUID + "=?"; + contentValues.put(Message.FILE_DELETED, 1); + db.beginTransaction(); + for (String uuid : uuids) { + db.update(Message.TABLENAME, contentValues, where, new String[]{uuid}); + } + db.setTransactionSuccessful(); + db.endTransaction(); + } + + public void markFilesAsChanged(List<FilePathInfo> files) { + SQLiteDatabase db = this.getReadableDatabase(); + final String where = Message.UUID + "=?"; + db.beginTransaction(); + for (FilePathInfo info : files) { + final ContentValues contentValues = new ContentValues(); + contentValues.put(Message.FILE_DELETED, info.FileDeleted ? 1 : 0); + db.update(Message.TABLENAME, contentValues, where, new String[]{info.uuid.toString()}); + } + db.setTransactionSuccessful(); + db.endTransaction(); + } + + public List<FilePathInfo> getFilePathInfo() { + final SQLiteDatabase db = this.getReadableDatabase(); + final Cursor cursor = db.query(Message.TABLENAME, new String[]{Message.UUID, Message.RELATIVE_FILE_PATH, Message.FILE_DELETED}, "type in (1,2) and " + Message.RELATIVE_FILE_PATH + " is not null", null, null, null, null); + final List<FilePathInfo> list = new ArrayList<>(); + while (cursor != null && cursor.moveToNext()) { + list.add(new FilePathInfo(cursor.getString(0), cursor.getString(1), cursor.getInt(2) > 0)); + } + if (cursor != null) { + cursor.close(); + } + return list; + } + public List<FilePath> getRelativeFilePaths(String account, Jid jid, int limit) { SQLiteDatabase db = this.getReadableDatabase(); - final String SQL = "select uuid,relativeFilePath from messages where type in (1,2) and conversationUuid=(select uuid from conversations where accountUuid=? and (contactJid=? or contactJid like ?)) order by timeSent desc"; + final String SQL = "select uuid,relativeFilePath from messages where type in (1,2) and file_deleted=0 and " + Message.RELATIVE_FILE_PATH + " is not null and conversationUuid=(select uuid from conversations where accountUuid=? and (contactJid=? or contactJid like ?)) order by timeSent desc"; final String[] args = {account, jid.toEscapedString(), jid.toEscapedString() + "/%"}; Cursor cursor = db.rawQuery(SQL + (limit > 0 ? " limit " + String.valueOf(limit) : ""), args); List<FilePath> filesPaths = new ArrayList<>(); @@ -820,6 +926,21 @@ public class DatabaseBackend extends SQLiteOpenHelper { } } + public static class FilePathInfo extends FilePath { + public boolean FileDeleted; + + private FilePathInfo(String uuid, String path, boolean deleted) { + super(uuid, path); + this.FileDeleted = deleted; + } + + public boolean setFileDeleted(boolean deleted) { + final boolean changed = deleted != this.FileDeleted; + this.FileDeleted = deleted; + return changed; + } + } + public Conversation findConversation(final Account account, final Jid contactJid) { SQLiteDatabase db = this.getReadableDatabase(); String[] selectionArgs = {account.getUuid(), @@ -854,11 +975,11 @@ public class DatabaseBackend extends SQLiteOpenHelper { return getAccounts(db); } - public List<Jid> getAccountJids() { + public List<Jid> getAccountJids(final boolean enabledOnly) { SQLiteDatabase db = this.getReadableDatabase(); final List<Jid> jids = new ArrayList<>(); final String[] columns = new String[]{Account.USERNAME, Account.SERVER}; - String where = "not options & (1 <<1)"; + String where = enabledOnly ? "not options & (1 <<1)" : null; Cursor cursor = db.query(Account.TABLENAME, columns, where, null, null, null, null); try { while (cursor.moveToNext()) { @@ -874,6 +995,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { } } + private List<Account> getAccounts(SQLiteDatabase db) { List<Account> list = new ArrayList<>(); Cursor cursor = db.query(Account.TABLENAME, null, null, null, null, @@ -910,11 +1032,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { return db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args) == 1; } - public void updateMessage(Message message, String uuid) { + public boolean updateMessage(Message message, String uuid) { SQLiteDatabase db = this.getWritableDatabase(); String[] args = {uuid}; - db.update(Message.TABLENAME, message.getContentValues(), Message.UUID - + "=?", args); + return db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args) == 1; } public void readRoster(Roster roster) { @@ -953,10 +1074,13 @@ public class DatabaseBackend extends SQLiteOpenHelper { public void deleteMessageInConversation(Message message) { long start = SystemClock.elapsedRealtime(); final SQLiteDatabase db = this.getWritableDatabase(); + db.beginTransaction(); ContentValues values = new ContentValues(); values.put(Message.DELETED, "1"); String[] args = {message.getUuid()}; int rows = db.update("messages", values, "uuid =?", args); + db.setTransactionSuccessful(); + db.endTransaction(); Log.d(Config.LOGTAG, "deleted " + rows + " message in " + (SystemClock.elapsedRealtime() - start) + "ms"); } @@ -1527,7 +1651,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { storeIdentityKey(account, account.getJid().asBareJid().toString(), true, CryptoHelper.bytesToHex(identityKeyPair.getPublicKey().serialize()), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), FingerprintStatus.createActiveVerified(false)); } - public void recreateAxolotlDb(SQLiteDatabase db) { + + private void recreateAxolotlDb(SQLiteDatabase db) { Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + ">>> (RE)CREATING AXOLOTL DATABASE <<<"); db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SESSION_TABLENAME); db.execSQL(CREATE_SESSIONS_STATEMENT); diff --git a/src/main/java/de/pixart/messenger/persistance/FileBackend.java b/src/main/java/de/pixart/messenger/persistance/FileBackend.java index 705e5489b..0023eec37 100644 --- a/src/main/java/de/pixart/messenger/persistance/FileBackend.java +++ b/src/main/java/de/pixart/messenger/persistance/FileBackend.java @@ -3,7 +3,6 @@ package de.pixart.messenger.persistance; import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.database.Cursor; @@ -15,6 +14,7 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.RectF; import android.media.MediaMetadataRetriever; +import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Build; import android.os.Environment; @@ -101,15 +101,57 @@ public class FileBackend { } } + public static Uri getMediaUri(Context context, File file) { + final String filePath = file.getAbsolutePath(); + final Cursor cursor = context.getContentResolver().query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + new String[]{MediaStore.Images.Media._ID}, + MediaStore.Images.Media.DATA + "=? ", + new String[]{filePath}, null); + if (cursor != null && cursor.moveToFirst()) { + final int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID)); + cursor.close(); + return Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, String.valueOf(id)); + } else { + return null; + } + } + public void updateMediaScanner(File file) { + updateMediaScanner(file, null); + } + + public void updateMediaScanner(File file, final Runnable callback) { if (file.getAbsolutePath().startsWith(getConversationsDirectory("Images")) || file.getAbsolutePath().startsWith(getConversationsDirectory("Videos"))) { - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + MediaScannerConnection.scanFile(mXmppConnectionService, new String[]{file.getAbsolutePath()}, null, new MediaScannerConnection.MediaScannerConnectionClient() { + @Override + public void onMediaScannerConnected() { + + } + + @Override + public void onScanCompleted(String path, Uri uri) { + if (callback != null && file.getAbsolutePath().equals(path)) { + callback.run(); + } else { + Log.d(Config.LOGTAG, "media scanner scanned wrong file"); + if (callback != null) { + callback.run(); + } + } + } + }); + return; + /*Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(Uri.fromFile(file)); - mXmppConnectionService.sendBroadcast(intent); + mXmppConnectionService.sendBroadcast(intent);*/ } else { createNoMedia(); } + if (callback != null) { + callback.run(); + } } public boolean deleteFile(File file) { @@ -130,6 +172,10 @@ public class FileBackend { return getFile(message, true); } + public DownloadableFile getFileForPath(String path) { + return getFileForPath(path, MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(path))); + } + public DownloadableFile getFileForPath(String path, String mime) { final DownloadableFile file; if (path.startsWith("/")) { @@ -148,6 +194,11 @@ public class FileBackend { return file; } + public boolean isInternalFile(final File file) { + final File internalFile = getFileForPath(file.getName()); + return file.getAbsolutePath().equals(internalFile.getAbsolutePath()); + } + public DownloadableFile getFile(Message message, boolean decrypted) { final boolean encrypted = !decrypted && (message.getEncryption() == Message.ENCRYPTION_PGP @@ -212,13 +263,8 @@ public class FileBackend { List<Attachment> attachments = new ArrayList<>(); for (DatabaseBackend.FilePath relativeFilePath : relativeFilePaths) { final String mime = MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(relativeFilePath.path)); - Log.d(Config.LOGTAG, "mime=" + mime); - File file = getFileForPath(relativeFilePath.path, mime); - if (file.exists()) { - attachments.add(Attachment.of(relativeFilePath.uuid, file, mime)); - } else { - Log.d(Config.LOGTAG, "file " + file.getAbsolutePath() + " doesn't exist"); - } + final File file = getFileForPath(relativeFilePath.path, mime); + attachments.add(Attachment.of(relativeFilePath.uuid, file, mime)); } return attachments; } @@ -975,11 +1021,11 @@ public class FileBackend { public void updateFileParams(Message message, URL url) { DownloadableFile file = getFile(message); final String mime = file.getMimeType(); - boolean image = message.getType() == Message.TYPE_IMAGE || (mime != null && mime.startsWith("image/")); - boolean video = mime != null && mime.startsWith("video/"); - boolean audio = mime != null && mime.startsWith("audio/"); - boolean vcard = mime != null && mime.contains("vcard"); - boolean apk = mime != null && mime.equals("application/vnd.android.package-archive"); + final boolean image = message.getType() == Message.TYPE_IMAGE || (mime != null && mime.startsWith("image/")); + final boolean video = mime != null && mime.startsWith("video/"); + final boolean audio = mime != null && mime.startsWith("audio/"); + final boolean vcard = mime != null && mime.contains("vcard"); + final boolean apk = mime != null && mime.equals("application/vnd.android.package-archive"); final StringBuilder body = new StringBuilder(); if (url != null) { body.append(url.toString()); @@ -1003,16 +1049,8 @@ public class FileBackend { body.append("|0|0|0|").append(getAPK(file, mXmppConnectionService.getApplicationContext())); } message.setBody(body.toString()); - } - - public int getMediaRuntime(Uri uri) { - try { - MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); - mediaMetadataRetriever.setDataSource(mXmppConnectionService, uri); - return Integer.parseInt(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)); - } catch (RuntimeException e) { - return 0; - } + message.setFileDeleted(false); + message.setType(image ? Message.TYPE_IMAGE : Message.TYPE_FILE); } private int getMediaRuntime(File file) { diff --git a/src/main/java/de/pixart/messenger/services/AlarmReceiver.java b/src/main/java/de/pixart/messenger/services/AlarmReceiver.java index e4c07366d..887a59684 100644 --- a/src/main/java/de/pixart/messenger/services/AlarmReceiver.java +++ b/src/main/java/de/pixart/messenger/services/AlarmReceiver.java @@ -3,7 +3,6 @@ package de.pixart.messenger.services; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.support.v4.content.ContextCompat; import android.util.Log; import de.pixart.messenger.Config; @@ -16,15 +15,7 @@ public class AlarmReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { if (intent.getAction().contains("exportlogs")) { Log.d(Config.LOGTAG, "Received alarm broadcast to export logs"); - try { - if (Compatibility.runsAndTargetsTwentySix(context)) { - ContextCompat.startForegroundService(context, new Intent(context, ExportLogsService.class)); - } else { - context.startService(new Intent(context, ExportLogsService.class)); - } - } catch (RuntimeException e) { - Log.d(Config.LOGTAG, "AlarmReceiver was unable to start ExportLogsService"); - } + Compatibility.startService(context, new Intent(context, ExportBackupService.class)); } } } diff --git a/src/main/java/de/pixart/messenger/services/AvatarService.java b/src/main/java/de/pixart/messenger/services/AvatarService.java index 23e2da4d6..b37811b7f 100644 --- a/src/main/java/de/pixart/messenger/services/AvatarService.java +++ b/src/main/java/de/pixart/messenger/services/AvatarService.java @@ -1,8 +1,8 @@ package de.pixart.messenger.services; +import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; @@ -13,6 +13,7 @@ import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.support.annotation.ColorInt; import android.support.annotation.Nullable; import android.support.v4.content.res.ResourcesCompat; import android.text.TextUtils; @@ -42,12 +43,16 @@ import de.pixart.messenger.xmpp.OnAdvancedStreamFeaturesLoaded; import de.pixart.messenger.xmpp.XmppConnection; import rocks.xmpp.addr.Jid; +import static de.pixart.messenger.ui.SettingsActivity.PREFER_XMPP_AVATAR; + public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private static final int FG_COLOR = 0xFFFAFAFA; private static final int TRANSPARENT = 0x00000000; private static final int PLACEHOLDER_COLOR = 0xFF202020; + public static final int SYSTEM_UI_AVATAR_SIZE = 48; + private static final String PREFIX_CONTACT = "contact"; private static final String PREFIX_CONVERSATION = "conversation"; private static final String PREFIX_ACCOUNT = "account"; @@ -62,17 +67,23 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { this.mXmppConnectionService = service; } - private static String getFirstLetter(String name) { - for (Character c : name.toCharArray()) { - if (Character.isLetterOrDigit(c)) { - return c.toString(); - } - } - return "X"; + public static int getSystemUiAvatarSize(final Context context) { + return (int) (SYSTEM_UI_AVATAR_SIZE * context.getResources().getDisplayMetrics().density); } - private static String emptyOnNull(@Nullable Jid value) { - return value == null ? "" : value.toString(); + public Bitmap get(final Avatarable avatarable, final int size, final boolean cachedOnly) { + if (avatarable instanceof Account) { + return get((Account) avatarable, size, cachedOnly); + } else if (avatarable instanceof Conversation) { + return get((Conversation) avatarable, size, cachedOnly); + } else if (avatarable instanceof Message) { + return get((Message) avatarable, size, cachedOnly); + } else if (avatarable instanceof ListItem) { + return get((ListItem) avatarable, size, cachedOnly); + } else if (avatarable instanceof MucOptions.User) { + return get((MucOptions.User) avatarable, size, cachedOnly); + } + throw new AssertionError("AvatarService does not know how to generate avatar from "+avatarable.getClass().getName()); } private Bitmap get(final Contact contact, final int size, boolean cachedOnly) { @@ -84,15 +95,14 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { if (avatar != null || cachedOnly) { return avatar; } - if (contact.getAvatarFilename() != null) { + if (contact.getAvatarFilename() != null && QuickConversationsService.isQuicksy() && mXmppConnectionService.getPreferences().getBoolean(PREFER_XMPP_AVATAR, mXmppConnectionService.getResources().getBoolean(R.bool.prefer_xmpp_avatar))) { + avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatarFilename(), size); + } + if (avatar == null && contact.getAvatarFilename() != null && mXmppConnectionService.getPreferences().getBoolean(PREFER_XMPP_AVATAR, mXmppConnectionService.getResources().getBoolean(R.bool.prefer_xmpp_avatar))) { avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatarFilename(), size); } if (avatar == null && contact.getProfilePhoto() != null) { - try { - avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size); - } catch (Exception e) { - e.printStackTrace(); - } + avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size); } if (avatar == null) { avatar = get(contact.getDisplayName(), contact.getJid().asBareJid().toString(), size, false); @@ -116,6 +126,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(output); final Paint paint = new Paint(); + drawAvatar(bitmap, canvas, paint); if (withIcon) { drawIcon(canvas, paint); @@ -311,7 +322,9 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { if (bitmap != null || cachedOnly) { return bitmap; } + bitmap = mXmppConnectionService.getFileBackend().getAvatar(mucOptions.getAvatar(), size); + if (bitmap == null) { final List<MucOptions.User> users = mucOptions.getUsersRelevantForNameAndAvatar(); if (users.size() == 0) { @@ -321,7 +334,9 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { bitmap = getImpl(users, size); } } + this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap); + return bitmap; } @@ -481,10 +496,6 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { } } - /*public Bitmap get(String name, int size) { - return get(name,null, size,false); - }*/ - public void clear(MucOptions.User user) { synchronized (this.sizes) { for (Integer size : sizes) { @@ -503,6 +514,10 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { + String.valueOf(size); } + /*public Bitmap get(String name, int size) { + return get(name,null, size,false); + }*/ + public Bitmap get(final String name, String seed, final int size, boolean cachedOnly) { final String KEY = key(seed == null ? name : name + "\0" + seed, size); Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(KEY); @@ -514,7 +529,11 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { return bitmap; } - private Bitmap getImpl(final String name, final String seed, final int size) { + public static Bitmap get(final Jid jid, final int size) { + return getImpl(jid.asBareJid().toEscapedString(), null, size); + } + + private static Bitmap getImpl(final String name, final String seed, final int size) { Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); final String trimmedName = name == null ? "" : name.trim(); @@ -531,7 +550,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { return PREFIX_GENERIC + "_" + name + "_" + String.valueOf(size); } - private boolean drawTile(Canvas canvas, String letter, int tileColor, int left, int top, int right, int bottom) { + private static boolean drawTile(Canvas canvas, String letter, int tileColor, int left, int top, int right, int bottom) { letter = letter.toUpperCase(Locale.getDefault()); Paint tilePaint = new Paint(), textPaint = new Paint(); tilePaint.setColor(tileColor); @@ -554,12 +573,10 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { Contact contact = user.getContact(); if (contact != null) { Uri uri = null; - if (contact.getAvatarFilename() != null) { - try { - uri = mXmppConnectionService.getFileBackend().getAvatarUri(contact.getAvatarFilename()); - } catch (Exception e) { - e.printStackTrace(); - } + if (contact.getAvatarFilename() != null && QuickConversationsService.isQuicksy() && mXmppConnectionService.getPreferences().getBoolean(PREFER_XMPP_AVATAR, mXmppConnectionService.getResources().getBoolean(R.bool.prefer_xmpp_avatar))) { + uri = mXmppConnectionService.getFileBackend().getAvatarUri(contact.getAvatarFilename()); + } else if (contact.getAvatarFilename() != null && mXmppConnectionService.getPreferences().getBoolean(PREFER_XMPP_AVATAR, mXmppConnectionService.getResources().getBoolean(R.bool.prefer_xmpp_avatar))) { + uri = mXmppConnectionService.getFileBackend().getAvatarUri(contact.getAvatarFilename()); } else if (contact.getProfilePhoto() != null) { uri = Uri.parse(contact.getProfilePhoto()); } @@ -571,16 +588,6 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { if (drawTile(canvas, uri, left, top, right, bottom)) { return true; } - } else if (user.getAvatar() != null) { - Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar()); - if (drawTile(canvas, uri, left, top, right, bottom)) { - return true; - } - } else if (user.getAvatar() != null) { - Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar()); - if (drawTile(canvas, uri, left, top, right, bottom)) { - return true; - } } if (contact != null) { String seed = contact.getJid().asBareJid().toString(); @@ -606,7 +613,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { return drawTile(canvas, name, name, left, top, right, bottom); } - private boolean drawTile(Canvas canvas, String name, String seed, int left, int top, int right, int bottom) { + private static boolean drawTile(Canvas canvas, String name, String seed, int left, int top, int right, int bottom) { if (name != null) { final String letter = getFirstLetter(name); final int color = UIHelper.getColorForName(seed == null ? name : seed); @@ -616,6 +623,15 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { return false; } + private static String getFirstLetter(String name) { + for (Character c : name.toCharArray()) { + if (Character.isLetterOrDigit(c)) { + return c.toString(); + } + } + return "X"; + } + private boolean drawTile(Canvas canvas, Uri uri, int left, int top, int right, int bottom) { if (uri != null) { Bitmap bitmap = mXmppConnectionService.getFileBackend() @@ -644,4 +660,13 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { } } } -} + + private static String emptyOnNull(@Nullable Jid value) { + return value == null ? "" : value.toString(); + } + + public interface Avatarable { + @ColorInt + int getAvatarBackgroundColor(); + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java b/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java index 4c0aec3ca..7bda592e5 100644 --- a/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java +++ b/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java @@ -18,31 +18,44 @@ import java.util.List; import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.ui.ConversationsActivity; +import de.pixart.messenger.utils.Compatibility; @TargetApi(Build.VERSION_CODES.M) public class ContactChooserTargetService extends ChooserTargetService implements ServiceConnection { private final Object lock = new Object(); - + private final int MAX_TARGETS = 5; private XmppConnectionService mXmppConnectionService; - private final int MAX_TARGETS = 5; + private static boolean textOnly(IntentFilter filter) { + for (int i = 0; i < filter.countDataTypes(); ++i) { + if (!"text/plain".equals(filter.getDataType(i))) { + return false; + } + } + return true; + } @Override public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) { - Intent intent = new Intent(this, XmppConnectionService.class); + final ArrayList<ChooserTarget> chooserTargets = new ArrayList<>(); + if (!EventReceiver.hasEnabledAccounts(this)) { + return chooserTargets; + } + final Intent intent = new Intent(this, XmppConnectionService.class); intent.setAction("contact_chooser"); + Compatibility.startService(this, intent); bindService(intent, this, Context.BIND_AUTO_CREATE); - ArrayList<ChooserTarget> chooserTargets = new ArrayList<>(); try { waitForService(); final ArrayList<Conversation> conversations = new ArrayList<>(); if (!mXmppConnectionService.areMessagesInitialized()) { return chooserTargets; } - mXmppConnectionService.populateWithOrderedConversations(conversations, false); + + mXmppConnectionService.populateWithOrderedConversations(conversations, textOnly(matchedFilter)); final ComponentName componentName = new ComponentName(this, ConversationsActivity.class); - final int pixel = (int) (48 * getResources().getDisplayMetrics().density); + final int pixel = AvatarService.getSystemUiAvatarSize(this); for (Conversation conversation : conversations) { if (conversation.sentMessagesCount() == 0) { continue; diff --git a/src/main/java/de/pixart/messenger/services/EventReceiver.java b/src/main/java/de/pixart/messenger/services/EventReceiver.java index 03bf68ed9..145c9893b 100644 --- a/src/main/java/de/pixart/messenger/services/EventReceiver.java +++ b/src/main/java/de/pixart/messenger/services/EventReceiver.java @@ -4,7 +4,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.preference.PreferenceManager; -import android.support.v4.content.ContextCompat; import android.util.Log; import de.pixart.messenger.Config; @@ -25,16 +24,7 @@ public class EventReceiver extends BroadcastReceiver { } final String action = originalIntent.getAction(); if (action.equals("ui") || hasEnabledAccounts(context)) { - try { - if (Compatibility.runsAndTargetsTwentySix(context)) { - intentForService.putExtra(EXTRA_NEEDS_FOREGROUND_SERVICE, true); - ContextCompat.startForegroundService(context, intentForService); - } else { - context.startService(intentForService); - } - } catch (RuntimeException e) { - Log.d(Config.LOGTAG, "EventReceiver was unable to start service"); - } + Compatibility.startService(context, intentForService); } else { Log.d(Config.LOGTAG, "EventReceiver ignored action " + intentForService.getAction()); } diff --git a/src/main/java/de/pixart/messenger/services/ExportBackupService.java b/src/main/java/de/pixart/messenger/services/ExportBackupService.java new file mode 100644 index 000000000..5ffae41a9 --- /dev/null +++ b/src/main/java/de/pixart/messenger/services/ExportBackupService.java @@ -0,0 +1,387 @@ +package de.pixart.messenger.services; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteDatabase; +import android.os.IBinder; +import android.os.PowerManager; +import android.preference.PreferenceManager; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +import java.io.BufferedWriter; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.zip.GZIPOutputStream; + +import javax.crypto.Cipher; +import javax.crypto.CipherOutputStream; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; + +import de.pixart.messenger.Config; +import de.pixart.messenger.R; +import de.pixart.messenger.crypto.axolotl.SQLiteAxolotlStore; +import de.pixart.messenger.entities.Account; +import de.pixart.messenger.entities.Conversation; +import de.pixart.messenger.entities.Message; +import de.pixart.messenger.persistance.DatabaseBackend; +import de.pixart.messenger.persistance.FileBackend; +import de.pixart.messenger.utils.BackupFileHeader; +import de.pixart.messenger.utils.Compatibility; +import de.pixart.messenger.utils.WakeLockHelper; +import rocks.xmpp.addr.Jid; + +public class ExportBackupService extends Service { + + private PowerManager.WakeLock wakeLock; + private PowerManager pm; + + public static final String KEYTYPE = "AES"; + public static final String CIPHERMODE = "AES/GCM/NoPadding"; + public static final String PROVIDER = "BC"; + + boolean ReadableLogsEnabled = false; + private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + private static final String DIRECTORY_STRING_FORMAT = FileBackend.getAppLogsDirectory() + "%s"; + private static final String MESSAGE_STRING_FORMAT = "(%s) %s: %s\n"; + + private static final int NOTIFICATION_ID = 19; + private static final int PAGE_SIZE = 20; + private static AtomicBoolean running = new AtomicBoolean(false); + private DatabaseBackend mDatabaseBackend; + private List<Account> mAccounts; + private NotificationManager notificationManager; + + private static void accountExport(SQLiteDatabase db, String uuid, PrintWriter writer) { + final StringBuilder builder = new StringBuilder(); + final Cursor accountCursor = db.query(Account.TABLENAME, null, Account.UUID + "=?", new String[]{uuid}, null, null, null); + while (accountCursor != null && accountCursor.moveToNext()) { + builder.append("INSERT INTO ").append(Account.TABLENAME).append("("); + for (int i = 0; i < accountCursor.getColumnCount(); ++i) { + if (i != 0) { + builder.append(','); + } + builder.append(accountCursor.getColumnName(i)); + } + builder.append(") VALUES("); + for (int i = 0; i < accountCursor.getColumnCount(); ++i) { + if (i != 0) { + builder.append(','); + } + final String value = accountCursor.getString(i); + if (value == null || Account.ROSTERVERSION.equals(accountCursor.getColumnName(i))) { + builder.append("NULL"); + } else if (value.matches("\\d+")) { + int intValue = Integer.parseInt(value); + if (Account.OPTIONS.equals(accountCursor.getColumnName(i))) { + intValue |= 1 << Account.OPTION_DISABLED; + } + builder.append(intValue); + } else { + DatabaseUtils.appendEscapedSQLString(builder, value); + } + } + builder.append(")"); + builder.append(';'); + builder.append('\n'); + } + if (accountCursor != null) { + accountCursor.close(); + } + writer.append(builder.toString()); + } + + private static void simpleExport(SQLiteDatabase db, String table, String column, String uuid, PrintWriter writer) { + final Cursor cursor = db.query(table, null, column + "=?", new String[]{uuid}, null, null, null); + while (cursor != null && cursor.moveToNext()) { + writer.write(cursorToString(table, cursor, PAGE_SIZE)); + } + if (cursor != null) { + cursor.close(); + } + } + + public static byte[] getKey(String password, byte[] salt) { + try { + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + return factory.generateSecret(new PBEKeySpec(password.toCharArray(), salt, 1024, 128)).getEncoded(); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new AssertionError(e); + } + } + + private static String cursorToString(String tablename, Cursor cursor, int max) { + return cursorToString(tablename, cursor, max, false); + } + + private static String cursorToString(String tablename, Cursor cursor, int max, boolean ignore) { + StringBuilder builder = new StringBuilder(); + builder.append("INSERT "); + if (ignore) { + builder.append("OR IGNORE "); + } + builder.append("INTO ").append(tablename).append("("); + for (int i = 0; i < cursor.getColumnCount(); ++i) { + if (i != 0) { + builder.append(','); + } + builder.append(cursor.getColumnName(i)); + } + builder.append(") VALUES"); + for (int i = 0; i < max; ++i) { + if (i != 0) { + builder.append(','); + } + appendValues(cursor, builder); + if (i < max - 1 && !cursor.moveToNext()) { + break; + } + } + builder.append(';'); + builder.append('\n'); + return builder.toString(); + } + + private static void appendValues(Cursor cursor, StringBuilder builder) { + builder.append("("); + for (int i = 0; i < cursor.getColumnCount(); ++i) { + if (i != 0) { + builder.append(','); + } + final String value = cursor.getString(i); + if (value == null) { + builder.append("NULL"); + } else if (value.matches("\\d+")) { + builder.append(value); + } else { + DatabaseUtils.appendEscapedSQLString(builder, value); + } + } + builder.append(")"); + + } + + @Override + public void onCreate() { + mDatabaseBackend = DatabaseBackend.getInstance(getBaseContext()); + mAccounts = mDatabaseBackend.getAccounts(); + notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + final SharedPreferences ReadableLogs = PreferenceManager.getDefaultSharedPreferences(this); + ReadableLogsEnabled = ReadableLogs.getBoolean("export_plain_text_logs", getResources().getBoolean(R.bool.plain_text_logs)); + pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Config.LOGTAG + ": ExportLogsService"); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (running.compareAndSet(false, true)) { + new Thread(() -> { + export(); + stopForeground(true); + running.set(false); + WakeLockHelper.release(wakeLock); + stopSelf(); + }).start(); + return START_STICKY; + } + return START_NOT_STICKY; + } + + private void messageExport(SQLiteDatabase db, String uuid, PrintWriter writer, Progress progress) { + Cursor cursor = db.rawQuery("select messages.* from messages join conversations on conversations.uuid=messages.conversationUuid where conversations.accountUuid=?", new String[]{uuid}); + int size = cursor != null ? cursor.getCount() : 0; + Log.d(Config.LOGTAG, "exporting " + size + " messages"); + int i = 0; + int p = 0; + while (cursor != null && cursor.moveToNext()) { + writer.write(cursorToString(Message.TABLENAME, cursor, PAGE_SIZE, false)); + if (i + PAGE_SIZE > size) { + i = size; + } else { + i += PAGE_SIZE; + } + final int percentage = i * 100 / size; + if (p < percentage) { + p = percentage; + notificationManager.notify(NOTIFICATION_ID, progress.build(p)); + } + } + if (cursor != null) { + cursor.close(); + } + } + + private void export() { + wakeLock.acquire(15 * 60 * 1000L /*15 minutes*/); + NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup"); + mBuilder.setContentTitle(getString(R.string.notification_create_backup_title)) + .setSmallIcon(R.drawable.ic_archive_white_24dp) + .setProgress(1, 0, false); + startForeground(NOTIFICATION_ID, mBuilder.build()); + try { + int count = 0; + final int max = this.mAccounts.size(); + final SecureRandom secureRandom = new SecureRandom(); + if (mAccounts.size() >= 1) { + if (ReadableLogsEnabled) { + List<Conversation> conversations = mDatabaseBackend.getConversations(Conversation.STATUS_AVAILABLE); + conversations.addAll(mDatabaseBackend.getConversations(Conversation.STATUS_ARCHIVED)); + for (Conversation conversation : conversations) { + writeToFile(conversation); + } + } + } + for (Account account : this.mAccounts) { + final byte[] IV = new byte[12]; + final byte[] salt = new byte[16]; + secureRandom.nextBytes(IV); + secureRandom.nextBytes(salt); + final BackupFileHeader backupFileHeader = new BackupFileHeader(getString(R.string.app_name), account.getJid(), System.currentTimeMillis(), IV, salt); + final Progress progress = new Progress(mBuilder, max, count); + final File file = new File(FileBackend.getBackupDirectory() + account.getJid().asBareJid().toEscapedString() + ".ceb"); + if (file.getParentFile().mkdirs()) { + Log.d(Config.LOGTAG, "created backup directory " + file.getParentFile().getAbsolutePath()); + } + final FileOutputStream fileOutputStream = new FileOutputStream(file); + final DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream); + backupFileHeader.write(dataOutputStream); + dataOutputStream.flush(); + + final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER); + byte[] key = getKey(account.getPassword(), salt); + Log.d(Config.LOGTAG, backupFileHeader.toString()); + SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE); + IvParameterSpec ivSpec = new IvParameterSpec(IV); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); + CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, cipher); + + GZIPOutputStream gzipOutputStream = new GZIPOutputStream(cipherOutputStream); + PrintWriter writer = new PrintWriter(gzipOutputStream); + SQLiteDatabase db = this.mDatabaseBackend.getReadableDatabase(); + final String uuid = account.getUuid(); + accountExport(db, uuid, writer); + simpleExport(db, Conversation.TABLENAME, Conversation.ACCOUNT, uuid, writer); + messageExport(db, uuid, writer, progress); + for (String table : Arrays.asList(SQLiteAxolotlStore.PREKEY_TABLENAME, SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, SQLiteAxolotlStore.SESSION_TABLENAME, SQLiteAxolotlStore.IDENTITIES_TABLENAME)) { + simpleExport(db, table, SQLiteAxolotlStore.ACCOUNT, uuid, writer); + } + writer.flush(); + writer.close(); + Log.d(Config.LOGTAG, "written backup to " + file.getAbsoluteFile()); + count++; + } + } catch (Exception e) { + Log.d(Config.LOGTAG, "unable to create backup ", e); + } + } + + private void writeToFile(Conversation conversation) { + Jid accountJid = resolveAccountUuid(conversation.getAccountUuid()); + Jid contactJid = conversation.getJid(); + + File dir = new File(String.format(DIRECTORY_STRING_FORMAT, accountJid.asBareJid().toString())); + dir.mkdirs(); + + BufferedWriter bw = null; + try { + for (Message message : mDatabaseBackend.getMessagesIterable(conversation)) { + if (message == null) + continue; + if (message.getType() == Message.TYPE_TEXT || message.hasFileOnRemoteHost()) { + String date = simpleDateFormat.format(new Date(message.getTimeSent())); + if (bw == null) { + bw = new BufferedWriter(new FileWriter( + new File(dir, contactJid.asBareJid().toString() + ".txt"))); + } + String jid = null; + switch (message.getStatus()) { + case Message.STATUS_RECEIVED: + jid = getMessageCounterpart(message); + break; + case Message.STATUS_SEND: + case Message.STATUS_SEND_RECEIVED: + case Message.STATUS_SEND_DISPLAYED: + jid = accountJid.asBareJid().toString(); + break; + } + if (jid != null) { + String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody(); + bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid, + body.replace("\\\n", "\\ \n").replace("\n", "\\ \n"))); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (bw != null) { + bw.close(); + } + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + + private String getMessageCounterpart(Message message) { + String trueCounterpart = (String) message.getContentValues().get(Message.TRUE_COUNTERPART); + if (trueCounterpart != null) { + return trueCounterpart; + } else { + return message.getCounterpart().toString(); + } + } + + private Jid resolveAccountUuid(String accountUuid) { + for (Account account : mAccounts) { + if (account.getUuid().equals(accountUuid)) { + return account.getJid(); + } + } + return null; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private class Progress { + private final NotificationCompat.Builder builder; + private final int max; + private final int count; + + private Progress(NotificationCompat.Builder builder, int max, int count) { + this.builder = builder; + this.max = max; + this.count = count; + } + + private Notification build(int percentage) { + builder.setProgress(max * 100, count * 100 + percentage, false); + return builder.build(); + } + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/services/ExportLogsService.java b/src/main/java/de/pixart/messenger/services/ExportLogsService.java deleted file mode 100644 index a607fcb66..000000000 --- a/src/main/java/de/pixart/messenger/services/ExportLogsService.java +++ /dev/null @@ -1,248 +0,0 @@ -package de.pixart.messenger.services; - -import android.app.NotificationManager; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.IBinder; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.preference.PreferenceManager; -import android.support.annotation.BoolRes; -import android.support.v4.app.NotificationCompat; -import android.util.Log; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.crypto.NoSuchPaddingException; - -import de.pixart.messenger.Config; -import de.pixart.messenger.R; -import de.pixart.messenger.entities.Account; -import de.pixart.messenger.entities.Conversation; -import de.pixart.messenger.entities.Message; -import de.pixart.messenger.persistance.DatabaseBackend; -import de.pixart.messenger.persistance.FileBackend; -import de.pixart.messenger.utils.EncryptDecryptFile; -import de.pixart.messenger.utils.WakeLockHelper; -import rocks.xmpp.addr.Jid; - -import static de.pixart.messenger.services.NotificationService.BACKUP_CHANNEL_ID; -import static de.pixart.messenger.services.NotificationService.NOTIFICATION_ID; -import static de.pixart.messenger.ui.SettingsActivity.USE_MULTI_ACCOUNTS; - -public class ExportLogsService extends XmppConnectionService { - - private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - private static final String DIRECTORY_STRING_FORMAT = FileBackend.getAppLogsDirectory() + "%s"; - private static final String MESSAGE_STRING_FORMAT = "(%s) %s: %s\n"; - private static AtomicBoolean running = new AtomicBoolean(false); - boolean ReadableLogsEnabled = false; - private DatabaseBackend mDatabaseBackend; - private List<Account> mAccounts; - private WakeLock wakeLock; - private PowerManager pm; - - @Override - public void onCreate() { - mDatabaseBackend = DatabaseBackend.getInstance(getBaseContext()); - mAccounts = mDatabaseBackend.getAccounts(); - final SharedPreferences ReadableLogs = PreferenceManager.getDefaultSharedPreferences(this); - ReadableLogsEnabled = ReadableLogs.getBoolean("export_plain_text_logs", getResources().getBoolean(R.bool.plain_text_logs)); - pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Config.LOGTAG + ": ExportLogsService"); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if (running.compareAndSet(false, true)) { - new Thread(() -> { - export(); - WakeLockHelper.release(wakeLock); - stopForeground(true); - running.set(false); - stopSelf(); - }).start(); - } - return START_NOT_STICKY; - } - - @Override - public void onDestroy() { - super.onDestroy(); - stopForeground(true); - } - - private void export() { - wakeLock.acquire(); - NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), BACKUP_CHANNEL_ID); - mBuilder.setContentTitle(getString(R.string.notification_export_logs_title)); - mBuilder.setSmallIcon(R.drawable.ic_import_export_white_24dp); - mBuilder.setProgress(0, 0, true); - startForeground(NOTIFICATION_ID, mBuilder.build()); - List<Conversation> conversations = mDatabaseBackend.getConversations(Conversation.STATUS_AVAILABLE); - conversations.addAll(mDatabaseBackend.getConversations(Conversation.STATUS_ARCHIVED)); - if (mAccounts.size() >= 1) { - if (ReadableLogsEnabled) { - for (Conversation conversation : conversations) { - writeToFile(conversation); - } - } - try { - ExportDatabase(); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - Log.d(Config.LOGTAG, "ExportLogsService: no accounts, aborting export"); - } - } - - private void writeToFile(Conversation conversation) { - Jid accountJid = resolveAccountUuid(conversation.getAccountUuid()); - Jid contactJid = conversation.getJid(); - - File dir = new File(String.format(DIRECTORY_STRING_FORMAT, accountJid.asBareJid().toString())); - dir.mkdirs(); - - BufferedWriter bw = null; - try { - for (Message message : mDatabaseBackend.getMessagesIterable(conversation)) { - if (message == null) - continue; - if (message.getType() == Message.TYPE_TEXT || message.hasFileOnRemoteHost()) { - String date = simpleDateFormat.format(new Date(message.getTimeSent())); - if (bw == null) { - bw = new BufferedWriter(new FileWriter( - new File(dir, contactJid.asBareJid().toString() + ".txt"))); - } - String jid = null; - switch (message.getStatus()) { - case Message.STATUS_RECEIVED: - jid = getMessageCounterpart(message); - break; - case Message.STATUS_SEND: - case Message.STATUS_SEND_RECEIVED: - case Message.STATUS_SEND_DISPLAYED: - jid = accountJid.asBareJid().toString(); - break; - } - if (jid != null) { - String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody(); - bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid, - body.replace("\\\n", "\\ \n").replace("\n", "\\ \n"))); - } - } - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (bw != null) { - bw.close(); - } - } catch (IOException e1) { - e1.printStackTrace(); - } - } - } - - private Jid resolveAccountUuid(String accountUuid) { - for (Account account : mAccounts) { - if (account.getUuid().equals(accountUuid)) { - return account.getJid(); - } - } - return null; - } - - private String getMessageCounterpart(Message message) { - String trueCounterpart = (String) message.getContentValues().get(Message.TRUE_COUNTERPART); - if (trueCounterpart != null) { - return trueCounterpart; - } else { - return message.getCounterpart().toString(); - } - } - - public void ExportDatabase() throws IOException { - Log.d(Config.LOGTAG, "ExportLogsService: start creating backup"); - Account mAccount = mAccounts.get(0); - String EncryptionKey = null; - // Get hold of the db: - FileInputStream InputFile = new FileInputStream(this.getDatabasePath(DatabaseBackend.DATABASE_NAME)); - // Set the output folder on the SDcard - File directory = new File(FileBackend.getBackupDirectory()); - // Create the folder if it doesn't exist: - if (!directory.exists()) { - boolean directory_created = directory.mkdirs(); - Log.d(Config.LOGTAG, "ExportLogsService: backup directory created " + directory_created); - } - //Delete old database export file - File temp_db_file = new File(directory + "/database.bak"); - if (temp_db_file.exists()) { - Log.d(Config.LOGTAG, "ExportLogsService: Delete temp database backup file from " + temp_db_file.toString()); - boolean temp_db_file_deleted = temp_db_file.delete(); - Log.d(Config.LOGTAG, "ExportLogsService: old backup file deleted " + temp_db_file_deleted); - } - // Set the output file stream up: - FileOutputStream OutputFile = new FileOutputStream(directory.getPath() + "/database.db.crypt"); - - if (mAccounts.size() == 1 && !multipleAccounts()) { - EncryptionKey = mAccount.getPassword(); //get account password - } else { - SharedPreferences multiaccount_prefs = getApplicationContext().getSharedPreferences(USE_MULTI_ACCOUNTS, Context.MODE_PRIVATE); - EncryptionKey = multiaccount_prefs.getString("BackupPW", null); - } - if (EncryptionKey == null) { - Log.d(Config.LOGTAG, "ExportLogsService: Database exporter: failed to write encryted backup to sdcard because of missing password"); - return; - } - - // encrypt database from the input file to the output file - try { - EncryptDecryptFile.encrypt(InputFile, OutputFile, EncryptionKey); - Log.d(Config.LOGTAG, "ExportLogsService: starting encrypted output to " + OutputFile.toString()); - } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { - Log.d(Config.LOGTAG, "ExportLogsService: Database exporter: encryption failed with " + e); - e.printStackTrace(); - } catch (InvalidKeyException e) { - Log.d(Config.LOGTAG, "ExportLogsService: Database exporter: encryption failed (invalid key) with " + e); - e.printStackTrace(); - } catch (IOException e) { - Log.d(Config.LOGTAG, "ExportLogsService: Database exporter: encryption failed (IO) with " + e); - e.printStackTrace(); - } finally { - Log.d(Config.LOGTAG, "ExportLogsService: backup job finished"); - } - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - public SharedPreferences getPreferences() { - return PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - } - - public boolean getBooleanPreference(String name, @BoolRes int res) { - return getPreferences().getBoolean(name, getResources().getBoolean(res)); - } - - public boolean multipleAccounts() { - return getBooleanPreference("enable_multi_accounts", R.bool.enable_multi_accounts); - } -} diff --git a/src/main/java/de/pixart/messenger/services/ImportBackupService.java b/src/main/java/de/pixart/messenger/services/ImportBackupService.java new file mode 100644 index 000000000..1b28640e5 --- /dev/null +++ b/src/main/java/de/pixart/messenger/services/ImportBackupService.java @@ -0,0 +1,294 @@ +package de.pixart.messenger.services; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.Binder; +import android.os.IBinder; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.zip.GZIPInputStream; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import de.pixart.messenger.Config; +import de.pixart.messenger.R; +import de.pixart.messenger.persistance.DatabaseBackend; +import de.pixart.messenger.persistance.FileBackend; +import de.pixart.messenger.ui.ManageAccountActivity; +import de.pixart.messenger.utils.BackupFileHeader; +import de.pixart.messenger.utils.Compatibility; +import de.pixart.messenger.utils.SerialSingleThreadExecutor; +import rocks.xmpp.addr.Jid; + +import static de.pixart.messenger.services.ExportBackupService.CIPHERMODE; +import static de.pixart.messenger.services.ExportBackupService.KEYTYPE; +import static de.pixart.messenger.services.ExportBackupService.PROVIDER; + +public class ImportBackupService extends Service { + + private static final int NOTIFICATION_ID = 21; + private static AtomicBoolean running = new AtomicBoolean(false); + private final ImportBackupServiceBinder binder = new ImportBackupServiceBinder(); + private final SerialSingleThreadExecutor executor = new SerialSingleThreadExecutor(getClass().getSimpleName()); + private final Set<OnBackupProcessed> mOnBackupProcessedListeners = Collections.newSetFromMap(new WeakHashMap<>()); + private DatabaseBackend mDatabaseBackend; + private NotificationManager notificationManager; + + private static int count(String input, char c) { + int count = 0; + for (char aChar : input.toCharArray()) { + if (aChar == c) { + ++count; + } + } + return count; + } + + @Override + public void onCreate() { + mDatabaseBackend = DatabaseBackend.getInstance(getBaseContext()); + notificationManager = (android.app.NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (intent == null) { + return START_NOT_STICKY; + } + final String password = intent.getStringExtra("password"); + final String file = intent.getStringExtra("file"); + if (password == null || file == null) { + return START_NOT_STICKY; + } + if (running.compareAndSet(false, true)) { + executor.execute(() -> { + startForegroundService(); + final boolean success = importBackup(new File(file), password); + stopForeground(true); + running.set(false); + if (success) { + notifySuccess(); + } + stopSelf(); + }); + } else { + Log.d(Config.LOGTAG, "backup already running"); + } + return START_NOT_STICKY; + } + + public boolean getLoadingState() { + return running.get(); + } + + public void loadBackupFiles(OnBackupFilesLoaded onBackupFilesLoaded) { + executor.execute(() -> { + List<Jid> accounts = mDatabaseBackend.getAccountJids(false); + final ArrayList<BackupFile> backupFiles = new ArrayList<>(); + final Set<String> apps = new HashSet<>(Arrays.asList(getString(R.string.app_name))); + for (String app : apps) { + final File directory = new File(FileBackend.getBackupDirectory()); + if (!directory.exists() || !directory.isDirectory()) { + Log.d(Config.LOGTAG, "directory not found: " + directory.getAbsolutePath()); + continue; + } + for (File file : directory.listFiles()) { + if (file.isFile() && file.getName().endsWith(".ceb")) { + try { + final BackupFile backupFile = BackupFile.read(file); + if (accounts.contains(backupFile.getHeader().getJid())) { + Log.d(Config.LOGTAG, "skipping backup for " + backupFile.getHeader().getJid()); + } else { + backupFiles.add(backupFile); + } + } catch (IOException e) { + Log.d(Config.LOGTAG, "unable to read backup file ", e); + } + } + } + } + Collections.sort(backupFiles, (a, b) -> a.header.getJid().toString().compareTo(b.header.getJid().toString())); + onBackupFilesLoaded.onBackupFilesLoaded(backupFiles); + }); + } + + private void startForegroundService() { + NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup"); + mBuilder.setContentTitle(getString(R.string.restoring_backup)) + .setSmallIcon(R.drawable.ic_unarchive_white_24dp) + .setProgress(1, 0, true); + startForeground(NOTIFICATION_ID, mBuilder.build()); + } + + private boolean importBackup(File file, String password) { + Log.d(Config.LOGTAG, "importing backup from file " + file.getAbsolutePath()); + try { + SQLiteDatabase db = mDatabaseBackend.getWritableDatabase(); + final FileInputStream fileInputStream = new FileInputStream(file); + final DataInputStream dataInputStream = new DataInputStream(fileInputStream); + BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream); + Log.d(Config.LOGTAG, backupFileHeader.toString()); + + final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER); + byte[] key = ExportBackupService.getKey(password, backupFileHeader.getSalt()); + SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE); + IvParameterSpec ivSpec = new IvParameterSpec(backupFileHeader.getIv()); + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + CipherInputStream cipherInputStream = new CipherInputStream(fileInputStream, cipher); + + GZIPInputStream gzipInputStream = new GZIPInputStream(cipherInputStream); + BufferedReader reader = new BufferedReader(new InputStreamReader(gzipInputStream, "UTF-8")); + String line; + StringBuilder multiLineQuery = null; + while ((line = reader.readLine()) != null) { + int count = count(line, '\''); + if (multiLineQuery != null) { + multiLineQuery.append('\n'); + multiLineQuery.append(line); + if (count % 2 == 1) { + db.execSQL(multiLineQuery.toString()); + multiLineQuery = null; + } + } else { + if (count % 2 == 0) { + db.execSQL(line); + } else { + multiLineQuery = new StringBuilder(line); + } + } + } + final Jid jid = backupFileHeader.getJid(); + Cursor countCursor = db.rawQuery("select count(messages.uuid) from messages join conversations on conversations.uuid=messages.conversationUuid join accounts on conversations.accountUuid=accounts.uuid where accounts.username=? and accounts.server=?", new String[]{jid.getEscapedLocal(), jid.getDomain()}); + countCursor.moveToFirst(); + int count = countCursor.getInt(0); + Log.d(Config.LOGTAG, "restored " + count + " messages"); + countCursor.close(); + stopBackgroundService(); + synchronized (mOnBackupProcessedListeners) { + for (OnBackupProcessed l : mOnBackupProcessedListeners) { + l.onBackupRestored(); + } + } + return true; + } catch (Exception e) { + Throwable throwable = e.getCause(); + final boolean reasonWasCrypto; + if (throwable instanceof BadPaddingException) { + reasonWasCrypto = true; + } else { + reasonWasCrypto = false; + } + synchronized (mOnBackupProcessedListeners) { + for (OnBackupProcessed l : mOnBackupProcessedListeners) { + if (reasonWasCrypto) { + l.onBackupDecryptionFailed(); + } else { + l.onBackupRestoreFailed(); + } + } + } + Log.d(Config.LOGTAG, "error restoring backup " + file.getAbsolutePath(), e); + return false; + } + } + + private void notifySuccess() { + NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup"); + mBuilder.setContentTitle(getString(R.string.notification_restored_backup_title)) + .setContentText(getString(R.string.notification_restored_backup_subtitle)) + .setAutoCancel(true) + .setContentIntent(PendingIntent.getActivity(this, 145, new Intent(this, ManageAccountActivity.class), PendingIntent.FLAG_UPDATE_CURRENT)) + .setSmallIcon(R.drawable.ic_unarchive_white_24dp); + notificationManager.notify(NOTIFICATION_ID, mBuilder.build()); + } + + private void stopBackgroundService() { + Intent intent = new Intent(this, XmppConnectionService.class); + stopService(intent); + } + + public void removeOnBackupProcessedListener(OnBackupProcessed listener) { + synchronized (mOnBackupProcessedListeners) { + mOnBackupProcessedListeners.remove(listener); + } + } + + public void addOnBackupProcessedListener(OnBackupProcessed listener) { + synchronized (mOnBackupProcessedListeners) { + mOnBackupProcessedListeners.add(listener); + } + } + + @Override + public IBinder onBind(Intent intent) { + return this.binder; + } + + public interface OnBackupFilesLoaded { + void onBackupFilesLoaded(List<BackupFile> files); + } + + public interface OnBackupProcessed { + void onBackupRestored(); + + void onBackupDecryptionFailed(); + + void onBackupRestoreFailed(); + } + + public static class BackupFile { + private final File file; + private final BackupFileHeader header; + + private BackupFile(File file, BackupFileHeader header) { + this.file = file; + this.header = header; + } + + private static BackupFile read(File file) throws IOException { + final FileInputStream fileInputStream = new FileInputStream(file); + final DataInputStream dataInputStream = new DataInputStream(fileInputStream); + BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream); + fileInputStream.close(); + return new BackupFile(file, backupFileHeader); + } + + public BackupFileHeader getHeader() { + return header; + } + + public File getFile() { + return file; + } + } + + public class ImportBackupServiceBinder extends Binder { + public ImportBackupService getService() { + return ImportBackupService.this; + } + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/services/NotificationService.java b/src/main/java/de/pixart/messenger/services/NotificationService.java index 83441cce1..d045879be 100644 --- a/src/main/java/de/pixart/messenger/services/NotificationService.java +++ b/src/main/java/de/pixart/messenger/services/NotificationService.java @@ -57,14 +57,12 @@ import de.pixart.messenger.entities.Message; import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.ui.ConversationsActivity; import de.pixart.messenger.ui.EditAccountActivity; -import de.pixart.messenger.ui.ManageAccountActivity; import de.pixart.messenger.ui.TimePreference; import de.pixart.messenger.utils.AccountUtils; import de.pixart.messenger.utils.Compatibility; import de.pixart.messenger.utils.GeoHelper; import de.pixart.messenger.utils.UIHelper; import de.pixart.messenger.xmpp.XmppConnection; -import rocks.xmpp.addr.Jid; public class NotificationService { @@ -383,7 +381,13 @@ public class NotificationService { mBuilder.setColor(ContextCompat.getColor(mXmppConnectionService, R.color.primary)); } - public void updateNotification(final boolean notify) { + public void updateNotification() { + synchronized (notifications) { + updateNotification(false); + } + } + + private void updateNotification(final boolean notify) { updateNotification(notify, null, false); } @@ -523,15 +527,15 @@ public class NotificationService { if (messages.size() >= 1) { final Conversation conversation = (Conversation) messages.get(0).getConversation(); final UnreadConversation.Builder mUnreadBuilder = new UnreadConversation.Builder(conversation.getName().toString()); - mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService() - .get(conversation, getPixel(64))); + mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService().get(conversation, AvatarService.getSystemUiAvatarSize(mXmppConnectionService))); mBuilder.setContentTitle(conversation.getName()); if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) { int count = messages.size(); mBuilder.setContentText(mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages, count, count)); } else { Message message; - if ((message = getImage(messages)) != null) { + //TODO starting with Android 9 we might want to put images in MessageStyle + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P && (message = getImage(messages)) != null) { modifyForImage(mBuilder, mUnreadBuilder, message, messages); } else { modifyForTextOnly(mBuilder, mUnreadBuilder, messages); @@ -615,8 +619,7 @@ public class NotificationService { private void modifyForImage(final Builder builder, final UnreadConversation.Builder uBuilder, final Message message, final ArrayList<Message> messages) { try { - final Bitmap bitmap = mXmppConnectionService.getFileBackend() - .getThumbnail(message, getPixel(288), false); + final Bitmap bitmap = mXmppConnectionService.getFileBackend().getThumbnail(message, getPixel(288), false); final ArrayList<Message> tmp = new ArrayList<>(); for (final Message msg : messages) { if (msg.getType() == Message.TYPE_TEXT @@ -651,44 +654,39 @@ public class NotificationService { } else { builder.setName(UIHelper.getMessageDisplayName(message)); } - IconCompat icon = getIcon(message); - if (icon != null) { - builder.setIcon(icon); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + builder.setIcon(IconCompat.createWithBitmap(mXmppConnectionService.getAvatarService().get(message, AvatarService.getSystemUiAvatarSize(mXmppConnectionService), false))); } return builder.build(); } - private IconCompat getIcon(Message message) { - final Contact contact; - if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { - contact = message.getContact(); - } else { - Jid jid = message.getTrueCounterpart(); - contact = jid == null ? null : message.getConversation().getAccount().getRoster().getContact(jid); - } - if (contact != null) { - if (contact.getProfilePhoto() != null && QuickConversationsService.isConversations()) { - return IconCompat.createWithContentUri(contact.getProfilePhoto()); - } - if (contact.getAvatarFilename() != null) { - return IconCompat.createWithContentUri(mXmppConnectionService.getFileBackend().getAvatarUri(contact.getAvatarFilename())); - } - } - return null; - } - private void modifyForTextOnly(final Builder builder, final UnreadConversation.Builder uBuilder, final ArrayList<Message> messages) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - final Person me = new Person.Builder().setName(mXmppConnectionService.getString(R.string.me)).build(); - NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(me); final Conversation conversation = (Conversation) messages.get(0).getConversation(); - if (conversation.getMode() == Conversation.MODE_MULTI) { + final Person.Builder meBuilder = new Person.Builder().setName(mXmppConnectionService.getString(R.string.me)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + meBuilder.setIcon(IconCompat.createWithBitmap(mXmppConnectionService.getAvatarService().get(conversation.getAccount(), AvatarService.getSystemUiAvatarSize(mXmppConnectionService)))); + } + final Person me = meBuilder.build(); + NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(me); + final boolean multiple = conversation.getMode() == Conversation.MODE_MULTI; + if (multiple) { messagingStyle.setConversationTitle(conversation.getName()); } for (Message message : messages) { final Person sender = message.getStatus() == Message.STATUS_RECEIVED ? getPerson(message) : null; - messagingStyle.addMessage(UIHelper.getMessagePreview(mXmppConnectionService, message).first, message.getTimeSent(), sender); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isImageMessage(message)) { + final Uri dataUri = FileBackend.getMediaUri(mXmppConnectionService, mXmppConnectionService.getFileBackend().getFile(message)); + NotificationCompat.MessagingStyle.Message imageMessage = new NotificationCompat.MessagingStyle.Message(UIHelper.getMessagePreview(mXmppConnectionService, message).first, message.getTimeSent(), sender); + if (dataUri != null) { + imageMessage.setData(message.getMimeType(), dataUri); + } + messagingStyle.addMessage(imageMessage); + } else { + messagingStyle.addMessage(UIHelper.getMessagePreview(mXmppConnectionService, message).first, message.getTimeSent(), sender); + } } + messagingStyle.setGroupConversation(multiple); builder.setStyle(messagingStyle); } else { if (messages.get(0).getConversation().getMode() == Conversation.MODE_SINGLE) { @@ -733,16 +731,21 @@ public class NotificationService { if (message.getStatus() != Message.STATUS_RECEIVED) { return null; } - if (message.getType() != Message.TYPE_TEXT - && message.getTransferable() == null - && message.getEncryption() != Message.ENCRYPTION_PGP - && message.getFileParams().height > 0) { + if (isImageMessage(message)) { image = message; } } return image; } + private static boolean isImageMessage(Message message) { + return message.getType() != Message.TYPE_TEXT + && message.getTransferable() == null + && !message.isFileDeleted() + && message.getEncryption() != Message.ENCRYPTION_PGP + && message.getFileParams().height > 0; + } + private Message getFirstDownloadableMessage(final Iterable<Message> messages) { for (final Message message : messages) { if (message.getTransferable() != null || (message.getType() == Message.TYPE_TEXT && message.treatAsDownloadable())) { @@ -773,7 +776,7 @@ public class NotificationService { } private PendingIntent createShowLocationIntent(final Message message) { - Iterable<Intent> intents = GeoHelper.createGeoIntentsFromMessage(message, mXmppConnectionService); + Iterable<Intent> intents = GeoHelper.createGeoIntentsFromMessage(mXmppConnectionService, message); for (Intent intent : intents) { if (intent.resolveActivity(mXmppConnectionService.getPackageManager()) != null) { return PendingIntent.getActivity(mXmppConnectionService, generateRequestCode(message.getConversation(), 18), intent, PendingIntent.FLAG_UPDATE_CURRENT); @@ -905,57 +908,52 @@ public class NotificationService { Notification createForegroundNotification() { final Notification.Builder mBuilder = new Notification.Builder(mXmppConnectionService); mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service)); - if (Compatibility.runsAndTargetsTwentySix(mXmppConnectionService) || Config.SHOW_CONNECTED_ACCOUNTS) { - List<Account> accounts = mXmppConnectionService.getAccounts(); - int enabled = 0; - int connected = 0; - String status; - Account mAccount = null; - if (accounts != null) { - for (Account account : accounts) { - if (account.isOnlineAndConnected()) { - connected++; - enabled++; - } else if (account.isEnabled()) { - enabled++; - } - } - if (accounts.size() == 1) { - mAccount = accounts.get(0); - if (mAccount.getStatus() == Account.State.ONLINE) { - status = "(" + mXmppConnectionService.getString(R.string.account_status_online) + ")"; - status = " " + status; - Log.d(Config.LOGTAG, "Status: " + status); - mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); - } else if (mAccount.getStatus() == Account.State.CONNECTING) { - status = "(" + mXmppConnectionService.getString(R.string.account_status_connecting) + ")"; - status = " " + status; - Log.d(Config.LOGTAG, "Status: " + status); - mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); - } else { - status = "(" + mXmppConnectionService.getString(R.string.account_status_offline) + ")"; - status = " " + status; - Log.d(Config.LOGTAG, "Status: " + status); - mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); - } - } else if (accounts.size() > 1) { - mBuilder.setContentText(mXmppConnectionService.getString(R.string.connected_accounts, connected, enabled)); - } else { - status = "(" + mXmppConnectionService.getString(R.string.account_status_offline) + ")"; - status = " " + status; - Log.d(Config.LOGTAG, "Status: " + status); - mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); + Account mAccount = null; + String status; + final List<Account> accounts = mXmppConnectionService.getAccounts(); + int enabled = 0; + int connected = 0; + if (accounts != null) { + for (Account account : accounts) { + if (account.isOnlineAndConnected()) { + connected++; + enabled++; + } else if (account.isEnabled()) { + enabled++; } + } + } + if (accounts.size() == 1) { + mAccount = accounts.get(0); + if (mAccount.getStatus() == Account.State.ONLINE) { + status = "(" + mXmppConnectionService.getString(R.string.account_status_online) + ")"; + status = " " + status; + Log.d(Config.LOGTAG, "Status: " + status); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); + } else if (mAccount.getStatus() == Account.State.CONNECTING) { + status = "(" + mXmppConnectionService.getString(R.string.account_status_connecting) + ")"; + status = " " + status; + Log.d(Config.LOGTAG, "Status: " + status); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); } else { - mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_open_conversations)); + status = "(" + mXmppConnectionService.getString(R.string.account_status_offline) + ")"; + status = " " + status; + Log.d(Config.LOGTAG, "Status: " + status); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); } + } else if (accounts.size() > 1) { + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service)); } else { - mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_open_conversations)); + status = "(" + mXmppConnectionService.getString(R.string.account_status_offline) + ")"; + status = " " + status; + Log.d(Config.LOGTAG, "Status: " + status); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); } + mBuilder.setContentText(mXmppConnectionService.getString(R.string.connected_accounts, connected, enabled)); mBuilder.setContentIntent(createOpenConversationsIntent()); mBuilder.setWhen(0); mBuilder.setPriority(Notification.PRIORITY_MIN); - mBuilder.setSmallIcon(R.drawable.ic_link_white_24dp); + mBuilder.setSmallIcon(connected > 0 ? R.drawable.ic_link_white_24dp : R.drawable.ic_link_off_white_24dp); if (Compatibility.runsTwentySix()) { mBuilder.setChannelId(FOREGROUND_CHANNEL_ID); } @@ -1042,7 +1040,6 @@ public class NotificationService { Notification AppUpdateNotification(PendingIntent intent, String version, String filesize) { Notification.Builder mBuilder = new Notification.Builder(mXmppConnectionService); mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.app_name)); - mBuilder.setContentText(mXmppConnectionService.getString(R.string.notification_export_logs_title)); mBuilder.setContentText(String.format(mXmppConnectionService.getString(R.string.update_available), version, filesize)); mBuilder.setSmallIcon(R.drawable.ic_update_notification); mBuilder.setContentIntent(intent); diff --git a/src/main/java/de/pixart/messenger/services/ShortcutService.java b/src/main/java/de/pixart/messenger/services/ShortcutService.java index fa4853665..94bbc15cd 100644 --- a/src/main/java/de/pixart/messenger/services/ShortcutService.java +++ b/src/main/java/de/pixart/messenger/services/ShortcutService.java @@ -123,6 +123,7 @@ public class ShortcutService { intent.setAction(Intent.ACTION_VIEW); intent.setData(Uri.parse("xmpp:" + contact.getJid().asBareJid().toString())); intent.putExtra("account", contact.getAccount().getJid().asBareJid().toString()); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); return intent; } diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java index 98e330ad0..551ff7818 100644 --- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java +++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java @@ -8,6 +8,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -52,6 +53,7 @@ import org.openintents.openpgp.IOpenPgpService2; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; +import java.io.File; import java.net.URL; import java.security.SecureRandom; import java.security.Security; @@ -94,7 +96,6 @@ import de.pixart.messenger.entities.Bookmark; import de.pixart.messenger.entities.Contact; import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.entities.Conversational; -import de.pixart.messenger.entities.DownloadableFile; import de.pixart.messenger.entities.Message; import de.pixart.messenger.entities.MucOptions; import de.pixart.messenger.entities.MucOptions.OnRenameListener; @@ -102,8 +103,6 @@ import de.pixart.messenger.entities.Presence; import de.pixart.messenger.entities.PresenceTemplate; import de.pixart.messenger.entities.Roster; import de.pixart.messenger.entities.ServiceDiscoveryResult; -import de.pixart.messenger.entities.Transferable; -import de.pixart.messenger.entities.TransferablePlaceholder; import de.pixart.messenger.generator.AbstractGenerator; import de.pixart.messenger.generator.IqGenerator; import de.pixart.messenger.generator.MessageGenerator; @@ -116,6 +115,7 @@ import de.pixart.messenger.parser.MessageParser; import de.pixart.messenger.parser.PresenceParser; import de.pixart.messenger.persistance.DatabaseBackend; import de.pixart.messenger.persistance.FileBackend; +import de.pixart.messenger.ui.ChooseAccountForProfilePictureActivity; import de.pixart.messenger.ui.SettingsActivity; import de.pixart.messenger.ui.UiCallback; import de.pixart.messenger.ui.interfaces.OnAvatarPublication; @@ -174,6 +174,7 @@ public class XmppConnectionService extends Service { public static final String ACTION_IDLE_PING = "idle_ping"; public static final String ACTION_FCM_TOKEN_REFRESH = "fcm_token_refresh"; public static final String ACTION_FCM_MESSAGE_RECEIVED = "fcm_message_received"; + private static final String ACTION_POST_CONNECTIVITY_CHANGE = "de.pixart.messenger.POST_CONNECTIVITY_CHANGE"; public static final String FDroid = "org.fdroid.fdroid"; public static final String PlayStore = "com.android.vending"; private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp"; @@ -192,7 +193,8 @@ public class XmppConnectionService extends Service { private final IBinder mBinder = new XmppConnectionBinder(); private final List<Conversation> conversations = new CopyOnWriteArrayList<>(); private final IqGenerator mIqGenerator = new IqGenerator(this); - private final List<String> mInProgressAvatarFetches = new ArrayList<>(); + private final Set<String> mInProgressAvatarFetches = new HashSet<>(); + private final Set<String> mOmittedPepAvatarFetches = new HashSet<>(); private final HashSet<Jid> mLowPingTimeoutMode = new HashSet<>(); private final OnIqPacketReceived mDefaultIqHandler = (account, packet) -> { if (packet.getType() != IqPacket.TYPE.RESULT) { @@ -238,7 +240,6 @@ public class XmppConnectionService extends Service { ) { @Override public void onEvent(int event, String path) { - Log.d(Config.LOGTAG, "event " + event + " path=" + path); markFileDeleted(path); } }; @@ -336,6 +337,7 @@ public class XmppConnectionService extends Service { syncDirtyContacts(account); } }; + private boolean destroyed = false; private int unreadCount = -1; private AtomicLong mLastExpiryRun = new AtomicLong(0); private SecureRandom mRandom; @@ -375,7 +377,6 @@ public class XmppConnectionService extends Service { if (!conversation.startOtrIfNeeded()) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": couldn't start OTR with " + conversation.getContact().getJid() + " when needed"); } - checkDeletedFiles(conversation); sendUnsentMessages(conversation); resendFailedFileMessages(conversation); } @@ -582,7 +583,7 @@ public class XmppConnectionService extends Service { final String action = intent == null ? null : intent.getAction(); final boolean needsForegroundService = intent != null && intent.getBooleanExtra(EventReceiver.EXTRA_NEEDS_FOREGROUND_SERVICE, false); if (needsForegroundService) { - Log.d(Config.LOGTAG, "toggle forced foreground service after receiving event"); + Log.d(Config.LOGTAG, "toggle forced foreground service after receiving event (action=" + action + ")"); toggleForegroundService(true); } String pushedAccountHash = null; @@ -591,8 +592,13 @@ public class XmppConnectionService extends Service { final String uuid = intent.getStringExtra("uuid"); switch (action) { case ConnectivityManager.CONNECTIVITY_ACTION: - if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) { - resetAllAttemptCounts(true, false); + if (hasInternetConnection()) { + if (Config.POST_CONNECTIVITY_CHANGE_PING_INTERVAL > 0) { + schedulePostConnectivityChange(); + } + if (Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) { + resetAllAttemptCounts(true, false); + } } break; case Intent.ACTION_SHUTDOWN: @@ -705,7 +711,7 @@ public class XmppConnectionService extends Service { } synchronized (this) { WakeLockHelper.acquire(wakeLock); - boolean pingNow = ConnectivityManager.CONNECTIVITY_ACTION.equals(action); + boolean pingNow = ConnectivityManager.CONNECTIVITY_ACTION.equals(action) || (Config.POST_CONNECTIVITY_CHANGE_PING_INTERVAL > 0 && ACTION_POST_CONNECTIVITY_CHANGE.equals(action)); HashSet<Account> pingCandidates = new HashSet<>(); for (Account account : accounts) { pingNow |= processAccountState(account, @@ -1117,6 +1123,7 @@ public class XmppConnectionService extends Service { @SuppressLint("TrulyRandom") @Override public void onCreate() { + this.destroyed = false; OmemoSetting.load(this); ExceptionHelper.init(getApplicationContext()); try { @@ -1152,8 +1159,10 @@ public class XmppConnectionService extends Service { editor.putBoolean(SettingsActivity.SHOW_FOREGROUND_SERVICE, true); Log.d(Config.LOGTAG, Build.MANUFACTURER + " is on blacklist. enabling foreground service"); } - editor.putBoolean(EventReceiver.SETTING_ENABLED_ACCOUNTS, hasEnabledAccounts()).apply(); + final boolean hasEnabledAccounts = hasEnabledAccounts(); + editor.putBoolean(EventReceiver.SETTING_ENABLED_ACCOUNTS, hasEnabledAccounts).apply(); editor.apply(); + toggleSetProfilePictureActivity(hasEnabledAccounts); restoreFromDatabase(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { @@ -1161,7 +1170,8 @@ public class XmppConnectionService extends Service { } if (Compatibility.hasStoragePermission(this)) { Log.d(Config.LOGTAG, "starting file observer"); - new Thread(fileObserver::startWatching).start(); + mFileAddingExecutor.execute(this.fileObserver::startWatching); + mFileAddingExecutor.execute(this::checkForDeletedFiles); } if (Config.supportOpenPgp()) { this.pgpServiceConnection = new OpenPgpServiceConnection(this, "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() { @@ -1202,6 +1212,32 @@ public class XmppConnectionService extends Service { CancelAutomaticExport(false); } + private void checkForDeletedFiles() { + if (destroyed) { + Log.d(Config.LOGTAG, "Do not check for deleted files because service has been destroyed"); + return; + } + final long start = SystemClock.elapsedRealtime(); + final List<DatabaseBackend.FilePathInfo> relativeFilePaths = databaseBackend.getFilePathInfo(); + final List<DatabaseBackend.FilePathInfo> changed = new ArrayList<>(); + for (final DatabaseBackend.FilePathInfo filePath : relativeFilePaths) { + if (destroyed) { + Log.d(Config.LOGTAG, "Stop checking for deleted files because service has been destroyed"); + return; + } + final File file = fileBackend.getFileForPath(filePath.path); + if (filePath.setFileDeleted(!file.exists())) { + changed.add(filePath); + } + } + final long duration = SystemClock.elapsedRealtime() - start; + Log.d(Config.LOGTAG, "found " + changed.size() + " changed files on start up. total=" + relativeFilePaths.size() + ". (" + duration + "ms)"); + if (changed.size() > 0) { + databaseBackend.markFilesAsChanged(changed); + markChangedFiles(changed); + } + } + public void startContactObserver() { getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, new ContentObserver(null) { @Override @@ -1230,6 +1266,7 @@ public class XmppConnectionService extends Service { } catch (IllegalArgumentException e) { //ignored } + destroyed = false; fileObserver.stopWatching(); super.onDestroy(); // cancel scheduled exporter @@ -1238,7 +1275,8 @@ public class XmppConnectionService extends Service { public void restartFileObserver() { Log.d(Config.LOGTAG, "restarting file observer"); - new Thread(fileObserver::restartWatching).start(); + mFileAddingExecutor.execute(this.fileObserver::restartWatching); + mFileAddingExecutor.execute(this::checkForDeletedFiles); } public void toggleScreenEventReceiver() { @@ -1289,13 +1327,15 @@ public class XmppConnectionService extends Service { private void logoutAndSave(boolean stop) { int activeAccounts = 0; - for (final Account account : accounts) { - if (account.getStatus() != Account.State.DISABLED) { - databaseBackend.writeRoster(account.getRoster()); - activeAccounts++; - } - if (account.getXmppConnection() != null) { - new Thread(() -> disconnect(account, false)).start(); + if (accounts != null) { + for (final Account account : accounts) { + if (account.getStatus() != Account.State.DISABLED) { + databaseBackend.writeRoster(account.getRoster()); + activeAccounts++; + } + if (account.getXmppConnection() != null) { + new Thread(() -> disconnect(account, false)).start(); + } } } if (stop || activeAccounts == 0) { @@ -1304,6 +1344,26 @@ public class XmppConnectionService extends Service { } } + private void schedulePostConnectivityChange() { + final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + if (alarmManager == null) { + return; + } + final long triggerAtMillis = SystemClock.elapsedRealtime() + (Config.POST_CONNECTIVITY_CHANGE_PING_INTERVAL * 1000); + final Intent intent = new Intent(this, EventReceiver.class); + intent.setAction(ACTION_POST_CONNECTIVITY_CHANGE); + try { + final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, intent, 0); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, pendingIntent); + } else { + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, pendingIntent); + } + } catch (RuntimeException e) { + Log.e(Config.LOGTAG, "unable to schedule alarm for post connectivity change", e); + } + } + public void scheduleWakeUpCall(int seconds, int requestCode) { final long timeToWake = SystemClock.elapsedRealtime() + (seconds < 0 ? 1 : seconds + 1) * 1000; final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); @@ -1401,15 +1461,16 @@ public class XmppConnectionService extends Service { boolean saveInDb = addToConversation; message.setStatus(Message.STATUS_WAITING); + if (message.getEncryption() != Message.ENCRYPTION_NONE && conversation.getMode() == Conversation.MODE_MULTI && conversation.isPrivateAndNonAnonymous()) { + if (conversation.setAttribute(Conversation.ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS, true)) { + databaseBackend.updateConversation(conversation); + } + } + if (!resend && message.getEncryption() != Message.ENCRYPTION_OTR) { conversation.endOtrIfNeeded(); conversation.findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR, - new Conversation.OnMessageFound() { - @Override - public void onMessageFound(Message message) { - markMessage(message, Message.STATUS_SEND_FAILED); - } - }); + message1 -> markMessage(message1, Message.STATUS_SEND_FAILED)); } if (account.isOnlineAndConnected()) { @@ -1776,7 +1837,6 @@ public class XmppConnectionService extends Service { private void restoreMessages(Conversation conversation) { conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE)); - checkDeletedFiles(conversation); conversation.findUnsentTextMessages(message -> markMessage(message, Message.STATUS_WAITING)); conversation.findUnreadMessages(message -> mNotificationService.pushFromBacklog(message)); } @@ -1817,45 +1877,52 @@ public class XmppConnectionService extends Service { return this.conversations; } - private void checkDeletedFiles(Conversation conversation) { - conversation.findMessagesWithFiles(message -> { - if (!getFileBackend().isFileAvailable(message)) { - message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); - final int s = message.getStatus(); - if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) { - markMessage(message, Message.STATUS_SEND_FAILED); - } - } - }); + private void markFileDeleted(final String path) { + final File file = new File(path); + final boolean isInternalFile = fileBackend.isInternalFile(file); + final List<String> uuids = databaseBackend.markFileAsDeleted(file, isInternalFile); + Log.d(Config.LOGTAG, "deleted file " + path + " internal=" + isInternalFile + ", database hits=" + uuids.size()); + markUuidsAsDeletedFiles(uuids); } - private void markFileDeleted(final String path) { - Log.d(Config.LOGTAG, "deleted file " + path); + private void markUuidsAsDeletedFiles(List<String> uuids) { + boolean deleted = false; for (Conversation conversation : getConversations()) { - conversation.findMessagesWithFiles(message -> { - DownloadableFile file = fileBackend.getFile(message); - if (file.getAbsolutePath().equals(path)) { - if (!file.exists()) { - message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); - final int s = message.getStatus(); - if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) { - markMessage(message, Message.STATUS_SEND_FAILED); - } else { - updateConversationUi(); - } - } else { - Log.d(Config.LOGTAG, "found matching message for file " + path + " but file still exists"); - } - } - }); + deleted |= conversation.markAsDeleted(uuids); + } + if (deleted) { + updateConversationUi(); + } + } + + private void markChangedFiles(List<DatabaseBackend.FilePathInfo> infos) { + boolean changed = false; + for (Conversation conversation : getConversations()) { + changed |= conversation.markAsChanged(infos); + } + if (changed) { + updateConversationUi(); } } public void populateWithOrderedConversations(final List<Conversation> list) { - populateWithOrderedConversations(list, true); + populateWithOrderedConversations(list, true, true); + } + + public void populateWithOrderedConversations(final List<Conversation> list, final boolean includeNoFileUpload) { + populateWithOrderedConversations(list, includeNoFileUpload, true); } - public void populateWithOrderedConversations(final List<Conversation> list, boolean includeNoFileUpload) { + public void populateWithOrderedConversations(final List<Conversation> list, final boolean includeNoFileUpload, final boolean sort) { + final List<String> orderedUuids; + if (sort) { + orderedUuids = null; + } else { + orderedUuids = new ArrayList<>(); + for (Conversation conversation : list) { + orderedUuids.add(conversation.getUuid()); + } + } list.clear(); if (includeNoFileUpload) { list.addAll(getConversations()); @@ -1868,7 +1935,18 @@ public class XmppConnectionService extends Service { } } try { - Collections.sort(list); + if (orderedUuids != null) { + Collections.sort(list, (a, b) -> { + final int indexA = orderedUuids.indexOf(a.getUuid()); + final int indexB = orderedUuids.indexOf(b.getUuid()); + if (indexA == -1 || indexB == -1 || indexA == indexB) { + return a.compareTo(b); + } + return indexA - indexB; + }); + } else { + Collections.sort(list); + } } catch (IllegalArgumentException e) { //ignore } @@ -1886,7 +1964,6 @@ public class XmppConnectionService extends Service { List<Message> messages = databaseBackend.getMessages(conversation, 50, timestamp); if (messages.size() > 0) { conversation.addAll(0, messages); - checkDeletedFiles(conversation); callback.onMoreMessagesLoaded(messages.size(), conversation); } else if (conversation.hasMessagesLeftOnServer() && account.isOnlineAndConnected() @@ -2033,7 +2110,6 @@ public class XmppConnectionService extends Service { } } } - checkDeletedFiles(c); if (joinAfterCreate) { joinMuc(c); @@ -2097,7 +2173,6 @@ public class XmppConnectionService extends Service { updateConversationUi(); c.messagesLoaded.set(true); } - checkDeletedFiles(c); } }); updateConversationUi(); @@ -2153,7 +2228,19 @@ public class XmppConnectionService extends Service { } private void syncEnabledAccountSetting() { - getPreferences().edit().putBoolean(EventReceiver.SETTING_ENABLED_ACCOUNTS, hasEnabledAccounts()).apply(); + final boolean hasEnabledAccounts = hasEnabledAccounts(); + getPreferences().edit().putBoolean(EventReceiver.SETTING_ENABLED_ACCOUNTS, hasEnabledAccounts).apply(); + toggleSetProfilePictureActivity(hasEnabledAccounts); + } + + private void toggleSetProfilePictureActivity(final boolean enabled) { + try { + final ComponentName name = new ComponentName(this, ChooseAccountForProfilePictureActivity.class); + final int targetState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; + getPackageManager().setComponentEnabledSetting(name, targetState, PackageManager.DONT_KILL_APP); + } catch (IllegalStateException e) { + Log.d(Config.LOGTAG, "unable to toggle profile picture actvitiy"); + } } public void createAccountFromKey(final String alias, final OnAccountCreated callback) { @@ -2584,6 +2671,9 @@ public class XmppConnectionService extends Service { if (mucOptions.nonanonymous() && !mucOptions.membersOnly() && !conversation.getBooleanAttribute("accept_non_anonymous", false)) { mucOptions.setError(MucOptions.Error.NON_ANONYMOUS); updateConversationUi(); + if (onConferenceJoined != null) { + onConferenceJoined.onConferenceJoined(conversation); + } return; } final Jid joinJid = mucOptions.getSelf().getFullJid(); @@ -2867,6 +2957,31 @@ public class XmppConnectionService extends Service { return null; } + public void createPublicChannel(final Account account, final String name, final Jid address, final UiCallback<Conversation> callback) { + joinMuc(findOrCreateConversation(account, address, true, false, true), conversation -> { + final Bundle configuration = IqGenerator.defaultChannelConfiguration(); + if (!TextUtils.isEmpty(name)) { + configuration.putString("muc#roomconfig_roomname", name); + } + pushConferenceConfiguration(conversation, configuration, new OnConfigurationPushed() { + @Override + public void onPushSucceeded() { + saveConversationAsBookmark(conversation, name); + callback.success(conversation); + } + + @Override + public void onPushFailed() { + if (conversation.getMucOptions().getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER)) { + callback.error(R.string.unable_to_set_channel_configuration, conversation); + } else { + callback.error(R.string.joined_an_existing_channel, conversation); + } + } + }); + }); + } + public boolean createAdhocConference(final Account account, final String name, final Iterable<Jid> jids, @@ -2886,7 +3001,7 @@ public class XmppConnectionService extends Service { joinMuc(conversation, new OnConferenceJoined() { @Override public void onConferenceJoined(final Conversation conversation) { - final Bundle configuration = IqGenerator.defaultRoomConfiguration(); + final Bundle configuration = IqGenerator.defaultGroupChatConfiguration(); if (!TextUtils.isEmpty(name)) { configuration.putString("muc#roomconfig_roomname", name); } @@ -3009,6 +3124,10 @@ public class XmppConnectionService extends Service { } public void pushConferenceConfiguration(final Conversation conversation, final Bundle options, final OnConfigurationPushed callback) { + if (options.getString("muc#roomconfig_whois", "moderators").equals("anyone")) { + conversation.setAttribute("accept_non_anonymous", true); + updateConversation(conversation); + } IqPacket request = new IqPacket(IqPacket.TYPE.GET); request.setTo(conversation.getJid().asBareJid()); request.query("http://jabber.org/protocol/muc#owner"); @@ -3018,6 +3137,7 @@ public class XmppConnectionService extends Service { if (packet.getType() == IqPacket.TYPE.RESULT) { Data data = Data.parse(packet.query().findChild("x", Namespace.DATA)); data.submit(options); + Log.d(Config.LOGTAG, data.toString()); IqPacket set = new IqPacket(IqPacket.TYPE.SET); set.setTo(conversation.getJid().asBareJid()); set.query("http://jabber.org/protocol/muc#owner").addChild(data); @@ -3075,18 +3195,12 @@ public class XmppConnectionService extends Service { sendIqPacket(conference.getAccount(), request, mDefaultIqHandler); } - public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role, final OnRoleChanged callback) { + public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role) { IqPacket request = this.mIqGenerator.changeRole(conference, nick, role.toString()); Log.d(Config.LOGTAG, request.toString()); - sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - Log.d(Config.LOGTAG, packet.toString()); - if (packet.getType() == IqPacket.TYPE.RESULT) { - callback.onRoleChangedSuccessful(nick); - } else { - callback.onRoleChangeFailed(nick, R.string.could_not_change_role); - } + sendIqPacket(conference.getAccount(), request, (account, packet) -> { + if (packet.getType() != IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + " unable to change role of " + nick); } }); } @@ -3386,6 +3500,7 @@ public class XmppConnectionService extends Service { if (account.setAvatar(avatar.getFilename())) { getAvatarService().clear(account); databaseBackend.updateAccount(account); + notifyAccountAvatarHasChanged(account); } Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": published avatar " + (avatar.size / 1024) + "KiB"); if (callback != null) { @@ -3465,7 +3580,7 @@ public class XmppConnectionService extends Service { public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { final String KEY = generateFetchKey(account, avatar); synchronized (this.mInProgressAvatarFetches) { - if (!this.mInProgressAvatarFetches.contains(KEY)) { + if (mInProgressAvatarFetches.add(KEY)) { switch (avatar.origin) { case PEP: this.mInProgressAvatarFetches.add(KEY); @@ -3476,6 +3591,10 @@ public class XmppConnectionService extends Service { fetchAvatarVcard(account, avatar, callback); break; } + } else if (avatar.origin == Avatar.Origin.PEP) { + mOmittedPepAvatarFetches.add(KEY); + } else { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": already fetching " + avatar.origin + " avatar for " + avatar.owner); } } } @@ -3533,54 +3652,54 @@ public class XmppConnectionService extends Service { private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) { IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar); - this.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - synchronized (mInProgressAvatarFetches) { - mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); - } - if (packet.getType() == IqPacket.TYPE.RESULT) { - Element vCard = packet.findChild("vCard", "vcard-temp"); - Element photo = vCard != null ? vCard.findChild("PHOTO") : null; - String image = photo != null ? photo.findChildContent("BINVAL") : null; - if (image != null) { - avatar.image = image; - if (getFileBackend().save(avatar)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() - + ": successfully fetched vCard avatar for " + avatar.owner); - if (avatar.owner.isBareJid()) { - if (account.getJid().asBareJid().equals(avatar.owner) && account.getAvatar() == null) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": had no avatar. replacing with vcard"); - account.setAvatar(avatar.getFilename()); - databaseBackend.updateAccount(account); - getAvatarService().clear(account); - updateAccountUi(); - } else { - Contact contact = account.getRoster().getContact(avatar.owner); - if (contact.setAvatar(avatar)) { - syncRoster(account); - getAvatarService().clear(contact); - updateRosterUi(); - } - } - updateConversationUi(); + this.sendIqPacket(account, packet, (account1, packet1) -> { + final boolean previouslyOmittedPepFetch; + synchronized (mInProgressAvatarFetches) { + final String KEY = generateFetchKey(account1, avatar); + mInProgressAvatarFetches.remove(KEY); + previouslyOmittedPepFetch = mOmittedPepAvatarFetches.remove(KEY); + } + if (packet1.getType() == IqPacket.TYPE.RESULT) { + Element vCard = packet1.findChild("vCard", "vcard-temp"); + Element photo = vCard != null ? vCard.findChild("PHOTO") : null; + String image = photo != null ? photo.findChildContent("BINVAL") : null; + if (image != null) { + avatar.image = image; + if (getFileBackend().save(avatar)) { + Log.d(Config.LOGTAG, account1.getJid().asBareJid() + + ": successfully fetched vCard avatar for " + avatar.owner + " omittedPep=" + previouslyOmittedPepFetch); + if (avatar.owner.isBareJid()) { + if (account1.getJid().asBareJid().equals(avatar.owner) && account1.getAvatar() == null) { + Log.d(Config.LOGTAG, account1.getJid().asBareJid() + ": had no avatar. replacing with vcard"); + account1.setAvatar(avatar.getFilename()); + databaseBackend.updateAccount(account1); + getAvatarService().clear(account1); + updateAccountUi(); } else { - Conversation conversation = find(account, avatar.owner.asBareJid()); - if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { - MucOptions.User user = conversation.getMucOptions().findUserByFullJid(avatar.owner); - if (user != null) { - if (user.setAvatar(avatar)) { - getAvatarService().clear(user); - updateConversationUi(); - updateMucRosterUi(); - } - if (user.getRealJid() != null) { - Contact contact = account.getRoster().getContact(user.getRealJid()); - if (contact.setAvatar(avatar)) { - syncRoster(account); - getAvatarService().clear(contact); - updateRosterUi(); - } + Contact contact = account1.getRoster().getContact(avatar.owner); + if (contact.setAvatar(avatar, previouslyOmittedPepFetch)) { + syncRoster(account1); + getAvatarService().clear(contact); + updateRosterUi(); + } + } + updateConversationUi(); + } else { + Conversation conversation = find(account1, avatar.owner.asBareJid()); + if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { + MucOptions.User user = conversation.getMucOptions().findUserByFullJid(avatar.owner); + if (user != null) { + if (user.setAvatar(avatar)) { + getAvatarService().clear(user); + updateConversationUi(); + updateMucRosterUi(); + } + if (user.getRealJid() != null) { + Contact contact = account1.getRoster().getContact(user.getRealJid()); + if (contact.setAvatar(avatar)) { + syncRoster(account1); + getAvatarService().clear(contact); + updateRosterUi(); } } } @@ -3625,6 +3744,23 @@ public class XmppConnectionService extends Service { }); } + public void notifyAccountAvatarHasChanged(final Account account) { + final XmppConnection connection = account.getXmppConnection(); + if (connection != null && connection.getFeatures().bookmarksConversion()) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": avatar changed. resending presence to online group chats"); + for (Conversation conversation : conversations) { + if (conversation.getAccount() == account && conversation.getMode() == Conversational.MODE_MULTI) { + final MucOptions mucOptions = conversation.getMucOptions(); + if (mucOptions.online()) { + PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, mucOptions.nonanonymous()); + packet.setTo(mucOptions.getSelf().getFullJid()); + connection.sendPresencePacket(packet); + } + } + } + } + } + public void deleteContactOnServer(Contact contact) { contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); contact.resetOption(Contact.Options.DIRTY_PUSH); @@ -4582,12 +4718,6 @@ public class XmppConnectionService extends Service { void onAffiliationChangeFailed(Jid jid, int resId); } - public interface OnRoleChanged { - void onRoleChangedSuccessful(String nick); - - void onRoleChangeFailed(String nick, int resid); - } - public interface OnRoomDestroy { void onRoomDestroySucceeded(int resId); diff --git a/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java b/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java index 7163f77cc..b642909b0 100644 --- a/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java +++ b/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java @@ -25,6 +25,7 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem BlockContactDialog.show(BlocklistActivity.this, (Contact) getListItems().get(position)); return true; }); + this.binding.fab.show(); this.binding.fab.setOnClickListener((v) -> showEnterJidDialog()); } @@ -38,7 +39,7 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem } filterContacts(); Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG); - if (fragment != null && fragment instanceof OnBackendConnected) { + if (fragment instanceof OnBackendConnected) { ((OnBackendConnected) fragment).onBackendConnected(); } } diff --git a/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java b/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java index e2021e7ea..df9113448 100644 --- a/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java @@ -5,7 +5,6 @@ import android.os.Bundle; import android.support.design.widget.TextInputLayout; import android.view.View; import android.widget.Button; -import android.widget.EditText; import android.widget.Toast; import de.pixart.messenger.R; diff --git a/src/main/java/de/pixart/messenger/ui/ChooseAccountForProfilePictureActivity.java b/src/main/java/de/pixart/messenger/ui/ChooseAccountForProfilePictureActivity.java new file mode 100644 index 000000000..2d109809e --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/ChooseAccountForProfilePictureActivity.java @@ -0,0 +1,82 @@ +package de.pixart.messenger.ui; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.widget.ListView; + +import java.util.ArrayList; +import java.util.List; + +import de.pixart.messenger.R; +import de.pixart.messenger.entities.Account; +import de.pixart.messenger.ui.adapter.AccountAdapter; + +public class ChooseAccountForProfilePictureActivity extends XmppActivity { + + protected final List<Account> accountList = new ArrayList<>(); + protected ListView accountListView; + protected AccountAdapter mAccountAdapter; + + @Override + protected void refreshUiReal() { + loadEnabledAccounts(); + mAccountAdapter.notifyDataSetChanged(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_manage_accounts); + setSupportActionBar(findViewById(R.id.toolbar)); + configureActionBar(getSupportActionBar(), false); + accountListView = findViewById(R.id.account_list); + this.mAccountAdapter = new AccountAdapter(this, accountList, false); + accountListView.setAdapter(this.mAccountAdapter); + accountListView.setOnItemClickListener((arg0, view, position, arg3) -> { + final Account account = accountList.get(position); + goToProfilePictureActivity(account); + }); + } + + @Override + protected void onStart() { + super.onStart(); + final int theme = findTheme(); + if (this.mTheme != theme) { + recreate(); + } + } + + @Override + void onBackendConnected() { + loadEnabledAccounts(); + if (accountList.size() == 1) { + goToProfilePictureActivity(accountList.get(0)); + return; + } + mAccountAdapter.notifyDataSetChanged(); + } + + private void loadEnabledAccounts() { + accountList.clear(); + for (Account account : xmppConnectionService.getAccounts()) { + if (account.isEnabled()) { + accountList.add(account); + } + } + } + + private void goToProfilePictureActivity(Account account) { + final Intent startIntent = getIntent(); + final Uri uri = startIntent == null ? null : startIntent.getData(); + if (uri != null) { + Intent intent = new Intent(this, PublishProfilePictureActivity.class); + intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toString()); + intent.setData(uri); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + startActivity(intent); + } + finish(); + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java index 7c9a5830b..646ab8027 100644 --- a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java @@ -3,14 +3,8 @@ package de.pixart.messenger.ui; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.content.IntentSender.SendIntentException; -import android.content.res.Resources; import android.databinding.DataBindingUtil; -import android.graphics.Bitmap; import android.graphics.PorterDuff; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; @@ -18,45 +12,34 @@ import android.text.Editable; import android.text.SpannableStringBuilder; import android.text.TextWatcher; import android.util.Log; -import android.view.ContextMenu; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; -import android.view.WindowManager; -import android.widget.CompoundButton; -import android.widget.ImageView; import android.widget.Toast; -import org.openintents.openpgp.util.OpenPgpUtils; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; import de.pixart.messenger.Config; import de.pixart.messenger.R; -import de.pixart.messenger.crypto.PgpEngine; import de.pixart.messenger.databinding.ActivityMucDetailsBinding; -import de.pixart.messenger.databinding.ContactBinding; import de.pixart.messenger.entities.Account; import de.pixart.messenger.entities.Bookmark; -import de.pixart.messenger.entities.Contact; import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.entities.MucOptions; import de.pixart.messenger.entities.MucOptions.User; -import de.pixart.messenger.services.EmojiService; import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.services.XmppConnectionService.OnConversationUpdate; import de.pixart.messenger.services.XmppConnectionService.OnMucRosterUpdate; import de.pixart.messenger.ui.adapter.MediaAdapter; +import de.pixart.messenger.ui.adapter.UserPreviewAdapter; import de.pixart.messenger.ui.interfaces.OnMediaLoaded; import de.pixart.messenger.ui.util.Attachment; +import de.pixart.messenger.ui.util.AvatarWorkerTask; import de.pixart.messenger.ui.util.GridManager; +import de.pixart.messenger.ui.util.MucConfiguration; import de.pixart.messenger.ui.util.MucDetailsContextMenuHelper; import de.pixart.messenger.ui.util.MyLinkify; import de.pixart.messenger.ui.util.SoftKeyboardUtils; @@ -66,26 +49,16 @@ import de.pixart.messenger.utils.MenuDoubleTabUtil; import de.pixart.messenger.utils.StringUtils; import de.pixart.messenger.utils.StylingHelper; import de.pixart.messenger.utils.TimeframeUtils; -import de.pixart.messenger.utils.UIHelper; import de.pixart.messenger.utils.XmppUri; import me.drakeet.support.toast.ToastCompat; import rocks.xmpp.addr.Jid; import static de.pixart.messenger.entities.Bookmark.printableValue; -import static de.pixart.messenger.ui.SettingsActivity.USE_BUNDLED_EMOJIS; import static de.pixart.messenger.utils.StringUtils.changed; -public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed, TextWatcher, OnMediaLoaded { +public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnConfigurationPushed, TextWatcher, OnMediaLoaded { public static final String ACTION_VIEW_MUC = "view_muc"; - private static final float INACTIVE_ALPHA = 0.4684f; //compromise between dark and light theme private Conversation mConversation; - private OnClickListener inviteListener = new OnClickListener() { - - @Override - public void onClick(View v) { - inviteToConversation(mConversation); - } - }; private OnClickListener destroyListener = new OnClickListener() { @Override public void onClick(View v) { @@ -108,8 +81,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers }; private ActivityMucDetailsBinding binding; private MediaAdapter mMediaAdapter; + private UserPreviewAdapter mUserPreviewAdapter; private String uuid = null; - private User mSelectedUser = null; private boolean mAdvancedMode = false; @@ -193,84 +166,22 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers public void onClick(View v) { final MucOptions mucOptions = mConversation.getMucOptions(); AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this); - builder.setTitle(R.string.conference_options); - final String[] options; - final boolean[] values; - if (mAdvancedMode) { - options = new String[]{ - getString(R.string.members_only), - getString(R.string.moderated), - getString(R.string.non_anonymous) - }; - values = new boolean[]{ - mucOptions.membersOnly(), - mucOptions.moderated(), - mucOptions.nonanonymous() - }; - } else { - options = new String[]{ - getString(R.string.members_only), - getString(R.string.non_anonymous) - }; - values = new boolean[]{ - mucOptions.membersOnly(), - mucOptions.nonanonymous() - }; - } - builder.setMultiChoiceItems(options, values, (dialog, which, isChecked) -> values[which] = isChecked); + MucConfiguration configuration = MucConfiguration.get(ConferenceDetailsActivity.this, mucOptions); + builder.setTitle(configuration.title); + final boolean[] values = configuration.values; + builder.setMultiChoiceItems(configuration.names, values, (dialog, which, isChecked) -> values[which] = isChecked); builder.setNegativeButton(R.string.cancel, null); builder.setPositiveButton(R.string.confirm, (dialog, which) -> { - if (!mucOptions.membersOnly() && values[0]) { - xmppConnectionService.changeAffiliationsInConference(mConversation, - MucOptions.Affiliation.NONE, - MucOptions.Affiliation.MEMBER); - } - Bundle options1 = new Bundle(); - options1.putString("muc#roomconfig_membersonly", values[0] ? "1" : "0"); - if (values.length == 2) { - options1.putString("muc#roomconfig_whois", values[1] ? "anyone" : "moderators"); - } else if (values.length == 3) { - options1.putString("muc#roomconfig_moderatedroom", values[1] ? "1" : "0"); - options1.putString("muc#roomconfig_whois", values[2] ? "anyone" : "moderators"); - } - options1.putString("muc#roomconfig_persistentroom", "1"); - final boolean whois = values.length == 2 ? values[1] : values[2]; - if (values[0] == whois) { - options1.putString("muc#roomconfig_publicroom", whois ? "0" : "1"); - } + Bundle options = configuration.toBundle(values); + options.putString("muc#roomconfig_persistentroom", "1"); xmppConnectionService.pushConferenceConfiguration(mConversation, - options1, + options, ConferenceDetailsActivity.this); }); builder.create().show(); } }; - public static boolean cancelPotentialWork(User user, ImageView imageView) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask != null) { - final User old = bitmapWorkerTask.o; - if (old == null || user != old) { - bitmapWorkerTask.cancel(true); - } else { - return false; - } - } - return true; - } - - private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - @Override public void onConversationUpdate() { refreshUi(); @@ -289,18 +200,12 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - boolean useBundledEmoji = getPreferences().getBoolean(USE_BUNDLED_EMOJIS, getResources().getBoolean(R.bool.use_bundled_emoji)); - new EmojiService(this).init(useBundledEmoji); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_muc_details); this.binding.changeConferenceButton.setOnClickListener(this.mChangeConferenceSettings); - this.binding.invite.setOnClickListener(inviteListener); - this.binding.invite.setVisibility(View.GONE); - this.binding.invite.setOnClickListener(inviteListener); this.binding.destroy.setVisibility(View.GONE); this.binding.destroy.setOnClickListener(destroyListener); this.binding.leaveMuc.setVisibility(View.GONE); this.binding.addContactButton.setVisibility(View.GONE); - this.binding.mucMoreDetails.setVisibility(View.GONE); setSupportActionBar((Toolbar) binding.toolbar); configureActionBar(getSupportActionBar()); this.binding.editNickButton.setOnClickListener(v -> quickEdit(mConversation.getMucOptions().getActualNick(), @@ -334,16 +239,27 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers this.binding.mucEditSubject.addTextChangedListener(this); this.binding.mucEditSubject.addTextChangedListener(new StylingHelper.MessageEditorStyler(this.binding.mucEditSubject)); this.binding.autojoinCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> { - final Bookmark bookmark = mConversation.getBookmark(); - if (bookmark != null) { - bookmark.setAutojoin(this.binding.autojoinCheckbox.isChecked()); - xmppConnectionService.pushBookmarks(bookmark.getAccount()); - updateView(); + if (mConversation != null) { + final Bookmark bookmark = mConversation.getBookmark(); + if (bookmark != null) { + bookmark.setAutojoin(this.binding.autojoinCheckbox.isChecked()); + xmppConnectionService.pushBookmarks(bookmark.getAccount()); + updateView(); + } } }); - mMediaAdapter = new MediaAdapter(this, R.dimen.media_size); + this.mMediaAdapter = new MediaAdapter(this, R.dimen.media_size); + this.mUserPreviewAdapter = new UserPreviewAdapter(); this.binding.media.setAdapter(mMediaAdapter); + this.binding.users.setAdapter(mUserPreviewAdapter); GridManager.setupLayoutManager(this, this.binding.media, R.dimen.media_size); + GridManager.setupLayoutManager(this, this.binding.users, R.dimen.media_size); + this.binding.invite.setOnClickListener(v -> inviteToConversation(mConversation)); + this.binding.showUsers.setOnClickListener(v -> { + Intent intent = new Intent(this, MucUsersActivity.class); + intent.putExtra("uuid", mConversation.getUuid()); + startActivity(intent); + }); } @Override @@ -384,6 +300,14 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers return super.onOptionsItemSelected(menuItem); } + @Override + public boolean onContextItemSelected(MenuItem item) { + if (!MucDetailsContextMenuHelper.onContextItemSelected(item, mUserPreviewAdapter.getSelectedUser(), this)) { + return super.onContextItemSelected(item); + } + return true; + } + public void onMucEditButtonClicked(View v) { if (this.binding.mucEditor.getVisibility() == View.GONE) { final MucOptions mucOptions = mConversation.getMucOptions(); @@ -465,40 +389,12 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.muc_details, menu); + final MenuItem share = menu.findItem(R.id.action_share); + share.setVisible(mConversation != null && !mConversation.isPrivateAndNonAnonymous()); return super.onCreateOptionsMenu(menu); } @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - Object tag = v.getTag(); - if (tag instanceof User) { - getMenuInflater().inflate(R.menu.muc_details_context, menu); - final User user = (User) tag; - this.mSelectedUser = user; - String name; - final Contact contact = user.getContact(); - if (contact != null && contact.showInContactList()) { - name = contact.getDisplayName(); - } else if (user.getRealJid() != null) { - name = user.getRealJid().asBareJid().toString(); - } else { - name = user.getName(); - } - menu.setHeaderTitle(name); - MucDetailsContextMenuHelper.configureMucDetailsContextMenu(this, menu, mConversation, user); - } - super.onCreateContextMenu(menu, v, menuInfo); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - if (!MucDetailsContextMenuHelper.onContextItemSelected(item, mSelectedUser, mConversation, this)) { - return super.onContextItemSelected(item); - } - return true; - } - - @Override public void onMediaLoaded(List<Attachment> attachments) { runOnUiThread(() -> { int limit = GridManager.getCurrentColumnCount(binding.media); @@ -568,7 +464,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers } else { account = mConversation.getAccount().getJid().asBareJid().toString(); } - + setTitle(mucOptions.isPrivateAndNonAnonymous() ? R.string.conference_details : R.string.channel_details); this.binding.editMucNameButton.setVisibility((self.getAffiliation().ranks(MucOptions.Affiliation.OWNER) || mucOptions.canChangeSubject()) ? View.VISIBLE : View.GONE); this.binding.detailsAccount.setText(getString(R.string.using_account, account)); this.binding.jid.setText(mConversation.getJid().asBareJid().toEscapedString()); @@ -577,8 +473,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers } else { this.binding.detailsAccount.setVisibility(View.GONE); } - this.binding.detailsMucAvatar.setImageBitmap(avatarService().get(mConversation, getPixel(Config.AVATAR_SIZE))); - this.binding.yourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48))); + AvatarWorkerTask.loadAvatar(mConversation, binding.detailsMucAvatar, R.dimen.avatar_big); + AvatarWorkerTask.loadAvatar(mConversation.getAccount(), binding.yourPhoto, R.dimen.avatar_on_details_screen_size); + String roomName = mucOptions.getName(); String subject = mucOptions.getSubject(); final boolean hasTitle; @@ -607,16 +504,19 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers } this.binding.mucYourNick.setText(EmojiWrapper.transform(mucOptions.getActualNick())); if (mucOptions.online()) { - this.binding.mucMoreDetails.setVisibility(View.VISIBLE); - this.binding.mucSettings.setVisibility(View.VISIBLE); + this.binding.usersWrapper.setVisibility(View.VISIBLE); this.binding.mucInfoMore.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE); this.binding.jid.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE); this.binding.mucRole.setVisibility(View.VISIBLE); this.binding.mucRole.setText(getStatus(self)); - if (mucOptions.membersOnly()) { - this.binding.mucConferenceType.setText(R.string.private_conference); + if (mucOptions.getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER)) { + this.binding.mucSettings.setVisibility(View.VISIBLE); + this.binding.mucConferenceType.setText(MucConfiguration.describe(this, mucOptions)); + } else if (!mucOptions.isPrivateAndNonAnonymous() && mucOptions.nonanonymous()) { + this.binding.mucSettings.setVisibility(View.VISIBLE); + this.binding.mucConferenceType.setText(R.string.group_chat_will_make_your_jabber_id_public); } else { - this.binding.mucConferenceType.setText(R.string.public_conference); + this.binding.mucSettings.setVisibility(View.GONE); } if (mucOptions.mamSupport()) { this.binding.mucInfoMam.setText(R.string.server_info_available); @@ -684,7 +584,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers }); } } else { - this.binding.mucMoreDetails.setVisibility(View.GONE); + this.binding.usersWrapper.setVisibility(View.GONE); this.binding.mucInfoMore.setVisibility(View.GONE); this.binding.mucSettings.setVisibility(View.GONE); } @@ -708,71 +608,44 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers this.binding.notificationStatusButton.setImageResource(ic_notifications_none); } - final LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - this.binding.mucMembers.removeAllViews(); - if (inflater == null) { - return; - } - final ArrayList<User> users = mucOptions.getUsers(); - Collections.sort(users); - for (final User user : users) { - ContactBinding binding = DataBindingUtil.inflate(inflater, R.layout.contact, this.binding.mucMembers, false); - this.setListItemBackgroundOnView(binding.getRoot()); - binding.getRoot().setOnClickListener(view1 -> highlightInMuc(mConversation, user.getName())); - registerForContextMenu(binding.getRoot()); - binding.getRoot().setTag(user); - if (mAdvancedMode && user.getPgpKeyId() != 0) { - binding.key.setVisibility(View.VISIBLE); - binding.key.setOnClickListener(v -> viewPgpKey(user)); - binding.key.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId())); - } - Contact contact = user.getContact(); - String name = user.getName(); - if (contact != null) { - binding.contactDisplayName.setText(EmojiWrapper.transform(contact.getDisplayName())); - binding.contactJid.setText((name != null ? EmojiWrapper.transform(name) + " \u2022 " : "") + getStatus(user)); + final List<User> users = mucOptions.getUsers(); + Collections.sort(users, (a, b) -> { + if (b.getAffiliation().outranks(a.getAffiliation())) { + return 1; + } else if (a.getAffiliation().outranks(b.getAffiliation())) { + return -1; } else { - binding.contactDisplayName.setText(name == null ? "" : EmojiWrapper.transform(name)); - binding.contactJid.setText(getStatus(user)); - - } - loadAvatar(user, binding.contactPhoto); - if (user.getRole() == MucOptions.Role.NONE) { - binding.contactJid.setAlpha(INACTIVE_ALPHA); - binding.key.setAlpha(INACTIVE_ALPHA); - binding.contactDisplayName.setAlpha(INACTIVE_ALPHA); - binding.contactPhoto.setAlpha(INACTIVE_ALPHA); + if (a.getAvatar() != null && b.getAvatar() == null) { + return -1; + } else if (a.getAvatar() == null && b.getAvatar() != null) { + return 1; + } else { + return a.getComparableName().compareToIgnoreCase(b.getComparableName()); + } } - this.binding.mucMembers.addView(binding.getRoot()); - } - if (mConversation.getMucOptions().canInvite()) { - this.binding.invite.setVisibility(View.VISIBLE); + }); + this.mUserPreviewAdapter.submitList(MucOptions.sub(users, GridManager.getCurrentColumnCount(binding.users))); + this.binding.invite.setVisibility(mucOptions.canInvite() ? View.VISIBLE : View.GONE); + this.binding.showUsers.setVisibility(users.size() > 0 ? View.VISIBLE : View.GONE); + this.binding.usersWrapper.setVisibility(users.size() > 0 || mucOptions.canInvite() ? View.VISIBLE : View.GONE); + if (users.size() == 0) { + this.binding.noUsersHints.setText(mucOptions.isPrivateAndNonAnonymous() ? R.string.no_users_hint_group_chat : R.string.no_users_hint_channel); + this.binding.noUsersHints.setVisibility(View.VISIBLE); } else { - this.binding.invite.setVisibility(View.GONE); + this.binding.noUsersHints.setVisibility(View.GONE); } } - private String getStatus(User user) { - if (mAdvancedMode) { - return getString(user.getAffiliation().getResId()) + - " (" + getString(user.getRole().getResId()) + ')'; + public static String getStatus(Context context, User user, final boolean advanced) { + if (advanced) { + return String.format("%s (%s)", context.getString(user.getAffiliation().getResId()), context.getString(user.getRole().getResId())); } else { - return getString(user.getAffiliation().getResId()); + return context.getString(user.getAffiliation().getResId()); } } - private void viewPgpKey(User user) { - PgpEngine pgp = xmppConnectionService.getPgpEngine(); - if (pgp != null) { - PendingIntent intent = pgp.getIntentForKey(user.getPgpKeyId()); - if (intent != null) { - try { - startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0); - } catch (SendIntentException ignored) { - - } - } - } + private String getStatus(User user) { + return getStatus(this, user, mAdvancedMode); } @Override @@ -786,16 +659,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers } @Override - public void onRoleChangedSuccessful(String nick) { - - } - - @Override - public void onRoleChangeFailed(String nick, int resId) { - displayToast(getString(resId, nick)); - } - - @Override public void onPushSucceeded() { displayToast(getString(R.string.modified_conference_options)); } @@ -814,28 +677,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers }); } - public void loadAvatar(User user, ImageView imageView) { - if (cancelPotentialWork(user, imageView)) { - final Bitmap bm = avatarService().get(user, getPixel(48), true); - if (bm != null) { - cancelPotentialWork(user, imageView); - imageView.setImageBitmap(bm); - imageView.setBackgroundColor(0x00000000); - } else { - String seed = user.getRealJid() != null ? user.getRealJid().asBareJid().toString() : null; - imageView.setBackgroundColor(UIHelper.getColorForName(seed == null ? user.getName() : seed)); - imageView.setImageDrawable(null); - final BitmapWorkerTask task = new BitmapWorkerTask(imageView); - final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), null, task); - imageView.setImageDrawable(asyncDrawable); - try { - task.execute(user); - } catch (final RejectedExecutionException ignored) { - } - } - } - } - @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { @@ -862,46 +703,4 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers } } } - - static class AsyncDrawable extends BitmapDrawable { - private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; - - AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); - } - - BitmapWorkerTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } - } - - class BitmapWorkerTask extends AsyncTask<User, Void, Bitmap> { - private final WeakReference<ImageView> imageViewReference; - private User o = null; - - private BitmapWorkerTask(ImageView imageView) { - imageViewReference = new WeakReference<>(imageView); - } - - @Override - protected Bitmap doInBackground(User... params) { - this.o = params[0]; - if (imageViewReference.get() == null) { - return null; - } - return avatarService().get(this.o, getPixel(48), isCancelled()); - } - - @Override - protected void onPostExecute(Bitmap bitmap) { - if (bitmap != null && !isCancelled()) { - final ImageView imageView = imageViewReference.get(); - if (imageView != null) { - imageView.setImageBitmap(bitmap); - imageView.setBackgroundColor(0x00000000); - } - } - } - } } diff --git a/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java index 9db7ce7c9..7fc150aa7 100644 --- a/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java @@ -50,6 +50,7 @@ import de.pixart.messenger.services.XmppConnectionService.OnRosterUpdate; import de.pixart.messenger.ui.adapter.MediaAdapter; import de.pixart.messenger.ui.interfaces.OnMediaLoaded; import de.pixart.messenger.ui.util.Attachment; +import de.pixart.messenger.ui.util.AvatarWorkerTask; import de.pixart.messenger.ui.util.GridManager; import de.pixart.messenger.ui.util.JidDialog; import de.pixart.messenger.utils.Compatibility; @@ -527,7 +528,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp account = contact.getAccount().getJid().asBareJid().toString(); } binding.detailsAccount.setText(getString(R.string.using_account, account)); - binding.detailsContactBadge.setImageBitmap(avatarService().get(contact, getPixel(Config.AVATAR_SIZE))); + AvatarWorkerTask.loadAvatar(contact, binding.detailsContactBadge, R.dimen.avatar_big); binding.detailsContactBadge.setOnClickListener(this.onBadgeClick); if (xmppConnectionService.multipleAccounts()) { diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java index 4df6391da..94ddc3f8e 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java @@ -107,7 +107,10 @@ import de.pixart.messenger.ui.util.ScrollState; import de.pixart.messenger.ui.util.SendButtonAction; import de.pixart.messenger.ui.util.SendButtonTool; import de.pixart.messenger.ui.util.ShareUtil; +import de.pixart.messenger.ui.util.ViewUtil; import de.pixart.messenger.ui.widget.EditMessage; +import de.pixart.messenger.utils.Compatibility; +import de.pixart.messenger.utils.GeoHelper; import de.pixart.messenger.utils.MenuDoubleTabUtil; import de.pixart.messenger.utils.MessageUtils; import de.pixart.messenger.utils.NickValidityChecker; @@ -123,6 +126,9 @@ import rocks.xmpp.addr.Jid; import static de.pixart.messenger.ui.XmppActivity.EXTRA_ACCOUNT; import static de.pixart.messenger.ui.XmppActivity.REQUEST_INVITE_TO_CONVERSATION; import static de.pixart.messenger.ui.util.SoftKeyboardUtils.hideSoftKeyboard; +import static de.pixart.messenger.utils.PermissionUtils.allGranted; +import static de.pixart.messenger.utils.PermissionUtils.getFirstDenied; +import static de.pixart.messenger.utils.PermissionUtils.writeGranted; import static de.pixart.messenger.xmpp.Patches.ENCRYPTION_EXCEPTIONS; public class ConversationFragment extends XmppFragment implements EditMessage.KeyboardListener, MessageAdapter.OnContactPictureLongClicked, MessageAdapter.OnContactPictureClicked { @@ -377,7 +383,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke private OnClickListener mHideUnencryptionHint = new OnClickListener() { @Override public void onClick(View v) { - if (Config.supportOmemo() && conversation.getAccount().getAxolotlService().isConversationAxolotlCapable(conversation)) { + if (Config.supportOmemo() && Conversation.suitableForOmemoByDefault(conversation) && conversation.isSingleOrPrivateAndNonAnonymous()) { conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL); activity.xmppConnectionService.updateConversation(conversation); activity.refreshUi(); @@ -593,33 +599,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke return getConversation(activity, R.id.main_fragment); } - private static boolean allGranted(int[] grantResults) { - for (int grantResult : grantResults) { - if (grantResult != PackageManager.PERMISSION_GRANTED) { - return false; - } - } - return true; - } - - private static boolean writeGranted(int[] grantResults, String[] permission) { - for (int i = 0; i < grantResults.length; ++i) { - if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission[i])) { - return grantResults[i] == PackageManager.PERMISSION_GRANTED; - } - } - return false; - } - - private static String getFirstDenied(int[] grantResults, String[] permissions) { - for (int i = 0; i < grantResults.length; ++i) { - if (grantResults[i] == PackageManager.PERMISSION_DENIED) { - return permissions[i]; - } - } - return null; - } - private static boolean scrolledToBottom(AbsListView listView) { final int count = listView.getCount(); if (count == 0) { @@ -1150,7 +1129,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke if (conversation != null) { if (conversation.getMode() == Conversation.MODE_MULTI) { - menuInviteContact.setVisible(true); + menuInviteContact.setVisible(conversation.getMucOptions().canInvite()); menuArchiveChat.setTitle(R.string.action_end_conversation_muc); } else { menuInviteContact.setVisible(false); @@ -1277,12 +1256,13 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke return; } - final boolean deleted = t != null && t instanceof TransferablePlaceholder; + final boolean deleted = m.isFileDeleted(); final boolean encrypted = m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED || m.getEncryption() == Message.ENCRYPTION_PGP; final boolean receiving = m.getStatus() == Message.STATUS_RECEIVED && (t instanceof JingleConnection || t instanceof HttpDownloadConnection); activity.getMenuInflater().inflate(R.menu.message_context, menu); menu.setHeaderTitle(R.string.message_options); + MenuItem openWith = menu.findItem(R.id.open_with); MenuItem copyMessage = menu.findItem(R.id.copy_message); MenuItem copyLink = menu.findItem(R.id.copy_link); MenuItem quoteMessage = menu.findItem(R.id.quote_message); @@ -1309,14 +1289,14 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke copyLink.setVisible(true); } } - if (m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { + if (m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED && !deleted) { retryDecryption.setVisible(true); } if (!showError && relevantForCorrection.getType() == Message.TYPE_TEXT + && !m.isGeoUri() && relevantForCorrection.isLastCorrectableMessage() - && m.getConversation() instanceof Conversation - && (((Conversation) m.getConversation()).getMucOptions().nonanonymous() || m.getConversation().getMode() == Conversation.MODE_SINGLE)) { + && m.getConversation() instanceof Conversation) { correctMessage.setVisible(true); } if ((m.isFileOrImage() && !deleted && !receiving) || (m.getType() == Message.TYPE_TEXT && !m.treatAsDownloadable())) { @@ -1355,6 +1335,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke if (showError) { showErrorMessage.setVisible(true); } + final String mime = m.isFileOrImage() ? m.getMimeType() : null; + if ((m.isGeoUri() && GeoHelper.openInOsmAnd(getActivity(), m)) || (mime != null && mime.startsWith("audio/"))) { + openWith.setVisible(true); + } } } @@ -1400,6 +1384,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke case R.id.show_error_message: showErrorMessage(selectedMessage); return true; + case R.id.open_with: + openWith(selectedMessage); + return true; default: return super.onContextItemSelected(item); } @@ -1499,38 +1486,43 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke if (conversation == null) { return; } + final boolean updated; switch (item.getItemId()) { case R.id.encryption_choice_none: - conversation.setNextEncryption(Message.ENCRYPTION_NONE); + updated = conversation.setNextEncryption(Message.ENCRYPTION_NONE); item.setChecked(true); break; case R.id.encryption_choice_otr: - conversation.setNextEncryption(Message.ENCRYPTION_OTR); + updated = conversation.setNextEncryption(Message.ENCRYPTION_OTR); item.setChecked(true); break; case R.id.encryption_choice_pgp: if (activity.hasPgp()) { if (conversation.getAccount().getPgpSignature() != null) { - conversation.setNextEncryption(Message.ENCRYPTION_PGP); + updated = conversation.setNextEncryption(Message.ENCRYPTION_PGP); item.setChecked(true); } else { + updated = false; activity.announcePgp(conversation.getAccount(), conversation, null, activity.onOpenPGPKeyPublished); } } else { activity.showInstallPgpDialog(); + updated = false; } break; case R.id.encryption_choice_axolotl: Log.d(Config.LOGTAG, AxolotlService.getLogprefix(conversation.getAccount()) + "Enabled axolotl for Contact " + conversation.getContact().getJid()); - conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL); + updated = conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL); item.setChecked(true); break; default: - conversation.setNextEncryption(Message.ENCRYPTION_NONE); + updated = conversation.setNextEncryption(Message.ENCRYPTION_NONE); break; } - activity.xmppConnectionService.updateConversation(conversation); + if (updated) { + activity.xmppConnectionService.updateConversation(conversation); + } updateChatMsgHint(); getActivity().invalidateOptionsMenu(); activity.refreshUi(); @@ -1670,7 +1662,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke Log.d(Config.LOGTAG, "type: " + transferable.getClass().getName()); Toast.makeText(getActivity(), R.string.not_connected_try_again, Toast.LENGTH_SHORT).show(); } - } else if (message.treatAsDownloadable()) { + } else if (message.treatAsDownloadable() || message.hasFileOnRemoteHost()) { createNewConnection(message); } } @@ -1843,6 +1835,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke return null; } + private void openWith(final Message message) { + if (message.isGeoUri()) { + GeoHelper.view(getActivity(), message); + } else { + final DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message); + ViewUtil.view(activity, file); + } + } + private void showErrorMessage(final Message message) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(R.string.error_message); @@ -1856,13 +1857,36 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } private void deleteMessage(Message message) { + while (message.mergeable(message.next())) { + message = message.next(); + } final Conversation conversation = (Conversation) message.getConversation(); + Message relevantForCorrection = message; + while (relevantForCorrection.mergeable(relevantForCorrection.next())) { + relevantForCorrection = relevantForCorrection.next(); + } AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setNegativeButton(R.string.cancel, null); builder.setTitle(R.string.delete_message_dialog); builder.setMessage(R.string.delete_message_dialog_msg); + Message finalRelevantForCorrection = relevantForCorrection; + Message finalMessage = message; builder.setPositiveButton(R.string.confirm, (dialog, which) -> { - activity.xmppConnectionService.deleteMessage(conversation, message); + if (finalRelevantForCorrection.getType() == Message.TYPE_TEXT + && !finalMessage.isGeoUri() + && finalRelevantForCorrection.isLastCorrectableMessage() + && finalMessage.getConversation() instanceof Conversation + && (((Conversation) finalMessage.getConversation()).getMucOptions().nonanonymous() || finalMessage.getConversation().getMode() == Conversation.MODE_SINGLE)) { + this.conversation.setCorrectingMessage(finalMessage); + Message deletedmessage = conversation.getCorrectingMessage(); + deletedmessage.setBody(getString(R.string.message_deleted)); + deletedmessage.setEdited(deletedmessage.getUuid()); + deletedmessage.setUuid(UUID.randomUUID().toString()); + sendMessage(deletedmessage); + activity.xmppConnectionService.deleteMessage(conversation, deletedmessage); + refresh(); + } + activity.xmppConnectionService.deleteMessage(conversation, finalMessage); activity.onConversationsListItemUpdated(); refresh(); }); @@ -1876,7 +1900,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke builder.setMessage(R.string.delete_file_dialog_msg); builder.setPositiveButton(R.string.confirm, (dialog, which) -> { if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) { - message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); + message.setFileDeleted(true); + activity.xmppConnectionService.updateMessage(message, false); activity.onConversationsListItemUpdated(); refresh(); } @@ -1890,8 +1915,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke return; } final Conversation conversation = (Conversation) message.getConversation(); - DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message); - if (file.exists()) { + final DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message); + if ((file.exists() && file.canRead()) || message.hasFileOnRemoteHost()) { final XmppConnection xmppConnection = conversation.getAccount().getXmppConnection(); if (!message.hasFileOnRemoteHost() && xmppConnection != null @@ -1907,9 +1932,13 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke }); return; } + } else if (!Compatibility.hasStoragePermission(getActivity())) { + Toast.makeText(activity, R.string.no_storage_permission, Toast.LENGTH_SHORT).show(); + return; } else { Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show(); - message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); + message.setFileDeleted(true); + activity.xmppConnectionService.updateMessage(message, false); activity.onConversationsListItemUpdated(); refresh(); return; @@ -2419,26 +2448,14 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke && conversation.isWithStranger()) { showSnackbar(R.string.received_message_from_stranger, R.string.block, mBlockClickListener); } else if (activity.xmppConnectionService.warnUnecryptedChat()) { - AxolotlService axolotlService = account.getAxolotlService(); - if ((mode == Conversation.MODE_SINGLE) && (conversation.getNextEncryption() == Message.ENCRYPTION_NONE && - ((Config.supportOmemo() && axolotlService != null && conversation.getAccount().getAxolotlService().isConversationAxolotlCapable(conversation)) || - (Config.supportOpenPgp() && account.isPgpDecryptionServiceConnected()) || - Config.supportOtr()))) { + if (conversation.getNextEncryption() == Message.ENCRYPTION_NONE && conversation.isSingleOrPrivateAndNonAnonymous() && ((Config.supportOmemo() && Conversation.suitableForOmemoByDefault(conversation)) || + (Config.supportOpenPgp() && account.isPgpDecryptionServiceConnected()) || ( + mode == Conversation.MODE_SINGLE && Config.supportOtr()))) { if (ENCRYPTION_EXCEPTIONS.contains(conversation.getJid().toString()) || conversation.getJid().toString().equals(account.getJid().getDomain())) { hideSnackbar(); } else { showSnackbar(R.string.conversation_unencrypted_hint, R.string.ok, mHideUnencryptionHint, null); } - } else if ((mode == Conversation.MODE_MULTI && conversation.getMucOptions().membersOnly() && conversation.getMucOptions().nonanonymous()) && - (conversation.getNextEncryption() == Message.ENCRYPTION_NONE && - ((Config.supportOmemo() && axolotlService != null && conversation.getAccount().getAxolotlService().isConversationAxolotlCapable(conversation)) || - (Config.supportOpenPgp() && account.isPgpDecryptionServiceConnected())))) { - if (ENCRYPTION_EXCEPTIONS.contains(conversation.getJid().toString()) || conversation.getJid().toString().equals(account.getJid().getDomain())) { - Log.d(Config.LOGTAG, "Don't show unencrypted warning because " + conversation.getJid().toString() + " is on exception list"); - hideSnackbar(); - } else { - showSnackbar(R.string.conversation_unencrypted_hint, R.string.ok, mHideUnencryptionHint, null); - } } else { hideSnackbar(); } @@ -3086,36 +3103,32 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke @Override public void onContactPictureLongClicked(View v, final Message message) { - if (message.getStatus() <= Message.STATUS_RECEIVED) { - final PopupMenu popupMenu = new PopupMenu(getActivity(), v); + final String fingerprint; + if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + fingerprint = "pgp"; + } else { + fingerprint = message.getFingerprint(); + } + final PopupMenu popupMenu = new PopupMenu(getActivity(), v); + final Contact contact = message.getContact(); + if (message.getStatus() <= Message.STATUS_RECEIVED && (contact == null || !contact.isSelf())) { if (message.getConversation().getMode() == Conversation.MODE_MULTI) { final Jid cp = message.getCounterpart(); if (cp == null || cp.isBareJid()) { return; } - Jid tcp = message.getTrueCounterpart(); - User userByRealJid = tcp != null ? conversation.getMucOptions().findOrCreateUserByRealJid(tcp, cp) : null; + final Jid tcp = message.getTrueCounterpart(); + final User userByRealJid = tcp != null ? conversation.getMucOptions().findOrCreateUserByRealJid(tcp, cp) : null; final User user = userByRealJid != null ? userByRealJid : conversation.getMucOptions().findUserByFullJid(cp); popupMenu.inflate(R.menu.muc_details_context); final Menu menu = popupMenu.getMenu(); MucDetailsContextMenuHelper.configureMucDetailsContextMenu(activity, menu, conversation, user); - popupMenu.setOnMenuItemClickListener(menuItem -> MucDetailsContextMenuHelper.onContextItemSelected(menuItem, user, conversation, activity)); + popupMenu.setOnMenuItemClickListener(menuItem -> MucDetailsContextMenuHelper.onContextItemSelected(menuItem, user, activity, fingerprint)); } else { - final Contact contact = message.getContact(); - if (contact.isSelf()) { - activity.showQrCode(conversation.getAccount().getShareableUri()); - return; - } popupMenu.inflate(R.menu.one_on_one_context); popupMenu.setOnMenuItemClickListener(item -> { switch (item.getItemId()) { case R.id.action_contact_details: - String fingerprint; - if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - fingerprint = "pgp"; - } else { - fingerprint = message.getFingerprint(); - } activity.switchToContactDetails(message.getContact(), fingerprint); break; case R.id.action_show_qr_code: @@ -3125,10 +3138,22 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke return true; }); } - popupMenu.show(); } else { - activity.showQrCode(conversation.getAccount().getShareableUri()); + popupMenu.inflate(R.menu.account_context); + final Menu menu = popupMenu.getMenu(); + popupMenu.setOnMenuItemClickListener(item -> { + switch (item.getItemId()) { + case R.id.action_show_qr_code: + activity.showQrCode(conversation.getAccount().getShareableUri()); + break; + case R.id.action_account_details: + activity.switchToAccount(message.getConversation().getAccount(), fingerprint); + break; + } + return true; + }); } + popupMenu.show(); } @Override diff --git a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java index 400519ab3..e7343ce63 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java @@ -75,7 +75,6 @@ import de.pixart.messenger.entities.Account; import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.entities.MucOptions; import de.pixart.messenger.entities.Presence; -import de.pixart.messenger.services.EmojiService; import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.ui.interfaces.OnBackendConnected; import de.pixart.messenger.ui.interfaces.OnConversationArchived; @@ -96,9 +95,8 @@ import de.pixart.messenger.xmpp.chatstate.ChatState; import rocks.xmpp.addr.Jid; import static de.pixart.messenger.ui.ConversationFragment.REQUEST_DECRYPT_PGP; -import static de.pixart.messenger.ui.SettingsActivity.USE_BUNDLED_EMOJIS; -public class ConversationsActivity extends XmppActivity implements OnConversationSelected, OnConversationArchived, OnConversationsListItemUpdated, OnConversationRead, XmppConnectionService.OnAccountUpdate, XmppConnectionService.OnConversationUpdate, XmppConnectionService.OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged { +public class ConversationsActivity extends XmppActivity implements OnConversationSelected, OnConversationArchived, OnConversationsListItemUpdated, OnConversationRead, XmppConnectionService.OnAccountUpdate, XmppConnectionService.OnConversationUpdate, XmppConnectionService.OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnAffiliationChanged { public static final String ACTION_VIEW_CONVERSATION = "de.pixart.messenger.VIEW"; public static final String EXTRA_CONVERSATION = "conversationUuid"; @@ -418,7 +416,6 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio super.onCreate(savedInstanceState); ConversationMenuConfigurator.reloadFeatures(this); OmemoSetting.load(this); - new EmojiService(this).init(useBundledEmoji()); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_conversations); setSupportActionBar((Toolbar) binding.toolbar); configureActionBar(getSupportActionBar()); @@ -491,15 +488,6 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio displayToast(getString(resId, jid.asBareJid().toString())); } - @Override - public void onRoleChangedSuccessful(String nick) { - } - - @Override - public void onRoleChangeFailed(String nick, int resId) { - displayToast(getString(resId, nick)); - } - private void openConversation(Conversation conversation, Bundle extras) { ConversationFragment conversationFragment = (ConversationFragment) getFragmentManager().findFragmentById(R.id.secondary_fragment); final boolean mainNeedsRefresh; @@ -766,65 +754,49 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio } } } else { - if (conversation.getParticipants() != null) { - ChatState state = ChatState.COMPOSING; - List<MucOptions.User> userWithChatStates = conversation.getMucOptions().getUsersWithChatState(state, 5); - if (userWithChatStates.size() == 0) { - state = ChatState.PAUSED; - userWithChatStates = conversation.getMucOptions().getUsersWithChatState(state, 5); - } - List<MucOptions.User> users = conversation.getMucOptions().getUsers(true); - if (state == ChatState.COMPOSING) { - if (userWithChatStates.size() > 0) { - if (userWithChatStates.size() == 1) { - MucOptions.User user = userWithChatStates.get(0); - absubtitle.setText(EmojiWrapper.transform(getString(R.string.contact_is_typing, UIHelper.getDisplayName(user)))); - } else { - StringBuilder builder = new StringBuilder(); - for (MucOptions.User user : userWithChatStates) { - if (builder.length() != 0) { - builder.append(", "); - } - builder.append(UIHelper.getDisplayName(user)); + ChatState state = ChatState.COMPOSING; + List<MucOptions.User> userWithChatStates = conversation.getMucOptions().getUsersWithChatState(state, 5); + if (userWithChatStates.size() == 0) { + state = ChatState.PAUSED; + userWithChatStates = conversation.getMucOptions().getUsersWithChatState(state, 5); + } + List<MucOptions.User> users = conversation.getMucOptions().getUsers(true); + if (state == ChatState.COMPOSING) { + if (userWithChatStates.size() > 0) { + if (userWithChatStates.size() == 1) { + MucOptions.User user = userWithChatStates.get(0); + absubtitle.setText(EmojiWrapper.transform(getString(R.string.contact_is_typing, UIHelper.getDisplayName(user)))); + } else { + StringBuilder builder = new StringBuilder(); + for (MucOptions.User user : userWithChatStates) { + if (builder.length() != 0) { + builder.append(", "); } - absubtitle.setText(EmojiWrapper.transform(getString(R.string.contacts_are_typing, builder.toString()))); + builder.append(UIHelper.getDisplayName(user)); } - } - } else { - if (users.size() == 0) { - absubtitle.setText(getString(R.string.one_participant)); - } else { - int size = users.size() + 1; - absubtitle.setText(getString(R.string.more_participants, size)); + absubtitle.setText(EmojiWrapper.transform(getString(R.string.contacts_are_typing, builder.toString()))); } } - absubtitle.setSelected(true); - absubtitle.setOnClickListener(view15 -> { - if (conversation.getMode() == Conversation.MODE_SINGLE) { - switchToContactDetails(conversation.getContact()); - } else if (conversation.getMode() == Conversation.MODE_MULTI) { - Intent intent = new Intent(ConversationsActivity.this, ConferenceDetailsActivity.class); - intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC); - intent.putExtra("uuid", conversation.getUuid()); - startActivity(intent); - overridePendingTransition(R.animator.fade_in, R.animator.fade_out); - } - }); } else { - absubtitle.setText(R.string.no_participants); - abtitle.setSelected(true); - absubtitle.setOnClickListener(view16 -> { - if (conversation.getMode() == Conversation.MODE_SINGLE) { - switchToContactDetails(conversation.getContact()); - } else if (conversation.getMode() == Conversation.MODE_MULTI) { - Intent intent = new Intent(ConversationsActivity.this, ConferenceDetailsActivity.class); - intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC); - intent.putExtra("uuid", conversation.getUuid()); - startActivity(intent); - overridePendingTransition(R.animator.fade_in, R.animator.fade_out); - } - }); + if (users.size() == 0) { + absubtitle.setText(getString(R.string.one_participant)); + } else { + int size = users.size() + 1; + absubtitle.setText(getString(R.string.more_participants, size)); + } } + absubtitle.setSelected(true); + absubtitle.setOnClickListener(view15 -> { + if (conversation.getMode() == Conversation.MODE_SINGLE) { + switchToContactDetails(conversation.getContact()); + } else if (conversation.getMode() == Conversation.MODE_MULTI) { + Intent intent = new Intent(ConversationsActivity.this, ConferenceDetailsActivity.class); + intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC); + intent.putExtra("uuid", conversation.getUuid()); + startActivity(intent); + overridePendingTransition(R.animator.fade_in, R.animator.fade_out); + } + }); } return; } @@ -837,10 +809,6 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio } } - public boolean useBundledEmoji() { - return getPreferences().getBoolean(USE_BUNDLED_EMOJIS, getResources().getBoolean(R.bool.use_bundled_emoji)); - } - public void verifyOtrSessionDialog(final Conversation conversation, View view) { if (!conversation.hasValidOtrSession() || conversation.getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) { Toast.makeText(this, R.string.otr_session_not_started, Toast.LENGTH_LONG).show(); diff --git a/src/main/java/de/pixart/messenger/ui/CreateConferenceDialog.java b/src/main/java/de/pixart/messenger/ui/CreatePrivateGroupChatDialog.java index 900f4a42d..c77211e9d 100644 --- a/src/main/java/de/pixart/messenger/ui/CreateConferenceDialog.java +++ b/src/main/java/de/pixart/messenger/ui/CreatePrivateGroupChatDialog.java @@ -19,15 +19,15 @@ import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.ui.util.DelayedHintHelper; -public class CreateConferenceDialog extends DialogFragment { +public class CreatePrivateGroupChatDialog extends DialogFragment { private static final String ACCOUNTS_LIST_KEY = "activated_accounts_list"; private static final String MULTIPLE_ACCOUNTS = "multiple_accounts_enabled"; public XmppConnectionService xmppConnectionService; private CreateConferenceDialogListener mListener; - public static CreateConferenceDialog newInstance(List<String> accounts, boolean multipleAccounts) { - CreateConferenceDialog dialog = new CreateConferenceDialog(); + public static CreatePrivateGroupChatDialog newInstance(List<String> accounts, boolean multipleAccounts) { + CreatePrivateGroupChatDialog dialog = new CreatePrivateGroupChatDialog(); Bundle bundle = new Bundle(); bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) accounts); bundle.putBoolean(MULTIPLE_ACCOUNTS, multipleAccounts); @@ -45,7 +45,7 @@ public class CreateConferenceDialog extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.create_conference); + builder.setTitle(R.string.create_private_group_chat); CreateConferenceDialogBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.create_conference_dialog, null, false); if (getArguments().getBoolean(MULTIPLE_ACCOUNTS)) { binding.yourAccount.setVisibility(View.VISIBLE); @@ -60,6 +60,10 @@ public class CreateConferenceDialog extends DialogFragment { builder.setPositiveButton(R.string.choose_participants, (dialog, which) -> mListener.onCreateDialogPositiveClick(binding.account, binding.groupChatName.getText().toString().trim())); builder.setNegativeButton(R.string.cancel, null); DelayedHintHelper.setHint(R.string.providing_a_name_is_optional, binding.groupChatName); + binding.groupChatName.setOnEditorActionListener((v, actionId, event) -> { + mListener.onCreateDialogPositiveClick(binding.account, binding.groupChatName.getText().toString().trim()); + return true; + }); return builder.create(); } diff --git a/src/main/java/de/pixart/messenger/ui/CreatePublicChannelDialog.java b/src/main/java/de/pixart/messenger/ui/CreatePublicChannelDialog.java new file mode 100644 index 000000000..cbaf3468c --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/CreatePublicChannelDialog.java @@ -0,0 +1,303 @@ +package de.pixart.messenger.ui; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.View; +import android.widget.AdapterView; +import android.widget.Button; + +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import de.pixart.messenger.R; +import de.pixart.messenger.databinding.CreatePublicChannelDialogBinding; +import de.pixart.messenger.entities.Account; +import de.pixart.messenger.services.XmppConnectionService; +import de.pixart.messenger.ui.adapter.KnownHostsAdapter; +import de.pixart.messenger.ui.interfaces.OnBackendConnected; +import de.pixart.messenger.ui.util.DelayedHintHelper; +import de.pixart.messenger.utils.CryptoHelper; +import de.pixart.messenger.xmpp.XmppConnection; +import rocks.xmpp.addr.Jid; + +public class CreatePublicChannelDialog extends DialogFragment implements OnBackendConnected { + + private static final char[] FORBIDDEN = new char[]{'\u0022', '&', '\'', '/', ':', '<', '>', '@'}; + + private static final String ACCOUNTS_LIST_KEY = "activated_accounts_list"; + private static final String MULTIPLE_ACCOUNTS = "multiple_accounts_enabled"; + private CreatePublicChannelDialogListener mListener; + private KnownHostsAdapter knownHostsAdapter; + private boolean jidWasModified = false; + private boolean nameEntered = false; + private boolean skipTetxWatcher = false; + private static final SecureRandom RANDOM = new SecureRandom(); + + public static CreatePublicChannelDialog newInstance(List<String> accounts) { + CreatePublicChannelDialog dialog = new CreatePublicChannelDialog(); + Bundle bundle = new Bundle(); + bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) accounts); + dialog.setArguments(bundle); + return dialog; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setRetainInstance(true); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + jidWasModified = savedInstanceState != null && savedInstanceState.getBoolean("jid_was_modified_false", false); + nameEntered = savedInstanceState != null && savedInstanceState.getBoolean("name_entered", false); + final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.create_public_channel); + final CreatePublicChannelDialogBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.create_public_channel_dialog, null, false); + if (getArguments().getBoolean(MULTIPLE_ACCOUNTS)) { + binding.yourAccount.setVisibility(View.VISIBLE); + binding.account.setVisibility(View.VISIBLE); + } else { + binding.yourAccount.setVisibility(View.GONE); + binding.account.setVisibility(View.GONE); + } + binding.account.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + updateJidSuggestion(binding); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + + } + }); + binding.jid.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + if (skipTetxWatcher) { + return; + } + if (jidWasModified) { + jidWasModified = !TextUtils.isEmpty(s); + } else { + jidWasModified = !s.toString().equals(getJidSuggestion(binding)); + } + } + }); + updateInputs(binding, false); + ArrayList<String> mActivatedAccounts = getArguments().getStringArrayList(ACCOUNTS_LIST_KEY); + StartConversationActivity.populateAccountSpinner(getActivity(), mActivatedAccounts, binding.account); + builder.setView(binding.getRoot()); + builder.setPositiveButton(nameEntered ? R.string.create : R.string.next, null); + builder.setNegativeButton(nameEntered ? R.string.back : R.string.cancel, null); + DelayedHintHelper.setHint(R.string.channel_bare_jid_example, binding.jid); + this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.simple_list_item); + binding.jid.setAdapter(knownHostsAdapter); + final AlertDialog dialog = builder.create(); + binding.groupChatName.setOnEditorActionListener((v, actionId, event) -> { + submit(dialog, binding); + return true; + }); + dialog.setOnShowListener(dialogInterface -> { + dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(v -> goBack(dialog, binding)); + dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> submit(dialog, binding)); + }); + return dialog; + } + + private void updateJidSuggestion(CreatePublicChannelDialogBinding binding) { + if (jidWasModified) { + return; + } + String jid = getJidSuggestion(binding); + skipTetxWatcher = true; + binding.jid.setText(jid); + skipTetxWatcher = false; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putBoolean("jid_was_modified", jidWasModified); + outState.putBoolean("name_entered", nameEntered); + super.onSaveInstanceState(outState); + } + + private static String getJidSuggestion(CreatePublicChannelDialogBinding binding) { + final Account account = StartConversationActivity.getSelectedAccount(binding.getRoot().getContext(), binding.account); + final XmppConnection connection = account == null ? null : account.getXmppConnection(); + if (connection == null) { + return ""; + } + final Editable nameText = binding.groupChatName.getText(); + final String name = nameText == null ? "" : nameText.toString().trim(); + final String domain = connection.getMucServer(); + if (domain == null) { + return ""; + } + final String localpart = clean(name); + if (TextUtils.isEmpty(localpart)) { + return ""; + } else { + try { + return Jid.of(localpart, domain, null).toEscapedString(); + } catch (IllegalArgumentException e) { + return Jid.of(CryptoHelper.pronounceable(RANDOM), domain, null).toEscapedString(); + } + } + } + + private static String clean(String name) { + for (char c : FORBIDDEN) { + name = name.replace(String.valueOf(c), ""); + } + return name.replaceAll("\\s+", "-"); + } + + private void goBack(AlertDialog dialog, CreatePublicChannelDialogBinding binding) { + if (nameEntered) { + nameEntered = false; + updateInputs(binding, true); + updateButtons(dialog); + } else { + dialog.dismiss(); + } + } + + private void submit(AlertDialog dialog, CreatePublicChannelDialogBinding binding) { + final Context context = binding.getRoot().getContext(); + final Editable nameText = binding.groupChatName.getText(); + final String name = nameText == null ? "" : nameText.toString().trim(); + final Editable addressText = binding.jid.getText(); + final String address = addressText == null ? "" : addressText.toString().trim(); + if (nameEntered) { + binding.nameLayout.setError(null); + if (address.isEmpty()) { + binding.xmppAddressLayout.setError(context.getText(R.string.please_enter_xmpp_address)); + } else { + final Jid jid; + try { + jid = Jid.ofEscaped(address); + } catch (IllegalArgumentException e) { + binding.xmppAddressLayout.setError(context.getText(R.string.invalid_jid)); + return; + } + final Account account = StartConversationActivity.getSelectedAccount(context, binding.account); + if (account == null) { + return; + } + final XmppConnectionService service = ((XmppActivity) context).xmppConnectionService; + if (service != null && service.findFirstMuc(jid) != null) { + binding.xmppAddressLayout.setError(context.getString(R.string.channel_already_exists)); + return; + } + mListener.onCreatePublicChannel(account, name, jid); + dialog.dismiss(); + } + } else { + binding.xmppAddressLayout.setError(null); + if (name.isEmpty()) { + binding.nameLayout.setError(context.getText(R.string.please_enter_name)); + } else if (StartConversationActivity.isValidJid(name)) { + binding.nameLayout.setError(context.getText(R.string.this_is_an_xmpp_address)); + } else { + binding.nameLayout.setError(null); + nameEntered = true; + updateInputs(binding, true); + updateButtons(dialog); + binding.jid.setText(""); + binding.jid.append(getJidSuggestion(binding)); + } + } + } + + + private void updateInputs(CreatePublicChannelDialogBinding binding, boolean requestFocus) { + binding.xmppAddressLayout.setVisibility(nameEntered ? View.VISIBLE : View.GONE); + binding.nameLayout.setVisibility(nameEntered ? View.GONE : View.VISIBLE); + if (!requestFocus) { + return; + } + if (nameEntered) { + binding.xmppAddressLayout.requestFocus(); + } else { + binding.nameLayout.requestFocus(); + } + } + + private void updateButtons(AlertDialog dialog) { + final Button positive = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + final Button negative = dialog.getButton(DialogInterface.BUTTON_NEGATIVE); + positive.setText(nameEntered ? R.string.create : R.string.next); + negative.setText(nameEntered ? R.string.back : R.string.cancel); + } + + @Override + public void onBackendConnected() { + refreshKnownHosts(); + } + + private void refreshKnownHosts() { + Activity activity = getActivity(); + if (activity instanceof XmppActivity) { + Collection<String> hosts = ((XmppActivity) activity).xmppConnectionService.getKnownConferenceHosts(); + this.knownHostsAdapter.refresh(hosts); + } + } + + public interface CreatePublicChannelDialogListener { + void onCreatePublicChannel(Account account, String name, Jid address); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + try { + mListener = (CreatePublicChannelDialogListener) context; + } catch (ClassCastException e) { + throw new ClassCastException(context.toString() + + " must implement CreateConferenceDialogListener"); + } + } + + @Override + public void onStart() { + super.onStart(); + final Activity activity = getActivity(); + if (activity instanceof XmppActivity && ((XmppActivity) activity).xmppConnectionService != null) { + refreshKnownHosts(); + } + } + + @Override + public void onDestroyView() { + Dialog dialog = getDialog(); + if (dialog != null && getRetainInstance()) { + dialog.setDismissMessage(null); + } + super.onDestroyView(); + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java index a20190d17..bec621534 100644 --- a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java +++ b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java @@ -16,6 +16,7 @@ import android.preference.PreferenceManager; import android.provider.Settings; import android.security.KeyChain; import android.security.KeyChainAliasCallback; +import android.support.annotation.NonNull; import android.support.design.widget.TextInputLayout; import android.support.v4.content.ContextCompat; import android.support.v7.app.ActionBar; @@ -29,7 +30,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; -import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.ImageView; @@ -63,6 +63,7 @@ import de.pixart.messenger.services.XmppConnectionService.OnAccountUpdate; import de.pixart.messenger.services.XmppConnectionService.OnCaptchaRequested; import de.pixart.messenger.ui.adapter.KnownHostsAdapter; import de.pixart.messenger.ui.adapter.PresenceTemplateAdapter; +import de.pixart.messenger.ui.util.AvatarWorkerTask; import de.pixart.messenger.ui.util.PendingItem; import de.pixart.messenger.ui.util.SoftKeyboardUtils; import de.pixart.messenger.utils.CryptoHelper; @@ -82,6 +83,9 @@ import de.pixart.messenger.xmpp.forms.Data; import de.pixart.messenger.xmpp.pep.Avatar; import rocks.xmpp.addr.Jid; +import static de.pixart.messenger.utils.PermissionUtils.allGranted; +import static de.pixart.messenger.utils.PermissionUtils.writeGranted; + public class EditAccountActivity extends OmemoActivity implements OnAccountUpdate, OnUpdateBlocklist, OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched { @@ -90,6 +94,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat private static final int REQUEST_DATA_SAVER = 0xf244; private static final int REQUEST_CHANGE_STATUS = 0xee11; private static final int REQUEST_ORBOT = 0xff22; + private static final int REQUEST_IMPORT_BACKUP = 0x63fb; private AlertDialog mCaptchaDialog = null; private final AtomicBoolean mPendingReconnect = new AtomicBoolean(false); @@ -619,7 +624,11 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat } private void onEditYourNameClicked(View view) { - quickEdit(mAccount.getDisplayName(), R.string.your_name, value -> { + String nick = ""; + if (mAccount.getDisplayName() != null) { + nick = mAccount.getDisplayName(); + } + quickEdit(nick, R.string.your_name, value -> { final String displayName = value.trim(); updateDisplayName(displayName); mAccount.setDisplayName(displayName); @@ -630,7 +639,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat } private void refreshAvatar() { - binding.avater.setImageBitmap(avatarService().get(mAccount, (int) getResources().getDimension(R.dimen.avatar_on_details_screen_size))); + AvatarWorkerTask.loadAvatar(mAccount, binding.avater, R.dimen.avatar_on_details_screen_size); } @Override @@ -825,6 +834,12 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat return false; } switch (item.getItemId()) { + case R.id.action_import_backup: + if (hasStoragePermission(REQUEST_IMPORT_BACKUP)) { + startActivity(new Intent(this, ImportBackupActivity.class)); + } + overridePendingTransition(R.animator.fade_in, R.animator.fade_out); + break; case R.id.mgmt_account_reconnect: XmppConnection connection = mAccount.getXmppConnection(); if (connection != null) { @@ -978,7 +993,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat binding.xa.setChecked(true); break; case AWAY: - binding.xa.setChecked(true); + binding.away.setChecked(true); break; default: binding.online.setChecked(true); @@ -1042,7 +1057,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat if (!mInitMode) { binding.avater.setVisibility(View.VISIBLE); - binding.avater.setImageBitmap(avatarService().get(this.mAccount, getPixel(Config.AVATAR_SIZE))); + AvatarWorkerTask.loadAvatar(mAccount, binding.avater, R.dimen.avatar_big); this.binding.accountJid.setEnabled(false); } else { binding.avater.setVisibility(View.GONE); @@ -1449,6 +1464,26 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat } @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + if (grantResults.length > 0) { + if (allGranted(grantResults)) { + switch (requestCode) { + case REQUEST_IMPORT_BACKUP: + startActivity(new Intent(this, ImportBackupActivity.class)); + break; + } + } else { + Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show(); + } + } + if (writeGranted(grantResults, permissions)) { + if (xmppConnectionService != null) { + xmppConnectionService.restartFileObserver(); + } + } + } + + @Override public void OnUpdateBlocklist(Status status) { if (isFinishing()) { return; diff --git a/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java b/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java index d0c7980fd..02aa46277 100644 --- a/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java +++ b/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java @@ -114,42 +114,51 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected AlertDialog dialog = builder.create(); View.OnClickListener dialogOnClick = v -> { - final Jid accountJid; - if (!binding.account.isEnabled() && account == null) { - return; - } - try { - if (Config.DOMAIN_LOCK != null) { - accountJid = Jid.of((String) binding.account.getSelectedItem(), Config.DOMAIN_LOCK, null); - } else { - accountJid = Jid.of((String) binding.account.getSelectedItem()); - } - } catch (final IllegalArgumentException e) { - return; - } - final Jid contactJid; - try { - contactJid = Jid.of(binding.jid.getText().toString()); - } catch (final IllegalArgumentException e) { - binding.jid.setError(getActivity().getString(R.string.invalid_jid)); - return; - } - - if (mListener != null) { - try { - if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) { - dialog.dismiss(); - } - } catch (JidError error) { - binding.jid.setError(error.toString()); - } - } + handleEnter(binding, account, dialog); }; + binding.jid.setOnEditorActionListener((v, actionId, event) -> { + handleEnter(binding, account, dialog); + return true; + }); + dialog.show(); dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(dialogOnClick); return dialog; } + private void handleEnter(EnterJidDialogBinding binding, String account, Dialog dialog) { + final Jid accountJid; + if (!binding.account.isEnabled() && account == null) { + return; + } + try { + if (Config.DOMAIN_LOCK != null) { + accountJid = Jid.of((String) binding.account.getSelectedItem(), Config.DOMAIN_LOCK, null); + } else { + accountJid = Jid.of((String) binding.account.getSelectedItem()); + } + } catch (final IllegalArgumentException e) { + return; + } + final Jid contactJid; + try { + contactJid = Jid.of(binding.jid.getText().toString()); + } catch (final IllegalArgumentException e) { + binding.jid.setError(getActivity().getString(R.string.invalid_jid)); + return; + } + + if (mListener != null) { + try { + if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) { + dialog.dismiss(); + } + } catch (JidError error) { + binding.jid.setError(error.toString()); + } + } + } + public void setOnEnterJidDialogPositiveListener(OnEnterJidDialogPositiveListener listener) { this.mListener = listener; } diff --git a/src/main/java/de/pixart/messenger/ui/EnterNameActivity.java b/src/main/java/de/pixart/messenger/ui/EnterNameActivity.java index ac400b9f3..a1a6b28b5 100644 --- a/src/main/java/de/pixart/messenger/ui/EnterNameActivity.java +++ b/src/main/java/de/pixart/messenger/ui/EnterNameActivity.java @@ -7,6 +7,7 @@ import android.support.v7.widget.Toolbar; import android.view.View; import java.util.concurrent.atomic.AtomicBoolean; + import de.pixart.messenger.R; import de.pixart.messenger.databinding.ActivityEnterNameBinding; import de.pixart.messenger.entities.Account; diff --git a/src/main/java/de/pixart/messenger/ui/ImportBackupActivity.java b/src/main/java/de/pixart/messenger/ui/ImportBackupActivity.java new file mode 100644 index 000000000..5397eeaf2 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/ImportBackupActivity.java @@ -0,0 +1,145 @@ +package de.pixart.messenger.ui; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.os.IBinder; +import android.support.design.widget.Snackbar; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AlertDialog; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; + +import java.util.List; + +import de.pixart.messenger.Config; +import de.pixart.messenger.R; +import de.pixart.messenger.databinding.ActivityImportBackupBinding; +import de.pixart.messenger.databinding.DialogEnterPasswordBinding; +import de.pixart.messenger.services.ImportBackupService; +import de.pixart.messenger.ui.adapter.BackupFileAdapter; + +public class ImportBackupActivity extends XmppActivity implements ServiceConnection, ImportBackupService.OnBackupFilesLoaded, BackupFileAdapter.OnItemClickedListener, ImportBackupService.OnBackupProcessed { + + private ActivityImportBackupBinding binding; + + private BackupFileAdapter backupFileAdapter; + private ImportBackupService service; + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = DataBindingUtil.setContentView(this, R.layout.activity_import_backup); + setSupportActionBar((Toolbar) binding.toolbar); + configureActionBar(getSupportActionBar()); + this.backupFileAdapter = new BackupFileAdapter(); + this.binding.list.setAdapter(this.backupFileAdapter); + this.backupFileAdapter.setOnItemClickedListener(this); + } + + @Override + protected void refreshUiReal() { + } + + @Override + public void onStart() { + super.onStart(); + bindService(new Intent(this, ImportBackupService.class), this, Context.BIND_AUTO_CREATE); + } + + @Override + public void onStop() { + super.onStop(); + if (this.service != null) { + this.service.removeOnBackupProcessedListener(this); + } + unbindService(this); + } + + @Override + void onBackendConnected() { + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + ImportBackupService.ImportBackupServiceBinder binder = (ImportBackupService.ImportBackupServiceBinder) service; + this.service = binder.getService(); + this.service.addOnBackupProcessedListener(this); + setLoadingState(this.service.getLoadingState()); + this.service.loadBackupFiles(this); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + this.service = null; + } + + @Override + public void onBackupFilesLoaded(final List<ImportBackupService.BackupFile> files) { + runOnUiThread(() -> backupFileAdapter.setFiles(files)); + } + + @Override + public void onClick(ImportBackupService.BackupFile backupFile) { + final DialogEnterPasswordBinding enterPasswordBinding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.dialog_enter_password, null, false); + Log.d(Config.LOGTAG, "attempting to import " + backupFile.getFile().getAbsolutePath()); + enterPasswordBinding.explain.setText(getString(R.string.enter_password_to_restore, backupFile.getHeader().getJid().toString())); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setView(enterPasswordBinding.getRoot()); + builder.setTitle(R.string.enter_password); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.restore, (dialog, which) -> { + final String password = enterPasswordBinding.accountPassword.getEditableText().toString(); + Intent intent = new Intent(this, ImportBackupService.class); + intent.putExtra("password", password); + intent.putExtra("file", backupFile.getFile().getAbsolutePath()); + setLoadingState(true); + ContextCompat.startForegroundService(this, intent); + }); + builder.setCancelable(false); + builder.create().show(); + } + + private void setLoadingState(final boolean loadingState) { + binding.coordinator.setVisibility(loadingState ? View.GONE : View.VISIBLE); + binding.inProgress.setVisibility(loadingState ? View.VISIBLE : View.GONE); + setTitle(loadingState ? R.string.restoring_backup : R.string.restore_backup); + configureActionBar(getSupportActionBar(), !loadingState); + } + + @Override + public void onBackupRestored() { + runOnUiThread(this::restart); + } + + private void restart() { + Log.d(Config.LOGTAG, "Restarting " + getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext().getPackageName())); + Intent intent = getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext().getPackageName()); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + overridePendingTransition(R.animator.fade_in, R.animator.fade_out); + System.exit(0); + } + + @Override + public void onBackupDecryptionFailed() { + runOnUiThread(() -> { + setLoadingState(false); + Snackbar.make(binding.coordinator, R.string.unable_to_decrypt_backup, Snackbar.LENGTH_LONG).show(); + }); + } + + @Override + public void onBackupRestoreFailed() { + runOnUiThread(() -> { + setLoadingState(false); + Snackbar.make(binding.coordinator, R.string.unable_to_restore_backup, Snackbar.LENGTH_LONG).show(); + }); + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/JoinConferenceDialog.java b/src/main/java/de/pixart/messenger/ui/JoinConferenceDialog.java index a8702ebb6..67cc3a9d2 100644 --- a/src/main/java/de/pixart/messenger/ui/JoinConferenceDialog.java +++ b/src/main/java/de/pixart/messenger/ui/JoinConferenceDialog.java @@ -54,9 +54,9 @@ public class JoinConferenceDialog extends DialogFragment implements OnBackendCon @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.join_conference); + builder.setTitle(R.string.join_public_channel); DialogJoinConferenceBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.dialog_join_conference, null, false); - DelayedHintHelper.setHint(R.string.conference_address_example, binding.jid); + DelayedHintHelper.setHint(R.string.channel_full_jid_example, binding.jid); this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.simple_list_item); binding.jid.setAdapter(knownHostsAdapter); String prefilledJid = getArguments().getString(PREFILLED_JID_KEY); @@ -77,6 +77,10 @@ public class JoinConferenceDialog extends DialogFragment implements OnBackendCon AlertDialog dialog = builder.create(); dialog.show(); dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(view -> mListener.onJoinDialogPositiveClick(dialog, binding.account, binding.jid, binding.bookmark.isChecked())); + binding.jid.setOnEditorActionListener((v, actionId, event) -> { + mListener.onJoinDialogPositiveClick(dialog, binding.account, binding.jid, binding.bookmark.isChecked()); + return true; + }); return dialog; } diff --git a/src/main/java/de/pixart/messenger/ui/LocationActivity.java b/src/main/java/de/pixart/messenger/ui/LocationActivity.java index ec8893e5e..c7668a142 100644 --- a/src/main/java/de/pixart/messenger/ui/LocationActivity.java +++ b/src/main/java/de/pixart/messenger/ui/LocationActivity.java @@ -8,7 +8,6 @@ import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.support.v4.app.ActivityCompat; -import android.support.v7.app.AppCompatActivity; import de.pixart.messenger.Config; diff --git a/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java b/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java index b5f2c02e7..89da3d3d8 100644 --- a/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java +++ b/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java @@ -1,5 +1,6 @@ package de.pixart.messenger.ui; +import android.app.AlertDialog; import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Bundle; @@ -12,7 +13,6 @@ import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; -import android.widget.Toast; import java.security.SecureRandom; import java.util.Arrays; @@ -85,8 +85,9 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher, Ad } else { mUsername.setError(null); Account account = xmppConnectionService.findAccountByJid(jid); + String password = CryptoHelper.createPassword(new SecureRandom()); if (account == null) { - account = new Account(jid, CryptoHelper.createPassword(new SecureRandom())); + account = new Account(jid, password); account.setOption(Account.OPTION_REGISTER, true); account.setOption(Account.OPTION_DISABLED, true); account.setOption(Account.OPTION_MAGIC_CREATE, true); @@ -96,12 +97,27 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher, Ad intent.putExtra("jid", account.getJid().asBareJid().toString()); intent.putExtra("init", true); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - Toast.makeText(MagicCreateActivity.this, R.string.secure_password_generated, Toast.LENGTH_LONG).show(); - StartConversationActivity.addInviteUri(intent, getIntent()); - startActivity(intent); - overridePendingTransition(R.animator.fade_in, R.animator.fade_out); - finish(); - overridePendingTransition(R.animator.fade_in, R.animator.fade_out); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.create_account)); + builder.setCancelable(false); + StringBuilder messasge = new StringBuilder(); + messasge.append(getString(R.string.secure_password_generated)); + messasge.append("\n\n"); + messasge.append(getString(R.string.password)); + messasge.append(": "); + messasge.append(password); + builder.setMessage(messasge); + builder.setPositiveButton(getString(R.string.copy_to_clipboard), (dialogInterface, i) -> { + if (copyTextToClipboard(password, R.string.create_account)) { + StartConversationActivity.addInviteUri(intent, getIntent()); + startActivity(intent); + overridePendingTransition(R.animator.fade_in, R.animator.fade_out); + finish(); + overridePendingTransition(R.animator.fade_in, R.animator.fade_out); + } + }); + builder.create().show(); } } catch (IllegalArgumentException e) { mUsername.setError(getString(R.string.invalid_username)); @@ -136,7 +152,7 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher, Ad generateJID(mUsername.getText().toString()); } - private void generateJID (String s) { + private void generateJID(String s) { domain = mServer.getSelectedItem().toString(); if (s.trim().length() > 0) { try { diff --git a/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java b/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java index bca4d3dbc..fc5563df0 100644 --- a/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java @@ -5,6 +5,7 @@ import android.content.Intent; import android.os.Bundle; import android.security.KeyChain; import android.security.KeyChainAliasCallback; +import android.support.annotation.NonNull; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.util.Pair; @@ -13,9 +14,7 @@ import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; import android.widget.Toast; @@ -35,10 +34,15 @@ import de.pixart.messenger.utils.MenuDoubleTabUtil; import de.pixart.messenger.xmpp.XmppConnection; import rocks.xmpp.addr.Jid; -public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated { +import static de.pixart.messenger.utils.PermissionUtils.allGranted; +import static de.pixart.messenger.utils.PermissionUtils.writeGranted; + +public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated, AccountAdapter.OnTglAccountState { private final String STATE_SELECTED_ACCOUNT = "selected_account"; + private static final int REQUEST_IMPORT_BACKUP = 0x63fb; + protected Account selectedAccount = null; protected Jid selectedAccountJid = null; @@ -75,7 +79,6 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda super.onCreate(savedInstanceState); setContentView(R.layout.activity_manage_accounts); - setSupportActionBar(findViewById(R.id.toolbar)); configureActionBar(getSupportActionBar()); if (savedInstanceState != null) { @@ -92,14 +95,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda accountListView = findViewById(R.id.account_list); this.mAccountAdapter = new AccountAdapter(this, accountList); accountListView.setAdapter(this.mAccountAdapter); - accountListView.setOnItemClickListener(new OnItemClickListener() { - - @Override - public void onItemClick(AdapterView<?> arg0, View view, - int position, long arg3) { - switchToAccount(accountList.get(position)); - } - }); + accountListView.setOnItemClickListener((arg0, view, position, arg3) -> switchToAccount(accountList.get(position))); registerForContextMenu(accountListView); } @@ -163,6 +159,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda addAccount.setVisible(false); addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); } + return true; } @@ -194,7 +191,13 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda } switch (item.getItemId()) { case R.id.action_add_account: - startActivity(new Intent(getApplicationContext(), EditAccountActivity.class)); + startActivity(new Intent(this, EditAccountActivity.class)); + overridePendingTransition(R.animator.fade_in, R.animator.fade_out); + break; + case R.id.action_import_backup: + if (hasStoragePermission(REQUEST_IMPORT_BACKUP)) { + startActivity(new Intent(this, ImportBackupActivity.class)); + } overridePendingTransition(R.animator.fade_in, R.animator.fade_out); break; case R.id.action_add_account_with_cert: @@ -207,6 +210,26 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda } @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + if (grantResults.length > 0) { + if (allGranted(grantResults)) { + switch (requestCode) { + case REQUEST_IMPORT_BACKUP: + startActivity(new Intent(this, ImportBackupActivity.class)); + break; + } + } else { + Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show(); + } + } + if (writeGranted(grantResults, permissions)) { + if (xmppConnectionService != null) { + xmppConnectionService.restartFileObserver(); + } + } + } + + @Override public boolean onNavigateUp() { if (xmppConnectionService.getConversations().size() == 0) { Intent contactsIntent = new Intent(this, @@ -227,6 +250,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda } } + @Override public void onClickTglAccountState(Account account, boolean enable) { if (enable) { enableAccount(account); diff --git a/src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java b/src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java index 2f2dcdf97..a51ef21ab 100644 --- a/src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java +++ b/src/main/java/de/pixart/messenger/ui/MediaViewerActivity.java @@ -7,21 +7,16 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; +import android.databinding.DataBindingUtil; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.MediaMetadataRetriever; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; -import android.support.design.widget.FloatingActionButton; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; -import android.support.v7.view.menu.MenuBuilder; -import android.support.v7.view.menu.MenuPopupHelper; -import android.support.v7.widget.PopupMenu; import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.webkit.MimeTypeMap; @@ -29,8 +24,6 @@ import android.widget.ImageView; import android.widget.Toast; import com.davemorrissey.labs.subscaleview.ImageSource; -import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; -import com.github.rtoshiro.view.video.FullscreenVideoLayout; import java.io.File; import java.io.FileNotFoundException; @@ -40,28 +33,25 @@ import java.util.List; import de.pixart.messenger.Config; import de.pixart.messenger.R; +import de.pixart.messenger.databinding.ActivityMediaViewerBinding; import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.utils.ExifHelper; import de.pixart.messenger.utils.MimeUtils; -import pl.droidsonroids.gif.GifImageView; import static de.pixart.messenger.persistance.FileBackend.close; public class MediaViewerActivity extends XmppActivity { Integer oldOrientation; - SubsamplingScaleImageView mImage; - GifImageView mGIF; - FullscreenVideoLayout mVideo; ImageView mFullscreenbutton; Uri mFileUri; File mFile; - FloatingActionButton fab; int height = 0; int width = 0; int rotation = 0; boolean isImage = false; boolean isVideo = false; + private ActivityMediaViewerBinding binding; public static String getMimeType(String path) { try { @@ -80,6 +70,7 @@ public class MediaViewerActivity extends XmppActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + this.binding = DataBindingUtil.setContentView(this, R.layout.activity_media_viewer); this.mTheme = findTheme(); setTheme(this.mTheme); @@ -97,56 +88,27 @@ public class MediaViewerActivity extends XmppActivity { getWindow().setAttributes(layout); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_media_viewer); - mImage = findViewById(R.id.message_image_view); - mGIF = findViewById(R.id.message_gif_view); - mVideo = findViewById(R.id.message_video_view); mFullscreenbutton = findViewById(R.id.vcv_img_fullscreen); - fab = findViewById(R.id.fab); - fab.setOnClickListener(v -> { - PopupMenu popup = new PopupMenu(MediaViewerActivity.this, v); - popup.inflate(R.menu.media_viewer); - final Menu menu = popup.getMenu(); - MenuItem delete = menu.findItem(R.id.action_delete); - MenuItem open = menu.findItem(R.id.action_open); - Log.d(Config.LOGTAG, "Path = " + mFile.toString()); - if (mFile == null || !mFile.toString().startsWith("/") || mFile.toString().contains(FileBackend.getConversationsDirectory("null"))) { - delete.setVisible(true); - } else { - delete.setVisible(false); - } - if (isVideo) { - if (isDarkTheme()) { - open.setIcon(R.drawable.ic_video_white_24dp); - } else { - open.setIcon(R.drawable.ic_video_black_24dp); - } - } else if (isImage) { - if (isDarkTheme()) { - open.setIcon(R.drawable.ic_image_white_24dp); - } else { - open.setIcon(R.drawable.ic_image_black_24dp); - } - } - popup.setOnMenuItemClickListener(item -> { - switch (item.getItemId()) { - case R.id.action_share: - share(); - break; - case R.id.action_open: - open(); - break; - case R.id.action_delete: + binding.speedDial.inflate(R.menu.media_viewer); + binding.speedDial.setOnActionSelectedListener(actionItem -> { + switch (actionItem.getId()) { + case R.id.action_share: + share(); + break; + case R.id.action_open: + open(); + break; + /* + case R.id.action_delete: + if (mFile == null || !mFile.toString().startsWith("/") || mFile.toString().contains(FileBackend.getConversationsDirectory("null"))) { deleteFile(); - break; - default: - return false; - } - return true; - }); - MenuPopupHelper menuHelper = new MenuPopupHelper(MediaViewerActivity.this, (MenuBuilder) menu, v); - menuHelper.setForceShowIcon(true); - menuHelper.show(); + } + break; + */ + default: + return false; + } + return false; }); } @@ -262,11 +224,11 @@ public class MediaViewerActivity extends XmppActivity { } try { if (gif) { - mGIF.setVisibility(View.VISIBLE); - mGIF.setImageURI(FileUri); + binding.messageGifView.setVisibility(View.VISIBLE); + binding.messageGifView.setImageURI(FileUri); } else { - mImage.setVisibility(View.VISIBLE); - mImage.setImage(ImageSource.uri(FileUri)); + binding.messageImageView.setVisibility(View.VISIBLE); + binding.messageImageView.setImage(ImageSource.uri(FileUri)); } } catch (Exception e) { Toast.makeText(this, getString(R.string.error_file_corrupt), Toast.LENGTH_LONG).show(); @@ -296,10 +258,10 @@ public class MediaViewerActivity extends XmppActivity { rotateScreen(width, height, rotation); } try { - mVideo.setVisibility(View.VISIBLE); - mVideo.setVideoURI(uri); + binding.messageVideoView.setVisibility(View.VISIBLE); + binding.messageVideoView.setVideoURI(uri); mFullscreenbutton.setVisibility(View.INVISIBLE); - mVideo.setShouldAutoplay(true); + binding.messageVideoView.setShouldAutoplay(true); } catch (IOException e) { Toast.makeText(this, getString(R.string.error_file_corrupt), Toast.LENGTH_LONG).show(); @@ -348,13 +310,13 @@ public class MediaViewerActivity extends XmppActivity { } getWindow().setAttributes(layout); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - mVideo.setShouldAutoplay(true); + binding.messageVideoView.setShouldAutoplay(true); super.onResume(); } @Override public void onPause() { - mVideo.reset(); + binding.messageVideoView.reset(); WindowManager.LayoutParams layout = getWindow().getAttributes(); if (useMaxBrightness()) { layout.screenBrightness = -1; @@ -367,7 +329,7 @@ public class MediaViewerActivity extends XmppActivity { @Override public void onStop() { - mVideo.reset(); + binding.messageVideoView.reset(); WindowManager.LayoutParams layout = getWindow().getAttributes(); if (useMaxBrightness()) { layout.screenBrightness = -1; diff --git a/src/main/java/de/pixart/messenger/ui/MucUsersActivity.java b/src/main/java/de/pixart/messenger/ui/MucUsersActivity.java new file mode 100644 index 000000000..9961e5512 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/MucUsersActivity.java @@ -0,0 +1,87 @@ +package de.pixart.messenger.ui; + +import android.content.Intent; +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.Collections; + +import de.pixart.messenger.R; +import de.pixart.messenger.databinding.ActivityMucUsersBinding; +import de.pixart.messenger.entities.Conversation; +import de.pixart.messenger.entities.MucOptions; +import de.pixart.messenger.services.XmppConnectionService; +import de.pixart.messenger.ui.adapter.UserAdapter; +import de.pixart.messenger.ui.util.MucDetailsContextMenuHelper; +import rocks.xmpp.addr.Jid; + +public class MucUsersActivity extends XmppActivity implements XmppConnectionService.OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged { + + private UserAdapter userAdapter; + + private Conversation mConversation = null; + + @Override + protected void refreshUiReal() { + } + + @Override + void onBackendConnected() { + final Intent intent = getIntent(); + final String uuid = intent == null ? null : intent.getStringExtra("uuid"); + if (uuid != null) { + mConversation = xmppConnectionService.findConversationByUuid(uuid); + } + loadAndSubmitUsers(); + } + + private void loadAndSubmitUsers() { + if (mConversation != null) { + ArrayList<MucOptions.User> users = mConversation.getMucOptions().getUsers(); + Collections.sort(users); + userAdapter.submitList(users); + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + if (!MucDetailsContextMenuHelper.onContextItemSelected(item, userAdapter.getSelectedUser(), this)) { + return super.onContextItemSelected(item); + } + return true; + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ActivityMucUsersBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_muc_users); + setSupportActionBar((Toolbar) binding.toolbar); + configureActionBar(getSupportActionBar(), true); + this.userAdapter = new UserAdapter(getPreferences().getBoolean("advanced_muc_mode", false)); + binding.list.setAdapter(this.userAdapter); + } + + + @Override + public void onMucRosterUpdate() { + loadAndSubmitUsers(); + } + + private void displayToast(final String msg) { + runOnUiThread(() -> Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()); + } + + @Override + public void onAffiliationChangedSuccessful(Jid jid) { + + } + + @Override + public void onAffiliationChangeFailed(Jid jid, int resId) { + displayToast(getString(resId, jid.asBareJid().toString())); + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java b/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java index 469ce8021..e38591848 100644 --- a/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java +++ b/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java @@ -15,6 +15,8 @@ import android.widget.Toast; import com.theartofdev.edmodo.cropper.CropImage; +import java.util.concurrent.atomic.AtomicBoolean; + import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.entities.Account; @@ -34,6 +36,7 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC private Account account; private boolean support = false; private boolean publishing = false; + private AtomicBoolean handledExternalUri = new AtomicBoolean(false); private OnLongClickListener backToDefaultListener = new OnLongClickListener() { @Override @@ -103,6 +106,19 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC }); this.avatar.setOnClickListener(v -> chooseAvatar()); this.defaultUri = PhoneHelper.getProfilePictureUri(getApplicationContext()); + if (savedInstanceState != null) { + this.avatarUri = savedInstanceState.getParcelable("uri"); + this.handledExternalUri.set(savedInstanceState.getBoolean("handle_external_uri", false)); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + if (this.avatarUri != null) { + outState.putParcelable("uri", this.avatarUri); + } + outState.putBoolean("handle_external_uri", handledExternalUri.get()); + super.onSaveInstanceState(outState); } @@ -160,10 +176,20 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC final Intent intent = getIntent(); this.mInitialAccountSetup = intent != null && intent.getBooleanExtra("setup", false); + final Uri uri = intent != null ? intent.getData() : null; + + if (uri != null && handledExternalUri.compareAndSet(false, true)) { + CropImage.activity(uri).setOutputCompressFormat(Bitmap.CompressFormat.PNG) + .setAspectRatio(1, 1) + .setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE) + .start(this); + return; + } + if (this.mInitialAccountSetup) { this.cancelButton.setText(R.string.skip); } - configureActionBar(getSupportActionBar(), !this.mInitialAccountSetup); + configureActionBar(getSupportActionBar(), !this.mInitialAccountSetup && !handledExternalUri.get()); } protected void loadImageIntoPreview(Uri uri) { diff --git a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java index dc41011fb..ecae24588 100644 --- a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java @@ -20,9 +20,6 @@ import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.EditText; import android.widget.Toast; import java.io.File; @@ -36,10 +33,10 @@ import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.crypto.OmemoSetting; import de.pixart.messenger.entities.Account; -import de.pixart.messenger.services.ExportLogsService; +import de.pixart.messenger.persistance.FileBackend; +import de.pixart.messenger.services.ExportBackupService; import de.pixart.messenger.services.MemorizingTrustManager; import de.pixart.messenger.ui.util.StyledAttributes; -import de.pixart.messenger.utils.Compatibility; import de.pixart.messenger.utils.TimeframeUtils; import rocks.xmpp.addr.Jid; @@ -63,8 +60,9 @@ public class SettingsActivity extends XmppActivity implements public static final String QUICK_SHARE_ATTACHMENT_CHOICE = "quick_share_attachment_choice"; public static final String NUMBER_OF_ACCOUNTS = "number_of_accounts"; public static final String PLAY_GIF_INSIDE = "play_gif_inside"; + public static final String PREFER_XMPP_AVATAR = "prefer_xmpp_avatar"; - public static final int REQUEST_WRITE_LOGS = 0xbf8701; + public static final int REQUEST_CREATE_BACKUP = 0xbf8701; Preference multiAccountPreference; Preference BundledEmojiPreference; Preference QuickShareAttachmentChoicePreference; @@ -249,11 +247,12 @@ public class SettingsActivity extends XmppActivity implements }); } - final Preference exportLogsPreference = mSettingsFragment.findPreference("export_logs"); - if (exportLogsPreference != null) { - exportLogsPreference.setOnPreferenceClickListener(preference -> { - if (hasStoragePermission(REQUEST_WRITE_LOGS)) { - startExport(); + final Preference createBackupPreference = mSettingsFragment.findPreference("create_backup"); + if (createBackupPreference != null) { + createBackupPreference.setSummary(getString(R.string.pref_create_backup_summary, FileBackend.getBackupDirectory())); + createBackupPreference.setOnPreferenceClickListener(preference -> { + if (hasStoragePermission(REQUEST_CREATE_BACKUP)) { + createBackup(); } return true; }); @@ -424,75 +423,9 @@ public class SettingsActivity extends XmppActivity implements private void enableMultiAccounts() { if (!isMultiAccountChecked) { multiAccountPreference.setEnabled(true); - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setCancelable(false); - builder.setTitle(R.string.pref_enable_multi_accounts_title); - builder.setMessage(R.string.pref_enable_multi_accounts_summary); - builder.setNegativeButton(R.string.cancel, (dialog, which) -> { - ((CheckBoxPreference) multiAccountPreference).setChecked(false); - }); - builder.setPositiveButton(R.string.enter_password, (dialog, which) -> { - ((CheckBoxPreference) multiAccountPreference).setChecked(false); - enterPasswordDialog(); - }); - AlertDialog dialog = builder.create(); - dialog.show(); } } - public void enterPasswordDialog() { - LayoutInflater li = LayoutInflater.from(this); - View promptsView = li.inflate(R.layout.password, null); - - final Preference preference = mSettingsFragment.findPreference("enable_multi_accounts"); - - final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); - alertDialogBuilder.setView(promptsView); - final EditText password = promptsView.findViewById(R.id.password); - final EditText confirm_password = promptsView.findViewById(R.id.confirm_password); - confirm_password.setVisibility(View.VISIBLE); - alertDialogBuilder.setTitle(R.string.enter_password); - alertDialogBuilder.setMessage(R.string.enter_password); - alertDialogBuilder - .setCancelable(false) - .setPositiveButton(R.string.ok, - (dialog, id) -> { - final String pw1 = password.getText().toString(); - final String pw2 = confirm_password.getText().toString(); - if (!pw1.equals(pw2)) { - ((CheckBoxPreference) preference).setChecked(false); - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.error); - builder.setMessage(R.string.passwords_do_not_match); - builder.setNegativeButton(R.string.cancel, null); - builder.setPositiveButton(R.string.try_again, (dialog12, id12) -> enterPasswordDialog()); - builder.create().show(); - } else if (pw1.trim().isEmpty()) { - ((CheckBoxPreference) preference).setChecked(false); - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.error); - builder.setMessage(R.string.password_should_not_be_empty); - builder.setNegativeButton(R.string.cancel, null); - builder.setPositiveButton(R.string.try_again, (dialog1, id1) -> enterPasswordDialog()); - builder.create().show(); - } else { - ((CheckBoxPreference) preference).setChecked(true); - SharedPreferences multiaccount_prefs = getApplicationContext().getSharedPreferences(USE_MULTI_ACCOUNTS, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = multiaccount_prefs.edit(); - editor.putString("BackupPW", pw1); - boolean passwordstored = editor.commit(); - Log.d(Config.LOGTAG, "saving multiaccount password " + passwordstored); - if (passwordstored) { - recreate(); - } else { - //handleMultiAccountChanges(); - } - } - }) - .setNegativeButton(R.string.cancel, null); - alertDialogBuilder.create().show(); - } - @Override public void onStop() { @@ -538,26 +471,19 @@ public class SettingsActivity extends XmppActivity implements @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (grantResults.length > 0) + if (grantResults.length > 0) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (requestCode == REQUEST_WRITE_LOGS) { - startExport(); + if (requestCode == REQUEST_CREATE_BACKUP) { + createBackup(); } } else { Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show(); } + } } - private void startExport() { - try { - if (Compatibility.runsAndTargetsTwentySix(this)) { - ContextCompat.startForegroundService(this, new Intent(this, ExportLogsService.class)); - } else { - this.startService(new Intent(this, ExportLogsService.class)); - } - } catch (RuntimeException e) { - Log.d(Config.LOGTAG, "SettingsActivity was unable to start ExportLogsService"); - } + private void createBackup() { + ContextCompat.startForegroundService(this, new Intent(this, ExportBackupService.class)); } private void displayToast(final String msg) { diff --git a/src/main/java/de/pixart/messenger/ui/ShareViaAccountActivity.java b/src/main/java/de/pixart/messenger/ui/ShareViaAccountActivity.java index 2d535a44c..4f0e81e0d 100644 --- a/src/main/java/de/pixart/messenger/ui/ShareViaAccountActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ShareViaAccountActivity.java @@ -1,7 +1,6 @@ package de.pixart.messenger.ui; import android.os.Bundle; -import android.support.v7.app.ActionBar; import android.widget.ListView; import java.util.ArrayList; @@ -27,11 +26,6 @@ public class ShareViaAccountActivity extends XmppActivity { accountList.clear(); accountList.addAll(xmppConnectionService.getAccounts()); } - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.setHomeButtonEnabled(this.accountList.size() > 0); - actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0); - } mAccountAdapter.notifyDataSetChanged(); } diff --git a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java index 0d1a3286d..26d8debc5 100644 --- a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java @@ -18,13 +18,10 @@ import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.entities.Account; import de.pixart.messenger.entities.Conversation; -import de.pixart.messenger.services.EmojiService; import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.ui.adapter.ConversationAdapter; import rocks.xmpp.addr.Jid; -import static de.pixart.messenger.ui.SettingsActivity.USE_BUNDLED_EMOJIS; - public class ShareWithActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate { private static final int REQUEST_STORAGE_PERMISSION = 0x733f32; @@ -73,8 +70,6 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - boolean useBundledEmoji = getPreferences().getBoolean(USE_BUNDLED_EMOJIS, getResources().getBoolean(R.bool.use_bundled_emoji)); - new EmojiService(this).init(useBundledEmoji); setContentView(R.layout.activity_share_with); setSupportActionBar(findViewById(R.id.toolbar)); if (getSupportActionBar() != null) { @@ -130,7 +125,7 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); } if (xmppConnectionServiceBound) { - xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0); + xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0, false); } } @@ -182,7 +177,8 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer } public void refreshUiReal() { - xmppConnectionService.populateWithOrderedConversations(mConversations, this.share != null && this.share.uris.size() == 0); + //TODO inject desired order to not resort on refresh + xmppConnectionService.populateWithOrderedConversations(mConversations, this.share != null && this.share.uris.size() == 0, false); mAdapter.notifyDataSetChanged(); } diff --git a/src/main/java/de/pixart/messenger/ui/ShortcutActivity.java b/src/main/java/de/pixart/messenger/ui/ShortcutActivity.java index 68ad0cb86..9afb2659f 100644 --- a/src/main/java/de/pixart/messenger/ui/ShortcutActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ShortcutActivity.java @@ -5,7 +5,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.ActionBar; -import android.view.View; import android.view.inputmethod.InputMethodManager; import java.util.Arrays; diff --git a/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java b/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java index d8c565345..26865aed0 100644 --- a/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java @@ -28,11 +28,8 @@ import java.util.Locale; import de.pixart.messenger.Config; import de.pixart.messenger.R; -import de.pixart.messenger.services.EmojiService; import de.pixart.messenger.utils.MenuDoubleTabUtil; -import static de.pixart.messenger.ui.SettingsActivity.USE_BUNDLED_EMOJIS; - public class ShowLocationActivity extends XmppActivity { FloatingActionButton fab; private Location location; @@ -65,8 +62,6 @@ public class ShowLocationActivity extends XmppActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - boolean useBundledEmoji = getPreferences().getBoolean(USE_BUNDLED_EMOJIS, getResources().getBoolean(R.bool.use_bundled_emoji)); - new EmojiService(this).init(useBundledEmoji); setContentView(R.layout.activity_show_locaction); setTitle(getString(R.string.show_location)); setSupportActionBar(findViewById(R.id.toolbar)); diff --git a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java index 825494d90..b87a26b0c 100644 --- a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java +++ b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java @@ -13,7 +13,6 @@ import android.databinding.DataBindingUtil; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; @@ -21,7 +20,6 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.app.ListFragment; import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; @@ -63,7 +61,7 @@ import de.pixart.messenger.entities.Contact; import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.entities.ListItem; import de.pixart.messenger.entities.Presence; -import de.pixart.messenger.services.EmojiService; +import de.pixart.messenger.services.QuickConversationsService; import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.services.XmppConnectionService.OnRosterUpdate; import de.pixart.messenger.ui.adapter.ListItemAdapter; @@ -77,11 +75,9 @@ import de.pixart.messenger.xmpp.OnUpdateBlocklist; import de.pixart.messenger.xmpp.XmppConnection; import rocks.xmpp.addr.Jid; -import static de.pixart.messenger.ui.SettingsActivity.USE_BUNDLED_EMOJIS; +public class StartConversationActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, CreatePrivateGroupChatDialog.CreateConferenceDialogListener, JoinConferenceDialog.JoinConferenceDialogListener, CreatePublicChannelDialog.CreatePublicChannelDialogListener { -public class StartConversationActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, CreateConferenceDialog.CreateConferenceDialogListener, JoinConferenceDialog.JoinConferenceDialogListener { - - public static final String EXTRA_INVITE_URI = "eu.siacs.conversations.invite_uri"; + public static final String EXTRA_INVITE_URI = "de.pixart.messenger.invite_uri"; private final int REQUEST_SYNC_CONTACTS = 0x28cf; private final int REQUEST_CREATE_CONFERENCE = 0x39da; @@ -115,7 +111,9 @@ public class StartConversationActivity extends XmppActivity implements XmppConne imm.showSoftInput(mSearchEditText, InputMethodManager.SHOW_IMPLICIT); } }); - + if (binding.speedDial.isOpen()) { + binding.speedDial.close(); + } return true; } @@ -202,12 +200,6 @@ public class StartConversationActivity extends XmppActivity implements XmppConne return true; } }; - private ViewPager.SimpleOnPageChangeListener mOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() { - @Override - public void onPageSelected(int position) { - onTabChanged(); - } - }; public static void populateAccountSpinner(Context context, List<String> accounts, Spinner spinner) { if (accounts.size() > 0) { @@ -261,32 +253,12 @@ public class StartConversationActivity extends XmppActivity implements XmppConne @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - new EmojiService(this).init(useBundledEmoji()); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_start_conversation); Toolbar toolbar = (Toolbar) binding.toolbar; setSupportActionBar(toolbar); configureActionBar(getSupportActionBar()); - this.binding.fab.setOnClickListener((v) -> { - if (binding.startConversationViewPager.getCurrentItem() == 0) { - String searchString = mSearchEditText != null ? mSearchEditText.getText().toString() : null; - if (searchString != null && !searchString.trim().isEmpty()) { - try { - Jid jid = Jid.of(searchString); - if (jid.getLocal() != null && jid.isBareJid() && jid.getDomain().contains(".")) { - showCreateContactDialog(jid.toString(), null); - return; - } - } catch (IllegalArgumentException ignored) { - //ignore and fall through - } - } - showCreateContactDialog(null, null); - } else { - showCreateConferenceDialog(); - } - }); + binding.speedDial.inflate(R.menu.start_conversation_fab_submenu); binding.tabLayout.setupWithViewPager(binding.startConversationViewPager); - binding.startConversationViewPager.addOnPageChangeListener(mOnPageChangeListener); mListPagerAdapter = new ListPagerAdapter(getSupportFragmentManager()); binding.startConversationViewPager.setAdapter(mListPagerAdapter); @@ -295,7 +267,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne mContactsAdapter.setOnTagClickedListener(this.mOnTagClickedListener); final SharedPreferences preferences = getPreferences(); - this.mHideOfflineContacts = preferences.getBoolean("hide_offline", false); + this.mHideOfflineContacts = QuickConversationsService.isConversations() && preferences.getBoolean("hide_offline", false); final boolean startSearching = preferences.getBoolean("start_searching", getResources().getBoolean(R.bool.start_searching)); @@ -318,6 +290,40 @@ public class StartConversationActivity extends XmppActivity implements XmppConne } else if (startSearching && mInitialSearchValue.peek() == null) { mInitialSearchValue.push(""); } + mRequestedContactsPermission.set(savedInstanceState != null && savedInstanceState.getBoolean("requested_contacts_permission", false)); + binding.speedDial.setOnActionSelectedListener(actionItem -> { + final String searchString = mSearchEditText != null ? mSearchEditText.getText().toString() : null; + final String prefilled; + if (isValidJid(searchString)) { + prefilled = Jid.ofEscaped(searchString).toEscapedString(); + } else { + prefilled = null; + } + switch (actionItem.getId()) { + case R.id.join_public_channel: + showJoinConferenceDialog(prefilled); + break; + case R.id.create_private_group_chat: + showCreatePrivateGroupChatDialog(); + break; + case R.id.create_public_channel: + showPublicChannelDialog(); + break; + case R.id.create_contact: + showCreateContactDialog(prefilled, null); + break; + } + return false; + }); + } + + public static boolean isValidJid(String input) { + try { + Jid jid = Jid.ofEscaped(input); + return !jid.isDomainJid(); + } catch (IllegalArgumentException e) { + return false; + } } @Override @@ -367,10 +373,6 @@ public class StartConversationActivity extends XmppActivity implements XmppConne switchToConversation(conversation); } - protected void openConversationForBookmark() { - openConversationForBookmark(conference_context_id); - } - protected void openConversationForBookmark(int position) { Bookmark bookmark = (Bookmark) conferences.get(position); openConversationsForBookmark(bookmark); @@ -476,8 +478,8 @@ public class StartConversationActivity extends XmppActivity implements XmppConne ft.addToBackStack(null); EnterJidDialog dialog = EnterJidDialog.newInstance( mActivatedAccounts, - getString(R.string.create_contact), - getString(R.string.create), + getString(R.string.add_contact), + getString(R.string.add), prefilledJid, null, invite == null || !invite.hasFingerprints(), @@ -528,32 +530,51 @@ public class StartConversationActivity extends XmppActivity implements XmppConne joinConferenceFragment.show(ft, FRAGMENT_TAG_DIALOG); } - private void showCreateConferenceDialog() { + private void showCreatePrivateGroupChatDialog() { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); Fragment prev = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG); if (prev != null) { ft.remove(prev); } ft.addToBackStack(null); - CreateConferenceDialog createConferenceFragment = CreateConferenceDialog.newInstance(mActivatedAccounts, xmppConnectionService.multipleAccounts()); + CreatePrivateGroupChatDialog createConferenceFragment = CreatePrivateGroupChatDialog.newInstance(mActivatedAccounts, xmppConnectionService.multipleAccounts()); createConferenceFragment.show(ft, FRAGMENT_TAG_DIALOG); } - private Account getSelectedAccount(Spinner spinner) { - if (!spinner.isEnabled()) { + private void showPublicChannelDialog() { + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + Fragment prev = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG); + if (prev != null) { + ft.remove(prev); + } + ft.addToBackStack(null); + CreatePublicChannelDialog dialog = CreatePublicChannelDialog.newInstance(mActivatedAccounts); + dialog.show(ft, FRAGMENT_TAG_DIALOG); + } + + public static Account getSelectedAccount(Context context, Spinner spinner) { + if (spinner == null || !spinner.isEnabled()) { return null; } - Jid jid; - try { - if (Config.DOMAIN_LOCK != null) { - jid = Jid.of((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null); - } else { - jid = Jid.of((String) spinner.getSelectedItem()); + if (context instanceof XmppActivity) { + Jid jid; + try { + if (Config.DOMAIN_LOCK != null) { + jid = Jid.of((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null); + } else { + jid = Jid.of((String) spinner.getSelectedItem()); + } + } catch (final IllegalArgumentException e) { + return null; } - } catch (final IllegalArgumentException e) { + final XmppConnectionService service = ((XmppActivity) context).xmppConnectionService; + if (service == null) { + return null; + } + return service.findAccountByJid(jid); + } else { return null; } - return xmppConnectionService.findAccountByJid(jid); } protected void switchToConversation(Contact contact) { @@ -598,9 +619,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne } else { menuActionAccounts.setTitle(R.string.action_accounts); } - MenuItem joinGroupChat = menu.findItem(R.id.action_join_conference); MenuItem qrCodeScanMenuItem = menu.findItem(R.id.action_scan_qr_code); - joinGroupChat.setVisible(binding.startConversationViewPager.getCurrentItem() == 1); qrCodeScanMenuItem.setVisible(isCameraFeatureAvailable()); menuHideOffline.setChecked(this.mHideOfflineContacts); mMenuSearchView = menu.findItem(R.id.action_search); @@ -628,9 +647,6 @@ public class StartConversationActivity extends XmppActivity implements XmppConne case android.R.id.home: navigateBack(); return true; - case R.id.action_join_conference: - showJoinConferenceDialog(null); - return true; case R.id.action_scan_qr_code: UriHandlerActivity.scan(this); return true; @@ -901,17 +917,6 @@ public class StartConversationActivity extends XmppActivity implements XmppConne mConferenceAdapter.notifyDataSetChanged(); } - private void onTabChanged() { - @DrawableRes final int fabDrawable; - if (binding.startConversationViewPager.getCurrentItem() == 0) { - fabDrawable = R.drawable.ic_person_add_white_24dp; - } else { - fabDrawable = R.drawable.ic_group_add_white_24dp; - } - binding.fab.setImageResource(fabDrawable); - invalidateOptionsMenu(); - } - @Override public void OnUpdateBlocklist(final Status status) { refreshUi(); @@ -927,6 +932,10 @@ public class StartConversationActivity extends XmppActivity implements XmppConne @Override public void onBackPressed() { + if (binding.speedDial.isOpen()) { + binding.speedDial.close(); + return; + } navigateBack(); } @@ -939,16 +948,12 @@ public class StartConversationActivity extends XmppActivity implements XmppConne finish(); } - public boolean useBundledEmoji() { - return getPreferences().getBoolean(USE_BUNDLED_EMOJIS, getResources().getBoolean(R.bool.use_bundled_emoji)); - } - @Override public void onCreateDialogPositiveClick(Spinner spinner, String name) { if (!xmppConnectionServiceBound) { return; } - final Account account = getSelectedAccount(spinner); + final Account account = getSelectedAccount(this, spinner); if (account == null) { return; } @@ -967,7 +972,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne if (!xmppConnectionServiceBound) { return; } - final Account account = getSelectedAccount(spinner); + final Account account = getSelectedAccount(this, spinner); if (account == null) { return; } @@ -1010,6 +1015,35 @@ public class StartConversationActivity extends XmppActivity implements XmppConne refreshUi(); } + @Override + public void onCreatePublicChannel(Account account, String name, Jid address) { + mToast = Toast.makeText(this, R.string.creating_channel, Toast.LENGTH_LONG); + mToast.show(); + xmppConnectionService.createPublicChannel(account, name, address, new UiCallback<Conversation>() { + @Override + public void success(Conversation conversation) { + runOnUiThread(() -> { + hideToast(); + switchToConversation(conversation); + }); + + } + + @Override + public void error(int errorCode, Conversation conversation) { + runOnUiThread(() -> { + replaceToast(getString(errorCode)); + switchToConversation(conversation); + }); + } + + @Override + public void userInputRequried(PendingIntent pi, Conversation object) { + + } + }); + } + public static class MyListFragment extends ListFragment { private AdapterView.OnItemClickListener mOnItemClickListener; private int mResContextMenu; @@ -1091,9 +1125,6 @@ public class StartConversationActivity extends XmppActivity implements XmppConne case R.id.context_delete_contact: activity.deleteContact(); break; - case R.id.context_join_conference: - activity.openConversationForBookmark(); - break; case R.id.context_share_uri: activity.shareBookmarkUri(); break; @@ -1158,7 +1189,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne case 0: return getResources().getString(R.string.contacts); case 1: - return getResources().getString(R.string.conferences); + return getResources().getString(R.string.bookmarks); default: return super.getPageTitle(position); } diff --git a/src/main/java/de/pixart/messenger/ui/UriHandlerActivity.java b/src/main/java/de/pixart/messenger/ui/UriHandlerActivity.java index 559e20648..4a6dfa234 100644 --- a/src/main/java/de/pixart/messenger/ui/UriHandlerActivity.java +++ b/src/main/java/de/pixart/messenger/ui/UriHandlerActivity.java @@ -7,7 +7,6 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.widget.Toast; @@ -16,7 +15,6 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.persistance.DatabaseBackend; import de.pixart.messenger.utils.SignupUtils; @@ -86,7 +84,7 @@ public class UriHandlerActivity extends AppCompatActivity { private void handleUri(Uri uri, final boolean scanned) { final Intent intent; final XmppUri xmppUri = new XmppUri(uri); - final List<Jid> accounts = DatabaseBackend.getInstance(this).getAccountJids(); //TODO only look at enabled accounts + final List<Jid> accounts = DatabaseBackend.getInstance(this).getAccountJids(true); if (accounts.size() == 0) { if (xmppUri.isJidValid()) { intent = SignupUtils.getSignUpIntent(this); @@ -102,7 +100,7 @@ public class UriHandlerActivity extends AppCompatActivity { if (jid != null) { Class clazz; try { - clazz = Class.forName("eu.siacs.conversations.ui.ShareViaAccountActivity"); + clazz = Class.forName("de.pixart.messenger.ui.ShareViaAccountActivity"); } catch (ClassNotFoundException e) { clazz = null; } diff --git a/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java b/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java index 75493d6c8..0ea648370 100644 --- a/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java +++ b/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java @@ -1,49 +1,27 @@ package de.pixart.messenger.ui; -import android.Manifest; -import android.app.ProgressDialog; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.os.Build; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v7.app.ActionBar; -import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.view.LayoutInflater; import android.view.View; import android.widget.Button; -import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.util.List; -import javax.crypto.NoSuchPaddingException; - -import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.entities.Account; -import de.pixart.messenger.persistance.DatabaseBackend; -import de.pixart.messenger.persistance.FileBackend; -import de.pixart.messenger.utils.EncryptDecryptFile; -import de.pixart.messenger.utils.XmppUri; + +import static de.pixart.messenger.utils.PermissionUtils.allGranted; +import static de.pixart.messenger.utils.PermissionUtils.writeGranted; public class WelcomeActivity extends XmppActivity { - boolean importSuccessful = false; + private static final int REQUEST_IMPORT_BACKUP = 0x63fb; @Override protected void refreshUiReal() { @@ -85,21 +63,14 @@ public class WelcomeActivity extends XmppActivity { ab.setDisplayHomeAsUpEnabled(false); } - //check if there is a backed up database -- - if (hasStoragePermission(REQUEST_READ_EXTERNAL_STORAGE)) { - BackupAvailable(); - } - - final Button ImportDatabase = findViewById(R.id.import_database); final TextView ImportText = findViewById(R.id.import_text); - - if (BackupAvailable() != 0) { + if (hasStoragePermission(REQUEST_IMPORT_BACKUP)) { ImportDatabase.setVisibility(View.VISIBLE); ImportText.setVisibility(View.VISIBLE); } + ImportDatabase.setOnClickListener(v -> startActivity(new Intent(this, ImportBackupActivity.class))); - ImportDatabase.setOnClickListener(v -> enterPasswordDialog(BackupAvailable())); final Button createAccount = findViewById(R.id.create_account); createAccount.setOnClickListener(v -> { @@ -127,290 +98,34 @@ public class WelcomeActivity extends XmppActivity { } - public void enterPasswordDialog(final int backup_type) { - if (backup_type == 1) { - LayoutInflater li = LayoutInflater.from(WelcomeActivity.this); - View promptsView = li.inflate(R.layout.password, null); - final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(WelcomeActivity.this); - alertDialogBuilder.setView(promptsView); - final EditText userInput = promptsView - .findViewById(R.id.password); - alertDialogBuilder.setTitle(R.string.enter_password); - alertDialogBuilder.setMessage(R.string.enter_account_password); - alertDialogBuilder - .setCancelable(false) - .setPositiveButton(R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - final String password = userInput.getText().toString(); - final ProgressDialog pd = ProgressDialog.show(WelcomeActivity.this, getString(R.string.please_wait), getString(R.string.databaseimport_started), true); - if (!password.isEmpty()) { - new Thread(new Runnable() { - @Override - public void run() { - try { - checkDatabase(password); - } catch (IOException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - pd.dismiss(); - } - }).start(); - } else { - AlertDialog.Builder builder = new AlertDialog.Builder(WelcomeActivity.this); - builder.setTitle(R.string.error); - builder.setMessage(R.string.password_should_not_be_empty); - builder.setNegativeButton(R.string.cancel, null); - builder.setPositiveButton(R.string.try_again, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - enterPasswordDialog(backup_type); - } - }); - builder.create().show(); - } - } - }) - .setNegativeButton(R.string.cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - Toast.makeText(WelcomeActivity.this, R.string.import_canceled, Toast.LENGTH_LONG).show(); - dialog.dismiss(); - } - } - ); - WelcomeActivity.this.runOnUiThread(new Runnable() { - public void run() { - // create alert dialog - AlertDialog alertDialog = alertDialogBuilder.create(); - // show it - alertDialog.show(); - } - }); - } else { - try { - checkDatabase(null); - } catch (IOException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - } + public void addInviteUri(Intent intent) { + StartConversationActivity.addInviteUri(intent, getIntent()); } - private int BackupAvailable() { - // Set the folder on the SDcard - File filePath_enc = new File(FileBackend.getBackupDirectory() + "/database.db.crypt"); - File filePath_dec = new File(FileBackend.getBackupDirectory() + "/database.db"); - if (filePath_enc.exists()) { - Log.d(Config.LOGTAG, "DB Path existing (encrypted)"); - return 1; - } else if (filePath_dec.exists()) { - Log.d(Config.LOGTAG, "DB Path existing (decrypted)"); - return 2; - } else { - Log.d(Config.LOGTAG, "DB Path not existing"); - return 0; - } + public static void launch(AppCompatActivity activity) { + Intent intent = new Intent(activity, WelcomeActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + activity.startActivity(intent); + activity.overridePendingTransition(0, 0); } - private void checkDatabase(String DecryptionKey) throws IOException { - if (DecryptionKey != null) { - // Set the folder on the SDcard - File directory = new File(FileBackend.getBackupDirectory()); - // Set the input file stream up: - FileInputStream InputFile = new FileInputStream(directory.getPath() + "/database.db.crypt"); - // Temp output for DB checks - File TempFile = new File(directory.getPath() + "database.bak"); - FileOutputStream OutputTemp = new FileOutputStream(TempFile); - - try { - EncryptDecryptFile.decrypt(InputFile, OutputTemp, DecryptionKey); - } catch (NoSuchAlgorithmException e) { - Log.d(Config.LOGTAG, "Database importer: decryption failed with " + e); - e.printStackTrace(); - } catch (NoSuchPaddingException e) { - Log.d(Config.LOGTAG, "Database importer: decryption failed with " + e); - e.printStackTrace(); - } catch (InvalidKeyException e) { - Log.d(Config.LOGTAG, "Database importer: decryption failed (invalid key) with " + e); - e.printStackTrace(); - } catch (IOException e) { - Log.d(Config.LOGTAG, "Database importer: decryption failed (IO) with " + e); - e.printStackTrace(); - } catch (Exception e) { - Log.d(Config.LOGTAG, "Database importer: Error " + e); - e.printStackTrace(); - } - - SQLiteDatabase checkDB = null; - int DB_Version = DatabaseBackend.DATABASE_VERSION; - int Backup_DB_Version = 0; - - try { - String dbPath = TempFile.toString(); - checkDB = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY); - Backup_DB_Version = checkDB.getVersion(); - Log.d(Config.LOGTAG, "Backup found: " + checkDB + " Version: " + checkDB.getVersion()); - } catch (SQLiteException e) { - //database does't exist yet. - Log.d(Config.LOGTAG, "No backup found: " + checkDB); - } catch (Exception e) { - Log.d(Config.LOGTAG, "Error importing backup: " + e); - } - - if (checkDB != null) { - checkDB.close(); - } - if (checkDB != null) { - Log.d(Config.LOGTAG, "checkDB = " + checkDB.toString() + ", Backup DB = " + Backup_DB_Version + ", DB = " + DB_Version); - } - if (checkDB != null && Backup_DB_Version != 0 && Backup_DB_Version <= DB_Version) { - try { - ImportDatabase(); - importSuccessful = true; - } catch (Exception e) { - importSuccessful = false; - e.printStackTrace(); - } finally { - if (importSuccessful) { - restart(); - } - } - } else if (checkDB != null && Backup_DB_Version == 0) { - WelcomeActivity.this.runOnUiThread(new Runnable() { - public void run() { - Toast.makeText(WelcomeActivity.this, R.string.Password_wrong, Toast.LENGTH_LONG).show(); - enterPasswordDialog(1); - } - }); - } else { - WelcomeActivity.this.runOnUiThread(new Runnable() { - public void run() { - Toast.makeText(WelcomeActivity.this, R.string.Import_failed, Toast.LENGTH_LONG).show(); - } - }); - } - } else { - // Set the folder on the SDcard - File directory = new File(FileBackend.getBackupDirectory()); - // Set the input file stream up: - FileInputStream InputFile = new FileInputStream(directory.getPath() + "/database.db"); - // Temp output for DB checks - File TempFile = new File(directory.getPath() + "database.bak"); - FileOutputStream OutputTemp = new FileOutputStream(TempFile); - - try { - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - while ((len = InputFile.read(buf)) > 0) { - OutputTemp.write(buf, 0, len); - } - } finally { - OutputTemp.close(); - } - - SQLiteDatabase checkDB = null; - int DB_Version = DatabaseBackend.DATABASE_VERSION; - int Backup_DB_Version = 0; - - try { - String dbPath = TempFile.toString(); - checkDB = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY); - Backup_DB_Version = checkDB.getVersion(); - Log.d(Config.LOGTAG, "Backup found: " + checkDB + " Version: " + checkDB.getVersion()); - } catch (SQLiteException e) { - //database does't exist yet. - Log.d(Config.LOGTAG, "No backup found: " + checkDB); - } catch (Exception e) { - Log.d(Config.LOGTAG, "Error importing backup: " + e); - } - - if (checkDB != null) { - checkDB.close(); - } - if (checkDB != null) { - Log.d(Config.LOGTAG, "checkDB = " + checkDB.toString() + ", Backup DB = " + Backup_DB_Version + ", DB = " + DB_Version); - } - if (checkDB != null && Backup_DB_Version != 0 && Backup_DB_Version <= DB_Version) { - try { - ImportDatabase(); - importSuccessful = true; - } catch (Exception e) { - importSuccessful = false; - e.printStackTrace(); - } finally { - if (importSuccessful) { - restart(); - } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + if (grantResults.length > 0) { + if (allGranted(grantResults)) { + switch (requestCode) { + case REQUEST_IMPORT_BACKUP: + startActivity(new Intent(this, ImportBackupActivity.class)); + break; } } else { - WelcomeActivity.this.runOnUiThread(new Runnable() { - public void run() { - Toast.makeText(WelcomeActivity.this, R.string.Import_failed, Toast.LENGTH_LONG).show(); - } - }); + Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show(); } } - } - - private void ImportDatabase() throws Exception { - // Set location for the db: - final OutputStream OutputFile = new FileOutputStream(this.getDatabasePath(DatabaseBackend.DATABASE_NAME)); - // Set the folder on the SDcard - File directory = new File(FileBackend.getBackupDirectory()); - // Set the input file stream up: - final InputStream InputFile = new FileInputStream(directory.getPath() + "database.bak"); - //set temp file - File TempFile = new File(directory.getPath() + "database.bak"); - - // Transfer bytes from the input file to the output file - byte[] buffer = new byte[1024]; - int length; - while ((length = InputFile.read(buffer)) > 0) { - OutputFile.write(buffer, 0, length); - } - if (TempFile.exists()) { - Log.d(Config.LOGTAG, "Delete temp file from " + TempFile.toString()); - TempFile.delete(); - } - } - - private void restart() { - //restart app - Log.d(Config.LOGTAG, "Restarting " + getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext().getPackageName())); - Intent intent = getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext().getPackageName()); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - overridePendingTransition(R.animator.fade_in, R.animator.fade_out); - System.exit(0); - } - - public boolean hasStoragePermission(int requestCode) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, requestCode); - return false; - } else { - return true; + if (writeGranted(grantResults, permissions)) { + if (xmppConnectionService != null) { + xmppConnectionService.restartFileObserver(); } - } else { - return true; } } - - public void addInviteUri(Intent intent) { - StartConversationActivity.addInviteUri(intent, getIntent()); - } - - public static void launch(AppCompatActivity activity) { - Intent intent = new Intent(activity, WelcomeActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - activity.startActivity(intent); - activity.overridePendingTransition(0, 0); - } }
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/XmppActivity.java b/src/main/java/de/pixart/messenger/ui/XmppActivity.java index f0a180353..b64b4ae71 100644 --- a/src/main/java/de/pixart/messenger/ui/XmppActivity.java +++ b/src/main/java/de/pixart/messenger/ui/XmppActivity.java @@ -38,6 +38,7 @@ import android.os.SystemClock; import android.preference.PreferenceManager; import android.provider.Settings; import android.support.annotation.BoolRes; +import android.support.annotation.NonNull; import android.support.annotation.StringRes; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; @@ -76,6 +77,7 @@ import de.pixart.messenger.entities.Message; import de.pixart.messenger.entities.Presences; import de.pixart.messenger.services.AvatarService; import de.pixart.messenger.services.BarcodeProvider; +import de.pixart.messenger.services.EmojiService; import de.pixart.messenger.services.UpdateService; import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.services.XmppConnectionService.XmppConnectionBinder; @@ -92,6 +94,8 @@ import de.pixart.messenger.xmpp.XmppConnection; import pl.droidsonroids.gif.GifDrawable; import rocks.xmpp.addr.Jid; +import static de.pixart.messenger.ui.SettingsActivity.USE_BUNDLED_EMOJIS; + public abstract class XmppActivity extends ActionBarActivity { protected static final int REQUEST_ANNOUNCE_PGP = 0x0101; @@ -238,7 +242,11 @@ public abstract class XmppActivity extends ActionBarActivity { public void connectToBackend() { Intent intent = new Intent(this, XmppConnectionService.class); intent.setAction("ui"); - startService(intent); + try { + startService(intent); + } catch (IllegalStateException e) { + Log.w(Config.LOGTAG, "unable to start service from " + getClass().getSimpleName()); + } bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @@ -392,6 +400,7 @@ public abstract class XmppActivity extends ActionBarActivity { setVolumeControlStream(AudioManager.STREAM_NOTIFICATION); metrics = getResources().getDisplayMetrics(); ExceptionHelper.init(getApplicationContext()); + new EmojiService(this).init(getPreferences().getBoolean(USE_BUNDLED_EMOJIS, getResources().getBoolean(R.bool.use_bundled_emoji))); this.isCameraFeatureAvailable = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA); mColorRed = ContextCompat.getColor(this, R.color.red800); if (isDarkTheme()) { @@ -947,6 +956,10 @@ public abstract class XmppActivity extends ActionBarActivity { Toast.makeText(this, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show(); return; } + if (xmppConnectionService.getAccounts() == null){ + Toast.makeText(this, R.string.no_accounts, Toast.LENGTH_SHORT).show(); + return; + } if (!xmppConnectionService.multipleAccounts()) { Account mAccount = xmppConnectionService.getAccounts().get(0); String user = Jid.of(mAccount.getJid()).getLocal(); @@ -1122,7 +1135,7 @@ public abstract class XmppActivity extends ActionBarActivity { if (cancelPotentialWork(message, imageView)) { imageView.setBackgroundColor(0xff333333); imageView.setImageDrawable(null); - final BitmapWorkerTask task = new BitmapWorkerTask(this, imageView); + final BitmapWorkerTask task = new BitmapWorkerTask(imageView); final AsyncDrawable asyncDrawable = new AsyncDrawable( getResources(), null, task); imageView.setImageDrawable(asyncDrawable); @@ -1177,11 +1190,9 @@ public abstract class XmppActivity extends ActionBarActivity { static class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; - private final WeakReference<XmppActivity> activity; private Message message = null; - private BitmapWorkerTask(XmppActivity activity, ImageView imageView) { - this.activity = new WeakReference<>(activity); + private BitmapWorkerTask(ImageView imageView) { this.imageViewReference = new WeakReference<>(imageView); } @@ -1192,7 +1203,7 @@ public abstract class XmppActivity extends ActionBarActivity { } message = params[0]; try { - XmppActivity activity = this.activity.get(); + final XmppActivity activity = find(imageViewReference); if (activity != null && activity.xmppConnectionService != null) { return activity.xmppConnectionService.getFileBackend().getThumbnail(message, (int) (activity.metrics.density * 288), false); } else { @@ -1230,6 +1241,19 @@ public abstract class XmppActivity extends ActionBarActivity { } } + public static XmppActivity find(@NonNull WeakReference<ImageView> viewWeakReference) { + final View view = viewWeakReference.get(); + return view == null ? null : find(view); + } + + public static XmppActivity find(@NonNull final View view) { + final Context context = view.getContext(); + if (context instanceof XmppActivity) { + return (XmppActivity) context; + } + return null; + } + protected boolean installFromUnknownSourceAllowed() { boolean installFromUnknownSource = false; final PackageManager packageManager = this.getPackageManager(); diff --git a/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java index e37ea6121..e37c0ea16 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java @@ -1,31 +1,21 @@ package de.pixart.messenger.ui.adapter; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; +import android.databinding.DataBindingUtil; import android.support.annotation.NonNull; -import android.support.v7.widget.SwitchCompat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.TextView; -import java.lang.ref.WeakReference; import java.util.List; -import java.util.concurrent.RejectedExecutionException; import de.pixart.messenger.Config; import de.pixart.messenger.R; +import de.pixart.messenger.databinding.AccountRowBinding; import de.pixart.messenger.entities.Account; -import de.pixart.messenger.ui.ManageAccountActivity; import de.pixart.messenger.ui.XmppActivity; +import de.pixart.messenger.ui.util.AvatarWorkerTask; import de.pixart.messenger.ui.util.StyledAttributes; -import de.pixart.messenger.utils.UIHelper; public class AccountAdapter extends ArrayAdapter<Account> { @@ -44,136 +34,63 @@ public class AccountAdapter extends ArrayAdapter<Account> { this.showStateButton = true; } - @NonNull @Override public View getView(int position, View view, @NonNull ViewGroup parent) { final Account account = getItem(position); + final ViewHolder viewHolder; if (view == null) { - LayoutInflater inflater = (LayoutInflater) getContext() - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = inflater.inflate(R.layout.account_row, parent, false); + AccountRowBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.account_row, parent, false); + view = binding.getRoot(); + viewHolder = new ViewHolder(binding); + view.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) view.getTag(); } - TextView jid = view.findViewById(R.id.account_jid); if (Config.DOMAIN_LOCK != null) { - jid.setText(account.getJid().getLocal()); + viewHolder.binding.accountJid.setText(account.getJid().getLocal()); } else { - jid.setText(account.getJid().asBareJid().toString()); + viewHolder.binding.accountJid.setText(account.getJid().asBareJid().toString()); } - TextView statusView = view.findViewById(R.id.account_status); - ImageView imageView = view.findViewById(R.id.account_image); - loadAvatar(account,imageView); - statusView.setText(getContext().getString(account.getStatus().getReadableId())); + AvatarWorkerTask.loadAvatar(account, viewHolder.binding.accountImage, R.dimen.avatar); + viewHolder.binding.accountStatus.setText(getContext().getString(account.getStatus().getReadableId())); switch (account.getStatus()) { case ONLINE: - statusView.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorOnline)); + viewHolder.binding.accountStatus.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorOnline)); break; case DISABLED: case CONNECTING: - statusView.setTextColor(StyledAttributes.getColor(activity, android.R.attr.textColorSecondary)); + viewHolder.binding.accountStatus.setTextColor(StyledAttributes.getColor(activity, android.R.attr.textColorSecondary)); break; default: - statusView.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorError)); + viewHolder.binding.accountStatus.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorError)); break; } - final SwitchCompat tglAccountState = view.findViewById(R.id.tgl_account_status); final boolean isDisabled = (account.getStatus() == Account.State.DISABLED); - tglAccountState.setOnCheckedChangeListener(null); - tglAccountState.setChecked(!isDisabled); + viewHolder.binding.tglAccountStatus.setOnCheckedChangeListener(null); + viewHolder.binding.tglAccountStatus.setChecked(!isDisabled); if (this.showStateButton) { - tglAccountState.setVisibility(View.VISIBLE); + viewHolder.binding.tglAccountStatus.setVisibility(View.VISIBLE); } else { - tglAccountState.setVisibility(View.GONE); + viewHolder.binding.tglAccountStatus.setVisibility(View.GONE); } - tglAccountState.setOnCheckedChangeListener((compoundButton, b) -> { - if (b == isDisabled && activity instanceof ManageAccountActivity) { - ((ManageAccountActivity) activity).onClickTglAccountState(account, b); + viewHolder.binding.tglAccountStatus.setOnCheckedChangeListener((compoundButton, b) -> { + if (b == isDisabled && activity instanceof OnTglAccountState) { + ((OnTglAccountState) activity).onClickTglAccountState(account, b); } }); return view; } - public static boolean cancelPotentialWork(Account account, ImageView imageView) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask != null) { - final Account oldAccount = bitmapWorkerTask.account; - if (oldAccount == null || account != oldAccount) { - bitmapWorkerTask.cancel(true); - } else { - return false; - } - } - return true; - } - - private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - - public void loadAvatar(Account account, ImageView imageView) { - if (cancelPotentialWork(account, imageView)) { - final Bitmap bm = activity.avatarService().get(account, activity.getPixel(56), true); - if (bm != null) { - cancelPotentialWork(account, imageView); - imageView.setImageBitmap(bm); - imageView.setBackgroundColor(0x00000000); - } else { - imageView.setBackgroundColor(UIHelper.getColorForName(account.getJid().asBareJid().toString())); - imageView.setImageDrawable(null); - final BitmapWorkerTask task = new BitmapWorkerTask(imageView); - final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task); - imageView.setImageDrawable(asyncDrawable); - try { - task.executeOnExecutor(BitmapWorkerTask.THREAD_POOL_EXECUTOR, account); - } catch (final RejectedExecutionException ignored) { - } - } - } - } - - static class AsyncDrawable extends BitmapDrawable { - private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; + private static class ViewHolder { + private final AccountRowBinding binding; - public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); - } - - public BitmapWorkerTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); + private ViewHolder(AccountRowBinding binding) { + this.binding = binding; } } - class BitmapWorkerTask extends AsyncTask<Account, Void, Bitmap> { - private final WeakReference<ImageView> imageViewReference; - private Account account = null; - public BitmapWorkerTask(ImageView imageView) { - imageViewReference = new WeakReference<>(imageView); - } - - @Override - protected Bitmap doInBackground(Account... params) { - this.account = params[0]; - return activity.avatarService().get(this.account, activity.getPixel(56), isCancelled()); - } - - @Override - protected void onPostExecute(Bitmap bitmap) { - if (bitmap != null && !isCancelled()) { - final ImageView imageView = imageViewReference.get(); - if (imageView != null) { - imageView.setImageBitmap(bitmap); - imageView.setBackgroundColor(0x00000000); - } - } - } + public interface OnTglAccountState { + void onClickTglAccountState(Account account, boolean state); } } diff --git a/src/main/java/de/pixart/messenger/ui/adapter/BackupFileAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/BackupFileAdapter.java new file mode 100644 index 000000000..9a7f70d12 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/adapter/BackupFileAdapter.java @@ -0,0 +1,167 @@ +package de.pixart.messenger.ui.adapter; + +import android.content.res.Resources; +import android.databinding.DataBindingUtil; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import android.text.format.DateUtils; +import android.util.DisplayMetrics; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; + +import de.pixart.messenger.R; +import de.pixart.messenger.databinding.AccountRowBinding; +import de.pixart.messenger.services.AvatarService; +import de.pixart.messenger.services.ImportBackupService; +import de.pixart.messenger.utils.BackupFileHeader; +import de.pixart.messenger.utils.UIHelper; +import rocks.xmpp.addr.Jid; + +public class BackupFileAdapter extends RecyclerView.Adapter<BackupFileAdapter.BackupFileViewHolder> { + + private OnItemClickedListener listener; + + private final List<ImportBackupService.BackupFile> files = new ArrayList<>(); + + + @NonNull + @Override + public BackupFileViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { + return new BackupFileViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.account_row, viewGroup, false)); + } + + @Override + public void onBindViewHolder(@NonNull BackupFileViewHolder backupFileViewHolder, int position) { + final ImportBackupService.BackupFile backupFile = files.get(position); + final BackupFileHeader header = backupFile.getHeader(); + backupFileViewHolder.binding.accountJid.setText(header.getJid().asBareJid().toString()); + backupFileViewHolder.binding.accountStatus.setText(String.format("%s · %s", header.getApp(), DateUtils.formatDateTime(backupFileViewHolder.binding.getRoot().getContext(), header.getTimestamp(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_TIME))); + backupFileViewHolder.binding.tglAccountStatus.setVisibility(View.GONE); + backupFileViewHolder.binding.getRoot().setOnClickListener(v -> { + if (listener != null) { + listener.onClick(backupFile); + } + }); + loadAvatar(header.getJid(), backupFileViewHolder.binding.accountImage); + } + + @Override + public int getItemCount() { + return files.size(); + } + + public void setFiles(List<ImportBackupService.BackupFile> files) { + this.files.clear(); + this.files.addAll(files); + notifyDataSetChanged(); + } + + public void setOnItemClickedListener(OnItemClickedListener listener) { + this.listener = listener; + } + + static class BackupFileViewHolder extends RecyclerView.ViewHolder { + private final AccountRowBinding binding; + + BackupFileViewHolder(AccountRowBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } + + public interface OnItemClickedListener { + void onClick(ImportBackupService.BackupFile backupFile); + } + + static class BitmapWorkerTask extends AsyncTask<Jid, Void, Bitmap> { + private final WeakReference<ImageView> imageViewReference; + private Jid jid = null; + private final int size; + + BitmapWorkerTask(ImageView imageView) { + imageViewReference = new WeakReference<>(imageView); + DisplayMetrics metrics = imageView.getContext().getResources().getDisplayMetrics(); + this.size = ((int) (48 * metrics.density)); + } + + @Override + protected Bitmap doInBackground(Jid... params) { + this.jid = params[0]; + return AvatarService.get(this.jid, size); + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + if (bitmap != null && !isCancelled()) { + final ImageView imageView = imageViewReference.get(); + if (imageView != null) { + imageView.setImageBitmap(bitmap); + imageView.setBackgroundColor(0x00000000); + } + } + } + } + + private void loadAvatar(Jid jid, ImageView imageView) { + if (cancelPotentialWork(jid, imageView)) { + imageView.setBackgroundColor(UIHelper.getColorForName(jid.asBareJid().toString())); + imageView.setImageDrawable(null); + final BitmapWorkerTask task = new BitmapWorkerTask(imageView); + final AsyncDrawable asyncDrawable = new AsyncDrawable(imageView.getContext().getResources(), null, task); + imageView.setImageDrawable(asyncDrawable); + try { + task.execute(jid); + } catch (final RejectedExecutionException ignored) { + } + } + } + + private static boolean cancelPotentialWork(Jid jid, ImageView imageView) { + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + + if (bitmapWorkerTask != null) { + final Jid oldJid = bitmapWorkerTask.jid; + if (oldJid == null || jid != oldJid) { + bitmapWorkerTask.cancel(true); + } else { + return false; + } + } + return true; + } + + private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { + if (imageView != null) { + final Drawable drawable = imageView.getDrawable(); + if (drawable instanceof AsyncDrawable) { + final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; + return asyncDrawable.getBitmapWorkerTask(); + } + } + return null; + } + + static class AsyncDrawable extends BitmapDrawable { + private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; + + AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { + super(res, bitmap); + bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); + } + + BitmapWorkerTask getBitmapWorkerTask() { + return bitmapWorkerTaskReference.get(); + } + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java index d61510611..27d623117 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java @@ -1,13 +1,8 @@ package de.pixart.messenger.ui.adapter; -import android.content.Context; import android.content.SharedPreferences; -import android.content.res.Resources; -import android.graphics.Bitmap; +import android.databinding.DataBindingUtil; import android.graphics.Typeface; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; @@ -16,24 +11,18 @@ import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; -import java.lang.ref.WeakReference; import java.util.List; -import java.util.concurrent.RejectedExecutionException; import de.pixart.messenger.R; +import de.pixart.messenger.databinding.ConversationListRowBinding; import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.entities.Message; import de.pixart.messenger.entities.MucOptions; -import de.pixart.messenger.entities.Transferable; import de.pixart.messenger.ui.ConversationFragment; import de.pixart.messenger.ui.XmppActivity; +import de.pixart.messenger.ui.util.AvatarWorkerTask; import de.pixart.messenger.ui.util.StyledAttributes; -import de.pixart.messenger.ui.widget.FailedCountCustomView; -import de.pixart.messenger.ui.widget.UnreadCountCustomView; import de.pixart.messenger.utils.EmojiWrapper; import de.pixart.messenger.utils.IrregularUnicodeDetector; import de.pixart.messenger.utils.UIHelper; @@ -44,44 +33,17 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte private XmppActivity activity; private List<Conversation> conversations; - private OnConversationClickListener listener; + private OnConversationClickListener listener; public ConversationAdapter(XmppActivity activity, List<Conversation> conversations) { this.activity = activity; this.conversations = conversations; } - private static boolean cancelPotentialWork(Conversation conversation, ImageView imageView) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask != null) { - final Conversation oldConversation = bitmapWorkerTask.conversation; - if (oldConversation == null || conversation != oldConversation) { - bitmapWorkerTask.cancel(true); - } else { - return false; - } - } - return true; - } - - private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - @NonNull @Override public ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View view = inflater.inflate(R.layout.conversation_list_row, parent, false); - return ConversationViewHolder.get(view); + return new ConversationViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.conversation_list_row, parent, false)); } @Override @@ -92,15 +54,15 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte } CharSequence name = conversation.getName(); if (name instanceof Jid) { - viewHolder.name.setText(IrregularUnicodeDetector.style(activity, (Jid) name)); + viewHolder.binding.conversationName.setText(IrregularUnicodeDetector.style(activity, (Jid) name)); } else { - viewHolder.name.setText(EmojiWrapper.transform(name)); + viewHolder.binding.conversationName.setText(EmojiWrapper.transform(name)); } if (conversation == ConversationFragment.getConversation(activity)) { - viewHolder.frame.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.color_background_tertiary)); + viewHolder.binding.frame.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.color_background_tertiary)); } else { - viewHolder.frame.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.color_background_secondary)); + viewHolder.binding.frame.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.color_background_secondary)); } Message message = conversation.getLatestMessage(); @@ -109,37 +71,39 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte final boolean isRead = conversation.isRead(); final Conversation.Draft draft = isRead ? conversation.getDraft() : null; - viewHolder.receivedStatus.setVisibility(View.GONE); - viewHolder.readStatus.setVisibility(View.GONE); + viewHolder.binding.indicatorReceived.setVisibility(View.GONE); + viewHolder.binding.indicatorRead.setVisibility(View.GONE); + viewHolder.binding.unreadCount.setVisibility(View.GONE); + viewHolder.binding.failedCount.setVisibility(View.GONE); if (isRead) { - viewHolder.name.setTypeface(null, Typeface.NORMAL); + viewHolder.binding.conversationName.setTypeface(null, Typeface.NORMAL); } else { - viewHolder.name.setTypeface(null, Typeface.BOLD); + viewHolder.binding.conversationName.setTypeface(null, Typeface.BOLD); } if (unreadCount > 0) { - viewHolder.unreadCount.setVisibility(View.VISIBLE); - viewHolder.unreadCount.setUnreadCount(unreadCount); + viewHolder.binding.unreadCount.setVisibility(View.VISIBLE); + viewHolder.binding.unreadCount.setUnreadCount(unreadCount); } else { - viewHolder.unreadCount.setVisibility(View.GONE); + viewHolder.binding.unreadCount.setVisibility(View.GONE); } if (failedCount > 0) { - viewHolder.failedCount.setVisibility(View.VISIBLE); - viewHolder.failedCount.setFailedCount(failedCount); + viewHolder.binding.failedCount.setVisibility(View.VISIBLE); + viewHolder.binding.failedCount.setFailedCount(failedCount); } else { - viewHolder.failedCount.setVisibility(View.GONE); + viewHolder.binding.failedCount.setVisibility(View.GONE); } if (draft != null) { - viewHolder.lastMessageIcon.setVisibility(View.GONE); - viewHolder.lastMessage.setText(EmojiWrapper.transform(draft.getMessage())); - viewHolder.sender.setText(R.string.draft); - viewHolder.sender.setVisibility(View.VISIBLE); - viewHolder.lastMessage.setTypeface(null, Typeface.NORMAL); - viewHolder.sender.setTypeface(null, Typeface.ITALIC); + viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE); + viewHolder.binding.conversationLastmsg.setText(EmojiWrapper.transform(draft.getMessage())); + viewHolder.binding.senderName.setText(R.string.draft); + viewHolder.binding.senderName.setVisibility(View.VISIBLE); + viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL); + viewHolder.binding.senderName.setTypeface(null, Typeface.ITALIC); } else { - final boolean fileAvailable = message.getTransferable() == null || message.getTransferable().getStatus() != Transferable.STATUS_DELETED; + final boolean fileAvailable = !message.isFileDeleted(); final boolean showPreviewText; if (fileAvailable && (message.isFileOrImage() || message.treatAsDownloadable() || message.isGeoUri())) { final int imageResource; @@ -167,65 +131,66 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte break; } } - viewHolder.lastMessageIcon.setImageResource(imageResource); - viewHolder.lastMessageIcon.setVisibility(View.VISIBLE); + viewHolder.binding.conversationLastmsgImg.setImageResource(imageResource); + viewHolder.binding.conversationLastmsgImg.setVisibility(View.VISIBLE); } else { - viewHolder.lastMessageIcon.setVisibility(View.GONE); + viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE); showPreviewText = true; } - final Pair<CharSequence, Boolean> preview = UIHelper.getMessagePreview(activity, message, viewHolder.lastMessage.getCurrentTextColor()); + final Pair<CharSequence, Boolean> preview = UIHelper.getMessagePreview(activity, message, viewHolder.binding.conversationLastmsg.getCurrentTextColor()); if (showPreviewText) { - viewHolder.lastMessage.setText(EmojiWrapper.transform(UIHelper.shorten(preview.first))); + viewHolder.binding.conversationLastmsg.setText(EmojiWrapper.transform(UIHelper.shorten(preview.first))); } else { - viewHolder.lastMessageIcon.setContentDescription(preview.first); + viewHolder.binding.conversationLastmsgImg.setContentDescription(preview.first); } - viewHolder.lastMessage.setVisibility(showPreviewText ? View.VISIBLE : View.GONE); + viewHolder.binding.conversationLastmsg.setVisibility(showPreviewText ? View.VISIBLE : View.GONE); if (preview.second) { if (isRead) { - viewHolder.lastMessage.setTypeface(null, Typeface.ITALIC); - viewHolder.sender.setTypeface(null, Typeface.NORMAL); + viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.ITALIC); + viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL); } else { - viewHolder.lastMessage.setTypeface(null, Typeface.BOLD_ITALIC); - viewHolder.sender.setTypeface(null, Typeface.BOLD); + viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD_ITALIC); + viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD); } } else { if (isRead) { - viewHolder.lastMessage.setTypeface(null, Typeface.NORMAL); - viewHolder.sender.setTypeface(null, Typeface.NORMAL); + viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL); + viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL); } else { - viewHolder.lastMessage.setTypeface(null, Typeface.BOLD); - viewHolder.sender.setTypeface(null, Typeface.BOLD); + viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD); + viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD); } } if (message.getStatus() == Message.STATUS_RECEIVED) { if (conversation.getMode() == Conversation.MODE_MULTI) { - viewHolder.sender.setVisibility(View.VISIBLE); - viewHolder.sender.setText(UIHelper.getMessageDisplayName(message).split("\\s+")[0] + ':'); + viewHolder.binding.senderName.setVisibility(View.VISIBLE); + viewHolder.binding.senderName.setText(UIHelper.getMessageDisplayName(message).split("\\s+")[0] + ':'); } else { - viewHolder.sender.setVisibility(View.GONE); + viewHolder.binding.senderName.setVisibility(View.GONE); } } else if (message.getType() != Message.TYPE_STATUS) { - viewHolder.sender.setVisibility(View.VISIBLE); - viewHolder.sender.setText(activity.getString(R.string.me) + ':'); + viewHolder.binding.senderName.setVisibility(View.VISIBLE); + viewHolder.binding.senderName.setText(activity.getString(R.string.me) + ':'); } else { - viewHolder.sender.setVisibility(View.GONE); + viewHolder.binding.senderName.setVisibility(View.GONE); } } long muted_till = conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0); if (muted_till == Long.MAX_VALUE) { + viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); int ic_notifications_off = activity.getThemeResource(R.attr.icon_notifications_off, R.drawable.ic_notifications_off_black_24dp); - viewHolder.notificationIcon.setImageResource(ic_notifications_off); + viewHolder.binding.notificationStatus.setImageResource(ic_notifications_off); } else if (muted_till >= System.currentTimeMillis()) { - viewHolder.notificationIcon.setVisibility(View.VISIBLE); + viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); int ic_notifications_paused = activity.getThemeResource(R.attr.icon_notifications_paused, R.drawable.ic_notifications_paused_black_24dp); - viewHolder.notificationIcon.setImageResource(ic_notifications_paused); + viewHolder.binding.notificationStatus.setImageResource(ic_notifications_paused); } else if (conversation.alwaysNotify()) { - viewHolder.notificationIcon.setVisibility(View.GONE); + viewHolder.binding.notificationStatus.setVisibility(View.GONE); } else { - viewHolder.notificationIcon.setVisibility(View.VISIBLE); + viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); int ic_notifications_none = activity.getThemeResource(R.attr.icon_notifications_none, R.drawable.ic_notifications_none_black_24dp); - viewHolder.notificationIcon.setImageResource(ic_notifications_none); + viewHolder.binding.notificationStatus.setImageResource(ic_notifications_none); } long timestamp; @@ -234,81 +199,83 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte } else { timestamp = conversation.getLatestMessage().getTimeSent(); } - viewHolder.timestamp.setText(UIHelper.readableTimeDifference(activity, timestamp)); - loadAvatar(conversation, viewHolder.avatar); + viewHolder.binding.conversationLastupdate.setText(UIHelper.readableTimeDifference(activity, timestamp)); + AvatarWorkerTask.loadAvatar(conversation, viewHolder.binding.conversationImage, R.dimen.avatar_on_conversation_overview); + viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v, conversation)); if (conversation.getMode() == Conversation.MODE_SINGLE && ShowPresenceColoredNames()) { switch (conversation.getContact().getPresences().getShownStatus()) { case CHAT: case ONLINE: - viewHolder.name.setTextColor(ContextCompat.getColor(activity, R.color.online)); + viewHolder.binding.conversationName.setTextColor(ContextCompat.getColor(activity, R.color.online)); break; case AWAY: - viewHolder.name.setTextColor(ContextCompat.getColor(activity, R.color.away)); + viewHolder.binding.conversationName.setTextColor(ContextCompat.getColor(activity, R.color.away)); break; case XA: case DND: - viewHolder.name.setTextColor(ContextCompat.getColor(activity, R.color.notavailable)); + viewHolder.binding.conversationName.setTextColor(ContextCompat.getColor(activity, R.color.notavailable)); break; default: - viewHolder.name.setTextColor(StyledAttributes.getColor(activity, R.attr.text_Color_Main)); + viewHolder.binding.conversationName.setTextColor(StyledAttributes.getColor(activity, R.attr.text_Color_Main)); break; } } else { - viewHolder.name.setTextColor(StyledAttributes.getColor(activity, R.attr.text_Color_Main)); + viewHolder.binding.conversationName.setTextColor(StyledAttributes.getColor(activity, R.attr.text_Color_Main)); } if (activity.xmppConnectionService.indicateReceived()) { switch (message.getMergedStatus()) { case Message.STATUS_SEND_RECEIVED: - viewHolder.receivedStatus.setVisibility(View.VISIBLE); + viewHolder.binding.indicatorReceived.setVisibility(View.VISIBLE); break; case Message.STATUS_SEND_DISPLAYED: - viewHolder.receivedStatus.setVisibility(View.VISIBLE); - viewHolder.readStatus.setVisibility(View.VISIBLE); + viewHolder.binding.indicatorReceived.setVisibility(View.VISIBLE); + viewHolder.binding.indicatorRead.setVisibility(View.VISIBLE); break; + default: + viewHolder.binding.indicatorReceived.setVisibility(View.GONE); + viewHolder.binding.indicatorRead.setVisibility(View.GONE); } } if (conversation.getMode() == Conversation.MODE_SINGLE) { if (conversation.getIncomingChatState().equals(ChatState.COMPOSING)) { - viewHolder.lastMessage.setText(R.string.is_typing); - viewHolder.lastMessage.setTypeface(null, Typeface.BOLD_ITALIC); - viewHolder.sender.setVisibility(View.GONE); + viewHolder.binding.conversationLastmsg.setText(R.string.is_typing); + viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD_ITALIC); + viewHolder.binding.senderName.setVisibility(View.GONE); } } else { - if (conversation.getParticipants() != null) { - ChatState state = ChatState.COMPOSING; - List<MucOptions.User> userWithChatStates = conversation.getMucOptions().getUsersWithChatState(state, 5); - if (userWithChatStates.size() == 0) { - state = ChatState.PAUSED; - userWithChatStates = conversation.getMucOptions().getUsersWithChatState(state, 5); - } - if (state == ChatState.COMPOSING) { - if (userWithChatStates.size() > 0) { - if (userWithChatStates.size() == 1) { - MucOptions.User user = userWithChatStates.get(0); - viewHolder.lastMessage.setText(activity.getString(R.string.contact_is_typing, UIHelper.getDisplayName(user))); - viewHolder.lastMessage.setTypeface(null, Typeface.BOLD_ITALIC); - viewHolder.sender.setVisibility(View.GONE); - } else { - StringBuilder builder = new StringBuilder(); - for (MucOptions.User user : userWithChatStates) { - if (builder.length() != 0) { - builder.append(", "); - } - builder.append(UIHelper.getDisplayName(user)); + ChatState state = ChatState.COMPOSING; + List<MucOptions.User> userWithChatStates = conversation.getMucOptions().getUsersWithChatState(state, 5); + if (userWithChatStates.size() == 0) { + state = ChatState.PAUSED; + userWithChatStates = conversation.getMucOptions().getUsersWithChatState(state, 5); + } + if (state == ChatState.COMPOSING) { + if (userWithChatStates.size() > 0) { + if (userWithChatStates.size() == 1) { + MucOptions.User user = userWithChatStates.get(0); + viewHolder.binding.conversationLastmsg.setText(activity.getString(R.string.contact_is_typing, UIHelper.getDisplayName(user))); + viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD_ITALIC); + viewHolder.binding.senderName.setVisibility(View.GONE); + } else { + StringBuilder builder = new StringBuilder(); + for (MucOptions.User user : userWithChatStates) { + if (builder.length() != 0) { + builder.append(", "); } - viewHolder.lastMessage.setText(activity.getString(R.string.contacts_are_typing, builder.toString())); - viewHolder.lastMessage.setTypeface(null, Typeface.BOLD_ITALIC); - viewHolder.sender.setVisibility(View.GONE); + builder.append(UIHelper.getDisplayName(user)); } + viewHolder.binding.conversationLastmsg.setText(activity.getString(R.string.contacts_are_typing, builder.toString())); + viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD_ITALIC); + viewHolder.binding.senderName.setVisibility(View.GONE); } } } } - viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v, conversation)); } + @Override public int getItemCount() { return conversations.size(); @@ -318,27 +285,6 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte this.listener = listener; } - private void loadAvatar(Conversation conversation, ImageView imageView) { - if (cancelPotentialWork(conversation, imageView)) { - final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true); - if (bm != null) { - cancelPotentialWork(conversation, imageView); - imageView.setImageBitmap(bm); - imageView.setBackgroundColor(0x00000000); - } else { - imageView.setBackgroundColor(UIHelper.getColorForName(conversation.getName().toString())); - imageView.setImageDrawable(null); - final BitmapWorkerTask task = new BitmapWorkerTask(imageView); - final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task); - imageView.setImageDrawable(asyncDrawable); - try { - task.executeOnExecutor(BitmapWorkerTask.THREAD_POOL_EXECUTOR, conversation); - } catch (final RejectedExecutionException ignored) { - } - } - } - } - public void insert(Conversation c, int position) { conversations.add(position, c); notifyDataSetChanged(); @@ -349,94 +295,24 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte notifyItemRemoved(position); } - public static class ConversationViewHolder extends RecyclerView.ViewHolder { - private TextView name; - private TextView lastMessage; - private ImageView lastMessageIcon; - private TextView timestamp; - private TextView sender; - private ImageView notificationIcon; - private UnreadCountCustomView unreadCount; - private FailedCountCustomView failedCount; - private ImageView receivedStatus; - private ImageView readStatus; - private ImageView avatar; - private FrameLayout frame; - - private ConversationViewHolder(View view) { - super(view); - } - - public static ConversationViewHolder get(View layout) { - ConversationViewHolder conversationViewHolder = (ConversationViewHolder) layout.getTag(); - if (conversationViewHolder == null) { - conversationViewHolder = new ConversationViewHolder(layout); - conversationViewHolder.frame = layout.findViewById(R.id.frame); - conversationViewHolder.name = layout.findViewById(R.id.conversation_name); - conversationViewHolder.lastMessage = layout.findViewById(R.id.conversation_lastmsg); - conversationViewHolder.lastMessageIcon = layout.findViewById(R.id.conversation_lastmsg_img); - conversationViewHolder.timestamp = layout.findViewById(R.id.conversation_lastupdate); - conversationViewHolder.avatar = layout.findViewById(R.id.conversation_image); - conversationViewHolder.sender = layout.findViewById(R.id.sender_name); - conversationViewHolder.notificationIcon = layout.findViewById(R.id.notification_status); - conversationViewHolder.unreadCount = layout.findViewById(R.id.conversation_unread); - conversationViewHolder.failedCount = layout.findViewById(R.id.conversation_failed); - conversationViewHolder.receivedStatus = layout.findViewById(R.id.indicator_received); - conversationViewHolder.readStatus = layout.findViewById(R.id.indicator_read); - layout.setTag(conversationViewHolder); - } - return conversationViewHolder; - } - } - - static class AsyncDrawable extends BitmapDrawable { - private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; - - public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); - } - - public BitmapWorkerTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } + public interface OnConversationClickListener { + void onConversationClick(View view, Conversation conversation); } - class BitmapWorkerTask extends AsyncTask<Conversation, Void, Bitmap> { - private final WeakReference<ImageView> imageViewReference; - private Conversation conversation = null; - - public BitmapWorkerTask(ImageView imageView) { - imageViewReference = new WeakReference<>(imageView); - } + static class ConversationViewHolder extends RecyclerView.ViewHolder { + private final ConversationListRowBinding binding; - @Override - protected Bitmap doInBackground(Conversation... params) { - this.conversation = params[0]; - return activity.avatarService().get(this.conversation, activity.getPixel(56), isCancelled()); - } - - @Override - protected void onPostExecute(Bitmap bitmap) { - if (bitmap != null) { - final ImageView imageView = imageViewReference.get(); - if (imageView != null) { - imageView.setImageBitmap(bitmap); - imageView.setBackgroundColor(0x00000000); - } - } + private ConversationViewHolder(ConversationListRowBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } - public boolean ShowPresenceColoredNames() { + private boolean ShowPresenceColoredNames() { return getPreferences().getBoolean("presence_colored_names", activity.getResources().getBoolean(R.bool.presence_colored_names)); } protected SharedPreferences getPreferences() { return PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()); } - - public interface OnConversationClickListener { - void onConversationClick(View view, Conversation conversation); - } }
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java index e99353f13..93a822ea3 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java @@ -1,12 +1,7 @@ package de.pixart.messenger.ui.adapter; import android.content.SharedPreferences; -import android.content.res.Resources; import android.databinding.DataBindingUtil; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; import android.preference.PreferenceManager; import android.view.LayoutInflater; import android.view.View; @@ -17,19 +12,17 @@ import android.widget.TextView; import com.wefika.flowlayout.FlowLayout; -import java.lang.ref.WeakReference; import java.util.List; -import java.util.concurrent.RejectedExecutionException; import de.pixart.messenger.R; import de.pixart.messenger.databinding.ContactBinding; import de.pixart.messenger.entities.ListItem; import de.pixart.messenger.ui.SettingsActivity; import de.pixart.messenger.ui.XmppActivity; +import de.pixart.messenger.ui.util.AvatarWorkerTask; import de.pixart.messenger.ui.util.StyledAttributes; import de.pixart.messenger.utils.EmojiWrapper; import de.pixart.messenger.utils.IrregularUnicodeDetector; -import de.pixart.messenger.utils.UIHelper; import rocks.xmpp.addr.Jid; public class ListItemAdapter extends ArrayAdapter<ListItem> { @@ -74,9 +67,8 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { } else { viewHolder = (ViewHolder) view.getTag(); } - + view.setBackground(StyledAttributes.getDrawable(view.getContext(), R.attr.list_item_background)); List<ListItem.Tag> tags = item.getTags(activity); - if (tags.size() == 0 || !this.showDynamicTags) { viewHolder.tags.setVisibility(View.GONE); } else { @@ -121,7 +113,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { viewHolder.avatar.setAlpha(ACTIVE_ALPHA); viewHolder.tags.setAlpha(ACTIVE_ALPHA); } - loadAvatar(item, viewHolder.avatar); + AvatarWorkerTask.loadAvatar(item, viewHolder.avatar, R.dimen.avatar); return view; } @@ -133,92 +125,6 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { void onTagClicked(String tag); } - class BitmapWorkerTask extends AsyncTask<ListItem, Void, Bitmap> { - private final WeakReference<ImageView> imageViewReference; - private ListItem item = null; - - public BitmapWorkerTask(ImageView imageView) { - imageViewReference = new WeakReference<>(imageView); - } - - @Override - protected Bitmap doInBackground(ListItem... params) { - this.item = params[0]; - return activity.avatarService().get(this.item, activity.getPixel(56), isCancelled()); - } - - @Override - protected void onPostExecute(Bitmap bitmap) { - if (bitmap != null && !isCancelled()) { - final ImageView imageView = imageViewReference.get(); - if (imageView != null) { - imageView.setImageBitmap(bitmap); - imageView.setBackgroundColor(0x00000000); - } - } - } - } - - public void loadAvatar(ListItem item, ImageView imageView) { - if (cancelPotentialWork(item, imageView)) { - final Bitmap bm = activity.avatarService().get(item, activity.getPixel(48), true); - if (bm != null) { - cancelPotentialWork(item, imageView); - imageView.setImageBitmap(bm); - imageView.setBackgroundColor(0x00000000); - } else { - String seed = item.getJid() != null ? item.getJid().asBareJid().toString() : item.getDisplayName(); - imageView.setBackgroundColor(UIHelper.getColorForName(seed)); - imageView.setImageDrawable(null); - final BitmapWorkerTask task = new BitmapWorkerTask(imageView); - final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task); - imageView.setImageDrawable(asyncDrawable); - try { - task.executeOnExecutor(BitmapWorkerTask.THREAD_POOL_EXECUTOR, item); - } catch (final RejectedExecutionException ignored) { - } - } - } - } - - public static boolean cancelPotentialWork(ListItem item, ImageView imageView) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask != null) { - final ListItem oldItem = bitmapWorkerTask.item; - if (oldItem == null || item != oldItem) { - bitmapWorkerTask.cancel(true); - } else { - return false; - } - } - return true; - } - - private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - - static class AsyncDrawable extends BitmapDrawable { - private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; - - public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); - } - - public BitmapWorkerTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } - } - private static class ViewHolder { private TextView name; private TextView jid; @@ -239,8 +145,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { } } - - public boolean ShowPresenceColoredNames() { + private boolean ShowPresenceColoredNames() { return getPreferences().getBoolean("presence_colored_names", activity.getResources().getBoolean(R.bool.presence_colored_names)); } diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java index 8b8c05d05..e8968bbac 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java @@ -89,7 +89,7 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol return attr; } - public static void renderPreview(Context context, Attachment attachment, ImageView imageView) { + static void renderPreview(Context context, Attachment attachment, ImageView imageView) { imageView.setBackgroundColor(StyledAttributes.getColor(context, R.attr.color_background_tertiary)); imageView.setImageAlpha(Math.round(StyledAttributes.getFloat(context, R.attr.icon_alpha) * 255)); imageView.setImageDrawable(StyledAttributes.getDrawable(context, getImageAttr(attachment))); @@ -138,7 +138,7 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol cancelPotentialWork(attachment, holder.binding.media); renderPreview(activity, attachment, holder.binding.media); } - holder.binding.media.setOnClickListener(v -> ViewUtil.view(activity, attachment)); + holder.binding.getRoot().setOnClickListener(v -> ViewUtil.view(activity, attachment)); } public void setAttachments(List<Attachment> attachments) { @@ -161,7 +161,7 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol } else { imageView.setBackgroundColor(0xff333333); imageView.setImageDrawable(null); - final BitmapWorkerTask task = new BitmapWorkerTask(imageView); + final BitmapWorkerTask task = new BitmapWorkerTask(mediaSize, imageView); final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task); imageView.setImageDrawable(asyncDrawable); try { @@ -200,17 +200,23 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol } } - class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> { + private static class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private Attachment attachment = null; + private final int mediaSize; - BitmapWorkerTask(ImageView imageView) { + BitmapWorkerTask(int mediaSize, ImageView imageView) { + this.mediaSize = mediaSize; imageViewReference = new WeakReference<>(imageView); } @Override protected Bitmap doInBackground(Attachment... params) { this.attachment = params[0]; + final XmppActivity activity = XmppActivity.find(imageViewReference); + if (activity == null) { + return null; + } return activity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, mediaSize, false); } diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java index 64cb63439..f27158927 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java @@ -1,6 +1,5 @@ package de.pixart.messenger.ui.adapter; -import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.databinding.DataBindingUtil; @@ -158,7 +157,7 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte } } - class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> { + private static class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private Attachment attachment = null; @@ -168,14 +167,12 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte @Override protected Bitmap doInBackground(Attachment... params) { - Activity activity = conversationFragment.getActivity(); - if (activity instanceof XmppActivity) { - final XmppActivity xmppActivity = (XmppActivity) activity; - this.attachment = params[0]; - return xmppActivity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, Math.round(xmppActivity.getResources().getDimension(R.dimen.media_preview_size)), false); - } else { + this.attachment = params[0]; + final XmppActivity activity = XmppActivity.find(imageViewReference); + if (activity == null) { return null; } + return activity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, Math.round(activity.getResources().getDimension(R.dimen.media_preview_size)), false); } @Override diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java index 1c53f1b5d..7e3d26728 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java @@ -5,16 +5,9 @@ import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Color; import android.graphics.Typeface; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.AsyncTask; import android.preference.PreferenceManager; -import android.support.annotation.ColorInt; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.text.Spannable; @@ -45,12 +38,9 @@ import android.widget.Toast; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; -import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.lang.ref.WeakReference; import java.net.URL; import java.util.List; -import java.util.concurrent.RejectedExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -74,6 +64,7 @@ import de.pixart.messenger.ui.ConversationsActivity; import de.pixart.messenger.ui.XmppActivity; import de.pixart.messenger.ui.text.DividerSpan; import de.pixart.messenger.ui.text.QuoteSpan; +import de.pixart.messenger.ui.util.AvatarWorkerTask; import de.pixart.messenger.ui.util.MyLinkify; import de.pixart.messenger.ui.util.ViewUtil; import de.pixart.messenger.ui.widget.ClickableMovementMethod; @@ -86,7 +77,6 @@ import de.pixart.messenger.utils.GeoHelper; import de.pixart.messenger.utils.StylingHelper; import de.pixart.messenger.utils.UIHelper; import de.pixart.messenger.xmpp.mam.MamReference; -import pl.droidsonroids.gif.GifDrawable; import pl.droidsonroids.gif.GifImageView; import static de.pixart.messenger.ui.SettingsActivity.PLAY_GIF_INSIDE; @@ -121,31 +111,6 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie updatePreferences(); } - public static boolean cancelPotentialWork(Message message, ImageView imageView) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask != null) { - final Message oldMessage = bitmapWorkerTask.message; - if (oldMessage == null || message != oldMessage) { - bitmapWorkerTask.cancel(true); - } else { - return false; - } - } - return true; - } - - private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - private static void resetClickListener(View... views) { for (View view : views) { view.setOnClickListener(null); @@ -246,13 +211,13 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie final Transferable transferable = message.getTransferable(); boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI && message.getMergedStatus() <= Message.STATUS_RECEIVED; - if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || transferable != null) { + if (message.isFileOrImage() || transferable != null) { FileParams params = message.getFileParams(); if (params.size > (1 * 1024 * 1024)) { filesize = Math.round(params.size * 1f / (1024 * 1024)) + " MiB"; } else if (params.size >= (1 * 1024)) { filesize = Math.round(params.size * 1f / 1024) + " KiB"; - } else if (params.size > 0){ + } else if (params.size > 0) { filesize = params.size + " B"; } if (transferable != null && transferable.getStatus() == Transferable.STATUS_FAILED) { @@ -605,7 +570,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie if (highlightedTerm != null) { StylingHelper.highlight(activity, body, highlightedTerm, StylingHelper.isDarkText(viewHolder.messageBody)); } - MyLinkify.addLinks(body,true); + MyLinkify.addLinks(body, true); viewHolder.messageBody.setAutoLinkMask(0); viewHolder.messageBody.setText(EmojiWrapper.transform(body)); viewHolder.messageBody.setTextIsSelectable(true); @@ -922,10 +887,10 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie boolean showAvatar; if (conversation.getMode() == Conversation.MODE_SINGLE) { showAvatar = true; - loadAvatar(message,viewHolder.contact_picture,activity.getPixel(32)); + AvatarWorkerTask.loadAvatar(message, viewHolder.contact_picture, R.dimen.avatar_on_status_message); } else if (message.getCounterpart() != null || message.getTrueCounterpart() != null || (message.getCounterparts() != null && message.getCounterparts().size() > 0)) { showAvatar = true; - loadAvatar(message,viewHolder.contact_picture,activity.getPixel(32)); + AvatarWorkerTask.loadAvatar(message, viewHolder.contact_picture, R.dimen.avatar_on_status_message); } else { showAvatar = false; } @@ -938,7 +903,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie } return view; } else { - loadAvatar(message, viewHolder.contact_picture, activity.getPixel(48)); + AvatarWorkerTask.loadAvatar(message, viewHolder.contact_picture, R.dimen.avatar); } resetClickListener(viewHolder.message_box, viewHolder.messageBody); @@ -962,10 +927,10 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie }); final Transferable transferable = message.getTransferable(); - if (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING) { - if (transferable.getStatus() == Transferable.STATUS_OFFER) { + if (message.isFileDeleted() || (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING)) { + if (transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER) { displayDownloadableMessage(viewHolder, message, activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message))); - } else if (transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) { + } else if (transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) { displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message))); } else { displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first, darkBackground); @@ -1035,7 +1000,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie viewHolder.encryption.setVisibility(View.GONE); viewHolder.encryption.setTextColor(this.getMessageTextColor(darkBackground, false)); } else { - viewHolder.message_box.setBackgroundResource(darkBackground ? R.drawable.message_bubble_received_warning_dark: R.drawable.message_bubble_received_warning); + viewHolder.message_box.setBackgroundResource(darkBackground ? R.drawable.message_bubble_received_warning_dark : R.drawable.message_bubble_received_warning); viewHolder.encryption.setVisibility(View.VISIBLE); viewHolder.encryption.setTextColor(this.getWarningTextColor(darkBackground)); if (omemoEncryption && !message.isTrusted()) { @@ -1119,20 +1084,12 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, ConversationsActivity.REQUEST_OPEN_MESSAGE); return; } - DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message); - if (!file.exists()) { - Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show(); - return; - } - String mime = file.getMimeType(); - if (mime == null) { - mime = "*/*"; - } - ViewUtil.view(activity, file, mime); + final DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message); + ViewUtil.view(activity, file); } - public void showLocation(Message message) { - for (Intent intent : GeoHelper.createGeoIntentsFromMessage(message, this.getContext())) { + private void showLocation(Message message) { + for (Intent intent : GeoHelper.createGeoIntentsFromMessage(this.getContext(), message)) { if (intent.resolveActivity(getContext().getPackageManager()) != null) { getContext().startActivity(intent); activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out); @@ -1147,33 +1104,6 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie this.mIndicateReceived = p.getBoolean("indicate_received", activity.getResources().getBoolean(R.bool.indicate_received)); } - public void loadAvatar(Message message, ImageView imageView, int size) { - if (cancelPotentialWork(message, imageView)) { - final Bitmap bm = activity.avatarService().get(message, size, true); - if (bm != null) { - cancelPotentialWork(message, imageView); - imageView.setImageBitmap(bm); - imageView.setBackgroundColor(Color.TRANSPARENT); - } else { - @ColorInt int bg; - if (message.getType() == Message.TYPE_STATUS && message.getCounterparts() != null && message.getCounterparts().size() > 1) { - bg = Color.TRANSPARENT; - } else { - bg = UIHelper.getColorForName(UIHelper.getMessageDisplayName(message)); - } - imageView.setBackgroundColor(bg); - imageView.setImageDrawable(null); - final BitmapWorkerTask task = new BitmapWorkerTask(imageView, size); - final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task); - imageView.setImageDrawable(asyncDrawable); - try { - task.executeOnExecutor(BitmapWorkerTask.THREAD_POOL_EXECUTOR, message); - } catch (final RejectedExecutionException ignored) { - } - } - } - } - public void setHighlightedTerm(List<String> terms) { this.highlightedTerm = terms == null ? null : StylingHelper.filterHighlightedWords(terms); } @@ -1210,19 +1140,6 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie protected TextView encryption; } - static class AsyncDrawable extends BitmapDrawable { - private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; - - public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); - } - - public BitmapWorkerTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } - } - private class MessageBodyActionModeCallback implements ActionMode.Callback { private final TextView textView; @@ -1265,34 +1182,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie } @Override - public void onDestroyActionMode(ActionMode mode) {} - } - - class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> { - private final WeakReference<ImageView> imageViewReference; - private final int size; - private Message message = null; - - public BitmapWorkerTask(ImageView imageView, int size) { - imageViewReference = new WeakReference<>(imageView); - this.size = size; - } - - @Override - protected Bitmap doInBackground(Message... params) { - this.message = params[0]; - return activity.avatarService().get(this.message, size, isCancelled()); - } - - @Override - protected void onPostExecute(Bitmap bitmap) { - if (bitmap != null && !isCancelled()) { - final ImageView imageView = imageViewReference.get(); - if (imageView != null) { - imageView.setImageBitmap(bitmap); - imageView.setBackgroundColor(0x00000000); - } - } + public void onDestroyActionMode(ActionMode mode) { } } }
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/adapter/UserAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/UserAdapter.java new file mode 100644 index 000000000..e615f2f42 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/adapter/UserAdapter.java @@ -0,0 +1,136 @@ +package de.pixart.messenger.ui.adapter; + +import android.app.PendingIntent; +import android.content.IntentSender; +import android.databinding.DataBindingUtil; +import android.support.annotation.NonNull; +import android.support.v7.recyclerview.extensions.ListAdapter; +import android.support.v7.util.DiffUtil; +import android.support.v7.widget.RecyclerView; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.openintents.openpgp.util.OpenPgpUtils; + +import de.pixart.messenger.R; +import de.pixart.messenger.crypto.PgpEngine; +import de.pixart.messenger.databinding.ContactBinding; +import de.pixart.messenger.entities.Contact; +import de.pixart.messenger.entities.MucOptions; +import de.pixart.messenger.services.XmppConnectionService; +import de.pixart.messenger.ui.ConferenceDetailsActivity; +import de.pixart.messenger.ui.XmppActivity; +import de.pixart.messenger.ui.util.AvatarWorkerTask; +import de.pixart.messenger.ui.util.MucDetailsContextMenuHelper; +import rocks.xmpp.addr.Jid; + +public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHolder> implements View.OnCreateContextMenuListener { + + static final DiffUtil.ItemCallback<MucOptions.User> DIFF = new DiffUtil.ItemCallback<MucOptions.User>() { + @Override + public boolean areItemsTheSame(@NonNull MucOptions.User a, @NonNull MucOptions.User b) { + final Jid fullA = a.getFullJid(); + final Jid fullB = b.getFullJid(); + final Jid realA = a.getRealJid(); + final Jid realB = b.getRealJid(); + if (fullA != null && fullB != null) { + return fullA.equals(fullB); + } else if (realA != null && realB != null) { + return realA.equals(realB); + } else { + return false; + } + } + + @Override + public boolean areContentsTheSame(@NonNull MucOptions.User a, @NonNull MucOptions.User b) { + return a.equals(b); + } + }; + private final boolean advancedMode; + private MucOptions.User selectedUser = null; + + public UserAdapter(final boolean advancedMode) { + super(DIFF); + this.advancedMode = advancedMode; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) { + return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.contact, viewGroup, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { + final MucOptions.User user = getItem(position); + AvatarWorkerTask.loadAvatar(user, viewHolder.binding.contactPhoto, R.dimen.avatar); + viewHolder.binding.getRoot().setOnClickListener(v -> { + final XmppActivity activity = XmppActivity.find(v); + if (activity != null) { + activity.highlightInMuc(user.getConversation(), user.getName()); + } + }); + viewHolder.binding.getRoot().setTag(user); + viewHolder.binding.getRoot().setOnCreateContextMenuListener(this); + viewHolder.binding.getRoot().setOnLongClickListener(v -> { + selectedUser = user; + return false; + }); + final String name = user.getName(); + final Contact contact = user.getContact(); + if (contact != null) { + viewHolder.binding.contactDisplayName.setText(contact.getDisplayName()); + if (name != null) { + viewHolder.binding.contactJid.setText(String.format("%s \u2022 %s", name, ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode))); + } else { + viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode)); + } + } else { + viewHolder.binding.contactDisplayName.setText(name == null ? "" : name); + viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode)); + } + if (advancedMode && user.getPgpKeyId() != 0) { + viewHolder.binding.key.setVisibility(View.VISIBLE); + viewHolder.binding.key.setOnClickListener(v -> { + final XmppActivity activity = XmppActivity.find(v); + final XmppConnectionService service = activity == null ? null : activity.xmppConnectionService; + final PgpEngine pgpEngine = service == null ? null : service.getPgpEngine(); + if (pgpEngine != null) { + PendingIntent intent = pgpEngine.getIntentForKey(user.getPgpKeyId()); + if (intent != null) { + try { + activity.startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0); + } catch (IntentSender.SendIntentException ignored) { + + } + } + } + }); + viewHolder.binding.key.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId())); + } + + + } + + public MucOptions.User getSelectedUser() { + return selectedUser; + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + MucDetailsContextMenuHelper.onCreateContextMenu(menu, v); + } + + class ViewHolder extends RecyclerView.ViewHolder { + + private final ContactBinding binding; + + private ViewHolder(ContactBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/adapter/UserPreviewAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/UserPreviewAdapter.java new file mode 100644 index 000000000..0a0e9f587 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/adapter/UserPreviewAdapter.java @@ -0,0 +1,70 @@ +package de.pixart.messenger.ui.adapter; + +import android.databinding.DataBindingUtil; +import android.support.annotation.NonNull; +import android.support.v7.recyclerview.extensions.ListAdapter; +import android.support.v7.widget.RecyclerView; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import de.pixart.messenger.R; +import de.pixart.messenger.databinding.UserPreviewBinding; +import de.pixart.messenger.entities.MucOptions; +import de.pixart.messenger.ui.XmppActivity; +import de.pixart.messenger.ui.util.AvatarWorkerTask; +import de.pixart.messenger.ui.util.MucDetailsContextMenuHelper; + +public class UserPreviewAdapter extends ListAdapter<MucOptions.User, UserPreviewAdapter.ViewHolder> implements View.OnCreateContextMenuListener { + + private MucOptions.User selectedUser = null; + + public UserPreviewAdapter() { + super(UserAdapter.DIFF); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) { + return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.user_preview, viewGroup, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { + final MucOptions.User user = getItem(position); + AvatarWorkerTask.loadAvatar(user, viewHolder.binding.avatar, R.dimen.media_size); + viewHolder.binding.getRoot().setOnClickListener(v -> { + final XmppActivity activity = XmppActivity.find(v); + if (activity != null) { + activity.highlightInMuc(user.getConversation(), user.getName()); + } + }); + viewHolder.binding.getRoot().setOnCreateContextMenuListener(this); + viewHolder.binding.getRoot().setTag(user); + viewHolder.binding.getRoot().setOnLongClickListener(v -> { + selectedUser = user; + return false; + }); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + MucDetailsContextMenuHelper.onCreateContextMenu(menu, v); + } + + public MucOptions.User getSelectedUser() { + return selectedUser; + } + + + class ViewHolder extends RecyclerView.ViewHolder { + + private final UserPreviewBinding binding; + + private ViewHolder(UserPreviewBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/util/AvatarWorkerTask.java b/src/main/java/de/pixart/messenger/ui/util/AvatarWorkerTask.java new file mode 100644 index 000000000..afddb7e8b --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/util/AvatarWorkerTask.java @@ -0,0 +1,111 @@ +package de.pixart.messenger.ui.util; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.support.annotation.DimenRes; +import android.widget.ImageView; + +import java.lang.ref.WeakReference; +import java.util.concurrent.RejectedExecutionException; + +import de.pixart.messenger.services.AvatarService; +import de.pixart.messenger.ui.XmppActivity; + +public class AvatarWorkerTask extends AsyncTask<AvatarService.Avatarable, Void, Bitmap> { + private final WeakReference<ImageView> imageViewReference; + private AvatarService.Avatarable avatarable = null; + private @DimenRes + int size; + + public AvatarWorkerTask(ImageView imageView, @DimenRes int size) { + imageViewReference = new WeakReference<>(imageView); + this.size = size; + } + + @Override + protected Bitmap doInBackground(AvatarService.Avatarable... params) { + this.avatarable = params[0]; + final XmppActivity activity = XmppActivity.find(imageViewReference); + if (activity == null) { + return null; + } + return activity.avatarService().get(avatarable, (int) activity.getResources().getDimension(size), isCancelled()); + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + if (bitmap != null && !isCancelled()) { + final ImageView imageView = imageViewReference.get(); + if (imageView != null) { + imageView.setImageBitmap(bitmap); + imageView.setBackgroundColor(0x00000000); + } + } + } + + public static boolean cancelPotentialWork(AvatarService.Avatarable avatarable, ImageView imageView) { + final AvatarWorkerTask workerTask = getBitmapWorkerTask(imageView); + + if (workerTask != null) { + final AvatarService.Avatarable old = workerTask.avatarable; + if (old == null || avatarable != old) { + workerTask.cancel(true); + } else { + return false; + } + } + return true; + } + + public static AvatarWorkerTask getBitmapWorkerTask(ImageView imageView) { + if (imageView != null) { + final Drawable drawable = imageView.getDrawable(); + if (drawable instanceof AsyncDrawable) { + final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; + return asyncDrawable.getAvatarWorkerTask(); + } + } + return null; + } + + public static void loadAvatar(final AvatarService.Avatarable avatarable, final ImageView imageView, final @DimenRes int size) { + if (cancelPotentialWork(avatarable, imageView)) { + final XmppActivity activity = XmppActivity.find(imageView); + if (activity == null) { + return; + } + final Bitmap bm = activity.avatarService().get(avatarable, (int) activity.getResources().getDimension(size), true); + if (bm != null) { + cancelPotentialWork(avatarable, imageView); + imageView.setImageBitmap(bm); + imageView.setBackgroundColor(0x00000000); + } else { + imageView.setBackgroundColor(avatarable.getAvatarBackgroundColor()); + imageView.setImageDrawable(null); + final AvatarWorkerTask task = new AvatarWorkerTask(imageView, size); + final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task); + imageView.setImageDrawable(asyncDrawable); + try { + task.execute(avatarable); + } catch (final RejectedExecutionException ignored) { + } + } + } + } + + static class AsyncDrawable extends BitmapDrawable { + private final WeakReference<AvatarWorkerTask> avatarWorkerTaskReference; + + AsyncDrawable(Resources res, Bitmap bitmap, AvatarWorkerTask workerTask) { + super(res, bitmap); + avatarWorkerTaskReference = new WeakReference<>(workerTask); + } + + AvatarWorkerTask getAvatarWorkerTask() { + return avatarWorkerTaskReference.get(); + } + } +} diff --git a/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java b/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java index dd69b2337..07a03fbab 100644 --- a/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java +++ b/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java @@ -38,7 +38,6 @@ import android.view.MenuItem; import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.crypto.OmemoSetting; -import de.pixart.messenger.crypto.axolotl.AxolotlService; import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.entities.Conversational; import de.pixart.messenger.entities.Message; @@ -101,11 +100,17 @@ public class ConversationMenuConfigurator { final MenuItem pgp = menu.findItem(R.id.encryption_choice_pgp); final MenuItem axolotl = menu.findItem(R.id.encryption_choice_axolotl); + final int next = conversation.getNextEncryption(); + boolean visible; if (OmemoSetting.isAlways()) { visible = false; } else if (conversation.getMode() == Conversation.MODE_MULTI) { - visible = (Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices(); + if (next == Message.ENCRYPTION_NONE && !conversation.isPrivateAndNonAnonymous() && !conversation.getBooleanAttribute(Conversation.ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS, false)) { + visible = false; + } else { + visible = (Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices(); + } } else { visible = Config.multipleEncryptionChoices(); } @@ -127,10 +132,6 @@ public class ConversationMenuConfigurator { pgp.setVisible(Config.supportOpenPgp()); none.setVisible(Config.supportUnencrypted() || conversation.getMode() == Conversation.MODE_MULTI); axolotl.setVisible(Config.supportOmemo()); - final AxolotlService axolotlService = conversation.getAccount().getAxolotlService(); - if (axolotlService == null || !axolotlService.isConversationAxolotlCapable(conversation)) { - axolotl.setEnabled(false); - } switch (conversation.getNextEncryption()) { case Message.ENCRYPTION_NONE: none.setChecked(true); diff --git a/src/main/java/de/pixart/messenger/ui/util/GridManager.java b/src/main/java/de/pixart/messenger/ui/util/GridManager.java index 0b445f3ec..9cad626ff 100644 --- a/src/main/java/de/pixart/messenger/ui/util/GridManager.java +++ b/src/main/java/de/pixart/messenger/ui/util/GridManager.java @@ -29,7 +29,8 @@ public class GridManager { } final ColumnInfo columnInfo = calculateColumnCount(context, recyclerView.getMeasuredWidth(), desiredSize); Log.d(Config.LOGTAG, "final count " + columnInfo.count); - if (recyclerView.getAdapter().getItemCount() != 0) { + final RecyclerView.Adapter adapter = recyclerView.getAdapter(); + if (adapter != null && adapter.getItemCount() != 0) { Log.e(Config.LOGTAG, "adapter already has items; just go with it now"); return; } diff --git a/src/main/java/de/pixart/messenger/ui/util/MucConfiguration.java b/src/main/java/de/pixart/messenger/ui/util/MucConfiguration.java new file mode 100644 index 000000000..4630a5a93 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/util/MucConfiguration.java @@ -0,0 +1,111 @@ +package de.pixart.messenger.ui.util; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.StringRes; + +import de.pixart.messenger.R; +import de.pixart.messenger.entities.MucOptions; + +public class MucConfiguration { + + public final @StringRes + int title; + public final String[] names; + public final boolean[] values; + public final Option[] options; + + private MucConfiguration(@StringRes int title, String[] names, boolean[] values, Option[] options) { + this.title = title; + this.names = names; + this.values = values; + this.options = options; + } + + public static MucConfiguration get(Context context, MucOptions mucOptions) { + if (mucOptions.isPrivateAndNonAnonymous()) { + String[] names = new String[]{ + context.getString(R.string.allow_participants_to_edit_subject), + context.getString(R.string.allow_participants_to_invite_others) + }; + boolean[] values = new boolean[]{ + mucOptions.participantsCanChangeSubject(), + mucOptions.allowInvites() + }; + final Option[] options = new Option[]{ + new Option("muc#roomconfig_changesubject"), + new Option("muc#roomconfig_allowinvites") + }; + return new MucConfiguration(R.string.conference_options, names, values, options); + } else { + String[] names = new String[]{ + context.getString(R.string.non_anonymous), + context.getString(R.string.allow_participants_to_edit_subject), + }; + boolean[] values = new boolean[]{ + mucOptions.nonanonymous(), + mucOptions.participantsCanChangeSubject() + }; + final Option[] options = new Option[]{ + new Option("muc#roomconfig_whois", "anyone", "moderators"), + new Option("muc#roomconfig_changesubject") + }; + return new MucConfiguration(R.string.channel_options, names, values, options); + } + } + + public static String describe(final Context context, final MucOptions mucOptions) { + final StringBuilder builder = new StringBuilder(); + if (mucOptions.isPrivateAndNonAnonymous()) { + if (mucOptions.participantsCanChangeSubject()) { + builder.append(context.getString(R.string.anyone_can_edit_subject)); + } else { + builder.append(context.getString(R.string.owners_can_edit_subject)); + } + builder.append(' '); + if (mucOptions.allowInvites()) { + builder.append(context.getString(R.string.anyone_can_invite_others)); + } else { + builder.append(context.getString(R.string.owners_can_invite_others)); + } + } else { + if (mucOptions.nonanonymous()) { + builder.append(context.getString(R.string.jabber_ids_are_visible_to_anyone)); + } else { + builder.append(context.getString(R.string.jabber_ids_are_visible_to_admins)); + } + builder.append(' '); + if (mucOptions.participantsCanChangeSubject()) { + builder.append(context.getString(R.string.anyone_can_edit_subject)); + } else { + builder.append(context.getString(R.string.admins_can_edit_subject)); + } + } + return builder.toString(); + } + + public Bundle toBundle(boolean[] values) { + Bundle bundle = new Bundle(); + for (int i = 0; i < values.length; ++i) { + final Option option = options[i]; + bundle.putString(option.name, option.values[values[i] ? 0 : 1]); + } + return bundle; + } + + private static class Option { + public final String name; + public final String[] values; + + private Option(String name) { + this.name = name; + this.values = new String[]{"1", "0"}; + } + + private Option(String name, String on, String off) { + this.name = name; + this.values = new String[]{on, off}; + } + } + +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/util/MucDetailsContextMenuHelper.java b/src/main/java/de/pixart/messenger/ui/util/MucDetailsContextMenuHelper.java index f4777e8f3..c5c69577d 100644 --- a/src/main/java/de/pixart/messenger/ui/util/MucDetailsContextMenuHelper.java +++ b/src/main/java/de/pixart/messenger/ui/util/MucDetailsContextMenuHelper.java @@ -6,8 +6,10 @@ import android.support.v7.app.AlertDialog; import android.text.SpannableString; import android.text.Spanned; import android.text.style.TypefaceSpan; +import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import de.pixart.messenger.Config; import de.pixart.messenger.R; @@ -19,13 +21,36 @@ import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.ui.ConferenceDetailsActivity; import de.pixart.messenger.ui.ConversationFragment; import de.pixart.messenger.ui.ConversationsActivity; +import de.pixart.messenger.ui.MucUsersActivity; import de.pixart.messenger.ui.XmppActivity; import rocks.xmpp.addr.Jid; public final class MucDetailsContextMenuHelper { + + public static void onCreateContextMenu(ContextMenu menu, View v) { + final XmppActivity activity = XmppActivity.find(v); + final Object tag = v.getTag(); + if (tag instanceof MucOptions.User && activity != null) { + activity.getMenuInflater().inflate(R.menu.muc_details_context, menu); + final MucOptions.User user = (MucOptions.User) tag; + String name; + final Contact contact = user.getContact(); + if (contact != null && contact.showInContactList()) { + name = contact.getDisplayName(); + } else if (user.getRealJid() != null) { + name = user.getRealJid().asBareJid().toString(); + } else { + name = user.getName(); + } + menu.setHeaderTitle(name); + MucDetailsContextMenuHelper.configureMucDetailsContextMenu(activity, menu, user.getConversation(), user); + } + } + public static void configureMucDetailsContextMenu(Activity activity, Menu menu, Conversation conversation, User user) { final boolean advancedMode = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean("advanced_muc_mode", false); final MucOptions mucOptions = conversation.getMucOptions(); + final boolean isGroupChat = mucOptions.isPrivateAndNonAnonymous(); MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message); if (user != null && user.getRealJid() != null) { MenuItem showContactDetails = menu.findItem(R.id.action_contact_details); @@ -34,8 +59,13 @@ public final class MucDetailsContextMenuHelper { MenuItem removeMembership = menu.findItem(R.id.remove_membership); MenuItem giveAdminPrivileges = menu.findItem(R.id.give_admin_privileges); MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges); + MenuItem giveOwnerPrivileges = menu.findItem(R.id.give_owner_privileges); + MenuItem removeOwnerPrivileges = menu.findItem(R.id.revoke_owner_privileges); MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room); + MenuItem managePermisisons = menu.findItem(R.id.manage_permissions); + removeFromRoom.setTitle(isGroupChat ? R.string.remove_from_room : R.string.remove_from_channel); MenuItem banFromConference = menu.findItem(R.id.ban_from_conference); + banFromConference.setTitle(isGroupChat ? R.string.ban_from_conference : R.string.ban_from_channel); MenuItem invite = menu.findItem(R.id.invite); MenuItem highlightInMuc = menu.findItem(R.id.highlight_in_muc); startConversation.setVisible(true); @@ -44,7 +74,7 @@ public final class MucDetailsContextMenuHelper { if (contact != null && contact.showInRoster()) { showContactDetails.setVisible(!contact.isSelf()); } - if (activity instanceof ConferenceDetailsActivity && user.getRole() == MucOptions.Role.NONE) { + if ((activity instanceof ConferenceDetailsActivity || activity instanceof MucUsersActivity) && user.getRole() == MucOptions.Role.NONE) { invite.setVisible(true); } if (activity instanceof ConversationsActivity) { @@ -52,12 +82,13 @@ public final class MucDetailsContextMenuHelper { } else if (activity instanceof ConferenceDetailsActivity) { highlightInMuc.setVisible(true); } - if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) && - self.getAffiliation().outranks(user.getAffiliation())) { + boolean managePermissionsVisible = false; + if ((self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) && self.getAffiliation().outranks(user.getAffiliation())) || self.getAffiliation() == MucOptions.Affiliation.OWNER) { if (advancedMode) { - if (user.getAffiliation() == MucOptions.Affiliation.NONE) { + managePermissionsVisible = true; + if (!user.getAffiliation().ranks(MucOptions.Affiliation.MEMBER)) { giveMembership.setVisible(true); - } else { + } else if (user.getAffiliation() == MucOptions.Affiliation.MEMBER) { removeMembership.setVisible(true); } if (!Config.DISABLE_BAN) { @@ -65,36 +96,55 @@ public final class MucDetailsContextMenuHelper { } } else { if (!Config.DISABLE_BAN || conversation.getMucOptions().membersOnly()) { + managePermissionsVisible = true; removeFromRoom.setVisible(true); } } - if (user.getAffiliation() != MucOptions.Affiliation.ADMIN) { - giveAdminPrivileges.setVisible(true); - } else { - removeAdminPrivileges.setVisible(true); + } + if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) { + if (isGroupChat || advancedMode || user.getAffiliation() == MucOptions.Affiliation.OWNER) { + managePermissionsVisible = true; + if (!user.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) { + giveOwnerPrivileges.setVisible(true); + } else if (user.getAffiliation() == MucOptions.Affiliation.OWNER) { + removeOwnerPrivileges.setVisible(true); + } + } + if (!isGroupChat || advancedMode || user.getAffiliation() == MucOptions.Affiliation.ADMIN) { + managePermissionsVisible = true; + if (!user.getAffiliation().ranks(MucOptions.Affiliation.ADMIN)) { + giveAdminPrivileges.setVisible(true); + } else if (user.getAffiliation() == MucOptions.Affiliation.ADMIN) { + removeAdminPrivileges.setVisible(true); + } } } + managePermisisons.setVisible(managePermissionsVisible); sendPrivateMessage.setVisible(true); sendPrivateMessage.setEnabled(mucOptions.allowPm()); } else { sendPrivateMessage.setVisible(true); - sendPrivateMessage.setEnabled(user != null && mucOptions.allowPm() && user.getRole().ranks(MucOptions.Role.VISITOR)); + sendPrivateMessage.setVisible(!isGroupChat && mucOptions.allowPm() && user.getRole().ranks(MucOptions.Role.VISITOR)); } } - public static boolean onContextItemSelected(MenuItem item, User user, Conversation conversation, XmppActivity activity) { + public static boolean onContextItemSelected(MenuItem item, User user, XmppActivity activity) { + return onContextItemSelected(item, user, activity, null); + } + + public static boolean onContextItemSelected(MenuItem item, User user, XmppActivity activity, final String fingerprint) { + final Conversation conversation = user.getConversation(); final XmppConnectionService.OnAffiliationChanged onAffiliationChanged = activity instanceof XmppConnectionService.OnAffiliationChanged ? (XmppConnectionService.OnAffiliationChanged) activity : null; - final XmppConnectionService.OnRoleChanged onRoleChanged = activity instanceof XmppConnectionService.OnRoleChanged ? (XmppConnectionService.OnRoleChanged) activity : null; Jid jid = user.getRealJid(); switch (item.getItemId()) { case R.id.action_contact_details: Contact contact = user.getContact(); if (contact != null) { - activity.switchToContactDetails(contact); + activity.switchToContactDetails(contact, fingerprint); } return true; case R.id.start_conversation: - startConversation(user, conversation, activity); + startConversation(user, activity); return true; case R.id.give_admin_privileges: activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.ADMIN, onAffiliationChanged); @@ -102,19 +152,23 @@ public final class MucDetailsContextMenuHelper { case R.id.give_membership: activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.MEMBER, onAffiliationChanged); return true; + case R.id.give_owner_privileges: + activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.OWNER, onAffiliationChanged); + return true; case R.id.remove_membership: activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.NONE, onAffiliationChanged); return true; case R.id.remove_admin_privileges: + case R.id.revoke_owner_privileges: activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.MEMBER, onAffiliationChanged); return true; case R.id.remove_from_room: - removeFromRoom(user, conversation, activity, onAffiliationChanged, onRoleChanged); + removeFromRoom(user, activity, onAffiliationChanged); return true; case R.id.ban_from_conference: activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.OUTCAST, onAffiliationChanged); if (user.getRole() != MucOptions.Role.NONE) { - activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE, onRoleChanged); + activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE); } return true; case R.id.send_private_message: @@ -138,11 +192,12 @@ public final class MucDetailsContextMenuHelper { } } - public static void removeFromRoom(final User user, Conversation conversation, XmppActivity activity, XmppConnectionService.OnAffiliationChanged onAffiliationChanged, XmppConnectionService.OnRoleChanged onRoleChanged) { + private static void removeFromRoom(final User user, XmppActivity activity, XmppConnectionService.OnAffiliationChanged onAffiliationChanged) { + final Conversation conversation = user.getConversation(); if (conversation.getMucOptions().membersOnly()) { activity.xmppConnectionService.changeAffiliationInConference(conversation, user.getRealJid(), MucOptions.Affiliation.NONE, onAffiliationChanged); if (user.getRole() != MucOptions.Role.NONE) { - activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE, onRoleChanged); + activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE); } } else { AlertDialog.Builder builder = new AlertDialog.Builder(activity); @@ -158,16 +213,16 @@ public final class MucDetailsContextMenuHelper { builder.setPositiveButton(R.string.ban_now, (dialog, which) -> { activity.xmppConnectionService.changeAffiliationInConference(conversation, user.getRealJid(), MucOptions.Affiliation.OUTCAST, onAffiliationChanged); if (user.getRole() != MucOptions.Role.NONE) { - activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE, onRoleChanged); + activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE); } }); builder.create().show(); } } - public static void startConversation(User user, Conversation conversation, XmppActivity activity) { + private static void startConversation(User user, XmppActivity activity) { if (user.getRealJid() != null) { - Conversation newConversation = activity.xmppConnectionService.findOrCreateConversation(conversation.getAccount(), user.getRealJid().asBareJid(), false, true); + Conversation newConversation = activity.xmppConnectionService.findOrCreateConversation(user.getAccount(), user.getRealJid().asBareJid(), false, true); activity.switchToConversation(newConversation); } } diff --git a/src/main/java/de/pixart/messenger/ui/util/MyLinkify.java b/src/main/java/de/pixart/messenger/ui/util/MyLinkify.java index 8f2aa4e61..4b276e392 100644 --- a/src/main/java/de/pixart/messenger/ui/util/MyLinkify.java +++ b/src/main/java/de/pixart/messenger/ui/util/MyLinkify.java @@ -40,8 +40,6 @@ import de.pixart.messenger.utils.GeoHelper; import de.pixart.messenger.utils.Patterns; import de.pixart.messenger.utils.XmppUri; -import static java.lang.Character.isAlphabetic; - public class MyLinkify { private static final Linkify.TransformFilter WEBURL_TRANSFORM_FILTER = (matcher, url) -> { diff --git a/src/main/java/de/pixart/messenger/ui/util/SendButtonAction.java b/src/main/java/de/pixart/messenger/ui/util/SendButtonAction.java index c7feb2105..825ca3f17 100644 --- a/src/main/java/de/pixart/messenger/ui/util/SendButtonAction.java +++ b/src/main/java/de/pixart/messenger/ui/util/SendButtonAction.java @@ -32,8 +32,8 @@ package de.pixart.messenger.ui.util; import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE; import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_CHOOSE_IMAGE; import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_LOCATION; -import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_RECORD_VOICE; import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_RECORD_VIDEO; +import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_RECORD_VOICE; import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_TAKE_PHOTO; public enum SendButtonAction { diff --git a/src/main/java/de/pixart/messenger/ui/util/ViewUtil.java b/src/main/java/de/pixart/messenger/ui/util/ViewUtil.java index 07e25e31d..db729f0e4 100644 --- a/src/main/java/de/pixart/messenger/ui/util/ViewUtil.java +++ b/src/main/java/de/pixart/messenger/ui/util/ViewUtil.java @@ -14,6 +14,7 @@ import java.util.List; import de.pixart.messenger.Config; import de.pixart.messenger.R; +import de.pixart.messenger.entities.DownloadableFile; import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.ui.MediaViewerActivity; @@ -25,6 +26,18 @@ public class ViewUtil { view(context, file, mime); } + public static void view(Context context, DownloadableFile file) { + if (!file.exists()) { + Toast.makeText(context, R.string.file_deleted, Toast.LENGTH_SHORT).show(); + return; + } + String mime = file.getMimeType(); + if (mime == null) { + mime = "*/*"; + } + view(context, file, mime); + } + public static void view(Context context, File file, String mime) { Uri uri; try { diff --git a/src/main/java/de/pixart/messenger/utils/BackupFileHeader.java b/src/main/java/de/pixart/messenger/utils/BackupFileHeader.java new file mode 100644 index 000000000..19ad9bff9 --- /dev/null +++ b/src/main/java/de/pixart/messenger/utils/BackupFileHeader.java @@ -0,0 +1,84 @@ +package de.pixart.messenger.utils; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import rocks.xmpp.addr.Jid; + +public class BackupFileHeader { + + private static final int VERSION = 1; + + private String app; + private Jid jid; + private long timestamp; + private byte[] iv; + private byte[] salt; + + + @Override + public String toString() { + return "BackupFileHeader{" + + "app='" + app + '\'' + + ", jid=" + jid + + ", timestamp=" + timestamp + + ", iv=" + CryptoHelper.bytesToHex(iv) + + ", salt=" + CryptoHelper.bytesToHex(salt) + + '}'; + } + + public BackupFileHeader(String app, Jid jid, long timestamp, byte[] iv, byte[] salt) { + this.app = app; + this.jid = jid; + this.timestamp = timestamp; + this.iv = iv; + this.salt = salt; + } + + public void write(DataOutputStream dataOutputStream) throws IOException { + dataOutputStream.writeInt(VERSION); + dataOutputStream.writeUTF(app); + dataOutputStream.writeUTF(jid.asBareJid().toEscapedString()); + dataOutputStream.writeLong(timestamp); + dataOutputStream.write(iv); + dataOutputStream.write(salt); + } + + public static BackupFileHeader read(DataInputStream inputStream) throws IOException { + final int version = inputStream.readInt(); + if (version > VERSION) { + throw new IllegalArgumentException("Backup File version was " + version + " but app only supports up to version " + VERSION); + } + String app = inputStream.readUTF(); + String jid = inputStream.readUTF(); + long timestamp = inputStream.readLong(); + byte[] iv = new byte[12]; + inputStream.readFully(iv); + byte[] salt = new byte[16]; + inputStream.readFully(salt); + + return new BackupFileHeader(app, Jid.of(jid), timestamp, iv, salt); + + } + + public byte[] getSalt() { + return salt; + } + + public byte[] getIv() { + return iv; + } + + public Jid getJid() { + return jid; + } + + public String getApp() { + return app; + } + + public long getTimestamp() { + return timestamp; + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/Compatibility.java b/src/main/java/de/pixart/messenger/utils/Compatibility.java index 38f9db378..c8922e18b 100644 --- a/src/main/java/de/pixart/messenger/utils/Compatibility.java +++ b/src/main/java/de/pixart/messenger/utils/Compatibility.java @@ -1,6 +1,7 @@ package de.pixart.messenger.utils; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -11,15 +12,19 @@ import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.support.annotation.BoolRes; import android.support.v4.content.ContextCompat; +import android.util.Log; import java.util.Arrays; import java.util.Collections; import java.util.List; +import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.ui.SettingsActivity; import de.pixart.messenger.ui.SettingsFragment; +import static de.pixart.messenger.services.EventReceiver.EXTRA_NEEDS_FOREGROUND_SERVICE; + public class Compatibility { private static final List<String> UNUSED_SETTINGS_POST_TWENTYSIX = Arrays.asList( SettingsActivity.SHOW_FOREGROUND_SERVICE, @@ -102,4 +107,17 @@ public class Compatibility { } } } + + public static void startService(Context context, Intent intent) { + try { + if (Compatibility.runsAndTargetsTwentySix(context)) { + intent.putExtra(EXTRA_NEEDS_FOREGROUND_SERVICE, true); + ContextCompat.startForegroundService(context, intent); + } else { + context.startService(intent); + } + } catch (RuntimeException e) { + Log.d(Config.LOGTAG, context.getClass().getSimpleName() + " was unable to start service"); + } + } }
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java b/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java index 73d5589e4..5e2948b5b 100644 --- a/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java +++ b/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java @@ -8,6 +8,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Stack; +import java.util.concurrent.atomic.AtomicBoolean; import de.pixart.messenger.Config; @@ -21,16 +22,26 @@ public abstract class ConversationsFileObserver { private final String path; private final List<SingleFileObserver> mObservers = new ArrayList<>(); + private final AtomicBoolean shouldStop = new AtomicBoolean(true); protected ConversationsFileObserver(String path) { this.path = path; } - public synchronized void startWatching() { + public void startWatching() { + shouldStop.set(false); + startWatchingInternal(); + } + + private synchronized void startWatchingInternal() { Stack<String> stack = new Stack<>(); stack.push(path); while (!stack.empty()) { + if (shouldStop.get()) { + Log.d(Config.LOGTAG, "file observer received command to stop"); + return; + } String parent = stack.pop(); mObservers.add(new SingleFileObserver(parent, FileObserver.DELETE | FileObserver.MOVED_FROM)); final File path = new File(parent); @@ -44,6 +55,10 @@ public abstract class ConversationsFileObserver { continue; } for (File file : files) { + if (shouldStop.get()) { + Log.d(Config.LOGTAG, "file observer received command to stop"); + return; + } if (file.isDirectory() && file.getName().charAt(0) != '.') { final String currentPath = file.getAbsolutePath(); if (depth(file) <= 8 && !stack.contains(currentPath) && !observing(currentPath)) { @@ -74,7 +89,12 @@ public abstract class ConversationsFileObserver { return false; } - public synchronized void stopWatching() { + public void stopWatching() { + shouldStop.set(true); + stopWatchingInternal(); + } + + private synchronized void stopWatchingInternal() { for (FileObserver observer : mObservers) { observer.stopWatching(); } diff --git a/src/main/java/de/pixart/messenger/utils/CryptoHelper.java b/src/main/java/de/pixart/messenger/utils/CryptoHelper.java index 2104349d8..5d1e7980c 100644 --- a/src/main/java/de/pixart/messenger/utils/CryptoHelper.java +++ b/src/main/java/de/pixart/messenger/utils/CryptoHelper.java @@ -63,7 +63,8 @@ public final class CryptoHelper { } public static String pronounceable(SecureRandom random) { - char[] output = new char[random.nextInt(4) * 2 + 5]; + final int rand = random.nextInt(4); + char[] output = new char[rand * 2 + (5 - rand)]; boolean vowel = random.nextBoolean(); for (int i = 0; i < output.length; ++i) { output[i] = vowel ? VOWELS[random.nextInt(VOWELS.length)] : CONSONANTS[random.nextInt(CONSONANTS.length)]; diff --git a/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java b/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java index 18238ae60..428aa616e 100644 --- a/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java +++ b/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java @@ -17,7 +17,6 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.List; import java.util.Locale; import de.pixart.messenger.Config; @@ -27,7 +26,6 @@ import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.entities.Message; import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.ui.XmppActivity; -import rocks.xmpp.addr.Jid; public class ExceptionHelper { private static final String FILENAME = "stacktrace.txt"; diff --git a/src/main/java/de/pixart/messenger/utils/GeoHelper.java b/src/main/java/de/pixart/messenger/utils/GeoHelper.java index 209fd75db..d978c89e0 100644 --- a/src/main/java/de/pixart/messenger/utils/GeoHelper.java +++ b/src/main/java/de/pixart/messenger/utils/GeoHelper.java @@ -4,6 +4,8 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; +import org.osmdroid.util.GeoPoint; + import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; @@ -11,8 +13,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import de.pixart.messenger.Config; +import de.pixart.messenger.R; import de.pixart.messenger.entities.Contact; -import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.entities.Conversational; import de.pixart.messenger.entities.Message; import de.pixart.messenger.ui.ShowLocationActivity; @@ -43,41 +45,43 @@ public class GeoHelper { return "https://xmpp.pix-art.de/staticmap/staticmap.php?center=" + latitude + "," + longitude + "&size=500x500&markers=" + latitude + "," + longitude + "&zoom= " + Config.DEFAULT_ZOOM; } - public static ArrayList<Intent> createGeoIntentsFromMessage(Message message, Context context) { - final ArrayList<Intent> intents = new ArrayList<>(); - Matcher matcher = GEO_URI.matcher(message.getBody()); + private static GeoPoint parseGeoPoint(String body) throws IllegalArgumentException { + Matcher matcher = GEO_URI.matcher(body); + ; if (!matcher.matches()) { - return intents; + throw new IllegalArgumentException("Invalid geo uri"); } double latitude; double longitude; try { latitude = Double.parseDouble(matcher.group(1)); if (latitude > 90.0 || latitude < -90.0) { - return intents; + throw new IllegalArgumentException("Invalid geo uri"); } longitude = Double.parseDouble(matcher.group(2)); if (longitude > 180.0 || longitude < -180.0) { - return intents; + throw new IllegalArgumentException("Invalid geo uri"); } - } catch (NumberFormatException nfe) { + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid geo uri", e); + } + return new GeoPoint(latitude, longitude); + } + + public static ArrayList<Intent> createGeoIntentsFromMessage(Context context, Message message) { + final ArrayList<Intent> intents = new ArrayList<>(); + final GeoPoint geoPoint; + try { + geoPoint = parseGeoPoint(message.getBody()); + } catch (IllegalArgumentException e) { return intents; } final Conversational conversation = message.getConversation(); - String label; - if (conversation instanceof Conversation && conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) { - try { - label = "(" + URLEncoder.encode(((Conversation) conversation).getName().toString(), "UTF-8") + ")"; - } catch (UnsupportedEncodingException e) { - label = ""; - } - } else { - label = ""; - } + final String label = getLabel(context, message); Intent locationPluginIntent = new Intent(context, ShowLocationActivity.class); - locationPluginIntent.putExtra("latitude", latitude); - locationPluginIntent.putExtra("longitude", longitude); + locationPluginIntent.putExtra("latitude", geoPoint.getLatitude()); + locationPluginIntent.putExtra("longitude", geoPoint.getLongitude()); if (message.getStatus() != Message.STATUS_RECEIVED) { locationPluginIntent.putExtra("jid", conversation.getAccount().getJid().toString()); locationPluginIntent.putExtra("name", conversation.getAccount().getJid().getLocal()); @@ -93,8 +97,38 @@ public class GeoHelper { intents.add(locationPluginIntent); Intent geoIntent = new Intent(Intent.ACTION_VIEW); - geoIntent.setData(Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude) + "?q=" + String.valueOf(latitude) + "," + String.valueOf(longitude) + label)); + geoIntent.setData(Uri.parse("geo:" + String.valueOf(geoPoint.getLatitude()) + "," + String.valueOf(geoPoint.getLongitude()) + "?q=" + String.valueOf(geoPoint.getLatitude()) + "," + String.valueOf(geoPoint.getLongitude()) + label)); intents.add(geoIntent); return intents; } + + public static void view(Context context, Message message) { + final GeoPoint geoPoint = parseGeoPoint(message.getBody()); + final String label = getLabel(context, message); + context.startActivity(geoIntent(geoPoint, label)); + } + + private static Intent geoIntent(GeoPoint geoPoint, String label) { + Intent geoIntent = new Intent(Intent.ACTION_VIEW); + geoIntent.setData(Uri.parse("geo:" + String.valueOf(geoPoint.getLatitude()) + "," + String.valueOf(geoPoint.getLongitude()) + "?q=" + String.valueOf(geoPoint.getLatitude()) + "," + String.valueOf(geoPoint.getLongitude()) + "(" + label + ")")); + return geoIntent; + } + + public static boolean openInOsmAnd(Context context, Message message) { + final GeoPoint geoPoint = parseGeoPoint(message.getBody()); + final String label = getLabel(context, message); + return geoIntent(geoPoint, label).resolveActivity(context.getPackageManager()) != null; + } + + private static String getLabel(Context context, Message message) { + if (message.getStatus() == Message.STATUS_RECEIVED) { + try { + return URLEncoder.encode(UIHelper.getMessageDisplayName(message), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } + } else { + return context.getString(R.string.me); + } + } }
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/Namespace.java b/src/main/java/de/pixart/messenger/utils/Namespace.java index 83d63cdaa..3a3e32f73 100644 --- a/src/main/java/de/pixart/messenger/utils/Namespace.java +++ b/src/main/java/de/pixart/messenger/utils/Namespace.java @@ -24,4 +24,5 @@ public final class Namespace { public static final String BOOKMARKS_CONVERSION = "urn:xmpp:bookmarks-conversion:0"; public static final String BOOKMARKS = "storage:bookmarks"; public static final String SYNCHRONIZATION = "im.quicksy.synchronization:0"; + public static final String AVATAR_CONVERSION = "urn:xmpp:pep-vcard-conversion:0"; } diff --git a/src/main/java/de/pixart/messenger/utils/PermissionUtils.java b/src/main/java/de/pixart/messenger/utils/PermissionUtils.java new file mode 100644 index 000000000..b21ca8869 --- /dev/null +++ b/src/main/java/de/pixart/messenger/utils/PermissionUtils.java @@ -0,0 +1,34 @@ +package de.pixart.messenger.utils; + +import android.Manifest; +import android.content.pm.PackageManager; + +public class PermissionUtils { + + public static boolean allGranted(int[] grantResults) { + for (int grantResult : grantResults) { + if (grantResult != PackageManager.PERMISSION_GRANTED) { + return false; + } + } + return true; + } + + public static boolean writeGranted(int[] grantResults, String[] permission) { + for (int i = 0; i < grantResults.length; ++i) { + if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission[i])) { + return grantResults[i] == PackageManager.PERMISSION_GRANTED; + } + } + return false; + } + + public static String getFirstDenied(int[] grantResults, String[] permissions) { + for (int i = 0; i < grantResults.length; ++i) { + if (grantResults[i] == PackageManager.PERMISSION_DENIED) { + return permissions[i]; + } + } + return null; + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/PhoneHelper.java b/src/main/java/de/pixart/messenger/utils/PhoneHelper.java index 327ae1904..86a9bed1a 100644 --- a/src/main/java/de/pixart/messenger/utils/PhoneHelper.java +++ b/src/main/java/de/pixart/messenger/utils/PhoneHelper.java @@ -3,20 +3,13 @@ package de.pixart.messenger.utils; import android.Manifest; import android.annotation.SuppressLint; import android.content.Context; -import android.content.CursorLoader; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; -import android.os.Bundle; -import android.provider.ContactsContract; import android.provider.ContactsContract.Profile; import android.provider.Settings; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.RejectedExecutionException; - public class PhoneHelper { @SuppressLint("HardwareIds") diff --git a/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java b/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java index 9846146a8..dc2f2bc09 100644 --- a/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java +++ b/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java @@ -1,6 +1,5 @@ package de.pixart.messenger.utils; -import android.os.Looper; import android.util.Log; import java.util.ArrayDeque; diff --git a/src/main/java/de/pixart/messenger/utils/UIHelper.java b/src/main/java/de/pixart/messenger/utils/UIHelper.java index 03af31e23..ea1878557 100644 --- a/src/main/java/de/pixart/messenger/utils/UIHelper.java +++ b/src/main/java/de/pixart/messenger/utils/UIHelper.java @@ -263,8 +263,6 @@ public class UIHelper { case Transferable.STATUS_OFFER_CHECK_FILESIZE: return new Pair<>(context.getString(R.string.x_file_offered_for_download, getFileDescriptionString(context, message)), true); - case Transferable.STATUS_DELETED: - return new Pair<>(context.getString(R.string.file_deleted), true); case Transferable.STATUS_FAILED: return new Pair<>(context.getString(R.string.file_transmission_failed), true); case Transferable.STATUS_UPLOADING: @@ -278,6 +276,8 @@ public class UIHelper { default: return new Pair<>("", false); } + } else if (message.isFileOrImage() && message.isFileDeleted()) { + return new Pair<>(context.getString(R.string.file_deleted), true); } else if (message.getEncryption() == Message.ENCRYPTION_PGP) { return new Pair<>(context.getString(R.string.pgp_message), true); } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { @@ -286,7 +286,7 @@ public class UIHelper { return new Pair<>(context.getString(R.string.not_encrypted_for_this_device), true); } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_FAILED) { return new Pair<>(context.getString(R.string.omemo_decryption_failed), true); - } else if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) { + } else if (message.isFileOrImage()) { return new Pair<>(getFileDescriptionString(context, message), true); } else { final String body = MessageUtils.filterLtrRtl(message.getBody()); @@ -307,6 +307,9 @@ public class UIHelper { SpannableStringBuilder builder = new SpannableStringBuilder(); for (CharSequence l : CharSequenceUtils.split(styledBody, '\n')) { if (l.length() > 0) { + if (l.toString().equals("```")) { + continue; + } char first = l.charAt(0); if ((first != '>' || !isPositionFollowedByQuoteableCharacter(l,0)) && first != '\u00bb') { CharSequence line = CharSequenceUtils.trim(l); diff --git a/src/main/java/de/pixart/messenger/utils/XmppUri.java b/src/main/java/de/pixart/messenger/utils/XmppUri.java index 9c0b3e666..1ee90aa84 100644 --- a/src/main/java/de/pixart/messenger/utils/XmppUri.java +++ b/src/main/java/de/pixart/messenger/utils/XmppUri.java @@ -171,7 +171,7 @@ public class XmppUri { private boolean hasAction(String query, String action) { for(String pair : query == null ? new String[0] : query.split(";")) { final String[] parts = pair.split("=",2); - if (parts.length == 1 && parts[0].equals(action)) { + if (parts.length == 1 && parts[0].toLowerCase(Locale.US).startsWith(action)) { return true; } } diff --git a/src/main/java/de/pixart/messenger/xml/Element.java b/src/main/java/de/pixart/messenger/xml/Element.java index d7cace655..d1d3c50e9 100644 --- a/src/main/java/de/pixart/messenger/xml/Element.java +++ b/src/main/java/de/pixart/messenger/xml/Element.java @@ -74,7 +74,7 @@ public class Element { } private String findInternationalizedChildContent(String name, @NonNull String language) { - HashMap<String, String> contents = new HashMap<>(); + final HashMap<String, String> contents = new HashMap<>(); for (Element child : this.children) { if (name.equals(child.getName())) { String lang = child.getAttribute("xml:lang"); @@ -88,11 +88,12 @@ public class Element { } } } - String value = contents.get(null); + final String value = contents.get(null); if (value != null) { return value; } - return contents.size() > 0 ? contents.values().iterator().next() : null; + final String[] values = contents.values().toArray(new String[0]); + return values.length == 0 ? null : values[0]; } public Element findChild(String name, String xmlns) { diff --git a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java index 5e3205684..0eb5a5dcb 100644 --- a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java +++ b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java @@ -286,7 +286,7 @@ public class XmppConnection implements Runnable { } } else if (useTor) { String destination; - if (account.getHostname().isEmpty()) { + if (account.getHostname().isEmpty() || account.isOnion()) { destination = account.getServer(); } else { destination = account.getHostname(); @@ -372,9 +372,8 @@ public class XmppConnection implements Runnable { if (!tlsFactoryVerifier.verifier.verify(account.getServer(), verifiedHostname, ((SSLSocket) localSocket).getSession())) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed"); - if (!iterator.hasNext()) { - throw new StateChangingException(Account.State.TLS_ERROR); - } + FileBackend.close(localSocket); + throw new StateChangingException(Account.State.TLS_ERROR); } } localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000); @@ -385,13 +384,13 @@ public class XmppConnection implements Runnable { } break; // successfully connected to server that speaks xmpp } else { - localSocket.close(); - if (!iterator.hasNext()) { - throw new StateChangingException(Account.State.STREAM_OPENING_ERROR); - } + FileBackend.close(localSocket); + throw new StateChangingException(Account.State.STREAM_OPENING_ERROR); } } catch (final StateChangingException e) { - throw e; + if (!iterator.hasNext()) { + throw e; + } } catch (InterruptedException e) { Log.d(Config.LOGTAG,account.getJid().asBareJid()+": thread was interrupted before beginning stream"); return; @@ -867,7 +866,7 @@ public class XmppConnection implements Runnable { private void processStreamFeatures(final Tag currentTag) throws XmlPullParserException, IOException { this.streamFeatures = tagReader.readElement(currentTag); - final boolean isSecure = features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS; + final boolean isSecure = features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS || account.isOnion(); final boolean needsBinding = !isBound && !account.isOptionSet(Account.OPTION_REGISTER); if (this.streamFeatures.hasChild("starttls") && !features.encryptionEnabled) { sendStartTLS(); @@ -1297,7 +1296,7 @@ public class XmppConnection implements Runnable { for (Jid jid : items) { sendServiceDiscoveryInfo(jid); } - getInviteAdHoc(server); + getAdHocFeatures(server); } else { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not query disco items of " + server); } @@ -1310,9 +1309,8 @@ public class XmppConnection implements Runnable { }); } - private void getInviteAdHoc(final Jid server) { + private void getAdHocFeatures(final Jid server) { mPendingServiceDiscoveries.incrementAndGet(); - ArrayList<String> nodes = new ArrayList<String>(); IqPacket iq = new IqPacket(IqPacket.TYPE.GET); iq.setTo(Jid.ofDomain(server.getDomain())); iq.setFrom(Jid.of(account.getJid().asBareJid())); @@ -1332,12 +1330,10 @@ public class XmppConnection implements Runnable { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": query disco commands of " + server + " was successful"); getAdHocInviteUrl(server); } - nodes.add(node); } } } } else { - features.adhocinvite = false; Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not query disco commands of " + server); } if (packet.getType() != IqPacket.TYPE.TIMEOUT) { @@ -1346,7 +1342,6 @@ public class XmppConnection implements Runnable { finalizeBind(); } } - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": query disco commands of " + server + " was " + features.adhocinvite); }); } @@ -1853,6 +1848,10 @@ public class XmppConnection implements Runnable { return hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS_CONVERSION) && pepPublishOptions(); } + public boolean avatarConversion() { + return hasDiscoFeature(account.getJid().asBareJid(), Namespace.AVATAR_CONVERSION) && pepPublishOptions(); + } + public boolean blocking() { return hasDiscoFeature(Jid.of(account.getServer()), Namespace.BLOCKING); } diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java index 2f3ec09ea..d4c6c7eba 100644 --- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java +++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java @@ -117,8 +117,10 @@ public class JingleConnection implements Transferable { if (message.getEncryption() == Message.ENCRYPTION_PGP) { account.getPgpDecryptionService().decrypt(message, true); } else { - JingleConnection.this.mXmppConnectionService.getNotificationService().push(message); + mXmppConnectionService.getFileBackend().updateMediaScanner(file, () -> JingleConnection.this.mXmppConnectionService.getNotificationService().push(message)); } + Log.d(Config.LOGTAG, "successfully transmitted file:" + file.getAbsolutePath() + " (" + CryptoHelper.bytesToHex(file.getSha1Sum()) + ")"); + return; } } else { if (ftVersion == Content.Version.FT_5) { //older Conversations will break when receiving a session-info @@ -483,7 +485,7 @@ public class JingleConnection implements Transferable { private void sendInitRequest() { JinglePacket packet = this.bootstrapPacket("session-initiate"); Content content = new Content(this.contentCreator, this.contentName); - if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { + if (message.isFileOrImage()) { content.setTransportId(this.transportId); this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false); if (message.getEncryption() == Message.ENCRYPTION_OTR) { diff --git a/src/main/res/drawable-hdpi/ic_add_white_24dp.png b/src/main/res/drawable-hdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 000000000..bcd24f2d9 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_add_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_archive_white_24dp.png b/src/main/res/drawable-hdpi/ic_archive_white_24dp.png Binary files differnew file mode 100644 index 000000000..1f109a958 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_archive_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_barcode_scan_white_24dp.png b/src/main/res/drawable-hdpi/ic_barcode_scan_white_24dp.png Binary files differdeleted file mode 100644 index 705f40296..000000000 --- a/src/main/res/drawable-hdpi/ic_barcode_scan_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_close_white_24dp.png b/src/main/res/drawable-hdpi/ic_close_white_24dp.png Binary files differnew file mode 100644 index 000000000..bb58afd97 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_close_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_import_export_white_24dp.png b/src/main/res/drawable-hdpi/ic_import_export_white_24dp.png Binary files differdeleted file mode 100644 index 62d4a65da..000000000 --- a/src/main/res/drawable-hdpi/ic_import_export_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_input_white_24dp.png b/src/main/res/drawable-hdpi/ic_input_white_24dp.png Binary files differnew file mode 100644 index 000000000..d8249d985 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_input_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_link_off_white_24dp.png b/src/main/res/drawable-hdpi/ic_link_off_white_24dp.png Binary files differnew file mode 100644 index 000000000..54aa6708c --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_link_off_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_open_in_new_white_24dp.png b/src/main/res/drawable-hdpi/ic_open_in_new_white_24dp.png Binary files differnew file mode 100644 index 000000000..c48104c3f --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_open_in_new_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_public_white_24dp.png b/src/main/res/drawable-hdpi/ic_public_white_24dp.png Binary files differnew file mode 100644 index 000000000..242c0de59 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_public_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_unarchive_white_24dp.png b/src/main/res/drawable-hdpi/ic_unarchive_white_24dp.png Binary files differnew file mode 100644 index 000000000..a32579aa8 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_unarchive_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_video_black_24dp.png b/src/main/res/drawable-hdpi/ic_video_black_24dp.png Binary files differdeleted file mode 100644 index afd308697..000000000 --- a/src/main/res/drawable-hdpi/ic_video_black_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_video_white_24dp.png b/src/main/res/drawable-hdpi/ic_video_white_24dp.png Binary files differdeleted file mode 100644 index ca414b356..000000000 --- a/src/main/res/drawable-hdpi/ic_video_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_add_white_24dp.png b/src/main/res/drawable-mdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 000000000..c5d5ae2fe --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_add_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_archive_white_24dp.png b/src/main/res/drawable-mdpi/ic_archive_white_24dp.png Binary files differnew file mode 100644 index 000000000..7944feb2a --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_archive_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_barcode_scan_white_24dp.png b/src/main/res/drawable-mdpi/ic_barcode_scan_white_24dp.png Binary files differdeleted file mode 100644 index b952be675..000000000 --- a/src/main/res/drawable-mdpi/ic_barcode_scan_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_close_white_24dp.png b/src/main/res/drawable-mdpi/ic_close_white_24dp.png Binary files differnew file mode 100644 index 000000000..27c293b2e --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_close_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_import_export_white_24dp.png b/src/main/res/drawable-mdpi/ic_import_export_white_24dp.png Binary files differdeleted file mode 100644 index 5ec6d1c63..000000000 --- a/src/main/res/drawable-mdpi/ic_import_export_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_input_white_24dp.png b/src/main/res/drawable-mdpi/ic_input_white_24dp.png Binary files differnew file mode 100644 index 000000000..5bcfac74c --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_input_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_link_off_white_24dp.png b/src/main/res/drawable-mdpi/ic_link_off_white_24dp.png Binary files differnew file mode 100644 index 000000000..7e5e52942 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_link_off_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_open_in_new_white_24dp.png b/src/main/res/drawable-mdpi/ic_open_in_new_white_24dp.png Binary files differnew file mode 100644 index 000000000..56aa52136 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_open_in_new_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_public_white_24dp.png b/src/main/res/drawable-mdpi/ic_public_white_24dp.png Binary files differnew file mode 100644 index 000000000..5b99884ad --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_public_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_unarchive_white_24dp.png b/src/main/res/drawable-mdpi/ic_unarchive_white_24dp.png Binary files differnew file mode 100644 index 000000000..0480fea98 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_unarchive_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_video_black_24dp.png b/src/main/res/drawable-mdpi/ic_video_black_24dp.png Binary files differdeleted file mode 100644 index c4be08a55..000000000 --- a/src/main/res/drawable-mdpi/ic_video_black_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_video_white_24dp.png b/src/main/res/drawable-mdpi/ic_video_white_24dp.png Binary files differdeleted file mode 100644 index 60cc35c42..000000000 --- a/src/main/res/drawable-mdpi/ic_video_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_add_white_24dp.png b/src/main/res/drawable-xhdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 000000000..d7540be50 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_add_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_archive_white_24dp.png b/src/main/res/drawable-xhdpi/ic_archive_white_24dp.png Binary files differnew file mode 100644 index 000000000..9d1d46a5d --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_archive_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_barcode_scan_white_24dp.png b/src/main/res/drawable-xhdpi/ic_barcode_scan_white_24dp.png Binary files differdeleted file mode 100644 index f0d2b0e59..000000000 --- a/src/main/res/drawable-xhdpi/ic_barcode_scan_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_close_white_24dp.png b/src/main/res/drawable-xhdpi/ic_close_white_24dp.png Binary files differnew file mode 100644 index 000000000..79a80c2a0 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_close_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_import_export_white_24dp.png b/src/main/res/drawable-xhdpi/ic_import_export_white_24dp.png Binary files differdeleted file mode 100644 index e32a1325c..000000000 --- a/src/main/res/drawable-xhdpi/ic_import_export_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_input_white_24dp.png b/src/main/res/drawable-xhdpi/ic_input_white_24dp.png Binary files differnew file mode 100644 index 000000000..f39c9a39f --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_input_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_link_off_white_24dp.png b/src/main/res/drawable-xhdpi/ic_link_off_white_24dp.png Binary files differnew file mode 100644 index 000000000..dfad023bf --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_link_off_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_open_in_new_white_24dp.png b/src/main/res/drawable-xhdpi/ic_open_in_new_white_24dp.png Binary files differnew file mode 100644 index 000000000..ae0d6fc21 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_open_in_new_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_public_white_24dp.png b/src/main/res/drawable-xhdpi/ic_public_white_24dp.png Binary files differnew file mode 100644 index 000000000..800b7dbf9 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_public_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_unarchive_white_24dp.png b/src/main/res/drawable-xhdpi/ic_unarchive_white_24dp.png Binary files differnew file mode 100644 index 000000000..04e607bd4 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_unarchive_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_video_black_24dp.png b/src/main/res/drawable-xhdpi/ic_video_black_24dp.png Binary files differdeleted file mode 100644 index f695100bb..000000000 --- a/src/main/res/drawable-xhdpi/ic_video_black_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_video_white_24dp.png b/src/main/res/drawable-xhdpi/ic_video_white_24dp.png Binary files differdeleted file mode 100644 index 2c764cbb5..000000000 --- a/src/main/res/drawable-xhdpi/ic_video_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 000000000..d1c0a1d79 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png Binary files differnew file mode 100644 index 000000000..d526d6a1d --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_barcode_scan_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_barcode_scan_white_24dp.png Binary files differdeleted file mode 100644 index cd8375f5b..000000000 --- a/src/main/res/drawable-xxhdpi/ic_barcode_scan_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png Binary files differnew file mode 100644 index 000000000..8c82a4fed --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_import_export_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_import_export_white_24dp.png Binary files differdeleted file mode 100644 index a9069ce38..000000000 --- a/src/main/res/drawable-xxhdpi/ic_import_export_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_input_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_input_white_24dp.png Binary files differnew file mode 100644 index 000000000..b0f0de9e3 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_input_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_link_off_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_link_off_white_24dp.png Binary files differnew file mode 100644 index 000000000..44da13a51 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_link_off_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_open_in_new_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_open_in_new_white_24dp.png Binary files differnew file mode 100644 index 000000000..8236c23f1 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_open_in_new_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_public_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_public_white_24dp.png Binary files differnew file mode 100644 index 000000000..b8c8448a2 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_public_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_unarchive_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_unarchive_white_24dp.png Binary files differnew file mode 100644 index 000000000..1a426fb0b --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_unarchive_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_video_black_24dp.png b/src/main/res/drawable-xxhdpi/ic_video_black_24dp.png Binary files differdeleted file mode 100644 index 2cb46bf1a..000000000 --- a/src/main/res/drawable-xxhdpi/ic_video_black_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_video_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_video_white_24dp.png Binary files differdeleted file mode 100644 index 77ef5ec76..000000000 --- a/src/main/res/drawable-xxhdpi/ic_video_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 000000000..54a96a38a --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png Binary files differnew file mode 100644 index 000000000..62d27e00f --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_barcode_scan_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_barcode_scan_white_24dp.png Binary files differdeleted file mode 100644 index f8b6f3f3b..000000000 --- a/src/main/res/drawable-xxxhdpi/ic_barcode_scan_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png Binary files differnew file mode 100644 index 000000000..773ae970c --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_import_export_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_import_export_white_24dp.png Binary files differdeleted file mode 100644 index 73a3e0ba3..000000000 --- a/src/main/res/drawable-xxxhdpi/ic_import_export_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_input_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_input_white_24dp.png Binary files differnew file mode 100644 index 000000000..29168165a --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_input_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_link_off_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_link_off_white_24dp.png Binary files differnew file mode 100644 index 000000000..e0831c527 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_link_off_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_open_in_new_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_open_in_new_white_24dp.png Binary files differnew file mode 100644 index 000000000..e27a66906 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_open_in_new_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_public_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_public_white_24dp.png Binary files differnew file mode 100644 index 000000000..7fb0b9929 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_public_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png Binary files differnew file mode 100644 index 000000000..067e413b7 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_video_black_24dp.png b/src/main/res/drawable-xxxhdpi/ic_video_black_24dp.png Binary files differdeleted file mode 100644 index bd76e9db6..000000000 --- a/src/main/res/drawable-xxxhdpi/ic_video_black_24dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_video_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_video_white_24dp.png Binary files differdeleted file mode 100644 index d8ab46d9f..000000000 --- a/src/main/res/drawable-xxxhdpi/ic_video_white_24dp.png +++ /dev/null diff --git a/src/main/res/drawable/account_image_border.xml b/src/main/res/drawable/account_image_border.xml deleted file mode 100644 index ba262a1df..000000000 --- a/src/main/res/drawable/account_image_border.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - - <corners android:radius="2dp" /> - - <padding - android:bottom="1.5dp" - android:left="1.5dp" - android:right="1.5dp" - android:top="1.5dp" /> - - <solid android:color="@color/black12" /> - -</shape>
\ No newline at end of file diff --git a/src/main/res/layout/account_row.xml b/src/main/res/layout/account_row.xml index eaacbde5e..e3f3d99e1 100644 --- a/src/main/res/layout/account_row.xml +++ b/src/main/res/layout/account_row.xml @@ -1,60 +1,62 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?android:attr/activatedBackgroundIndicator" - android:paddingStart="8dp" - android:paddingLeft="8dp" - android:paddingTop="8dp" - android:paddingBottom="8dp"> +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> - <com.makeramen.roundedimageview.RoundedImageView - android:id="@+id/account_image" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_alignParentStart="true" - android:layout_alignParentLeft="true" - android:contentDescription="@string/account_image_description" - android:padding="1dp" - app:riv_corner_radius="@dimen/rounded_image_border" /> - - <LinearLayout - android:layout_width="fill_parent" + <RelativeLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_toStartOf="@+id/tgl_account_status" - android:layout_toLeftOf="@+id/tgl_account_status" - android:layout_toEndOf="@+id/account_image" - android:layout_toRightOf="@+id/account_image" - android:orientation="vertical" - android:paddingStart="@dimen/avatar_item_distance" - android:paddingLeft="@dimen/avatar_item_distance"> + android:background="?selectableItemBackground" + android:paddingStart="8dp" + android:paddingLeft="8dp" + android:paddingTop="8dp" + android:paddingBottom="8dp"> - <TextView - android:id="@+id/account_jid" - android:layout_width="wrap_content" + <com.makeramen.roundedimageview.RoundedImageView + android:id="@+id/account_image" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" + android:contentDescription="@string/account_image_description" + app:riv_corner_radius="2dp" /> + + <LinearLayout + android:layout_width="fill_parent" android:layout_height="wrap_content" - android:scrollHorizontally="false" - android:singleLine="true" - android:textAppearance="@style/TextAppearance.Conversations.Subhead" /> + android:layout_centerVertical="true" + android:layout_toStartOf="@+id/tgl_account_status" + android:layout_toLeftOf="@+id/tgl_account_status" + android:layout_toEndOf="@+id/account_image" + android:layout_toRightOf="@+id/account_image" + android:orientation="vertical" + android:paddingStart="@dimen/avatar_item_distance" + android:paddingLeft="@dimen/avatar_item_distance"> + + <TextView + android:id="@+id/account_jid" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:scrollHorizontally="false" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.Conversations.Subhead" /> - <TextView - android:id="@+id/account_status" + <TextView + android:id="@+id/account_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/account_status_unknown" + android:textAppearance="@style/TextAppearance.Conversations.Body2" /> + </LinearLayout> + + <android.support.v7.widget.SwitchCompat + android:id="@+id/tgl_account_status" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/account_status_unknown" - android:textAppearance="@style/TextAppearance.Conversations.Body2" /> - </LinearLayout> - - <android.support.v7.widget.SwitchCompat - android:id="@+id/tgl_account_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:focusable="false" - android:padding="16dp" /> + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:focusable="false" + android:padding="16dp" /> -</RelativeLayout>
\ No newline at end of file + </RelativeLayout> +</layout>
\ No newline at end of file diff --git a/src/main/res/layout/activity_change_password.xml b/src/main/res/layout/activity_change_password.xml index ee2726431..1b0768d12 100644 --- a/src/main/res/layout/activity_change_password.xml +++ b/src/main/res/layout/activity_change_password.xml @@ -67,7 +67,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" - android:hint="@string/new_password" /> + android:hint="@string/new_password" + android:inputType="textPassword" /> </android.support.design.widget.TextInputLayout> </LinearLayout> </android.support.v7.widget.CardView> diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml index 50dbda53d..ceb773e8b 100644 --- a/src/main/res/layout/activity_contact_details.xml +++ b/src/main/res/layout/activity_contact_details.xml @@ -308,6 +308,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginTop="8dp" + android:layout_gravity="end" android:orientation="horizontal"> <Button diff --git a/src/main/res/layout/activity_import_backup.xml b/src/main/res/layout/activity_import_backup.xml new file mode 100644 index 000000000..7d12f78c9 --- /dev/null +++ b/src/main/res/layout/activity_import_backup.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="?attr/color_background_tertiary" + android:orientation="vertical"> + + <include + android:id="@+id/toolbar" + layout="@layout/toolbar" /> + + <LinearLayout + android:id="@+id/in_progress" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:visibility="gone"> + + <ProgressBar + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" /> + </LinearLayout> + + <android.support.design.widget.CoordinatorLayout + android:id="@+id/coordinator" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/color_background_tertiary"> + + <android.support.v7.widget.RecyclerView + android:id="@+id/list" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/color_background_tertiary" + android:orientation="vertical" + app:layoutManager="android.support.v7.widget.LinearLayoutManager" /> + </android.support.design.widget.CoordinatorLayout> + + </LinearLayout> +</layout>
\ No newline at end of file diff --git a/src/main/res/layout/activity_magic_create.xml b/src/main/res/layout/activity_magic_create.xml index dbff620b4..523d210c0 100644 --- a/src/main/res/layout/activity_magic_create.xml +++ b/src/main/res/layout/activity_magic_create.xml @@ -1,7 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> diff --git a/src/main/res/layout/activity_media_viewer.xml b/src/main/res/layout/activity_media_viewer.xml index 42db37b86..463145662 100644 --- a/src/main/res/layout/activity_media_viewer.xml +++ b/src/main/res/layout/activity_media_viewer.xml @@ -1,44 +1,52 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@android:color/black" - android:orientation="vertical"> +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> - <FrameLayout - android:id="@+id/frameLayout" + <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:layout_alignParentRight="true"> + android:background="@android:color/black" + android:orientation="vertical"> <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView - android:id="@id/message_image_view" + android:id="@+id/messageImageView" android:layout_width="match_parent" android:layout_height="match_parent" android:adjustViewBounds="true" android:visibility="gone" /> <pl.droidsonroids.gif.GifImageView - android:id="@id/message_gif_view" + android:id="@+id/messageGifView" android:layout_width="match_parent" android:layout_height="match_parent" android:adjustViewBounds="true" android:visibility="gone" /> <com.github.rtoshiro.view.video.FullscreenVideoLayout - android:id="@id/message_video_view" + android:id="@+id/messageVideoView" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" /> - <android.support.design.widget.FloatingActionButton - android:id="@+id/fab" + <com.leinardi.android.speeddial.SpeedDialOverlayLayout + android:id="@+id/overlay" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_below="@id/tab_layout" + android:background="?color_background_overlay" /> + + <com.leinardi.android.speeddial.SpeedDialView + android:id="@+id/speed_dial" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="right|top" - android:layout_margin="16dp" - android:src="@drawable/ic_menu_white_24dp" /> - </FrameLayout> -</RelativeLayout>
\ No newline at end of file + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_alignParentBottom="true" + app:backgroundTint="?attr/colorAccent" + app:sdMainFabClosedBackgroundColor="?attr/colorAccent" + app:sdMainFabClosedSrc="@drawable/ic_menu_white_24dp" + app:sdMainFabOpenedBackgroundColor="?attr/colorAccent" + app:sdMainFabOpenedSrc="@drawable/ic_close_white_24dp" + app:sdOverlayLayout="@id/overlay" /> + </RelativeLayout> +</layout>
\ No newline at end of file diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml index c29d78c26..9b2b3333c 100644 --- a/src/main/res/layout/activity_muc_details.xml +++ b/src/main/res/layout/activity_muc_details.xml @@ -173,7 +173,6 @@ android:layout_centerVertical="true" android:layout_toStartOf="@+id/change_conference_button" android:layout_toLeftOf="@+id/change_conference_button" - android:text="@string/private_conference" android:textAppearance="@style/TextAppearance.Conversations.Body1" /> <ImageButton @@ -306,57 +305,6 @@ </android.support.v7.widget.CardView> <android.support.v7.widget.CardView - android:id="@+id/media_wrapper" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/activity_horizontal_margin" - android:layout_marginLeft="@dimen/activity_horizontal_margin" - android:layout_marginTop="@dimen/activity_vertical_margin" - android:layout_marginEnd="@dimen/activity_horizontal_margin" - android:layout_marginRight="@dimen/activity_horizontal_margin" - android:layout_marginBottom="@dimen/activity_vertical_margin" - card_view:cardBackgroundColor="?attr/color_background_secondary"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <android.support.v7.widget.RecyclerView - android:id="@+id/media" - android:layout_width="match_parent" - android:layout_height="100dp" - android:layout_marginStart="-2dp" - android:layout_marginEnd="-2dp" - android:orientation="horizontal" - android:paddingStart="@dimen/card_padding_regular" - android:paddingTop="@dimen/card_padding_regular" - android:paddingEnd="@dimen/card_padding_regular" - android:paddingBottom="@dimen/card_padding_list" /> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_gravity="end" - android:orientation="horizontal"> - - <Button - android:id="@+id/show_media" - style="@style/Widget.Conversations.Button.Borderless" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:minWidth="0dp" - android:paddingStart="16dp" - android:paddingLeft="16dp" - android:paddingEnd="16dp" - android:paddingRight="16dp" - android:text="@string/view_media" - android:textColor="?attr/colorAccent" /> - </LinearLayout> - </LinearLayout> - </android.support.v7.widget.CardView> - - <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="@dimen/activity_horizontal_margin" @@ -463,7 +411,7 @@ </android.support.v7.widget.CardView> <android.support.v7.widget.CardView - android:id="@+id/muc_more_details" + android:id="@+id/users_wrapper" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="@dimen/activity_horizontal_margin" @@ -477,28 +425,120 @@ <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:padding="@dimen/card_padding_list"> + android:orientation="vertical"> - <Button - android:id="@+id/invite" - style="?android:attr/buttonStyleSmall" - android:layout_width="match_parent" + <TextView + android:id="@+id/no_users_hints" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="24dp" - android:text="@string/invite_contact" /> + android:paddingStart="@dimen/card_padding_regular" + android:paddingTop="@dimen/card_padding_regular" + android:paddingEnd="@dimen/card_padding_regular" + android:text="@string/no_users_hint_channel" + android:textAppearance="@style/TextAppearance.Conversations.Body2" /> - <LinearLayout - android:id="@+id/muc_members" + <android.support.v7.widget.RecyclerView + android:id="@+id/users" android:layout_width="fill_parent" android:layout_height="0dp" + android:layout_marginStart="-2dp" + android:layout_marginEnd="-2dp" android:layout_weight="1" android:divider="?android:dividerHorizontal" - android:orientation="vertical" - android:showDividers="middle" /> + android:orientation="horizontal" + android:paddingStart="@dimen/card_padding_regular" + android:paddingTop="@dimen/card_padding_regular" + android:paddingEnd="@dimen/card_padding_regular" + android:paddingBottom="@dimen/card_padding_list" /> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="end" + android:orientation="horizontal"> + + <Button + android:id="@+id/invite" + style="@style/Widget.Conversations.Button.Borderless" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" + android:minWidth="0dp" + android:paddingStart="16dp" + android:paddingLeft="16dp" + android:paddingEnd="16dp" + android:paddingRight="16dp" + android:text="@string/invite_contact" + android:textColor="?attr/colorAccent" /> + + <Button + android:id="@+id/show_users" + style="@style/Widget.Conversations.Button.Borderless" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minWidth="0dp" + android:paddingStart="16dp" + android:paddingLeft="16dp" + android:paddingEnd="16dp" + android:paddingRight="16dp" + android:text="@string/view_users" + android:textColor="?attr/colorAccent" /> + </LinearLayout> </LinearLayout> + </android.support.v7.widget.CardView> + + <android.support.v7.widget.CardView + android:id="@+id/media_wrapper" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/activity_horizontal_margin" + android:layout_marginLeft="@dimen/activity_horizontal_margin" + android:layout_marginTop="@dimen/activity_vertical_margin" + android:layout_marginEnd="@dimen/activity_horizontal_margin" + android:layout_marginRight="@dimen/activity_horizontal_margin" + android:layout_marginBottom="@dimen/activity_vertical_margin" + card_view:cardBackgroundColor="?attr/color_background_secondary"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <android.support.v7.widget.RecyclerView + android:id="@+id/media" + android:layout_width="fill_parent" + android:layout_height="0dp" + android:layout_marginStart="-2dp" + android:layout_marginEnd="-2dp" + android:layout_weight="1" + android:divider="?android:dividerHorizontal" + android:orientation="horizontal" + android:paddingStart="@dimen/card_padding_regular" + android:paddingTop="@dimen/card_padding_regular" + android:paddingEnd="@dimen/card_padding_regular" + android:paddingBottom="@dimen/card_padding_list" /> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="end" + android:orientation="horizontal"> + + <Button + android:id="@+id/show_media" + style="@style/Widget.Conversations.Button.Borderless" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minWidth="0dp" + android:paddingStart="16dp" + android:paddingLeft="16dp" + android:paddingEnd="16dp" + android:paddingRight="16dp" + android:text="@string/view_media" + android:textColor="?attr/colorAccent" /> + </LinearLayout> + </LinearLayout> </android.support.v7.widget.CardView> </LinearLayout> </ScrollView> diff --git a/src/main/res/layout/activity_muc_users.xml b/src/main/res/layout/activity_muc_users.xml new file mode 100644 index 000000000..d6bdf8435 --- /dev/null +++ b/src/main/res/layout/activity_muc_users.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="?attr/color_background_tertiary" + android:orientation="vertical"> + + <include + android:id="@+id/toolbar" + layout="@layout/toolbar" /> + + + <android.support.design.widget.CoordinatorLayout + android:id="@+id/coordinator" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/color_background_tertiary"> + + <android.support.v7.widget.RecyclerView + android:id="@+id/list" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/color_background_tertiary" + android:orientation="vertical" + app:layoutManager="android.support.v7.widget.LinearLayoutManager" /> + </android.support.design.widget.CoordinatorLayout> + + </LinearLayout> +</layout>
\ No newline at end of file diff --git a/src/main/res/layout/activity_share_with.xml b/src/main/res/layout/activity_share_with.xml index 15545ef31..dec67bd81 100644 --- a/src/main/res/layout/activity_share_with.xml +++ b/src/main/res/layout/activity_share_with.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/src/main/res/layout/activity_start_conversation.xml b/src/main/res/layout/activity_start_conversation.xml index 7681cbc9b..2a77e6654 100644 --- a/src/main/res/layout/activity_start_conversation.xml +++ b/src/main/res/layout/activity_start_conversation.xml @@ -31,14 +31,25 @@ android:layout_below="@id/tab_layout" android:background="?attr/color_background_secondary" /> - <android.support.design.widget.FloatingActionButton - android:id="@+id/fab" + <com.leinardi.android.speeddial.SpeedDialOverlayLayout + android:id="@+id/overlay" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_below="@id/tab_layout" + android:background="?color_background_overlay" /> + + <com.leinardi.android.speeddial.SpeedDialView + android:id="@+id/speed_dial" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_alignParentBottom="true" - android:layout_margin="16dp" - android:src="?attr/icon_add_person" /> + app:backgroundTint="?attr/colorAccent" + app:sdMainFabClosedBackgroundColor="?attr/colorAccent" + app:sdMainFabClosedSrc="@drawable/ic_add_white_24dp" + app:sdMainFabOpenedBackgroundColor="?attr/colorAccent" + app:sdOverlayLayout="@id/overlay" + app:sdUseReverseAnimationOnClose="true" /> </RelativeLayout> </layout>
\ No newline at end of file diff --git a/src/main/res/layout/contact.xml b/src/main/res/layout/contact.xml index d7d8956ec..e5b71d743 100644 --- a/src/main/res/layout/contact.xml +++ b/src/main/res/layout/contact.xml @@ -5,7 +5,7 @@ <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?attr/list_item_background" + android:background="?selectableItemBackground" android:padding="@dimen/list_padding"> <com.makeramen.roundedimageview.RoundedImageView diff --git a/src/main/res/layout/conversation_list_row.xml b/src/main/res/layout/conversation_list_row.xml index 23ba4ac9d..3d03c3906 100644 --- a/src/main/res/layout/conversation_list_row.xml +++ b/src/main/res/layout/conversation_list_row.xml @@ -1,193 +1,199 @@ -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:descendantFocusability="blocksDescendants"> +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> <FrameLayout - android:id="@+id/frame" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="?attr/color_background_secondary"> + android:descendantFocusability="blocksDescendants"> - <RelativeLayout + <FrameLayout + android:id="@+id/frame" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="?android:selectableItemBackground" - android:orientation="horizontal" - android:padding="8dp"> - - <com.makeramen.roundedimageview.RoundedImageView - android:id="@+id/conversation_image" - android:layout_width="56dp" - android:layout_height="56dp" - android:layout_alignParentStart="true" - android:layout_alignParentLeft="true" - android:padding="1dp" - android:scaleType="centerCrop" - app:riv_corner_radius="@dimen/rounded_image_border" /> + android:background="?attr/color_background_secondary"> <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_toEndOf="@+id/conversation_image" - android:layout_toRightOf="@+id/conversation_image" - android:paddingStart="@dimen/avatar_item_distance" - android:paddingLeft="@dimen/avatar_item_distance"> - - <TextView - android:id="@+id/conversation_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignStart="@+id/conversation_lastwrapper" - android:layout_alignLeft="@+id/conversation_lastwrapper" - android:layout_toStartOf="@+id/conversation_failed" - android:layout_toLeftOf="@+id/conversation_failed" - android:ellipsize="end" - android:maxLines="1" - android:paddingEnd="4dp" - android:paddingRight="4dp" - android:textAppearance="@style/TextAppearance.Conversations.Subhead" /> + android:background="?android:selectableItemBackground" + android:orientation="horizontal" + android:padding="8dp"> + + <com.makeramen.roundedimageview.RoundedImageView + android:id="@+id/conversation_image" + android:layout_width="56dp" + android:layout_height="56dp" + android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" + android:padding="1dp" + android:scaleType="centerCrop" + app:riv_corner_radius="@dimen/rounded_image_border" /> <RelativeLayout - android:id="@+id/conversation_lastwrapper" - android:layout_width="match_parent" + android:layout_width="fill_parent" android:layout_height="wrap_content" - android:layout_below="@id/conversation_name" - android:layout_marginTop="4dp"> + android:layout_centerVertical="true" + android:layout_toEndOf="@+id/conversation_image" + android:layout_toRightOf="@+id/conversation_image" + android:paddingStart="@dimen/avatar_item_distance" + android:paddingLeft="@dimen/avatar_item_distance"> <TextView - android:id="@+id/sender_name" + android:id="@+id/conversation_name" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentStart="true" - android:layout_alignParentLeft="true" - android:layout_centerVertical="true" - android:layout_marginEnd="5sp" - android:layout_marginRight="5sp" - android:gravity="center_vertical" - android:minHeight="?attr/IconSize" - android:text="@string/me" - android:textAppearance="@style/TextAppearance.Conversations.Body1.Secondary" - android:visibility="visible" /> - - <LinearLayout + android:layout_alignStart="@+id/conversation_lastwrapper" + android:layout_alignLeft="@+id/conversation_lastwrapper" + android:layout_toStartOf="@+id/failed_count" + android:layout_toLeftOf="@+id/failed_count" + android:ellipsize="end" + android:maxLines="1" + android:paddingEnd="4dp" + android:paddingRight="4dp" + android:textAppearance="@style/TextAppearance.Conversations.Subhead" /> + + <RelativeLayout + android:id="@+id/conversation_lastwrapper" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_alignParentStart="false" - android:layout_alignParentLeft="false" - android:layout_centerVertical="true" - android:layout_toStartOf="@+id/indicators" - android:layout_toLeftOf="@+id/indicators" - android:layout_toEndOf="@+id/sender_name" - android:layout_toRightOf="@+id/sender_name" - android:orientation="horizontal"> - - <ImageView - android:id="@+id/conversation_lastmsg_img" - android:layout_width="?attr/IconSize" - android:layout_height="?attr/IconSize" - android:layout_marginEnd="?attr/TextSeparation" - android:layout_marginRight="?attr/TextSeparation" /> + android:layout_below="@id/conversation_name" + android:layout_marginTop="4dp"> <TextView - android:id="@+id/conversation_lastmsg" - android:layout_width="match_parent" + android:id="@+id/sender_name" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:ellipsize="end" + android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:layout_marginEnd="?attr/TextSeparation" + android:layout_marginRight="?attr/TextSeparation" android:gravity="center_vertical" - android:maxLines="1" android:minHeight="?attr/IconSize" - android:scrollHorizontally="false" - android:textAppearance="@style/TextAppearance.Conversations.Body1" /> - </LinearLayout> + android:text="@string/me" + android:textAppearance="@style/TextAppearance.Conversations.Body1.Secondary" + android:visibility="visible" /> - <LinearLayout - android:id="@+id/indicators" + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignWithParentIfMissing="true" + android:layout_centerVertical="true" + android:layout_toStartOf="@+id/indicators" + android:layout_toLeftOf="@+id/indicators" + android:layout_toEndOf="@+id/sender_name" + android:layout_toRightOf="@+id/sender_name" + android:orientation="horizontal"> + + <ImageView + android:id="@+id/conversation_lastmsg_img" + android:layout_width="?attr/IconSize" + android:layout_height="?attr/IconSize" + android:layout_marginEnd="?attr/TextSeparation" + android:layout_marginRight="?attr/TextSeparation" /> + + <TextView + android:id="@+id/conversation_lastmsg" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ellipsize="end" + android:gravity="center_vertical" + android:maxLines="1" + android:minHeight="?attr/IconSize" + android:scrollHorizontally="false" + android:textAppearance="@style/TextAppearance.Conversations.Body1" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/indicators" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="false" + android:layout_alignParentLeft="false" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:layout_marginStart="?attr/TextSeparation" + android:layout_marginLeft="?attr/TextSeparation" + android:orientation="horizontal"> + + <ImageView + android:id="@+id/indicator_received" + android:layout_width="?attr/TextSizeInfo" + android:layout_height="?attr/TextSizeInfo" + android:layout_gravity="center_vertical" + android:layout_marginStart="4dp" + android:layout_marginLeft="4dp" + android:gravity="center_vertical" + android:src="@drawable/ic_received_indicator" /> + + <ImageView + android:id="@+id/indicator_read" + android:layout_width="?attr/TextSizeInfo" + android:layout_height="?attr/TextSizeInfo" + android:layout_gravity="center_vertical" + android:layout_marginStart="-8dp" + android:layout_marginLeft="-8dp" + android:adjustViewBounds="false" + android:gravity="center_vertical" + android:src="@drawable/ic_read_indicator" /> + + <ImageView + android:id="@+id/notification_status" + android:layout_width="?attr/IconSize" + android:layout_height="?attr/IconSize" + android:layout_marginStart="4dp" + android:layout_marginLeft="4dp" + android:alpha="?attr/icon_alpha" + android:src="@drawable/ic_notifications_grey600_24dp" /> + </LinearLayout> + </RelativeLayout> + + <de.pixart.messenger.ui.widget.UnreadCountCustomView + android:id="@+id/unread_count" + android:layout_width="?attr/IconSize" + android:layout_height="?attr/IconSize" + android:layout_margin="1dp" + android:layout_toStartOf="@+id/conversation_lastupdate" + android:layout_toLeftOf="@+id/conversation_lastupdate" + android:paddingStart="4dp" + android:paddingLeft="4dp" + android:paddingEnd="4dp" + android:paddingRight="4dp" + android:paddingBottom="2dp" + android:visibility="gone" + app:backgroundColor="?attr/colorAccent" /> + + <de.pixart.messenger.ui.widget.FailedCountCustomView + android:id="@+id/failed_count" + android:layout_width="?attr/IconSize" + android:layout_height="?attr/IconSize" + android:layout_margin="1dp" + android:layout_toStartOf="@+id/unread_count" + android:layout_toLeftOf="@+id/unread_count" + android:paddingStart="4dp" + android:paddingLeft="4dp" + android:paddingEnd="4dp" + android:paddingRight="4dp" + android:paddingBottom="2dp" + android:visibility="gone" + app:backgroundColor="?attr/color_warning" /> + + <TextView + android:id="@+id/conversation_lastupdate" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentStart="false" - android:layout_alignParentLeft="false" + android:layout_alignBaseline="@+id/conversation_name" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:orientation="horizontal"> - - <ImageView - android:id="@+id/indicator_received" - android:layout_width="?attr/TextSizeInfo" - android:layout_height="?attr/TextSizeInfo" - android:layout_gravity="center_vertical" - android:layout_marginStart="4sp" - android:layout_marginLeft="4sp" - android:gravity="center_vertical" - android:src="@drawable/ic_received_indicator" /> - - <ImageView - android:id="@+id/indicator_read" - android:layout_width="?attr/TextSizeInfo" - android:layout_height="?attr/TextSizeInfo" - android:layout_gravity="center_vertical" - android:layout_marginStart="-8sp" - android:layout_marginLeft="-8sp" - android:adjustViewBounds="false" - android:gravity="center_vertical" - android:src="@drawable/ic_read_indicator" /> - - <ImageView - android:id="@+id/notification_status" - android:layout_width="?attr/IconSize" - android:layout_height="?attr/IconSize" - android:layout_marginStart="4dp" - android:layout_marginLeft="4dp" - android:alpha="?attr/icon_alpha" - android:src="@drawable/ic_notifications_grey600_24dp" /> - </LinearLayout> + android:gravity="end" + android:paddingStart="4dp" + android:paddingLeft="4dp" + android:paddingEnd="4dp" + android:paddingRight="4dp" + android:textAppearance="@style/TextAppearance.Conversations.Caption" /> </RelativeLayout> - - <de.pixart.messenger.ui.widget.UnreadCountCustomView - android:id="@+id/conversation_unread" - android:layout_width="?attr/IconSize" - android:layout_height="?attr/IconSize" - android:layout_margin="1dp" - android:layout_toStartOf="@+id/conversation_lastupdate" - android:layout_toLeftOf="@+id/conversation_lastupdate" - android:paddingStart="4dp" - android:paddingLeft="4dp" - android:paddingEnd="4dp" - android:paddingRight="4dp" - android:paddingBottom="2dp" - android:visibility="gone" - app:backgroundColor="?attr/colorAccent" /> - - <de.pixart.messenger.ui.widget.FailedCountCustomView - android:id="@+id/conversation_failed" - android:layout_width="?attr/IconSize" - android:layout_height="?attr/IconSize" - android:layout_margin="1dp" - android:layout_toStartOf="@+id/conversation_unread" - android:layout_toLeftOf="@+id/conversation_unread" - android:paddingStart="4dp" - android:paddingLeft="4dp" - android:paddingEnd="4dp" - android:paddingRight="4dp" - android:paddingBottom="2dp" - android:visibility="gone" - app:backgroundColor="?attr/color_warning" /> - - <TextView - android:id="@+id/conversation_lastupdate" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignBaseline="@+id/conversation_name" - android:layout_alignParentEnd="true" - android:layout_alignParentRight="true" - android:gravity="end" - android:paddingStart="4dp" - android:paddingLeft="4dp" - android:textAppearance="@style/TextAppearance.Conversations.Caption" /> </RelativeLayout> - </RelativeLayout> + </FrameLayout> </FrameLayout> -</FrameLayout>
\ No newline at end of file +</layout>
\ No newline at end of file diff --git a/src/main/res/layout/create_conference_dialog.xml b/src/main/res/layout/create_conference_dialog.xml index 3496b17f5..fc4aefaf5 100644 --- a/src/main/res/layout/create_conference_dialog.xml +++ b/src/main/res/layout/create_conference_dialog.xml @@ -38,7 +38,8 @@ style="@style/Widget.Conversations.EditText" android:layout_width="match_parent" android:layout_height="wrap_content" - android:hint="@string/create_dialog_group_chat_name" /> + android:hint="@string/create_dialog_group_chat_name" + android:imeOptions="actionNext|flagNoExtractUi" /> </android.support.design.widget.TextInputLayout> </LinearLayout> </layout>
\ No newline at end of file diff --git a/src/main/res/layout/create_public_channel_dialog.xml b/src/main/res/layout/create_public_channel_dialog.xml new file mode 100644 index 000000000..457f9c010 --- /dev/null +++ b/src/main/res/layout/create_public_channel_dialog.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="?dialogPreferredPadding"> + + <TextView + android:id="@+id/your_account" + style="@style/InputLabel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/your_account" /> + + <Spinner + android:id="@+id/account" + android:layout_width="fill_parent" + android:layout_height="wrap_content" /> + + <android.support.design.widget.TextInputLayout + android:id="@+id/name_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:errorTextAppearance="@style/TextAppearance.Conversations.Design.Error" + app:hintTextAppearance="@style/TextAppearance.Conversations.Design.Hint"> + + <de.pixart.messenger.ui.widget.EmojiWrapperEditText + android:id="@+id/group_chat_name" + style="@style/Widget.Conversations.EditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/create_dialog_channel_name" + android:imeOptions="actionNext|flagNoExtractUi" /> + </android.support.design.widget.TextInputLayout> + + <android.support.design.widget.TextInputLayout + android:id="@+id/xmpp_address_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/xmpp_address" + android:visibility="gone" + app:errorTextAppearance="@style/TextAppearance.Conversations.Design.Error" + app:hintTextAppearance="@style/TextAppearance.Conversations.Design.Hint"> + + <AutoCompleteTextView + android:id="@+id/jid" + style="@style/Widget.Conversations.EditText" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:imeOptions="actionDone|flagNoExtractUi" + android:inputType="textEmailAddress" /> + </android.support.design.widget.TextInputLayout> + </LinearLayout> +</layout>
\ No newline at end of file diff --git a/src/main/res/layout/dialog_enter_password.xml b/src/main/res/layout/dialog_enter_password.xml new file mode 100644 index 000000000..935ebad6c --- /dev/null +++ b/src/main/res/layout/dialog_enter_password.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="?dialogPreferredPadding"> + + <TextView + android:id="@+id/explain" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/enter_password_to_restore" + android:textAppearance="@style/TextAppearance.Conversations.Body2" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="?TextSizeBody1" + android:text="@string/restore_warning" + android:textAppearance="@style/TextAppearance.Conversations.Body1" /> + + <android.support.design.widget.TextInputLayout + android:id="@+id/account_password_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + app:errorTextAppearance="@style/TextAppearance.Conversations.Design.Error" + app:hintTextAppearance="@style/TextAppearance.Conversations.Design.Hint" + app:passwordToggleDrawable="@drawable/visibility_toggle_drawable" + app:passwordToggleEnabled="true" + app:passwordToggleTint="?android:textColorSecondary"> + + <de.pixart.messenger.ui.widget.TextInputEditText + android:id="@+id/account_password" + style="@style/Widget.Conversations.EditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/password" + android:inputType="textPassword" + android:textColor="?attr/edit_text_color" /> + + </android.support.design.widget.TextInputLayout> + </LinearLayout> +</layout>
\ No newline at end of file diff --git a/src/main/res/layout/dialog_join_conference.xml b/src/main/res/layout/dialog_join_conference.xml index 15017d5c1..3d8478a34 100644 --- a/src/main/res/layout/dialog_join_conference.xml +++ b/src/main/res/layout/dialog_join_conference.xml @@ -31,7 +31,7 @@ android:id="@+id/account_jid_layout" android:layout_width="match_parent" android:layout_height="wrap_content" - android:hint="@string/conference_address" + android:hint="@string/xmpp_address" app:errorTextAppearance="@style/TextAppearance.Conversations.Design.Error" app:hintTextAppearance="@style/TextAppearance.Conversations.Design.Hint"> @@ -40,6 +40,7 @@ style="@style/Widget.Conversations.EditText" android:layout_width="fill_parent" android:layout_height="wrap_content" + android:imeOptions="actionDone|flagNoExtractUi" android:inputType="textEmailAddress" /> </android.support.design.widget.TextInputLayout> diff --git a/src/main/res/layout/dialog_quickedit.xml b/src/main/res/layout/dialog_quickedit.xml index 4ce88b2ba..b309bc173 100644 --- a/src/main/res/layout/dialog_quickedit.xml +++ b/src/main/res/layout/dialog_quickedit.xml @@ -6,11 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:paddingStart="?attr/dialog_horizontal_padding" - android:paddingLeft="?attr/dialog_horizontal_padding" - android:paddingTop="?attr/dialog_vertical_padding" - android:paddingEnd="?attr/dialog_horizontal_padding" - android:paddingRight="?attr/dialog_horizontal_padding"> + android:padding="?dialogPreferredPadding"> <android.support.design.widget.TextInputLayout android:id="@+id/input_layout" diff --git a/src/main/res/layout/enter_jid_dialog.xml b/src/main/res/layout/enter_jid_dialog.xml index 4c0214f96..932868771 100644 --- a/src/main/res/layout/enter_jid_dialog.xml +++ b/src/main/res/layout/enter_jid_dialog.xml @@ -40,6 +40,7 @@ style="@style/Widget.Conversations.EditText" android:layout_width="fill_parent" android:layout_height="wrap_content" + android:imeOptions="actionDone|flagNoExtractUi" android:inputType="textEmailAddress" android:textColor="?attr/text_Color_Main" /> </android.support.design.widget.TextInputLayout> diff --git a/src/main/res/layout/media.xml b/src/main/res/layout/media.xml index f95ddeda3..345641762 100644 --- a/src/main/res/layout/media.xml +++ b/src/main/res/layout/media.xml @@ -4,6 +4,7 @@ <de.pixart.messenger.ui.widget.SquareFrameLayout android:layout_width="match_parent" android:layout_height="match_parent" + android:background="?selectableItemBackground" android:padding="2dp"> <ImageView diff --git a/src/main/res/layout/password.xml b/src/main/res/layout/password.xml deleted file mode 100644 index ed97898c3..000000000 --- a/src/main/res/layout/password.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> - - <EditText - android:id="@+id/password" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:hint="@string/account_settings_password" - android:inputType="textPassword" /> - - <EditText - android:id="@+id/confirm_password" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:hint="@string/confirm_password" - android:inputType="textPassword" - android:visibility="gone" /> -</LinearLayout>
\ No newline at end of file diff --git a/src/main/res/layout/user_preview.xml b/src/main/res/layout/user_preview.xml new file mode 100644 index 000000000..ecc05e623 --- /dev/null +++ b/src/main/res/layout/user_preview.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<layout xmlns:android="http://schemas.android.com/apk/res/android"> + + <de.pixart.messenger.ui.widget.SquareFrameLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?selectableItemBackground" + android:padding="2dp"> + + <ImageView + android:id="@+id/avatar" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/black54" + android:scaleType="centerInside" /> + </de.pixart.messenger.ui.widget.SquareFrameLayout> +</layout>
\ No newline at end of file diff --git a/src/main/res/menu/account_context.xml b/src/main/res/menu/account_context.xml new file mode 100644 index 000000000..4e591f1eb --- /dev/null +++ b/src/main/res/menu/account_context.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> +<item + android:id="@+id/action_account_details" + android:title="@string/account_details" /> +<item + android:id="@+id/action_show_qr_code" + android:title="@string/show_qr_code" /> +</menu>
\ No newline at end of file diff --git a/src/main/res/menu/conference_context.xml b/src/main/res/menu/conference_context.xml index 051a468d4..dbc6ebfa6 100644 --- a/src/main/res/menu/conference_context.xml +++ b/src/main/res/menu/conference_context.xml @@ -2,9 +2,6 @@ <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item - android:id="@+id/context_join_conference" - android:title="@string/join_conference" /> - <item android:id="@+id/context_delete_conference" android:title="@string/delete_bookmark" /> <item diff --git a/src/main/res/menu/editaccount.xml b/src/main/res/menu/editaccount.xml index 09919b9fe..59667c1c7 100644 --- a/src/main/res/menu/editaccount.xml +++ b/src/main/res/menu/editaccount.xml @@ -26,6 +26,11 @@ </item> <item + android:id="@+id/action_import_backup" + android:title="@string/restore_backup" + app:showAsAction="never" /> + + <item android:id="@+id/action_show_qr_code" android:icon="@drawable/ic_qrcode_white_24dp" android:title="@string/show_qr_code" diff --git a/src/main/res/menu/manageaccounts.xml b/src/main/res/menu/manageaccounts.xml index 044c715c5..3199fc2be 100644 --- a/src/main/res/menu/manageaccounts.xml +++ b/src/main/res/menu/manageaccounts.xml @@ -5,18 +5,22 @@ <item android:id="@+id/action_add_account" android:icon="?attr/icon_add_person" - app:showAsAction="always" - android:title="@string/action_add_account" /> + android:title="@string/action_add_account" + app:showAsAction="always" /> + <item + android:id="@+id/action_import_backup" + android:title="@string/restore_backup" + app:showAsAction="never" /> <item android:id="@+id/action_add_account_with_cert" - app:showAsAction="never" android:icon="?attr/icon_add_person" android:title="@string/action_add_account_with_certificate" - android:visible="true" /> + android:visible="true" + app:showAsAction="never" /> <item android:id="@+id/action_settings" android:orderInCategory="100" - app:showAsAction="never" - android:title="@string/action_settings" /> + android:title="@string/action_settings" + app:showAsAction="never" /> </menu>
\ No newline at end of file diff --git a/src/main/res/menu/media_viewer.xml b/src/main/res/menu/media_viewer.xml index c3898c79b..36a89214c 100644 --- a/src/main/res/menu/media_viewer.xml +++ b/src/main/res/menu/media_viewer.xml @@ -2,16 +2,12 @@ <item android:id="@+id/action_share" - android:icon="?attr/icon_share" + android:icon="@drawable/ic_share_white_24dp" android:orderInCategory="10" android:title="@string/share" /> <item android:id="@+id/action_open" + android:icon="@drawable/ic_open_in_new_white_24dp" android:orderInCategory="20" android:title="@string/action_open" /> - <item - android:id="@+id/action_delete" - android:icon="?attr/icon_delete" - android:orderInCategory="30" - android:title="@string/action_delete" /> </menu>
\ No newline at end of file diff --git a/src/main/res/menu/message_context.xml b/src/main/res/menu/message_context.xml index 4112dfb8c..d92f85af4 100644 --- a/src/main/res/menu/message_context.xml +++ b/src/main/res/menu/message_context.xml @@ -2,9 +2,13 @@ <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item + android:id="@+id/open_with" + android:title="@string/open_with" + android:visible="false" /> + <item android:id="@+id/share_with" android:title="@string/share_with" - android:visible="false"/> + android:visible="false" /> <item android:id="@+id/copy_message" android:title="@string/copy_to_clipboard" diff --git a/src/main/res/menu/muc_details_context.xml b/src/main/res/menu/muc_details_context.xml index 94693c0a5..0a8ad87c4 100644 --- a/src/main/res/menu/muc_details_context.xml +++ b/src/main/res/menu/muc_details_context.xml @@ -21,28 +21,41 @@ android:title="@string/send_private_message" android:visible="false" /> <item - android:id="@+id/give_membership" - android:title="@string/grant_membership" - android:visible="false" /> - <item - android:id="@+id/give_admin_privileges" - android:title="@string/grant_admin_privileges" - android:visible="false" /> - <item - android:id="@+id/remove_admin_privileges" - android:title="@string/remove_admin_privileges" - android:visible="false" /> - - <item - android:id="@+id/remove_membership" - android:title="@string/remove_membership" - android:visible="false" /> - <item - android:id="@+id/remove_from_room" - android:title="@string/remove_from_room" - android:visible="false" /> - <item - android:id="@+id/ban_from_conference" - android:title="@string/ban_from_conference" - android:visible="false" /> + android:id="@+id/manage_permissions" + android:title="@string/manage_permission"> + <menu> + <item + android:id="@+id/give_membership" + android:title="@string/grant_membership" + android:visible="false" /> + <item + android:id="@+id/remove_membership" + android:title="@string/remove_membership" + android:visible="false" /> + <item + android:id="@+id/give_admin_privileges" + android:title="@string/grant_admin_privileges" + android:visible="false" /> + <item + android:id="@+id/remove_admin_privileges" + android:title="@string/remove_admin_privileges" + android:visible="false" /> + <item + android:id="@+id/give_owner_privileges" + android:title="@string/grant_owner_privileges" + android:visible="false" /> + <item + android:id="@+id/revoke_owner_privileges" + android:title="@string/remove_owner_privileges" + android:visible="false" /> + <item + android:id="@+id/ban_from_conference" + android:title="@string/ban_from_conference" + android:visible="false" /> + <item + android:id="@+id/remove_from_room" + android:title="@string/remove_from_room" + android:visible="false" /> + </menu> + </item> </menu> diff --git a/src/main/res/menu/start_conversation.xml b/src/main/res/menu/start_conversation.xml index 554131c8d..86ebe732a 100644 --- a/src/main/res/menu/start_conversation.xml +++ b/src/main/res/menu/start_conversation.xml @@ -8,10 +8,7 @@ android:title="@string/search" app:actionLayout="@layout/actionview_search" app:showAsAction="collapseActionView|always" /> - <item - android:id="@+id/action_join_conference" - android:title="@string/join_conference" - app:showAsAction="never" /> + <item android:id="@+id/action_scan_qr_code" android:icon="?attr/icon_scan_qr_code" diff --git a/src/main/res/menu/start_conversation_fab_submenu.xml b/src/main/res/menu/start_conversation_fab_submenu.xml new file mode 100644 index 000000000..212a94a7e --- /dev/null +++ b/src/main/res/menu/start_conversation_fab_submenu.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/join_public_channel" + android:icon="@drawable/ic_input_white_24dp" + android:title="@string/join_public_channel" /> + <item + android:id="@+id/create_public_channel" + android:icon="@drawable/ic_public_white_24dp" + android:title="@string/create_public_channel" /> + <item + android:id="@+id/create_private_group_chat" + android:icon="@drawable/ic_group_white_24dp" + android:title="@string/create_private_group_chat" /> + <item + android:id="@+id/create_contact" + android:icon="@drawable/ic_person_white_48dp" + android:title="@string/add_contact" /> +</menu>
\ No newline at end of file diff --git a/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml index 3c50c60fc..653730fbb 100644 --- a/src/main/res/values-ar/strings.xml +++ b/src/main/res/values-ar/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">إلغاء حجب اسم النطاق</string> <string name="title_activity_manage_accounts">إدارة الحسابات</string> <string name="title_activity_settings">الإعدادات</string> - <string name="title_activity_conference_details">تفاصيل فريق المحادثة</string> + <string name="conference_details">تفاصيل فريق المحادثة</string> <string name="title_activity_contact_details">تفاصيل المُراسِل</string> <string name="title_activity_sharewith">شارك بواسطة بيكسل آرت ماسنجر</string> <string name="title_activity_start_conversation">الشروع في محادثة</string> @@ -69,11 +69,8 @@ <string name="add_contact">إضافة مُراسِل</string> <string name="send_failed">فشل التسليم</string> <string name="preparing_image">جارٍ تهيئة الصورة للإرسال</string> - <string name="preparing_images">جارٍ تهيئة الصور للإرسال</string> - <string name="sharing_files_please_wait">مشاركة الملفات جارية. يُرجى الانتظار…</string> <string name="action_clear_history">مسح السجلّ</string> <string name="clear_conversation_history">محو سجلّ المحادثة</string> - <string name="delete_messages">حذف الرسائل</string> <string name="choose_presence">اختر الجهاز</string> <string name="send_unencrypted_message">ارسل رسالة غير مشفّرة</string> <string name="send_message_to_x">إرسال رسالة إلى %s</string> @@ -83,7 +80,6 @@ <string name="openkeychain_required">OpenKeychain</string> <string name="restart">إعادة التشغيل</string> <string name="install">تثبيت</string> - <string name="openkeychain_not_installed">يُرجى تثبيت OpenKeychain</string> <string name="waiting">في الانتظار…</string> <string name="no_pgp_key">تم يتم العثور على أي مفتاح OpenPGP</string> <string name="no_pgp_keys">تم يتم العثور على أية مفاتيح OpenPGP</string> @@ -107,7 +103,6 @@ <string name="receive_presence_updates">استلم تحديثات حالة الحضور</string> <string name="ask_for_presence_updates">أطلب تحديثات حالة الحضور</string> <string name="attach_choose_picture">اختر صورة</string> - <string name="attach_take_from_camera">صورة من الكاميرا</string> <string name="error_not_an_image_file">إنّ الملف الذي قمت باختياره ليس صورة</string> <string name="error_compressing_image">خطأ أثناء تحويل ملف الصورة</string> <string name="error_file_not_found">لم يتم العثور على الملف</string> @@ -137,11 +132,8 @@ <string name="mgmt_account_are_you_sure">هل أنت متأكد؟</string> <string name="attach_record_voice">تسجيل مقطع صوتي</string> <string name="account_settings_jabber_id">حساب جابر</string> - <string name="account_settings_password">الكلمة السرية</string> <string name="account_settings_example_jabber_id">username@pix-art.de</string> <string name="password">الكلمة السرية</string> - <string name="confirm_password">تأكيد كلمة السر</string> - <string name="passwords_do_not_match">كلمتا المرور غير متطابقتين</string> <string name="server_info_show_more">معلومات الخادم</string> <string name="server_info_available">نعم</string> <string name="server_info_unavailable">لا</string> @@ -162,11 +154,9 @@ <string name="done">تم</string> <string name="verify">تحقق</string> <string name="decrypt">فك التشفير</string> - <string name="conferences">المحادثاث الجماعية</string> <string name="search">بحث</string> - <string name="create_contact">انشاء مُراسِل</string> <string name="enter_contact">أدخل مراسلا</string> - <string name="join_conference">الالتحاق بفريق المحادثة</string> + <string name="join_public_channel">الالتحاق بفريق المحادثة</string> <string name="delete_contact">حذف المراسِل</string> <string name="view_contact_details">عرض تفاصيل المُراسِل</string> <string name="block_contact">حجب مُراسِل</string> @@ -174,11 +164,9 @@ <string name="create">إنشاء</string> <string name="select">اختيار</string> <string name="join">الانضمام</string> - <string name="conference_address">عنوان فريق المحادثة</string> - <string name="conference_address_example">groupchat@room.pix-art.de</string> + <string name="channel_full_jid_example">groupchat@room.pix-art.de</string> <string name="save_as_bookmark">أضفه كفاصلة مرجعية</string> <string name="delete_bookmark">حذف الفاصلة المرجعية</string> - <string name="action_edit_subject">تعديل موضوع فريق المحادثة</string> <string name="topic">الموضوع</string> <string name="joining_conference">جارٍ الانضمام إلى فريق المحادثة…</string> <string name="leave">المغادرة</string> @@ -234,7 +222,7 @@ <string name="try_again">أعد المحاولة</string> <string name="finish">إنهاء</string> <string name="verified">تم التحقق منه!</string> - <string name="pref_export_logs">تصدير السجل</string> + <string name="pref_create_backup">إنشاء نسخة احتياطية</string> <string name="choose_file">إختر ملفا</string> <string name="download_x_file">تنزيل %s</string> <string name="delete_x_file">حذف %s</string> @@ -268,9 +256,7 @@ <string name="grant_admin_privileges">منح صلاحيات المُشرِف</string> <string name="remove_admin_privileges">الغاء صلاحيات المُشرِف</string> <string name="conference_options">خيارات المحادثة الجماعية</string> - <string name="members_only">خاص بالأعضاء المنتمين إليه فقط</string> <string name="non_anonymous">غير مجهول</string> - <string name="moderated">تحت الإشراف</string> <string name="never">أبدًا</string> <string name="pref_display_enter_key">أظهر زر الإدخال</string> <string name="video">فيديو</string> @@ -334,7 +320,6 @@ <string name="no">لا</string> <string name="no_accounts">(ليس هناك أي حساب نشط)</string> <string name="this_field_is_required">هذة الخانة ضرورية</string> - <string name="no_participants">لا يوجد أي مشارك</string> <string name="correct_message">تصحيح الرسالة</string> <string name="send_corrected_message">أرسل الرسالة المصححة</string> <string name="this_account_is_disabled">لقد قمت بتعطيل هذا الحساب</string> @@ -356,8 +341,7 @@ <string name="action_end_conversation_muc">مغادرة المحادثة الجماعية</string> <string name="show_password">إظهار الكلمة السرية</string> <string name="registration_password_too_weak">فشلت عملية إنشاء الحساب: إنّ الكلمة السرية ضعيفة</string> - <string name="create_conference">انشاء محادثة جماعية</string> - <string name="conference_subject">الموضوع</string> + <string name="create_private_group_chat">انشاء محادثة جماعية</string> <string name="choose_participants">اختر المشاركين</string> <string name="creating_conference">عملية انشاء فريق المحادثة جارية…</string> <string name="import_text">إنّ جهازك يحتوي على نسخة احتياطية يمكنك استيرادها.\nسوف يتم إعادة تشغيل تطبيقك بعد عملية الإسترجاع. هل تريد استعادة تلك النسخة الإحتياطية؟ </string> @@ -365,9 +349,6 @@ <string name="invite_again">إعادة دعوته مجددا</string> <string name="inviteUser_Subject">قام بدعوتك عبر</string> <string name="invite_user">الدعوة إلى بيكس آرت ماسنجر</string> - <string name="import_canceled">تم إلغاء الاستيراد</string> - <string name="Password_wrong">الكلمة السرية خاطئة، أعد المحاولة</string> - <string name="enter_account_password">يرجى ادخال الكلمة السرية الخاصة بحسابك قصد استيراد نسختك الإحتياطية.</string> <string name="please_wait">يُرجى الانتظار…</string> <string name="me">أنا</string> <string name="allow">سماح</string> @@ -392,7 +373,6 @@ <string name="show_inactive_devices">إظهار الخاملين</string> <string name="hide_inactive_devices">إخفاء الخاملين</string> <string name="reply">رد</string> - <string name="encrypting_message">تشفير الرسالة</string> <string name="transcoding_video">جارٍ ضغط الفيديو</string> <string name="today">اليوم</string> <string name="pref_use_max_brightness">أقصى سطوع للشاشة</string> @@ -424,12 +404,12 @@ <string name="pref_warn_unencrypted_chat">التنبيه إن كانت المحادثة غير مشفّرة</string> <string name="invalid_muc_nick">الاسم المستعار غير صالح</string> <string name="title_activity_share_via_account">شاركه عبر حساب</string> - <string name="private_messages_are_disabled">تم تعطيل الرسائل الخاصة</string> <string name="destroy_muc">حذف المحادثة الجماعية</string> <string name="conference_unknown_error">أنت لم تعد مِن ضمن فريق المحادثة هذا</string> <string name="snooze">غفوة</string> <string name="huawei_protected_apps">التطبيقات المحمية</string> <string name="pref_enable_multi_accounts_title">السماح باستخدام عدة حسابات</string> + <string name="pref_enable_multi_accounts_summary">هل تريد استخدام حسابات أخرى؟</string> <string name="mtm_accept_cert">هل تريد تقبُّل الشهادة المجهولة؟</string> <string name="mtm_cert_details">تفاصيل الشهادة:</string> <string name="once">مرة واحدة</string> @@ -471,7 +451,6 @@ <string name="delete_message">حذف الرسالة</string> <string name="conference_destroyed">لقد تم تدمير هذه المحادثة الجماعية</string> <string name="welcome_server_info">ننصح باستخدام blabber.im كمزوّد الخدمة الخاص بك.</string> - <string name="phone_book">دفتر العناوين</string> <string name="unable_to_save_recording">لا يمكن الاحتفاظ بالتسجيل</string> <string name="foreground_service_channel_name">الخدمة الأمامية</string> <string name="error_channel_name">مشاكل الاتصال</string> @@ -480,13 +459,12 @@ <string name="silent_messages_channel_name">رسائل صامتة</string> <string name="pref_more_notification_settings">إعدادات الإشعارات</string> <string name="video_compression_channel_name">ضغط الفيديو</string> - <string name="backup_channel_name">النسخ الاحتياطي لقاعدة البيانات</string> + <string name="backup_channel_name">النسخ الاحتياطي والاستعادة</string> <string name="app_update_channel_name">تحديث التطبيق</string> <string name="action_group_details">تفاصيل الفريق</string> <string name="view_media">عرض الوسائط</string> <string name="media_browser">مستعرض الوسائط</string> <string name="action_open">افتح</string> - <string name="action_delete">حذف</string> <string name="delete_file_dialog">احذف الملف</string> <string name="cancelled">ملغى</string> <string name="remote_server_timeout">انتهت مدة الاتصال مع الخادم عند بُعد</string> @@ -502,4 +480,9 @@ <string name="autojoin_groupchat">إلتحق بفريق المحادثة هذا افتراضيا</string> <string name="pref_play_gif_inside">تشغيل صور جيف المتحركة في المحادثة</string> <string name="pref_play_gif_inside_summary">إن تم تنشيط هذه الميزة فسوف يتم تشغيل صور جيف المتحركة تلقائيا داخل نافذة المحادثة.</string> + <string name="restore_backup">استرجاع النسخة الاحتياطية</string> + <string name="restore">إستعادة</string> + <string name="unable_to_restore_backup">تعذرت عملية استعادة النسخة الاحتياطية.</string> + <string name="view_users">عرض الأعضاء</string> + <string name="message_deleted">تم حذف الرسالة</string> </resources> diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index 448844ac8..69213d000 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -13,7 +13,7 @@ <string name="action_unblock_domain">Разблокираи доминио</string> <string name="title_activity_manage_accounts">Управление на профили</string> <string name="title_activity_settings">Настройки</string> - <string name="title_activity_conference_details">Детаили на чат групата</string> + <string name="conference_details">Детаили на чат групата</string> <string name="title_activity_contact_details">Детайли на контакта</string> <string name="title_activity_sharewith">Сподели със конверсация</string> <string name="title_activity_start_conversation">Започнете Разговор</string> diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index 7eff38c6f..7e105f7d6 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -68,11 +68,8 @@ <string name="add_contact">Afefgir contacte</string> <string name="send_failed">Error a l\'enviar</string> <string name="preparing_image">Preparant imatge per enviar</string> - <string name="preparing_images">Preparació d\'imatges per a la transmissió</string> - <string name="sharing_files_please_wait">Compartint fitxers. Espereu ...</string> <string name="action_clear_history">Netejar historial</string> <string name="clear_conversation_history">Netejar historial de conversa</string> - <string name="delete_messages">Esborrar missatges</string> <string name="also_end_conversation">Finalitza aquesta conversa després</string> <string name="choose_presence">Tria el dispositiu</string> <string name="send_unencrypted_message">Envia un missatge no xifrat</string> @@ -88,7 +85,6 @@ <string name="openkeychain_required_long">Conversations utilitza una aplicació de tercers anomenada <b>OpenKeychain</b> per xifrar i desxifrar missatges i gestionar les teves claus públiques..\n\nOpenKeychain està publicat sota llicència GPLv3 i disponible a la F-Droid i Google Play.\n\n<small>(Si us plau, reinicieu Conversations després.)</small></string> <string name="restart">Reiniciar</string> <string name="install">Instal·lar</string> - <string name="openkeychain_not_installed">Si us plau, instal·leu OpenKeychain</string> <string name="offering">oferint…</string> <string name="waiting">Esperant...</string> <string name="no_pgp_key">Clau OpenPGP no trobada</string> @@ -150,7 +146,6 @@ <string name="mgmt_account_delete_confirm_text">Si suprimiu el vostre compte, es perdrà tot l\'historial de la conversa</string> <string name="attach_record_voice">Registre de veu</string> <string name="account_settings_jabber_id">Identificació de Jabber</string> - <string name="account_settings_password">Contrasenya</string> <string name="account_settings_example_jabber_id">username@example.com</string> <string name="password">Contrasenya</string> <string name="invalid_jid">Aquesta identificació de Jabber no és vàlida</string> @@ -191,11 +186,9 @@ <string name="done">Fet</string> <string name="verify">Verificar</string> <string name="decrypt">Desxifrar</string> - <string name="conferences">Xats de grup</string> <string name="search">Cercar</string> - <string name="create_contact">Crea un contacte</string> <string name="enter_contact">Introduïu un contacte</string> - <string name="join_conference">Uniu-vos al xat en grup</string> + <string name="join_public_channel">Uniu-vos al xat en grup</string> <string name="delete_contact">Suprimeix el contacte</string> <string name="view_contact_details">Veure els detalls dels contactes</string> <string name="block_contact">Bloqueja contacte</string> @@ -204,12 +197,10 @@ <string name="select">Seleccioneu</string> <string name="contact_already_exists">El contacte ja existeix</string> <string name="join">Unir-se</string> - <string name="conference_address">Direcció del xat de grup</string> - <string name="conference_address_example">room@conference.example.com/nick</string> + <string name="channel_full_jid_example">room@conference.example.com/nick</string> <string name="save_as_bookmark">Guardar com a favorits</string> <string name="delete_bookmark">Eliminar de favorits</string> <string name="bookmark_already_exists">Aquesta llista de favorits ja existeix</string> - <string name="action_edit_subject">Edita el tema del xat de grup</string> <string name="joining_conference">S\'està unint al xat de grup...</string> <string name="leave">Sortir</string> <string name="contact_added_you">Afegir un contacte a la llista de contactes</string> @@ -287,9 +278,6 @@ <string name="verified">Verificat</string> <string name="smp_requested">Sol.licitar una verificació per SMP del contacte</string> <string name="no_otr_session_found">Cap sessió OTR ha estat trobada!</string> - <string name="pref_export_logs">Exportació de l\'historial</string> - <string name="pref_export_logs_summary">Escriviu el registre d\'històrial de converses a la targeta SD</string> - <string name="notification_export_logs_title">Escrivint registres a la targeta SD</string> <string name="choose_file">Escolliu un arxiu</string> <string name="receiving_x_file">Rebent %1$s (%2$d%% completat)</string> <string name="download_x_file">Descargat %s</string> @@ -353,13 +341,8 @@ mentrestant.</string> <string name="ban_from_conference">Prohibició del xat en grup</string> <string name="removing_from_public_conference">Esteu intentant eliminar %s d\'un xat de grup públic. L\'única manera de fer-ho és prohibir l\'usuari per sempre.</string> <string name="ban_now">Banejat ara</string> - <string name="could_not_change_role">No s\'ha pogut canviar les regles de %s</string> - <string name="public_conference">Xat de grup amb accés públic</string> - <string name="private_conference">Xat en grup privat, només membres</string> <string name="conference_options">Opcions de xat en grup</string> - <string name="members_only">Privat, només membres</string> <string name="non_anonymous">Sense anonimat</string> - <string name="moderated">Moderat</string> <string name="you_are_not_participating">No esteu participant</string> <string name="modified_conference_options">S\'han modificat les opcions de xat en grup.</string> <string name="could_not_modify_conference_options">No s\'han pogut modificar les opcions de xat de grup</string> @@ -376,7 +359,6 @@ mentrestant.</string> <string name="pdf_document">Document PDF</string> <string name="apk">Aplicació d\'Android</string> <string name="vcard">Contacte</string> - <string name="touch_to_open_conversations">Tocar per obrir les conversacions</string> <string name="avatar_has_been_published">L\'avatar ha sigut publicat!</string> <string name="sending_x_file">Enviant %s</string> <string name="offering_x_file">Oferint %s</string> @@ -449,11 +431,7 @@ mentrestant.</string> <item quantity="other">%d messages</item> </plurals> <string name="load_more_messages">Carregueu més missatges</string> - <string name="shared_file_with_x">Fitxer compartit amb %s</string> - <string name="shared_image_with_x">Imatge compartida amb %s</string> <string name="no_storage_permission">Les converses necessiten accés a emmagatzematge extern</string> - <string name="shared_images_with_x">Imatges compartides amb %s</string> - <string name="shared_text_with_x">Text compartit amb %s</string> <string name="sync_with_contacts">Sincronitza amb contactes</string> <string name="sync_with_contacts_long">Conversations vol coincidir amb la vostra llista XMPP amb els vostres contactes per mostrar els noms i avatars complets.\n\nConversations només @@ -475,7 +453,6 @@ missatges.\nEs recomana desactivar-les.</string> pot provocar notificacions retardades o fins i tot pèrdua de missatges.\n\nAra se us demanarà que desactiveu-las.</string> <string name="disable">Desactivar</string> - <string name="selection_too_large">L\'àrea seleccionada és massa gran</string> <string name="no_accounts">(Sense comptes activats)</string> <string name="this_field_is_required">Aquest camp és obligatori</string> <string name="correct_message">Corregeix el missatge</string> @@ -483,7 +460,6 @@ missatges.\n\nAra se us demanarà que desactiveu-las.</string> <string name="no_keys_just_confirm">Ja confieu en aquest contacte. Si seleccioneu \"fet\", només confirma que forma %s part d\'aquest xat en grup.</string> <string name="contacts_have_no_pgp_keys">Coversations no és possible xifrar les teves converses perquè els teus contactes no han mostrat la seva clau pública.\n\n<small> Si us plau, pregunti als seus contactes per configurar OpenPGP .</small></string> <string name="this_account_is_disabled">Heu desactivat aquest compte</string> - <string name="security_error_invalid_file_access">Error de seguretat: accés d\'arxiu no vàlid</string> <string name="no_application_to_share_uri">No s\'ha trobat cap aplicació que comparteixi URI</string> <string name="share_uri_with">Comparteix l\'URI amb...</string> <string name="welcome_text">XMPP és un protocol independent del proveïdor. Podeu utilitzar aquest client amb el servidor XMPP que trieu.\nPerò per a la vostra comoditat, hem facilitat crear un compte a conversations.im¹; un proveïdor especialment indicat per a l\'ús amb Conversations.</string> @@ -504,8 +480,7 @@ d\'altres proveïdors donant-los la vostra ID Jabber completa.</string> <string name="show_password">Ensenya la contrasenya</string> <string name="registration_please_wait">Error de registre: torna-ho a provar més tard</string> <string name="registration_password_too_weak">Ha fallat la inscripció: la contrasenya és massa feble</string> - <string name="create_conference">Crea un xat en grup</string> - <string name="conference_subject">Subjecte</string> + <string name="create_private_group_chat">Crea un xat en grup</string> <string name="choose_participants">Tria els participants</string> <string name="creating_conference">S\'està creant el xat en grup...</string> <string name="invite_again">Torna a convidar</string> @@ -551,7 +526,6 @@ d\'altres proveïdors donant-los la vostra ID Jabber completa.</string> <string name="i_followed_this_link_from_a_trusted_source">He seguit aquest enllaç des d\'una font de confiança</string> <string name="verifying_omemo_keys_trusted_source">Esteu a punt de verificar les claus OMEMO de %1$sdesprés de fer clic a un enllaç. Això només és segur si seguiu aquest enllaç des d\'una font de confiança on només %2$s podria haver publicat aquest enllaç.</string> <string name="verify_omemo_keys">Comproveu les claus OMEMO</string> - <string name="encrypting_message">Xifrant el missatge</string> <string name="transcoding_video">S\'està comprimint el vídeo</string> <string name="not_fetching_history_retention_period">No es recullen missatges deguts al període de retenció local.</string> <string name="today">Avui</string> diff --git a/src/main/res/values-ceb/strings.xml b/src/main/res/values-ceb/strings.xml index d63044770..0c57cad64 100644 --- a/src/main/res/values-ceb/strings.xml +++ b/src/main/res/values-ceb/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Ayaw i-block ang domain</string> <string name="title_activity_manage_accounts">Pagdumala sa mga account</string> <string name="title_activity_settings">Mga pagtakda</string> - <string name="title_activity_conference_details">Mga detalyi sa Group chat</string> + <string name="conference_details">Mga detalyi sa Group chat</string> <string name="title_activity_contact_details">Mga detalyi sa kontak</string> <string name="title_activity_sharewith">Ipakigbahin ang gepanag-estoryahan</string> <string name="title_activity_start_conversation">Pagsugod sa Panag-estorya</string> @@ -70,11 +70,8 @@ <string name="add_contact">Pun-e ang kontak</string> <string name="send_failed">napakyas paghatod</string> <string name="preparing_image">Nag-andam ug litrato para transmisyon</string> - <string name="preparing_images">Nag-andam ug litrato para transmisyon</string> - <string name="sharing_files_please_wait">Pagpakigbahin sa mga file. Palihug pag hulat…</string> <string name="action_clear_history">Hawana ang kasaysayan</string> <string name="clear_conversation_history">Hawana ang gepanag-estoryahan nga kasaysayan</string> - <string name="delete_messages">Tangtanga ang mga mensahe</string> <string name="also_end_conversation">Undanga kining panag-estoryahay nigkahuman</string> <string name="choose_presence">Pili ug device</string> <string name="send_unencrypted_message">Pagpadala ug wala\'y lihok nga mensahe</string> @@ -90,7 +87,6 @@ <string name="openkeychain_required_long">Ang Pix-Art Messenger naggamit sa ikatulong partido nga gitawag <b>OpenKeychain</b> sa pag-encrypt ug wala pag-encrypt sa mga mensahe ug sa pagdumala sa imong mga yawe sa publiko.\n\nOpenKeychain kay\n\n<small> lisensyado ubos sa GPLv3 ug magamit sa F-Droid ug Google Play.(Palihug i-restart ang Pix-Art Messenger pagkahuman.)</small></string> <string name="restart">I-restart</string> <string name="install">I-install</string> - <string name="openkeychain_not_installed">Palihog i-install ang OpenKeychain</string> <string name="offering">paghalad…</string> <string name="waiting">napa-abot…</string> <string name="no_pgp_key">Walay OpenPGP nga yawe nga nakita</string> @@ -124,7 +120,6 @@ <string name="receive_presence_updates">Dawat sa mga presensya nga update</string> <string name="ask_for_presence_updates">Mangayu ug mga presensya nga update</string> <string name="attach_choose_picture">Pili ug litrato</string> - <string name="attach_take_from_camera">Kuha gekan sa kamera</string> <string name="preemptively_grant">Sa unang grant nga subscription nga hangyo</string> <string name="error_not_an_image_file">Ang file nga imung gepili kay dili litrato</string> <string name="error_compressing_image">Sayop samtang pagkakabig sa litrato nga file</string> @@ -161,11 +156,8 @@ <string name="mgmt_account_delete_confirm_text">Kon imong tangtangon ang imong account, ang imong tibuok kasaysayan sa gepaga-istoryahan kay mawala</string> <string name="attach_record_voice">Irekord ang tingog</string> <string name="account_settings_jabber_id">Jabber-ID</string> - <string name="account_settings_password">Password</string> <string name="account_settings_example_jabber_id">username@pix-art.de</string> <string name="password">Password</string> - <string name="confirm_password">Kumpirmasyun sa password</string> - <string name="passwords_do_not_match">Ang password kay wala nagkapareha</string> <string name="invalid_jid">Dili kini balido nga Jabber-ID</string> <string name="error_out_of_memory">Wala nay memorya. Ang hulagway dako kaayo</string> <string name="add_phone_book_text">Gusto nimu i-dagdag %s sa imung address nga libro?</string> @@ -204,11 +196,9 @@ <string name="done">Humana</string> <string name="verify">Tinu-a</string> <string name="decrypt">Determinado</string> - <string name="conferences">Grupo sa mga ka chat</string> <string name="search">Pangita</string> - <string name="create_contact">Buhat ug kontak</string> <string name="enter_contact">Isulod ang kontak</string> - <string name="join_conference">Apil sa group chat</string> + <string name="join_public_channel">Apil sa group chat</string> <string name="delete_contact">Tangtanga ang kontak</string> <string name="view_contact_details">Tan-awa ang mga detayi sa kontak</string> <string name="block_contact">I-block ang kontak</string> @@ -217,12 +207,10 @@ <string name="select">Pili</string> <string name="contact_already_exists">Ang kontak kay naa na daan</string> <string name="join">Apil</string> - <string name="conference_address">Address sa group chat</string> - <string name="conference_address_example">groupchat@room.pix-art.de</string> + <string name="channel_full_jid_example">groupchat@room.pix-art.de</string> <string name="save_as_bookmark">Sagupa ingun sa bookmark</string> <string name="delete_bookmark">Tangtanga ang bookmark</string> <string name="bookmark_already_exists">Ang kini nga bookmark kay naa na daan</string> - <string name="action_edit_subject">I-edit ang subject sa grupo nga chat</string> <string name="joining_conference">Pag-apil sa grupo sa chat…</string> <string name="leave">Hawa</string> <string name="contact_added_you">Ang kontak ge puno ka sa lista sa kontak</string> @@ -303,9 +291,6 @@ <string name="verified">Tinu-a!</string> <string name="smp_requested">Hangyu-a ang kontak ug SMP nga pag-tinu</string> <string name="no_otr_session_found">Walay balido nga OTR nga sesyon nga nakita!</string> - <string name="pref_export_logs">Pag-eksport nga kasaysayan</string> - <string name="pref_export_logs_summary">Paguswat ug panag-estorya sa kasaysayan nga mga log adto sa SD card</string> - <string name="notification_export_logs_title">Buhat ug mga panag-estorya adto sa SD card</string> <string name="choose_file">Pili ug file</string> <string name="receiving_x_file">Dawata %1$s(%2$d%% kumpleto)</string> <string name="download_x_file">Pag-download %s</string> @@ -344,7 +329,6 @@ <string name="pdf_document">PDF nga dokumento</string> <string name="apk">Android App</string> <string name="vcard">Kontak</string> - <string name="touch_to_open_conversations">Hikapa para ma abli ang Pix-Art Messenger</string> <string name="avatar_has_been_published">Ang avatar kay gelahad!</string> <string name="sending_x_file">Pagpadala %s</string> <string name="offering_x_file">Ge-alay %s</string> @@ -433,11 +417,7 @@ %d mga mensahe</item> </plurals> <string name="load_more_messages">Pag-load og daghang mga mensahe</string> - <string name="shared_file_with_x">Gipaambit nga file uban %s</string> - <string name="shared_image_with_x">Gipakigbahin nga litrato %s</string> <string name="no_storage_permission">Ang Pix-Art Messenger nagkinahanglan og access sa external nga storage</string> - <string name="shared_images_with_x">Ipa-ambit ang mga litrato uban sa %s</string> - <string name="shared_text_with_x">Ipa-ambit ang mga teks uban sa %s</string> <string name="sync_with_contacts">Synchronize sa mga kontak</string> <string name="sync_with_contacts_long">Ang Pix-Art Messenger kay gusto mo pareha sa XMPP roster uban sa imung mga kontak para mapakita ang tubuok nga mga pangan ug mga avatar.\n\nPix-Art Messenger kay mubasa ra sa imong mga kontak ug iparehas sila sa lokal bisan walay pag-upload sa imung serber.\n\nIkaw gina pangutana namu nga mu grant ka ug permiso para e-access ang imung mga kontak.</string> <string name="notify_on_all_messages">Pahibal-a sa tanang mga mensahe</string> @@ -458,19 +438,16 @@ <string name="install_from_unknown_sources_disabled">Pag-install gikan sa wala mailhi nga mga tinubdan wala tugoti</string> <string name="install_from_unknown_sources_disabled_dialog">Ang imong device gi-configure aron dili sugtan ang mga app i-install gikan sa wala mailhi nga mga tinubdan gawas sa Google PlayStore. Kini maoy hinungdan sa mga problema uban sa updater sa app.\n\nIkaw kay pangutan-on kung mutugot ba sa app nga i-install gekan sa wa mailhing mga tinubdan.</string> <string name="disable">Wala gipagana</string> - <string name="selection_too_large">Ang mga gipili nga lugar kay sobra ra kadako</string> <string name="cancel_update">Ayaw ipadayun ang pagbag-o?</string> <string name="yes">Oo</string> <string name="no">Ayaw</string> <string name="no_accounts">(Walay aktibo nga mga account)</string> <string name="this_field_is_required">Kini nga umahan kay gikinahanglan</string> - <string name="no_participants">Walay mga artisipante</string> <string name="correct_message">Sakto nga mensahe</string> <string name="send_corrected_message">Pagpadala ug insakto nga mensahe</string> <string name="no_keys_just_confirm">Nagasalig naka niini nga kontak. Pinaagi sa pagpili sa \'nahuman\' gipamatuod nimo nga kana %s kay parte diri sa grupo nga chat.</string> <string name="contacts_have_no_pgp_keys">Ang Pix-Art Messenger kay dili makahimo sa pag-encrypt sa imong mga mensahe tungod kay ang imong mga kontak wala magpahibalo sa ilang mga yawe sa publiko.\n\n</string> <string name="this_account_is_disabled">Gipugngan nimo ang kini nga account</string> - <string name="security_error_invalid_file_access">Pakyas sa seguridad: Dili ma-access nga file</string> <string name="no_application_to_share_uri">Walay aplikasyon nga nakuha aron ipaambit ang URI</string> <string name="share_uri_with">Ipaambit ang URI kauban…</string> <string name="mgmt_account_reconnect">Konek ug balik</string> @@ -496,8 +473,7 @@ <string name="show_password">Ipakita ang password</string> <string name="registration_please_wait">Napakyas ang pagrehistro: Sulayi pag-usab taud-taud</string> <string name="registration_password_too_weak">Ang pag-rehistro napakyas: Ang password sobra ra ka-huyang</string> - <string name="create_conference">Buhat ug grupo sa chat</string> - <string name="conference_subject">Subject</string> + <string name="create_private_group_chat">Buhat ug grupo sa chat</string> <string name="choose_participants">Pili ug mga partisipante</string> <string name="creating_conference">Nagbuhat ug grupo sa chat…</string> <string name="import_database">Import nga tabang</string> @@ -510,14 +486,8 @@ <string name="request_permissions_message">Pix-Art Messenger mangutana sa imuha kung musugyot ka gayud sa pipila ka mga permiso. Importante nga imong tugotan ang tanan nga mga permiso nga gamiton ang tanang bahin niini nga mensahero. Kung imu ilimod bisag asa niini nga mga permiso ang app musira sa iyang kaugalingon.</string> <string name="request_permissions_message_again">Imuhang gelimod ang uban o tanang permisong kinahanglanun para sa Pix-Art Messenger. Gusto ka bang moambak sa mga setting ug tugotan kini nga mga permiso? Kung imu ilimod bisag unsa ining mag permiso, ang app mismo musira sa iyang kaugalingun.</string> <string name="unable_to_connect_to_keychain">Dili maka konek sa OpenKeychain</string> - <string name="send_image">Magpadala umg hulagway?</string> <string name="this_device_is_no_longer_in_use">Ang kini nga device kay wala na ginagamit</string> - <string name="import_canceled">Wa madayun ang pag-import</string> - <string name="Import_failed">Ang pag-import sa database napakyas, ang import dili posible</string> - <string name="Password_wrong">Sayop ang password, suwayi pag-usab</string> - <string name="enter_account_password">Palihog un pasulod sa imung account para ma-import sa imung tabang.</string> <string name="please_wait">Palihog paabot…</string> - <string name="databaseimport_started">Ang tabang kay ma-import ra gayud, mataudtauran lang jud.</string> <string name="pref_export_plain_text_logs_summary">Pagpagana sa eksport sa mga chat log ingon nga mabasa nga mga file sa text</string> <string name="pref_export_plain_text_logs">I-export ang mabasa nga mga log sa tawo</string> <string name="payment_required">Kinahanglan ug bayad</string> @@ -544,7 +514,6 @@ <string name="data_saver_enabled">Tigluwas sa Data gipagana</string> <string name="data_saver_enabled_explained">Ang imuhang operata nga sistema kay nagpugong sa Pix-Art Messenger gikan sa pag-access sa Internet kon anaa sa luyo. Para makadawat sa mga pagpahibalo sa bag-ong mga mensahe kinahanglan imong tugotan ang Pix-Art Messenger nga walay pugong nga access kon ang Data saver anaa.\\n Ang Pix-Art Messenger magpadayon pa sa paningkamot aron makaluwas sa datos kung posible.</string> <string name="device_does_not_support_data_saver">Ang imong device wala mosuporta sa pagputol sa tigluwas sa data alang sa Pix-Art Messenger.</string> - <string name="navigate">Pagdala sa lokasyon</string> <string name="add_to_contact_list">Ipuno sa lista sa mga kontak</string> <string name="contact">Kontak</string> <string name="unable_to_start_recording">Dili makasugod sa pagrekord</string> @@ -620,7 +589,6 @@ <string name="pref_use_bundled_emoji_summary">Gamita ang integrated library sa emoji imbis nga gamiton ang imong mga gamit. Ang mga pagbag-o mahimong makuha human sa pag-abli pag-usab sa app.</string> <string name="invalid_muc_nick">Dili balido nga angga nga pangan</string> <string name="title_activity_share_via_account">Pakigbahin pinaagi sa account</string> - <string name="private_messages_are_disabled">Ang mga pribadong mensahe wala pansina</string> <string name="destroy_muc">Guba-a ang grupo sa chat</string> <string name="conference_unknown_error">Wala nakay labot diri nga grupo sa chat</string> <string name="destroy_muc_text">Gusto ba nimu gubaun ang kini nga grupo sa chat%s? Kini grupoha kay makanunay na nga matangtang.</string> diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index d6732c588..85abda12f 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -65,11 +65,8 @@ <string name="add_contact">Přidat kontakt</string> <string name="send_failed">doručení selhalo</string> <string name="preparing_image">Připravuji obrázek na přenos</string> - <string name="preparing_images">Příprava obrázků pro přenos</string> - <string name="sharing_files_please_wait">Sdílení souborů. Chvíli strpení...</string> <string name="action_clear_history">Smazat historii</string> <string name="clear_conversation_history">Smaže historii konverzací</string> - <string name="delete_messages">Smazat zprávy</string> <string name="also_end_conversation">Poté ukončit tuto konverzaci</string> <string name="choose_presence">Vybrat přístroj</string> <string name="send_unencrypted_message">Odeslat nešifrovanou zprávu</string> @@ -85,7 +82,6 @@ <string name="openkeychain_required_long">Konverzace využívá aplikaci třetí strany, <b>OpenKeychain</b>, k šifrování a dešifrování zpráv a ke správě veřejných klíčů.\n\nOpenKeychain je licencován pod GPLv3 a dostupný na F-Droid a Google Play.\n\n<small>(Po instalaci prosím restartujte aplikaci Konverzace.)</small></string> <string name="restart">Restartovat</string> <string name="install">Instalovat</string> - <string name="openkeychain_not_installed">Nainstalujte prosím OpenKeychain</string> <string name="offering">nabízí…</string> <string name="waiting">čekám…</string> <string name="no_pgp_key">Nebyl nalezen žádný OpenPGP klíč</string> @@ -142,7 +138,6 @@ <string name="mgmt_account_are_you_sure">Jste si jisti?</string> <string name="attach_record_voice">Nahrát hlas</string> <string name="account_settings_jabber_id">Jabber ID</string> - <string name="account_settings_password">Heslo</string> <string name="account_settings_example_jabber_id">jmeno@server.cz</string> <string name="password">Heslo</string> <string name="invalid_jid">Toto není platné Jabber ID</string> @@ -192,7 +187,7 @@ <string name="select">Vybrat</string> <string name="contact_already_exists">Kontakt již existuje</string> <string name="join">Vstoupit</string> - <string name="conference_address_example">room@conference.example.com/nick</string> + <string name="channel_full_jid_example">room@conference.example.com/nick</string> <string name="save_as_bookmark">Uložit jako záložku</string> <string name="delete_bookmark">Smazat záložku</string> <string name="bookmark_already_exists">Tato záložka již existuje</string> @@ -267,9 +262,6 @@ <string name="verified">Ověřeno!</string> <string name="smp_requested">Kontakt zažádal o ověření SMP</string> <string name="no_otr_session_found">Nebylo nalezeno platné OTR sezení!</string> - <string name="pref_export_logs">Exportovat historii</string> - <string name="pref_export_logs_summary">Zapsat logy konverzací na SD kartu</string> - <string name="notification_export_logs_title">Zapisování logů na SD kartu</string> <string name="choose_file">Vybrat soubor</string> <string name="receiving_x_file">Přijímám %1$s (%2$d%% dokončeno)</string> <string name="download_x_file">Stáhnout %s</string> @@ -321,10 +313,7 @@ <string name="remove_admin_privileges">Odebrat administrátorská oprávnění</string> <string name="could_not_change_affiliation">Nelze změnit připojení uživatele %s</string> <string name="ban_now">Vypovědět</string> - <string name="could_not_change_role">Nelze změnit roli uživatele %s</string> - <string name="members_only">Soukromé, pouze pro členy</string> <string name="non_anonymous">Neanonymní</string> - <string name="moderated">Moderováno</string> <string name="you_are_not_participating">Neúčastníte se</string> <string name="never">Nikdy</string> <string name="until_further_notice">Než opět změním</string> @@ -339,7 +328,6 @@ <string name="pdf_document">dokument PDF</string> <string name="apk">Aplikace pro Android</string> <string name="vcard">Kontakt</string> - <string name="touch_to_open_conversations">Ťuknout pro otevření Pix-Art Messenger</string> <string name="avatar_has_been_published">Avatar byl zveřejněn!</string> <string name="sending_x_file">Odesílám %s</string> <string name="offering_x_file">Nabízím %s</string> @@ -400,11 +388,7 @@ <string name="not_valid_hostname">Toto není platné hostname</string> <string name="connected_accounts">%1$d z %2$d účtů připojeno</string> <string name="load_more_messages">Načíst více zpráv</string> - <string name="shared_file_with_x">Soubor sdílen s %s</string> - <string name="shared_image_with_x">Obrázek sdílen s %s</string> <string name="no_storage_permission">Pix-Art Messenger vyžaduje přístup k externímu úložišti</string> - <string name="shared_images_with_x">Obrázky sdíleny s %s</string> - <string name="shared_text_with_x">Text sdílen s %s</string> <string name="sync_with_contacts">Synchronizovat s kontakty</string> <string name="sync_with_contacts_long">Aplikace Pix-Art Messenger by ráda porovnala svůj XMPP seznam s vašimi kontakty, aby mohla zobrazit plná jména a avatary.\n\nPix-Art Messenger načte a porovná kontakty pouze lokálně, bez jejich nahrávání na server.\n\nNyní budete dotázáni na udělení práv pro přístup k seznamu kontaktů.</string> <string name="notify_on_all_messages">Upozorňovat na všechny zprávy</string> @@ -417,14 +401,12 @@ <string name="battery_optimizations_enabled_explained">Tento přístroj provádí agresivní optimalizace využití baterie pro aplikaci Pix-Art Messenger. Ty mohou způsobit zpoždění upozornění nebo ztrátu zpráv.\nJe doporučeno tyto vypnout.</string> <string name="battery_optimizations_enabled_dialog">Tento přístroj provádí agresivní optimalizace využití baterie pro aplikaci Pix-Art Messenger. Ty mohou způsobit zpoždění upozornění nebo ztrátu zpráv.\nnyní budete vyzváni k jejich vypnutí.</string> <string name="disable">Vypnout</string> - <string name="selection_too_large">Vybraný obsah je příliš dlouhý</string> <string name="no_accounts">(Žádné aktivované účty)</string> <string name="this_field_is_required">Toto pole je vyžadováno</string> <string name="correct_message">Opravit zprávu</string> <string name="send_corrected_message">Odeslat opravenou zprávu</string> <string name="contacts_have_no_pgp_keys">Není možné zašifrovat zprávy v aplikaci Konverzace, protože kontakty neoznamují svůj veřejný klíč.\n\n<small>Požádejte své kontakty ať si nastaví OpenPGP.</small></string> <string name="this_account_is_disabled">Tento účet byl vypnut</string> - <string name="security_error_invalid_file_access">Chyba zabezpečení: Neplatný přístup k souboru</string> <string name="no_application_to_share_uri">Nebyla nalezena aplikace pro sdílení URI</string> <string name="share_uri_with">Sdílet URI s...</string> <string name="your_full_jid_will_be">Vaše celé Jabber ID bude: %s</string> @@ -441,7 +423,6 @@ <string name="show_password">Zobrazit heslo</string> <string name="registration_please_wait">Registrace selhala: Zkuste znovu později</string> <string name="registration_password_too_weak">Registrace selhala: Příliš slabé heslo</string> - <string name="conference_subject">Předmět</string> <string name="choose_participants">Vybrat účastníky</string> <string name="invite_again">Pozvat znovu</string> <string name="pref_broadcast_last_activity">Odeslat poslední uživatelskou interakci</string> diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index ab8593f41..978ed38bc 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Domain entsperren</string> <string name="title_activity_manage_accounts">Profile verwalten</string> <string name="title_activity_settings">Einstellungen</string> - <string name="title_activity_conference_details">Gruppenchat-Details</string> + <string name="conference_details">Gruppenchat-Details</string> <string name="title_activity_contact_details">Kontakt-Details</string> <string name="title_activity_sharewith">Mit Unterhaltung teilen</string> <string name="title_activity_start_conversation">Unterhaltung beginnen</string> @@ -72,12 +72,9 @@ <string name="add_contact">Kontakt hinzufügen</string> <string name="send_failed">Zustellung fehlgeschlagen</string> <string name="preparing_image">Bild wird für Übertragung vorbereitet</string> - <string name="preparing_images">Bilder werden für Übertragung vorbereitet</string> - <string name="sharing_files_please_wait">Teile Dateien. Bitte warten…</string> <string name="action_clear_history">Verlauf löschen</string> <string name="clear_conversation_history">Verlauf löschen</string> <string name="clear_histor_msg">Bist du sicher, dass du alle Nachrichten in dieser Unterhaltung löschen möchtest?\n\n<b>Achtung:</b> Dies löscht keine Nachrichten, die auf anderen Geräten oder Servern gespeichert sind.</string> - <string name="delete_messages">Nachrichten löschen</string> <string name="also_end_conversation">Diese Unterhaltung danach beenden</string> <string name="choose_presence">Ressource des Kontakts auswählen</string> <string name="send_unencrypted_message">Normal schreiben…</string> @@ -93,7 +90,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger benutzt eine Drittanwendung namens <b>OpenKeychain</b>, um Nachrichten zu ver- und entschlüsseln und um deine Schlüssel zu verwalten.\n\nOpenKeychain ist GPLv3-lizenziert und kann über F-Droid oder Google Play bezogen werden.\n\n<small>(Bitte starte Pix-Art Messenger danach neu.)</small></string> <string name="restart">Neu starten</string> <string name="install">Installieren</string> - <string name="openkeychain_not_installed">Bitte OpenKeychain installieren</string> <string name="offering">angeboten…</string> <string name="waiting">warten…</string> <string name="no_pgp_key">Kein OpenPGP-Schlüssel gefunden</string> @@ -127,7 +123,6 @@ <string name="receive_presence_updates">Online-Status empfangen</string> <string name="ask_for_presence_updates">Online-Status anfragen</string> <string name="attach_choose_picture">Bild auswählen</string> - <string name="attach_take_from_camera">Mit Kamera aufnehmen</string> <string name="preemptively_grant">Statusanfragen vorab erlauben</string> <string name="error_not_an_image_file">Die ausgewählte Datei ist kein Bild</string> <string name="error_compressing_image">Fehler beim Umwandeln des Bildes</string> @@ -165,11 +160,8 @@ <string name="mgmt_account_delete_confirm_text">Wenn du dein Profil löschst, gehen alle Gesprächsverläufe verloren</string> <string name="attach_record_voice">Sprache aufzeichnen</string> <string name="account_settings_jabber_id">Jabber-ID</string> - <string name="account_settings_password">Passwort</string> <string name="account_settings_example_jabber_id">benutzer@pix-art.de</string> <string name="password">Passwort</string> - <string name="confirm_password">Passwort bestätigen</string> - <string name="passwords_do_not_match">Passwörter stimmen nicht überein</string> <string name="invalid_jid">Ungültige Jabber-ID</string> <string name="error_out_of_memory">Zu wenig Speicher vorhanden. Das Bild ist zu groß</string> <string name="add_phone_book_text">%s zum Telefonbuch hinzufügen?</string> @@ -210,11 +202,9 @@ <string name="done">Fertig</string> <string name="verify">Überprüfen</string> <string name="decrypt">Entschlüsseln</string> - <string name="conferences">Gruppenchats</string> <string name="search">Suchen</string> - <string name="create_contact">Kontakt hinzufügen</string> <string name="enter_contact">Kontakt eingeben</string> - <string name="join_conference">Gruppenchat beitreten</string> + <string name="join_public_channel">Gruppenchat beitreten</string> <string name="delete_contact">Kontakt löschen</string> <string name="view_contact_details">Kontakt-Details anzeigen</string> <string name="block_contact">Kontakt sperren</string> @@ -223,12 +213,10 @@ <string name="select">Auswählen</string> <string name="contact_already_exists">Der Kontakt existiert bereits</string> <string name="join">Beitreten</string> - <string name="conference_address">Gruppenchat-Adresse</string> - <string name="conference_address_example">gruppenchat@room.pix-art.de</string> + <string name="channel_full_jid_example">gruppenchat@room.pix-art.de</string> <string name="save_as_bookmark">Zur Kontaktliste hinzufügen</string> <string name="delete_bookmark">Von Kontaktliste entfernen</string> <string name="bookmark_already_exists">Die Konferenz befindet sich bereits auf deiner Kontaktliste</string> - <string name="action_edit_subject">Betreff dieses Gruppenchats ändern</string> <string name="topic">Thema</string> <string name="joining_conference">Gruppenchat beitreten…</string> <string name="leave">Verlassen</string> @@ -314,9 +302,12 @@ <string name="verified">Überprüft!</string> <string name="smp_requested">Kontakt fordert eine Überprüfung an</string> <string name="no_otr_session_found">Keine gültige OTR-Sitzung gefunden!</string> - <string name="pref_export_logs">Chats exportieren</string> - <string name="pref_export_logs_summary">Chats auf SD-Karte schreiben</string> - <string name="notification_export_logs_title">Backup wird auf SD-Karte geschrieben</string> + <string name="pref_create_backup">Sicherung erstellen</string> + <string name="pref_create_backup_summary">Sicherungsdateien schreiben nach %s</string> + <string name="notification_create_backup_title">Sicherungsdateien werden erstellt</string> + <string name="restoring_backup">Sicherung wird wieder hergestellt</string> + <string name="notification_restored_backup_title">Deine Sicherung wurde wiederhergestellt</string> + <string name="notification_restored_backup_subtitle">Vergiss nicht, das Profil zu aktivieren.</string> <string name="choose_file">Datei auswählen</string> <string name="receiving_x_file">Empfange %1$s (%2$d%% abgeschlossen)</string> <string name="download_x_file">%s herunterladen</string> @@ -378,13 +369,8 @@ <string name="ban_from_conference">Von Gruppenchat ausschließen</string> <string name="removing_from_public_conference">Du versuchst %s aus einem öffentlichen Gruppenchat zu entfernen. Die einzige Möglichkeit, dies dauerhaft zu tun, ist, den Kontakt von diesem Gruppenchat auszuschließen.</string> <string name="ban_now">Kontakt ausschließen</string> - <string name="could_not_change_role">Rolle von %s konnte nicht geändert werden</string> - <string name="public_conference">Öffentlich zugänglicher Gruppenchat</string> - <string name="private_conference">Privat, nur für Mitglieder</string> <string name="conference_options">Gruppenchat-Optionen</string> - <string name="members_only">Privat, nur Mitglieder</string> <string name="non_anonymous">De-anonymisiert</string> - <string name="moderated">Moderiert</string> <string name="you_are_not_participating">Du bist kein Mitglied</string> <string name="modified_conference_options">Gruppenchat-Optionen geändert!</string> <string name="could_not_modify_conference_options">Gruppenchat-Optionen konnten nicht geändert werden</string> @@ -401,7 +387,6 @@ <string name="pdf_document">PDF-Dokument</string> <string name="apk">Android App</string> <string name="vcard">Kontakt</string> - <string name="touch_to_open_conversations">Antippen, um Pix-Art Messenger zu öffnen</string> <string name="avatar_has_been_published">Profilbild wurde gespeichert</string> <string name="sending_x_file">%s wird gesendet</string> <string name="offering_x_file">%s wird angeboten</string> @@ -488,11 +473,7 @@ <item quantity="other">%d Nachrichten</item> </plurals> <string name="load_more_messages">weitere Nachrichten laden</string> - <string name="shared_file_with_x">Datei mit %s geteilt</string> - <string name="shared_image_with_x">Bild mit %s geteilt</string> <string name="no_storage_permission">Pix-Art Messenger benötigt Zugriff auf externen Speicher</string> - <string name="shared_images_with_x">Bilder mit %s geteilt</string> - <string name="shared_text_with_x">Text mit %s geteilt</string> <string name="sync_with_contacts">Mit Kontakten synchronisieren</string> <string name="sync_with_contacts_long">Pix-Art Messenger möchte deine XMPP-Kontaktliste mit deinen Kontakten abgleichen, um deren vollständige Namen und Profilbilder anzuzeigen.\n\nPix-Art Messenger wird deine Kontakte nur lokal lesen und abgleichen und überträgt diese nicht auf den Server.\n\nDu wirst nun gefragt, ob du den Zugriff auf deine Kontakte erlauben möchtest.</string> <string name="notify_on_all_messages">Bei allen Nachrichten benachrichtigen</string> @@ -514,19 +495,16 @@ <string name="install_from_unknown_sources_disabled">Installation von unbekannter Quelle ist nicht erlaubt</string> <string name="install_from_unknown_sources_disabled_dialog">Dein Gerät erlaubt keine Installationen von Apps aus unbekannten Quellen außer über Google PlayStore. Dies verursacht Probleme mit automatischen Updates des Messengers.\n\nDu wirst nun gefragt, die Installation von Apps aus unbekannten Quellen zu erlauben.</string> <string name="disable">Deaktivieren</string> - <string name="selection_too_large">Der ausgewählte Bereich ist zu groß</string> <string name="cancel_update">Update abbrechen?</string> <string name="yes">Ja</string> <string name="no">Nein</string> <string name="no_accounts">(Keine aktivierten Profile)</string> <string name="this_field_is_required">Dieses Feld ist erforderlich</string> - <string name="no_participants">Keine Mitglieder</string> <string name="correct_message">Nachricht korrigieren</string> <string name="send_corrected_message">Korrigierte Nachricht senden</string> <string name="no_keys_just_confirm">Du vertraust diesem Kontakt bereits. Durch Auswählen von Fertig bestätigst du, dass %s Teil dieses Gruppenchats ist.</string> <string name="contacts_have_no_pgp_keys">Pix-Art Messenger ist nicht in der Lage, deine Nachrichten zu verschlüsseln, weil deine Kontakte ihren öffentlichen Schlüssel nicht preisgeben.\n\n<small>Bitte sage deinen Kontakten, sie mögen OpenPGP einrichten.</small></string> <string name="this_account_is_disabled">Du hast dieses Profil deaktiviert</string> - <string name="security_error_invalid_file_access">Sicherheitsfehler: Dateizugriff nicht erlaubt</string> <string name="no_application_to_share_uri">Keine Anwendung zum Teilen der URI gefunden</string> <string name="share_uri_with">Teile URI mit…</string> <string name="mgmt_account_reconnect">Neu verbinden</string> @@ -555,8 +533,7 @@ <string name="show_password">Passwort anzeigen</string> <string name="registration_please_wait">Registrierung fehlgeschlagen: Bitte später versuchen</string> <string name="registration_password_too_weak">Registrierung fehlgeschlagen: Passwort zu schwach</string> - <string name="create_conference">Gruppenchat erstellen</string> - <string name="conference_subject">Titel</string> + <string name="create_private_group_chat">Gruppenchat erstellen</string> <string name="choose_participants">Mitglieder auswählen</string> <string name="creating_conference">Gruppenchat wird erstellt…</string> <string name="import_text">Es wurde ein Backup gefunden, welches importiert werden kann.\nDein Messenger startet während des Importvorgangs neu. Soll das Backup importiert werden?</string> @@ -570,14 +547,8 @@ <string name="request_permissions_message">Pix-Art Messenger wird dich nach ein paar Berechtigungen fragen. Es ist wichtig, dass du alle Berechtigungen erlaubst, um alle Funktionen nutzen zu können. Falls du eine dieser Berechtigungen ablehnst wird sich die App schließen.</string> <string name="request_permissions_message_again">Du hast eine oder alle Berechtigungen, welche für Pix-Art Messenger benötigt werden abgelehnt. Willst du zu den Einstellungen springen, um diese Berechtigungen zu erlauben? Falls du eine dieser Berechtigungen ablehnst wird sich die App schließen.</string> <string name="unable_to_connect_to_keychain">Keine Verbindung zu OpenKeychain</string> - <string name="send_image">Bild senden?</string> <string name="this_device_is_no_longer_in_use">Dieses Gerät wird nicht länger verwendet</string> - <string name="import_canceled">Import abgebrochen</string> - <string name="Import_failed">Datenbankimport ist fehlgeschlagen und nicht möglich.</string> - <string name="Password_wrong">Falsches Passwort, erneut versuchen</string> - <string name="enter_account_password">Bitte gib das Passwort deines Profils ein, um das Backup zu importieren.</string> <string name="please_wait">Bitte warten…</string> - <string name="databaseimport_started">Backup wird importiert, dies wird eine Weile dauern.</string> <string name="pref_export_plain_text_logs_summary">Aktiviere den Export von Chats als lesbare Text-Dateien</string> <string name="pref_export_plain_text_logs">Exportiere Chats als lesbare Text-Dateien</string> <string name="payment_required">Bezahlung erforderlich</string> @@ -604,7 +575,6 @@ <string name="data_saver_enabled">Datensparmodus aktiviert</string> <string name="data_saver_enabled_explained">Dein Betriebssystem verbietet Pix-Art Messenger im Hintergrund den Zugang zum Internet. Um Benachrichtigungen erhalten zu können, solltest du Pix-Art Messenger den Zugang erlauben, wenn der Datensparmodus aktiv ist. Pix-Art Messenger wird dennoch versuchen, so viele Daten wie möglich einzusparen.</string> <string name="device_does_not_support_data_saver">Dein Gerät unterstützt den Datensparmodus für Pix-Art Messenger nicht.</string> - <string name="navigate">Zum Standort navigieren</string> <string name="add_to_contact_list">Zur Kontaktliste hinzufügen</string> <string name="contact">Kontakt</string> <string name="unable_to_start_recording">Aufnahme kann nicht gestartet werden</string> @@ -631,7 +601,6 @@ <string name="show_inactive_devices">Inaktive anzeigen</string> <string name="hide_inactive_devices">Verberge inaktive</string> <string name="reply">Antworten</string> - <string name="encrypting_message">Nachricht wird verschlüsselt</string> <string name="transcoding_video">Video wird komprimiert</string> <string name="not_fetching_history_retention_period">Nachrichten werden aufgrund der eingestellten lokalen Speicherfrist nicht abgerufen.</string> <plurals name="seconds"> @@ -721,7 +690,6 @@ <string name="pref_use_bundled_emoji_summary">Nutze die in die App integrierten Emojis anstatt der von deinem Gerät. Die Änderungen werden nach dem nächsten Öffnen der App wirksam.</string> <string name="invalid_muc_nick">Nickname ungültig</string> <string name="title_activity_share_via_account">Teilen mit Profil</string> - <string name="private_messages_are_disabled">Private Nachrichten sind deaktiviert</string> <string name="destroy_muc">Gruppenchat auflösen</string> <string name="conference_unknown_error">Du bist nicht länger in diesem Gruppenchat</string> <string name="destroy_muc_text">Möchtest du den Gruppenchat %s auflösen? Dies wird den Gruppenchat dauerhaft entfernen.</string> @@ -732,7 +700,7 @@ <string name="huawei_protected_apps">Geschützte Apps</string> <string name="huawei_protected_apps_summary">Um Benachrichtigungen zu erhalten, selbst wenn der Bildschirm ausgeschaltet ist, füge Pix-Art Messenger bitte der Liste geschützter Apps hinzu.</string> <string name="pref_enable_multi_accounts_title">Mehrprofil-Modus aktivieren</string> - <string name="pref_enable_multi_accounts_summary">Sofern du mehrere Profile nutzen möchtest musst du ein Passwort zur Verschlüsselung der Datensicherung angeben.</string> + <string name="pref_enable_multi_accounts_summary">Möchtest du mehrere Profile benutzen?</string> <string name="mtm_accept_cert">Unbekanntes Zertifikat akzeptieren?</string> <string name="mtm_trust_anchor">Das Serverzertifikat wurde nicht von einer bekannten Zertifizierungsstelle signiert.</string> <string name="mtm_accept_servername">Nicht übereinstimmenden Servernamen akzeptieren?</string> @@ -809,7 +777,6 @@ <string name="pref_show_record_voice_btn">Zeige Sprachaufnahme-Taste</string> <string name="pref_use_quick_share_attachment_choice_summary">Ersetze Absende-Knopf durch Anlagen-Auswahl, wenn keine Nachricht eingetippt wurde. Andernfalls zeige Schnell-Tasten, welche in der folgenden Einstellung konfiguriert werden können.</string> <string name="pref_quick_share_attachment_choice">Anlagen-Auswahl</string> - <string name="phone_book">Adressbuch</string> <string name="unable_to_save_recording">Aufnahme kann nicht gespeichert werden</string> <string name="foreground_service_channel_name">Vordergrunddienst</string> <string name="foreground_service_channel_description">Diese Benachrichtigungskategorie zeigt eine Benachrichtigung, wenn Pix-Art Messenger ausgeführt wird.</string> @@ -823,14 +790,13 @@ <string name="pref_more_notification_settings">Benachrichtigungseinstellungen</string> <string name="pref_more_notification_settings_summary">Wichtigkeit, Töne, Vibration</string> <string name="video_compression_channel_name">Video-Komprimierung</string> - <string name="backup_channel_name">Datenbanksicherung</string> + <string name="backup_channel_name">Sicherung & Wiederherstellung</string> <string name="app_update_channel_name">App Aktualisierung</string> <string name="action_group_details">Gruppenchat-Details</string> <string name="view_media">Medien anzeigen</string> <string name="media_browser">Medien-Browser</string> <string name="account_status_stream_opening_error">Fehler beim Öffnen des Streams</string> <string name="action_open">Öffnen</string> - <string name="action_delete">Löschen</string> <string name="security_violation_not_attaching_file">Datei aufgrund von Sicherheitsverletzungen ausgelassen.</string> <string name="delete_file_dialog">Datei löschen</string> <string name="delete_file_dialog_msg">Bist du sicher, dass du diese Datei löschen möchtest?\n\n<b>Achtung:</b> Dies wird keine Kopien dieser Datei löschen, die auf anderen Geräten oder Servern gespeichert sind.</string> @@ -858,4 +824,17 @@ <string name="no_name_set_instructions">Keinen Nickname festgelegt.</string> <string name="autojoin_groupchat">Diesem Gruppenchat automatisch beitreten</string> <string name="pref_play_gif_inside">GIF-Dateien im Chat abspielen</string> + <string name="pref_play_gif_inside_summary">Dies auf \"wahr\" zu setzen, spielt GIF-Dateien direkt in der Chat-Ansicht ab.</string> + <string name="open_with">Öffnen mit…</string> + <string name="choose_account">Profil wählen</string> + <string name="set_profile_picture">Pix-Art Messenger Profilbild</string> + <string name="restore_backup">Sicherung wiederherstellen</string> + <string name="restore">Wiederherstellen</string> + <string name="enter_password_to_restore">Um die Sicherung wiederherzustellen, gib für das Profil %s dein Kennwort ein.</string> + <string name="unable_to_restore_backup">Sicherung kann nicht wiederhergestellt werden.</string> + <string name="unable_to_decrypt_backup">Sicherung kann nicht entschlüsselt werden. Stimmt das Kennwort?</string> + <string name="pref_prefer_xmpp_avatar">XMPP-Avatar bevorzugen</string> + <string name="view_users">Mitglieder ansehen</string> + <string name="group_chat_members">Gruppenchat-Mitglieder</string> + <string name="message_deleted">Nachricht wurde gelöscht</string> </resources> diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index 576e9c089..d9661e673 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -64,7 +64,6 @@ <string name="preparing_image">Προετοιμασία εικόνας για μετάδοση</string> <string name="action_clear_history">Καθαρισμός ιστορικού</string> <string name="clear_conversation_history">Καθαρισμός ιστορικού Συζήτησης</string> - <string name="delete_messages">Διαγραφή μηνυμάτων</string> <string name="send_otr_message">Αποστολή κρυπτογραφημένου μηνύματος OTR</string> <string name="send_pgp_message">Αποστολή κρυπτογραφημένου μηνύματος OpenPGP</string> <string name="your_nick_has_been_changed">Το ψευδώνυμό σας έχει αλλάξει</string> @@ -117,7 +116,6 @@ <string name="mgmt_account_are_you_sure">Είστε βέβαιοι;</string> <string name="attach_record_voice">Εγγραφή φωνής</string> <string name="account_settings_jabber_id">ταυτότητα Jabber</string> - <string name="account_settings_password">Συνθηματικό</string> <string name="account_settings_example_jabber_id">username@example.com</string> <string name="password">Συνθηματικό</string> <string name="invalid_jid">Αυτή δεν είναι έγκυρη ταυτότητα Jabber</string> @@ -261,7 +259,6 @@ <string name="remove_admin_privileges">Ανάκληση δικαιωμάτων διαχειριστή</string> <string name="could_not_change_affiliation">Δεν ήταν δυνατή η αλλαγή του δεσμού της επαφής %s</string> <string name="ban_now">Αποκλεισμός τώρα</string> - <string name="could_not_change_role">Δεν ήταν δυνατή η αλλαγή ρόλου της επαφής %s</string> <string name="non_anonymous">Μη-ανώνυμα</string> <string name="never">Ποτέ</string> <string name="until_further_notice">Μέχρι νεωτέρας</string> @@ -275,7 +272,6 @@ <string name="pdf_document">έγγραφο PDF</string> <string name="apk">Εφαρμογή Android</string> <string name="vcard">Επαφή</string> - <string name="touch_to_open_conversations">Επιλέξτε για να ανοίξετε το Pix-Art Messenger</string> <string name="avatar_has_been_published">Η εικόνα προφίλ έχει δημοσιευτεί!</string> <string name="sending_x_file">Αποστολή του %s</string> <string name="offering_x_file">Προσφορά του %s</string> diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 91e13d31c..dd87f8d88 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Desbloquear dominio</string> <string name="title_activity_manage_accounts">Gestionar Cuentas</string> <string name="title_activity_settings">Ajustes</string> - <string name="title_activity_conference_details">Detalles del chat de grupo</string> + <string name="conference_details">Detalles del chat de grupo</string> <string name="title_activity_contact_details">Detalles del contacto</string> <string name="title_activity_sharewith">Compartir con Conversación</string> <string name="title_activity_start_conversation">Comenzar conversación</string> @@ -72,12 +72,9 @@ <string name="add_contact">Añadir contacto</string> <string name="send_failed">Error al enviar</string> <string name="preparing_image">Preparando imagen para enviar</string> - <string name="preparing_images">Preparando imágenes para enviar</string> - <string name="sharing_files_please_wait">Compartiendo ficheros. Por favor, espera...</string> <string name="action_clear_history">Limpiar historial</string> <string name="clear_conversation_history">Limpiar historial de conversación</string> <string name="clear_histor_msg">¿Quieres borrar todos los mensajes de esta conversación?\n\n<b>Atención:</b> Esto no afectará a los mensajes guardados en otros dispositivos o servidores.</string> - <string name="delete_messages">Borrar mensajes</string> <string name="also_end_conversation">Además, terminar esta conversación</string> <string name="choose_presence">Seleccionar dispositivo</string> <string name="send_unencrypted_message">Enviar mensaje sin cifrar</string> @@ -93,7 +90,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger utiliza una aplicación de terceros llamada <b>OpenKeychain</b> para cifrar y descifrar mensajes y gestionar tus claves públicas.\n\nOpenKeychain está publicado bajo licencia GPLv3 y disponible on F-Droid y Google Play.\n\n<small>(Por favor, reinicie Pix-Art Messenger después.)</small></string> <string name="restart">Reiniciar</string> <string name="install">Instalar</string> - <string name="openkeychain_not_installed">Por favor, instala OpenKeyChain</string> <string name="offering">ofreciendo…</string> <string name="waiting">esperando…</string> <string name="no_pgp_key">Clave OpenPGP no encontrada</string> @@ -127,7 +123,6 @@ <string name="receive_presence_updates">Recibir actualizaciones de presencia</string> <string name="ask_for_presence_updates">Solicitar actualizaciones de presencia</string> <string name="attach_choose_picture">Seleccionar imagen</string> - <string name="attach_take_from_camera">Tomar foto</string> <string name="preemptively_grant">De forma automática conceder suscripción de presencia</string> <string name="error_not_an_image_file">El archivo seleccionado no es una imagen</string> <string name="error_compressing_image">Error comprimiendo el archivo de imagen</string> @@ -165,11 +160,8 @@ <string name="mgmt_account_delete_confirm_text">Si borras tu cuenta, tu historial de conversaciones también se perderá</string> <string name="attach_record_voice">Grabar audio</string> <string name="account_settings_jabber_id">Identificador Jabber</string> - <string name="account_settings_password">Contraseña</string> <string name="account_settings_example_jabber_id">usuario@ejemplo.com</string> <string name="password">Contraseña</string> - <string name="confirm_password">Confirmar contraseña</string> - <string name="passwords_do_not_match">Las contraseñas no coinciden</string> <string name="invalid_jid">El identificador no es un identificador Jabber válido</string> <string name="error_out_of_memory">Sin memoria. La imagen es demasiado grande</string> <string name="add_phone_book_text">¿Quieres añadir a %s a tus contactos?</string> @@ -210,11 +202,9 @@ <string name="done">Hecho</string> <string name="verify">Verificar</string> <string name="decrypt">Descifrar</string> - <string name="conferences">Chats de grupo</string> <string name="search">Buscar</string> - <string name="create_contact">Crear Contacto</string> <string name="enter_contact">Introducir contacto</string> - <string name="join_conference">Únete al grupo</string> + <string name="join_public_channel">Únete al grupo</string> <string name="delete_contact">Eliminar Contacto</string> <string name="view_contact_details">Ver detalles del contacto</string> <string name="block_contact">Bloquear contacto</string> @@ -223,12 +213,10 @@ <string name="select">Seleccionar</string> <string name="contact_already_exists">El contacto ya existe</string> <string name="join">Unirse</string> - <string name="conference_address">Dirección del chat de grupo</string> - <string name="conference_address_example">grupo@room.pix-art.de</string> + <string name="channel_full_jid_example">grupo@room.pix-art.de</string> <string name="save_as_bookmark">Guardar en marcadores</string> <string name="delete_bookmark">Eliminar marcador</string> <string name="bookmark_already_exists">Este marcador ya existe</string> - <string name="action_edit_subject">Editar tema del grupo</string> <string name="topic">Tema</string> <string name="joining_conference">Unirse al chat de grupo…</string> <string name="leave">Salir</string> @@ -314,9 +302,12 @@ <string name="verified">¡Verificado!</string> <string name="smp_requested">El contacto solicita verificación SMP</string> <string name="no_otr_session_found">¡No se ha encontrado una sesión OTR válida!</string> - <string name="pref_export_logs">Exportar historial</string> - <string name="pref_export_logs_summary">Escribir historial de conversación en la tarjeta SD</string> - <string name="notification_export_logs_title">Escribiendo logs en la tarjeta SD</string> + <string name="pref_create_backup">Crear copia de seguridad</string> + <string name="pref_create_backup_summary">Escribir archivos de copia de seguridad a %s</string> + <string name="notification_create_backup_title">Creando copia de seguridad</string> + <string name="restoring_backup">Restaurando copia de seguridad</string> + <string name="notification_restored_backup_title">Tu copia de seguridad ha sido restaurada</string> + <string name="notification_restored_backup_subtitle">No olvides habilitar la cuenta.</string> <string name="choose_file">Seleccionar archivo</string> <string name="receiving_x_file">Recibiendo %1$s (%2$d%% completado)</string> <string name="download_x_file">Descargar %s</string> @@ -378,13 +369,8 @@ <string name="ban_from_conference">Desterrar del grupo</string> <string name="removing_from_public_conference">Estás intentando expulsar a %s de un grupo de acceso público. La única forma de hacer esto es prohibir su entrada para siempre.</string> <string name="ban_now">Prohibir ahora</string> - <string name="could_not_change_role">No se puede cambiar el rol de %s</string> - <string name="public_conference">Grupo accesible al público</string> - <string name="private_conference">Privado, sólo miembros del grupo</string> <string name="conference_options">Opciones de chat de grupo</string> - <string name="members_only">Privada, solo miembros</string> <string name="non_anonymous">No anónima</string> - <string name="moderated">Moderada</string> <string name="you_are_not_participating">No estás participando</string> <string name="modified_conference_options">¡Opciones de chat de grupo modificadas!</string> <string name="could_not_modify_conference_options">Las opciones del grupo no se pudon cambiar</string> @@ -401,7 +387,6 @@ <string name="pdf_document">documento PDF</string> <string name="apk">Aplicación de Android</string> <string name="vcard">Contacto</string> - <string name="touch_to_open_conversations">Pulsa para abrir Pix-Art Messenger</string> <string name="avatar_has_been_published">¡La imagen de perfil ha sido publicada!</string> <string name="sending_x_file">Enviando %s</string> <string name="offering_x_file">Ofreciendo %s</string> @@ -488,11 +473,7 @@ <item quantity="other">%d mensajes</item> </plurals> <string name="load_more_messages">Cargar más mensajes</string> - <string name="shared_file_with_x">Archivo compartido con %s</string> - <string name="shared_image_with_x">Imagen compartida con %s</string> <string name="no_storage_permission">Pix-Art Messenger necesita acceder al almacenamiento externo</string> - <string name="shared_images_with_x">Imágenes compartidas con %s</string> - <string name="shared_text_with_x">Compartido texto con %s</string> <string name="sync_with_contacts">Sincronizar contactos</string> <string name="sync_with_contacts_long">Pix-Art Messenger quiere cruzar tu lista de contactos de XMPP con tus contactos del móvil para mostrar sus nombres completos y sus fotos de perfil.\n\nPix-Art Messenger solo leerá tus contactos y los cruzará localmente sin subirlos a tu servidor.\n\nEl sistema te preguntará ahora para conceder los permisos de acceso a tus contactos del móvil.</string> <string name="notify_on_all_messages">Notificar para todos los mensajes</string> @@ -514,19 +495,16 @@ <string name="install_from_unknown_sources_disabled">Instalación desde orígenes desconocidos desactivada</string> <string name="install_from_unknown_sources_disabled_dialog">El dispositivo está configurado para no permitir la instalación de aplicaciones desde orígenes desconocidos, salvo desde Google PlayStore.\n\nAhora se le pedirá que permita la instalación de aplicaciones desde orígenes desconocidos.</string> <string name="disable">Deshabilitar</string> - <string name="selection_too_large">El área seleccionada es demasiado grande</string> <string name="cancel_update">¿Cancelar actualización?</string> <string name="yes">Sí</string> <string name="no">No</string> <string name="no_accounts">(No hay cuentas activas)</string> <string name="this_field_is_required">Este campo es requerido</string> - <string name="no_participants">No participantes</string> <string name="correct_message">Corregir mensaje</string> <string name="send_corrected_message">Enviar mensaje corregido</string> <string name="no_keys_just_confirm">Ya confías en este contacto. Seleccionando \'hecho\', estás confirmando que %s es parte de este grupo.</string> <string name="contacts_have_no_pgp_keys">Pix-Art Messenger no ha podido cifrar tus mensajes porque tus contactos no están anunciando su clave pública.\n\n<small>Por favor, pide a tus contactos que configuren OpenPGP.</small></string> <string name="this_account_is_disabled">Has deshabilitado esta cuenta</string> - <string name="security_error_invalid_file_access">Error de seguridad: Acceso a fichero inválido</string> <string name="no_application_to_share_uri">No se ha encontrado ninguna aplicación para compartir la URI</string> <string name="share_uri_with">Compartir URI con...</string> <string name="mgmt_account_reconnect">Reconectar</string> @@ -555,8 +533,7 @@ <string name="show_password">Mostrar contraseña</string> <string name="registration_please_wait">El registro falló. Prueba de nuevo más tarde</string> <string name="registration_password_too_weak">Error en el registro: La contraseña es demasiado débil</string> - <string name="create_conference">Crear grupo</string> - <string name="conference_subject">Asunto</string> + <string name="create_private_group_chat">Crear grupo</string> <string name="choose_participants">Elige a los participantes</string> <string name="creating_conference">Crear chat de grupo…</string> <string name="import_text">Hay una copia de seguridad en tu dispositivo que se puede importar.\nTu aplicación se reiniciará durante el proceso. ¿Quieres importar la copia de seguridad?</string> @@ -570,14 +547,8 @@ <string name="request_permissions_message">Pix-Art Messenger le pedirá que conceda algunos permisos. Es importante que permitas que todos estos permisos usen todas las características de este mensajero. Si usted niega cualquiera de estos permisos, la aplicación se cerrará sola.</string> <string name="request_permissions_message_again">Usted ha negado uno o todos los permisos requeridos para Pix-Art Messenger. ¿Desea ir a la configuración para permitir estos permisos? Si rechaza uno de estos permisos, la aplicación se cerrará.</string> <string name="unable_to_connect_to_keychain">No se ha podido conectar a OpenKeychain</string> - <string name="send_image">¿Enviar imagen?</string> <string name="this_device_is_no_longer_in_use">Este dispositivo ya no está en uso</string> - <string name="import_canceled">Importación cancelada</string> - <string name="Import_failed">La importación de la base de datos falló, una importación no es posible</string> - <string name="Password_wrong">Contraseña incorrecta, inténtalo de nuevo</string> - <string name="enter_account_password">Ingrese la contraseña de tu perfil para importar la copia de seguridad.</string> <string name="please_wait">Espere, por favor…</string> - <string name="databaseimport_started">Se importará la copia de seguridad, esto puede llevar un tiempo.</string> <string name="pref_export_plain_text_logs_summary">Habilita la exportación de chats como archivos de texto legibles</string> <string name="pref_export_plain_text_logs">Exportar chats como archivos de texto legibles</string> <string name="payment_required">Pago requerido</string> @@ -604,7 +575,6 @@ <string name="data_saver_enabled">Optimización de datos habilitado</string> <string name="data_saver_enabled_explained">Tu sistema operativo está restringiendo a Pix-Art Messenger acceder a internet cuando está en segundo plano. Para recibir notificaciones de nuevos mensajes deberías permitir a Pix-Art Messenger acceder a internet cuando la optimización de datos está habilitada.\nPix-Art Messenger realizará igualmente optimizaciones para ahorrar datos cuando sea posible.</string> <string name="device_does_not_support_data_saver">Tu dispositivo no soporta la opción de deshabilitar la optimización de datos para Pix-Art Messenger</string> - <string name="navigate">Navegar hasta la ubicación</string> <string name="add_to_contact_list">Añadir a contactos</string> <string name="contact">Contacto</string> <string name="unable_to_start_recording">No se puede iniciar la grabación</string> @@ -631,7 +601,6 @@ <string name="show_inactive_devices">Mostrar desconectados</string> <string name="hide_inactive_devices">Ocultar desconectados</string> <string name="reply">Responder</string> - <string name="encrypting_message">Cifrando mensaje</string> <string name="transcoding_video">Comprimiendo video</string> <string name="not_fetching_history_retention_period">No buscar mensajes más antiguos que el establecido en el marco de tiempo.</string> <plurals name="seconds"> @@ -721,7 +690,6 @@ <string name="pref_use_bundled_emoji_summary">Utilice la librería emoji integrada en lugar de utilizar la de su dispositivo. Los cambios estarán disponibles después de volver a abrir la aplicación.</string> <string name="invalid_muc_nick">Apodo inválido</string> <string name="title_activity_share_via_account">Compartir a través de cuenta</string> - <string name="private_messages_are_disabled">Los mensajes privados están desactivados</string> <string name="destroy_muc">Destruye el chat de grupo</string> <string name="conference_unknown_error">Ya no estás en este chat de grupo</string> <string name="destroy_muc_text">¿Quieres eliminar el grupo %s? Esto eliminará este grupo de chat permanentemente.</string> @@ -732,7 +700,7 @@ <string name="huawei_protected_apps">Aplicaciones protegidas</string> <string name="huawei_protected_apps_summary">Para recibir notificaciones incluso cuando la pantalla está apagada, por favor añade Pix-Art Messenger a la lista de aplicaciones protegidas.</string> <string name="pref_enable_multi_accounts_title">Habilitar múltiples cuentas</string> - <string name="pref_enable_multi_accounts_summary">Para utilizar varias cuentas debe establecer una contraseña para las copias de seguridad diarias.</string> + <string name="pref_enable_multi_accounts_summary">¿Quieres usar cuentas múltiples?</string> <string name="mtm_accept_cert">¿Aceptar certificado desconocido?</string> <string name="mtm_trust_anchor">El certificado del servidor no está firmado por una Autoridad Certificadora conocida.</string> <string name="mtm_accept_servername">¿Aceptar el nombre del servidor que no coincide?</string> @@ -809,7 +777,6 @@ <string name="pref_show_record_voice_btn">Mostrar botón de grabación de voz</string> <string name="pref_use_quick_share_attachment_choice_summary">Reemplazar el botón de enviar con selección de adjunto si no se está tecleando un mensaje. En otro caso mostrar acciones rápidas, que pueden ser configuradas en la siguiente opción.</string> <string name="pref_quick_share_attachment_choice">Seleccionar adjunto</string> - <string name="phone_book">Libreta de direcciones</string> <string name="unable_to_save_recording">Imposible guardar la grabación</string> <string name="foreground_service_channel_name">Servicio en primer plano</string> <string name="foreground_service_channel_description">Esta categoría se usa para mostrar una notificación permanente indicando que Pix-Art Messenger se está ejecutando.</string> @@ -823,14 +790,13 @@ <string name="pref_more_notification_settings">Configuración de notificaciones</string> <string name="pref_more_notification_settings_summary">Importancia, Sonido, Vibración</string> <string name="video_compression_channel_name">Compresión de vídeo</string> - <string name="backup_channel_name">Copia de seguridad de base de datos</string> + <string name="backup_channel_name">Copia de seguridad& Restauración</string> <string name="app_update_channel_name">Actualizar aplicación</string> <string name="action_group_details">Detalles del grupo</string> <string name="view_media">Visualizar multimedia</string> <string name="media_browser">Navegador multimedia</string> <string name="account_status_stream_opening_error">Error en la apertura de la transmisión</string> <string name="action_open">Abrir</string> - <string name="action_delete">Borrar</string> <string name="security_violation_not_attaching_file">Fichero omitido por violación de seguridad.</string> <string name="delete_file_dialog">Borrar archivo</string> <string name="delete_file_dialog_msg">¿Está seguro de que quiere borrar este archivo?\n\n<b>Atención:</b> Esto no afectará a las copias guardadas en otros dispositivos o servidores.</string> @@ -857,4 +823,20 @@ <string name="enter_your_name_instructions">Por favor, escribe tu alias que será visible para tus contactos.</string> <string name="no_name_set_instructions">Alias no configurado.</string> <string name="autojoin_groupchat">Unirse automáticamente a este grupo de chat</string> + <string name="pref_play_gif_inside">Reproducir archivos GIF en la conversación</string> + <string name="pref_play_gif_inside_summary">Activando esto, ejecuta archivos GIF directamente en la conversación.</string> + <string name="open_with">Abrir con…</string> + <string name="choose_account">Elige cuenta</string> + <string name="set_profile_picture">Imagen de perfil de Pix-Art Messenger</string> + <string name="restore_backup">Restaurar copia de seguridad</string> + <string name="restore">Restaurar</string> + <string name="enter_password_to_restore">Escribe tu contraseña para la cuenta %s para restaurar la copia de seguridad.</string> + <string name="restore_warning">No use la característica de restaurar copia de seguridad para intentar clonar (ejecutar simultáneamente) una instalación. La restauración de copia de seguridad es solo para migraciones o para el caso de que haya perdido el dispositivo original.</string> + <string name="unable_to_restore_backup">No se puede restaurar la copia de seguridad.</string> + <string name="unable_to_decrypt_backup">No se puede descifrar la copia de seguridad. ¿Es correcta la contraseña?</string> + <string name="pref_prefer_xmpp_avatar_summary">Preferir el avatar de usuarios XMPP en lugar del de tu lista de contactos</string> + <string name="pref_prefer_xmpp_avatar">Preferir avatar XMPP</string> + <string name="view_users">Ver miembros</string> + <string name="group_chat_members">Miembros del grupo</string> + <string name="message_deleted">El mensaje fue eliminado</string> </resources> diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index 932ab3c4e..28e11619b 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -68,11 +68,8 @@ <string name="add_contact">Kontaktua gehitu</string> <string name="send_failed">huts bidaltzerakoan</string> <string name="preparing_image">Irudia transmisiorako prestatzen. Mesedez itxaron…</string> - <string name="preparing_images">Irudiak transmisiorako prestatzen</string> - <string name="sharing_files_please_wait">Fitxategiak partekatzen. Mesedez itxaron...</string> <string name="action_clear_history">Historia garbitu</string> <string name="clear_conversation_history">Elkarrizketa historia garbitu</string> - <string name="delete_messages">Mezuak ezabatu</string> <string name="also_end_conversation">Elkarrizketa hau jarraian amaitu</string> <string name="choose_presence">Gailua aukeratu</string> <string name="send_unencrypted_message">Enkriptatu gabeko mezua bidali</string> @@ -88,7 +85,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger ek <b>OpenKeychain</b> izeneko hirugarren app bat erabiltzen du mezuak enkriptatu eta desenkriptatzeko eta zure gako publikoak kudeatzeko.\n\nOpenKeychain GPLv3 lizentziapean dago eta F-Droid eta Google Playn eskura daiteke.\n\n<small>(Mesedez ondoren Pix-Art Messenger berrabiarazi)</small></string> <string name="restart">Berrabiarazi</string> <string name="install">Instalatu</string> - <string name="openkeychain_not_installed">Mesedez instalatu ezazu OpenKeychain</string> <string name="offering">eskeintzen…</string> <string name="waiting">itxaroten…</string> <string name="no_pgp_key">Ez da OpenPGP gakorik aurkitu</string> @@ -150,7 +146,6 @@ <string name="mgmt_account_delete_confirm_text">Zure kontua ezabatzen baduzu, zure elkarrizketen historia guztia galduko da</string> <string name="attach_record_voice">Ahotsa grabatu</string> <string name="account_settings_jabber_id">Jabber IDa</string> - <string name="account_settings_password">Pasahitza</string> <string name="account_settings_example_jabber_id">erabiltzailea@adibidea.com</string> <string name="password">Pasahitza</string> <string name="invalid_jid">Hau ez da Jabber ID baliodun bat</string> @@ -191,11 +186,9 @@ <string name="done">Eginda</string> <string name="verify">Egiaztatu</string> <string name="decrypt">Desenkriptatu</string> - <string name="conferences">Taldeak</string> <string name="search">Bilatu</string> - <string name="create_contact">Kontaktua sortu</string> <string name="enter_contact">Kontaktua sartu</string> - <string name="join_conference">Taldera batu</string> + <string name="join_public_channel">Taldera batu</string> <string name="delete_contact">Kontaktua ezabatu</string> <string name="view_contact_details">Kontaktuaren xehetasunak ikusi</string> <string name="block_contact">Kontaktua blokeatu</string> @@ -204,12 +197,10 @@ <string name="select">Hautatu</string> <string name="contact_already_exists">Kontaktua existitzen da dagoeneko</string> <string name="join">Batu</string> - <string name="conference_address">Taldearen helbidea</string> - <string name="conference_address_example">gela@konferentzia.adibidea.eus/ezizena</string> + <string name="channel_full_jid_example">gela@konferentzia.adibidea.eus/ezizena</string> <string name="save_as_bookmark">Gorde laster-marka bezala</string> <string name="delete_bookmark">Laster-marka ezabatu</string> <string name="bookmark_already_exists">Laster-marka hau existitzen da dagoeneko</string> - <string name="action_edit_subject">Taldearen gaia editatu</string> <string name="joining_conference">Taldera batzen...</string> <string name="leave">Alde egin</string> <string name="contact_added_you">Kontaktuak bere zerrendara gehitu zaitu</string> @@ -287,9 +278,6 @@ <string name="verified">Egiaztatuta!</string> <string name="smp_requested">Kontaktuak SMP egiaztapena eskatu du</string> <string name="no_otr_session_found">Ez da OTR saio baliodunik aurkitu!</string> - <string name="pref_export_logs">Historia esportatu</string> - <string name="pref_export_logs_summary">Elkarrizketen historiaren erregistroak SD txartelean gorde</string> - <string name="notification_export_logs_title">Erregistroak SD txartelean gordetzen</string> <string name="choose_file">Fitxategia aukeratu</string> <string name="receiving_x_file">%1$s jasotzen (%2$d%% osatua)</string> <string name="download_x_file">%s deskargatu</string> @@ -350,13 +338,8 @@ <string name="ban_from_conference">Taldean egotea debekatu</string> <string name="removing_from_public_conference">%s talde publiko batetik ateratzen saiatzen ari zara. Hori lortzeko modu bakarra erabiltzaile hori betiko debekatzea da.</string> <string name="ban_now">Debekatu orain</string> - <string name="could_not_change_role">%s(r)en rola ezin izan da aldatu</string> - <string name="public_conference">Publikoki edonor sar daiteken taldea</string> - <string name="private_conference">Talde pribatua, kideentzat soilik</string> <string name="conference_options">Taldearen aukerak</string> - <string name="members_only">Pribatua, kideentzat soilik</string> <string name="non_anonymous">Ez anonimoa</string> - <string name="moderated">Moderatua</string> <string name="you_are_not_participating">Ez zara parte hartzen ari</string> <string name="modified_conference_options">Taldearen aukerak aldatu dira</string> <string name="could_not_modify_conference_options">Ezin izan dira taldearen aukerak aldatu</string> @@ -373,7 +356,6 @@ <string name="pdf_document">PDF dokumentua</string> <string name="apk">Android aplikazioa</string> <string name="vcard">Kontaktua</string> - <string name="touch_to_open_conversations">Ikutu Pix-Art Messenger irekitzeko</string> <string name="avatar_has_been_published">Profileko argazkia argitaratu da</string> <string name="sending_x_file">%s bidaltzen</string> <string name="offering_x_file">%s eskeintzen...</string> @@ -446,11 +428,7 @@ <item quantity="other">%d mezu</item> </plurals> <string name="load_more_messages">Mezu gehiago kargatu</string> - <string name="shared_file_with_x">Fitxategia %s(r)ekin partekatu da</string> - <string name="shared_image_with_x">Irudia %s(r)ekin partekatu da</string> <string name="no_storage_permission">Pix-Art Messenger ek kanpoko biltegirako sarbidea behar du</string> - <string name="shared_images_with_x">Irudiak %s(r)ekin partekatu dira</string> - <string name="shared_text_with_x">Testua %s(r)ekin partekatu da</string> <string name="sync_with_contacts">Kontaktuekin sinkronizatu</string> <string name="sync_with_contacts_long">Pix-Art Messenger ek zure XMPP zerrenda eta zure kontaktuak uztartu nahi ditu haien izenak eta argazkiak erakusteko.\n\nPix-Art Messenger ek zure kontaktuak modu lokalean soilik irakurri eta uztartuko ditu, zure zerbitzarira kargatu gabe.\n\nJarraian baimenak eskatuko zaizkizu zure kontaktuetara sartu ahal izateko.</string> <string name="notify_on_all_messages">Mezu guztiak jakinarazi</string> @@ -464,7 +442,6 @@ <string name="battery_optimizations_enabled_explained">Zure gailua jakinarazpen atzeratuak edota mezuen galera ekar lezaketen bateriaren optimizazio handiak egiten ari da Pix-Art Messenger en.\nHoriek ezgaitzea gomendatzen da.</string> <string name="battery_optimizations_enabled_dialog">Zure gailua jakinarazpen atzeratuak edota mezuen galera ekar lezaketen bateriaren optimizazio handiak egiten ari da Conversationsen.\nJarraian hauek ezgaitzea eskatuko zaizu.</string> <string name="disable">Ezgaitu</string> - <string name="selection_too_large">Hautatutako zatia handiegia da</string> <string name="no_accounts">(Ez dago kontu aktiborik)</string> <string name="this_field_is_required">Datu hau beharrezkoa da</string> <string name="correct_message">Mezua zuzendu</string> @@ -472,7 +449,6 @@ <string name="no_keys_just_confirm">Kontaktu honetaz fidatzen zara dagoeneko. \'Eginda\' hautatuz %s talde honen kidea dela egiaztatzen duzu besterik gabe.</string> <string name="contacts_have_no_pgp_keys">Conversations ez da zure mezuak enkriptatzeko gai zure kontaktuak haien gako publikoa jakinarazten ez daudelako.\n\n<small>Mesedez eskatu ezaiezu zure kontakuei OpenPGP konfigura dezaten.</small></string> <string name="this_account_is_disabled">Kontu hau ezgaitu duzu</string> - <string name="security_error_invalid_file_access">Segurtasun akatsa: fitxategi sarrera baliogabea</string> <string name="no_application_to_share_uri">Ez da aplikaziorik aurkitu URIa partekatzeko</string> <string name="share_uri_with">URIa honekin partekatu...</string> <string name="welcome_text">XMPP hornitzaleez independiente den protokolo bat da. Bezero hau aukeratzen duzun edozein XMPP zerbitzariarekin erabili dezakezu.\nHala ere zure erosotasunerako conversations.im¹-en, Conversationsekin bereziki erabiltzeko egokia den hornitzaile batean, kontu bat sortzea erraz egin dugu.</string> @@ -491,8 +467,7 @@ <string name="show_password">Pasahitza erakutsi</string> <string name="registration_please_wait">Erregistroak huts egin du: saiatu berriz beranduago</string> <string name="registration_password_too_weak">Erregistroa huts egin du: pasahitza ahulegia da</string> - <string name="create_conference">Taldea sortu</string> - <string name="conference_subject">Gaia</string> + <string name="create_private_group_chat">Taldea sortu</string> <string name="choose_participants">Parte hartzaileak hautatu</string> <string name="creating_conference">Taldea sortzen…</string> <string name="invite_again">Berriz gonbidatu</string> @@ -537,7 +512,6 @@ <string name="i_followed_this_link_from_a_trusted_source">Lotura hau jatorri fidagarri batetik jarraitu dut</string> <string name="verifying_omemo_keys_trusted_source">%1$s(r)en OMEMO gakoak egiaztatuko dira lotura batean sakatu ondoren. Hau segurua da soilik lotura hau jatorri fidagarri batetik jarraitu baduzu eta soilik %2$s(e)k argitaratu izan ahal badu.</string> <string name="verify_omemo_keys">OMEMO gakoak egiaztatu</string> - <string name="encrypting_message">Mezua enkriptatzen</string> <string name="transcoding_video">Bideoa konprimatzen</string> <string name="not_fetching_history_retention_period">Mezurik ez eskuratzen gelditze tarte lokalarengatik.</string> <string name="today">Gaur</string> diff --git a/src/main/res/values-fil/strings.xml b/src/main/res/values-fil/strings.xml index f9e92fe91..8f8b7efe8 100644 --- a/src/main/res/values-fil/strings.xml +++ b/src/main/res/values-fil/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Wag harangan ang domain</string> <string name="title_activity_manage_accounts">Mamahala ng mga account</string> <string name="title_activity_settings">Mga setting</string> - <string name="title_activity_conference_details">Grupo ng detalye sa chat</string> + <string name="conference_details">Grupo ng detalye sa chat</string> <string name="title_activity_contact_details">Mga detalyi sa kontak</string> <string name="title_activity_sharewith">Ibahagi ang pag-uusap</string> <string name="title_activity_start_conversation">Simulan ang pag-uusap</string> @@ -70,11 +70,8 @@ <string name="add_contact">Dagdag kontak</string> <string name="send_failed">nabigo ang paghahatid</string> <string name="preparing_image">Paghahanda ng imahe para sa transmisyon</string> - <string name="preparing_images">Paghahanda ng mga imahi para sa transmisyon</string> - <string name="sharing_files_please_wait">Pagbahagi ng mga dokumento. Pakihintay…</string> <string name="action_clear_history">Burahin ang nangyari</string> <string name="clear_conversation_history">Burahin ang nakaraang paguusap</string> - <string name="delete_messages">Burahin ang mga mensahe</string> <string name="also_end_conversation">Tapusin itong usapan mamaya</string> <string name="choose_presence">Pumili ng instrumento</string> <string name="send_unencrypted_message">Maghatid ng unencrypted na mensahe</string> @@ -90,7 +87,6 @@ <string name="openkeychain_required_long">Ang Pix-Art Messenger ay gumagamit ng ikatlong partido na aplikasyon kung tawagin ay <b>OpenKeychain</b> para ma encrypt at decrpyt ang mga mensahe at para ma mahala ang pampublikong susi.\n\nOpenkeychain ay lisensyadong GPLv3 at makukuha sa F-Droid at Google Play.\n\n<small> (Paki restart ng Pix-Art Messenger pagkatapos.)</small></string> <string name="restart">Ibalik sa simula</string> <string name="install">I-lagay</string> - <string name="openkeychain_not_installed">Paki lagay ng OpenKeychain</string> <string name="offering">handog…</string> <string name="waiting">maghintay…</string> <string name="no_pgp_key">Walang OpenPGP Key na natagpuan</string> @@ -124,7 +120,6 @@ <string name="receive_presence_updates">Tumanggap ng mga presensya sa pagbabago</string> <string name="ask_for_presence_updates">Humingi ng mga presensya sa pagbabago</string> <string name="attach_choose_picture">Mamili ng larawan</string> - <string name="attach_take_from_camera">Kumuha galing sa kamera</string> <string name="preemptively_grant">Magbigay ng suskrisyong hiling</string> <string name="error_not_an_image_file">Ang napili mong dokumento ay hindi imahe</string> <string name="error_compressing_image">Nagkamali habang nagko-convert ng dokumentong imahe</string> @@ -161,11 +156,8 @@ <string name="mgmt_account_delete_confirm_text">Kapag dinelete mo ang iyong account, ang lahat na pag-uusap na nangyari sa iyong account ay mawala</string> <string name="attach_record_voice">I-rekord ang boses</string> <string name="account_settings_jabber_id">Jabber-ID</string> - <string name="account_settings_password">Ang Password</string> <string name="account_settings_example_jabber_id">username@pix-art.de</string> <string name="password">Ang password</string> - <string name="confirm_password">Kompirmahin ang password</string> - <string name="passwords_do_not_match">Ang password ay hindi magkatugma</string> <string name="invalid_jid">Hindi ito balido na Jabber-ID</string> <string name="error_out_of_memory">Wala ng memorya. Ang imahe ay napakalaki</string> <string name="add_phone_book_text">Gusto mo bang magdagdag %s sa iyong address book?</string> @@ -204,11 +196,9 @@ <string name="done">Natapos</string> <string name="verify">I-verify</string> <string name="decrypt">Decrypt</string> - <string name="conferences">Chat sa grupo</string> <string name="search">Magsaliksik</string> - <string name="create_contact">Gumawa ng kontak</string> <string name="enter_contact">Ipasok na kontak</string> - <string name="join_conference">Sumali sa pang-grupong chat</string> + <string name="join_public_channel">Sumali sa pang-grupong chat</string> <string name="delete_contact">Burahin ang kontak</string> <string name="view_contact_details">Tingnan ang mga detalye sa kontak</string> <string name="block_contact">Harangan ang kontak</string> @@ -217,12 +207,10 @@ <string name="select">Piliin</string> <string name="contact_already_exists">Mayroon ng kontak na umiiral</string> <string name="join">Sumali</string> - <string name="conference_address">Address ng grupong chat</string> - <string name="conference_address_example">groupchat@room.pix-art.de</string> + <string name="channel_full_jid_example">groupchat@room.pix-art.de</string> <string name="save_as_bookmark">I-save bilang bookmark</string> <string name="delete_bookmark">Burahin na bookmark</string> <string name="bookmark_already_exists">Umiiral na ang bookmark</string> - <string name="action_edit_subject">I-edit ang paksa sa grupo ng chat</string> <string name="joining_conference">Sumali sa pang grupong chat…</string> <string name="leave">Iwanan</string> <string name="contact_added_you">Ikaw ay idinagdag ni kontak sa listahan ng kontak</string> @@ -303,9 +291,6 @@ <string name="verified">Napatunayan!</string> <string name="smp_requested">Ang kontak hinihiling ang beripikasyon ng SMP</string> <string name="no_otr_session_found">Walang napatunayang OTR na sesyon ang nakita!</string> - <string name="pref_export_logs">I-export ang kasaysayan</string> - <string name="pref_export_logs_summary">Isulat ang kasaysayan ng pag-uusap sa SD kard</string> - <string name="notification_export_logs_title">Pagsulat ng pinag-usapan sa SD kard</string> <string name="choose_file">Pumili ng hilira</string> <string name="receiving_x_file">Pagtanggap %1$s (%2$d%% nakumpleto)</string> <string name="download_x_file">I-download %s</string> @@ -366,13 +351,8 @@ <string name="ban_from_conference">Ipinagbawal sa grupo ng chat</string> <string name="removing_from_public_conference">Gusto mong burahin %s mula pampublikong grupo ng chat. Ang isang paraan upang gawin ito ay ang pagbawal sa user kailanman.</string> <string name="ban_now">Bawal ngayon</string> - <string name="could_not_change_role">Hindi mabago ang tungkulin ng %s</string> - <string name="public_conference">Malalapitang pampublikong grupo ng chat</string> - <string name="private_conference">Pribadong, kasapi lamang ang grupo ng chat</string> <string name="conference_options">Mga pagpilian sa grupo ng chat</string> - <string name="members_only">Pribadong, kasapi lamang</string> <string name="non_anonymous">Hindi kilala</string> - <string name="moderated">Katamtaman</string> <string name="you_are_not_participating">Hindi ka nakikilahok</string> <string name="modified_conference_options">Binagong pagpilian ng grupo ng chat!</string> <string name="could_not_modify_conference_options">Hindi mabago ang mga pagpipilian sa grupo ng chat</string> @@ -389,7 +369,6 @@ <string name="pdf_document">Kodumentong PDF</string> <string name="apk">Android App</string> <string name="vcard">Pakikipag-ugnayan</string> - <string name="touch_to_open_conversations">Pindotin para mabuksan ang Pix-Art Messenger</string> <string name="avatar_has_been_published">Avatar ay nailathala!</string> <string name="sending_x_file">Nagpapadala %s</string> <string name="offering_x_file">Pag-aalay %s</string> @@ -472,11 +451,7 @@ <item quantity="other">%d mensahe</item> </plurals> <string name="load_more_messages">Mag-load ng higit pang mga mensahe</string> - <string name="shared_file_with_x">Ibinahagi ang file na may %s</string> - <string name="shared_image_with_x">Ibinahagi ang imahe %s</string> <string name="no_storage_permission">Kailangan ng Pix-Art Messenger ng access sa panlabas na imbakan</string> - <string name="shared_images_with_x">Ibinahagi ang mga larawan na may %s</string> - <string name="shared_text_with_x">Ibinahagi ang teksto %s</string> <string name="sync_with_contacts">I-synchronize ang mga kontak</string> <string name="sync_with_contacts_long">Nais ng Pix-Art Messenger na tumugma sa iyong XMPP roster sa iyong mga kontak upang maipakita ang kanilang mga buong pangalan at avatar. \n\nPix-Art Messenger ay magbabasa lamang ng iyong mga contact at itugma ang mga ito nang lokal nang hindi na-upload ang mga ito sa iyong server. hihiling na magbigay ng pahintulot na ma-access ang iyong mga contact.</string> <string name="notify_on_all_messages">I-notify sa lahat ng mensahe</string> @@ -497,19 +472,16 @@ <string name="install_from_unknown_sources_disabled">I-install mula sa hindi kilalang mga pinagkukunan</string> <string name="install_from_unknown_sources_disabled_dialog">Ang iyong aparato ay naka-configure upang hindi payagan ang mga pag-install ng app mula sa hindi kilalang mga mapagkukunan maliban mula sa Google PlayStore. Ito ay magdudulot ng mga problema sa updater sa app.\n\nIkaw ay tatanugin ngayon upang payagan ang mga pag-install ng app mula sa hindi kilalang mga mapagkukunan.</string> <string name="disable">Huwag paganahin</string> - <string name="selection_too_large">Masyadong malaki ang napiling lugar</string> <string name="cancel_update">Kanselahin ang pag-update?</string> <string name="yes">Oo</string> <string name="no">Hindi</string> <string name="no_accounts">(Walang mga aktibong account)</string> <string name="this_field_is_required">Ang larangan na ito ay kinakailangan</string> - <string name="no_participants">Walang kalahok</string> <string name="correct_message">Tamang mensahe</string> <string name="send_corrected_message">Magpadala ng naitama na mensahe</string> <string name="no_keys_just_confirm">Na pinagkakatiwalaan mo ang contact na ito. Sa pamamagitan ng pagpili ng \'tapos na\' pinapatunayan mo lang na ang %s s ay bahagi ng grupo ng chat na ito.</string> <string name="contacts_have_no_pgp_keys">Hindi ma-encrypt ng Pix-Art Messenger ang iyong mga mensahe dahil hindi nagpapahayag ng iyong mga contact ang kanilang mga pampublikong key. \n\n</string> <string name="this_account_is_disabled">Pinigilan mo ang account na ito</string> - <string name="security_error_invalid_file_access">Error sa seguridad: Di-wastong pag-access ng file</string> <string name="no_application_to_share_uri">Walang nahanap na application upang maibahagi ang URI</string> <string name="share_uri_with">Ibahagi ang URI sa…</string> <string name="mgmt_account_reconnect">Kumonekta muli</string> @@ -535,8 +507,7 @@ <string name="show_password">Ipakita ang password</string> <string name="registration_please_wait">Nabigo ang pagpaparehistro: Subukang muli sa ibang pagkakataon</string> <string name="registration_password_too_weak">Nabigo ang pagpaparehistro: Mahina ang password</string> - <string name="create_conference">Lumikha ng grupo ng chat</string> - <string name="conference_subject">Paksa</string> + <string name="create_private_group_chat">Lumikha ng grupo ng chat</string> <string name="choose_participants">Pumili ng mga kalahok</string> <string name="creating_conference">Paglikha ng grupo ng chat…</string> <string name="import_database">Mag-import ng backup</string> @@ -549,14 +520,8 @@ <string name="request_permissions_message">Hinihiling ka ng Pix-Art Messenger na pahintulutan ang ilang mga pahintulot. Mahalaga na payagan mo ang lahat ng mga pahintulot na gamitin ang lahat ng mga tampok ng sugo na ito. Kung tinatanggihan mo ang alinman sa mga pahintulot na ito ay sarhan ang app mismo.</string> <string name="request_permissions_message_again">Tinanggihan mo ang ilan o lahat ng mga pahintulot na kinakailangan para sa Pix-Art Messenger. Gusto mo bang tumalon sa mga setting at payagan ang mga pahintulot na ito? Kung tinanggihan mo ang alinman sa mga pahintulot na ito, tutuparin ng app ang sarili nito.</string> <string name="unable_to_connect_to_keychain">Hindi makakonekta sa OpenKeychain</string> - <string name="send_image">Magpadala ng larawan?</string> <string name="this_device_is_no_longer_in_use">Hindi na ginagamit ang aparatong ito</string> - <string name="import_canceled">Kinansela ang pag-import</string> - <string name="Import_failed">Nabigo ang pag-import ng database, imposible ang pag-import</string> - <string name="Password_wrong">Maling password, subukang muli</string> - <string name="enter_account_password">Pakipasok ang password ng iyong account upang i-import ang iyong backup.</string> <string name="please_wait">Mangyaring maghintay…</string> - <string name="databaseimport_started">I-import ang backup, maaaring tumagal ito ng ilang sandali.</string> <string name="pref_export_plain_text_logs_summary">Paganahin ang isang pag-export ng mga log ng chat bilang mga nababasa na text file ng tao</string> <string name="pref_export_plain_text_logs">I-export ang mga tao na nagbabasa na mga chat log</string> <string name="payment_required">Kinakailangan ang pagbabayad</string> @@ -583,7 +548,6 @@ <string name="data_saver_enabled">Pinagana ang data saver</string> <string name="data_saver_enabled_explained">Pinipigilan ng iyong operating system ang Pix-Art Messenger mula sa pag-access sa Internet kapag nasa background. Upang makatanggap ng mga abiso ng mga bagong mensahe dapat mong pahintulutan ang walang limitasyong access sa Pix-Art Messenger kapag naka-on ang Data saver. \\nPix-Art Messenger ay magsisikap pa rin upang i-save ang data kapag posible.</string> <string name="device_does_not_support_data_saver">Hindi sinusuportahan ng iyong aparato ang hindi pagpapagana ng data saver para sa Pix-Art Messenger.</string> - <string name="navigate">Mag-navigate sa lokasyon</string> <string name="add_to_contact_list">Idagdag sa listahan ng kontak</string> <string name="contact">Kontak</string> <string name="unable_to_start_recording">Hindi nagawang magsimulang mag-record</string> @@ -603,7 +567,6 @@ <string name="verifying_omemo_keys_trusted_source">Susubukan mong i-verify ang mga key ng OMEMO ng %1$s pagkatapos ng pag-click sa isang link. Ligtas na lamang ito kung sinunod mo ang link na ito mula sa pinagkakatiwalaang pinagmulan kung saan maaaring ma-publish lamang ang %2$s sa link na ito.</string> <string name="verify_omemo_keys">I-verify ang mga fingerprint ng OMEMO</string> <string name="reply">Sumagot</string> - <string name="encrypting_message">Pag-encrypt ng mensahe</string> <string name="transcoding_video">Pag-compress ng video</string> <string name="not_fetching_history_retention_period">Hindi nakakakuha ng mga mensahe dahil sa lokal na panahon ng pagpapanatili.</string> <string name="today">Ngayon</string> @@ -668,7 +631,6 @@ <string name="pref_use_bundled_emoji_summary">Gamitin ang pinagsama-samang emoji library sa halip na gamitin ang iyong mga device. Available ang mga pagbabago pagkatapos muling buksan ang app.</string> <string name="invalid_muc_nick">Di-wastong palayaw</string> <string name="title_activity_share_via_account">Ibahagi sa pamamagitan ng account</string> - <string name="private_messages_are_disabled">Hindi pinagana ang mga pribadong mensahe</string> <string name="destroy_muc">Wasakin ang chat ng grupo</string> <string name="conference_unknown_error">Hindi ka na nasa grupong chat na ito</string> <string name="destroy_muc_text">Gusto mo bang sirain ang %s chat ng grupo? Tatanggalin nito ang permanenteng grupo ng chat na ito.</string> diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index fb2483447..5fdd7161a 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Débloquer le domaine</string> <string name="title_activity_manage_accounts">Gestion des comptes</string> <string name="title_activity_settings">Paramètres</string> - <string name="title_activity_conference_details">Détails de la conférence</string> + <string name="conference_details">Détails de la conférence</string> <string name="title_activity_contact_details">Détails du contact</string> <string name="title_activity_sharewith">Partager avec Conversation</string> <string name="title_activity_start_conversation">Démarrer une conversation</string> @@ -72,12 +72,9 @@ <string name="add_contact">Ajouter un contact</string> <string name="send_failed">échec de l’envoi</string> <string name="preparing_image">Préparation de l’image pour envoi</string> - <string name="preparing_images">Préparation des images à l’envoi</string> - <string name="sharing_files_please_wait">Partage des fichiers. Veuillez patienter...</string> <string name="action_clear_history">Vider l’historique</string> <string name="clear_conversation_history">Vider l’historique de la conversation</string> <string name="clear_histor_msg">Voulez-vous supprimer tous les messages de cette conversation? \n\n<b>Attention :</b> Les messages à supprimer ne concernent pas ceux stockés sur d’autres serveurs et appareils mais uniquement ceux sur votre périphérique actuel.</string> - <string name="delete_messages">Supprimer les messages</string> <string name="also_end_conversation">Fermer cette conversation ensuite</string> <string name="choose_presence">Choisir l’appareil</string> <string name="send_unencrypted_message">Envoyer un message en clair</string> @@ -93,7 +90,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger requiert une application tierce nommée <b>OpenKeychain</b> pour chiffrer et déchiffrer les messages.\n\nOpenKeychain est sous licence GPLv3 et est disponible sur F-Droid et Google Play.\n\n<small>(Veuillez redémarrer Pix-Art Messenger après l’installation de l’app)</small></string> <string name="restart">Redémarrer</string> <string name="install">Installer</string> - <string name="openkeychain_not_installed">Veuillez installer OpenKeychain</string> <string name="offering">Proposition…</string> <string name="waiting">Patientez…</string> <string name="no_pgp_key">Aucune clef OpenPGP trouvée.</string> @@ -126,7 +122,6 @@ <string name="receive_presence_updates">Recevoir ses màj de disponibilité</string> <string name="ask_for_presence_updates">Demander les màj de disponibilité</string> <string name="attach_choose_picture">Choisir une image</string> - <string name="attach_take_from_camera">Depuis l’appareil photo</string> <string name="preemptively_grant">Accepter par avance les demandes de publication.</string> <string name="error_not_an_image_file">Le fichier choisi n\'est pas une image</string> <string name="error_compressing_image">Une erreur s’est produite lors de la conversion de l’image</string> @@ -164,11 +159,8 @@ <string name="mgmt_account_delete_confirm_text">Si vous supprimez votre compte ainsi que tout votre historique de conversation sera perdu</string> <string name="attach_record_voice">Enregistrer une voix</string> <string name="account_settings_jabber_id">Identifiant</string> - <string name="account_settings_password">Mot de passe</string> <string name="account_settings_example_jabber_id">nom@exemple.com</string> <string name="password">Mot de passe</string> - <string name="confirm_password">Confirmer le mot de passe</string> - <string name="passwords_do_not_match">Les deux mots de passe ne correspondent pas.</string> <string name="invalid_jid">Cet identifiant n\'est pas valide</string> <string name="error_out_of_memory">Plus de mémoire disponible. L’image est trop volumineuse</string> <string name="add_phone_book_text">Voulez-vous ajouter %s à votre carnet d’adresses ?</string> @@ -209,11 +201,9 @@ <string name="done">Terminé</string> <string name="verify">Vérifier</string> <string name="decrypt">Déchiffrer</string> - <string name="conferences">Conférences</string> <string name="search">Rechercher</string> - <string name="create_contact">Ajouter un contact</string> <string name="enter_contact">Ajouter contact</string> - <string name="join_conference">Rejoindre une conférence</string> + <string name="join_public_channel">Rejoindre une conférence</string> <string name="delete_contact">Supprimer le contact</string> <string name="view_contact_details">Afficher les détails du contact</string> <string name="block_contact">Bloquer le contact</string> @@ -222,12 +212,10 @@ <string name="select">Sélectionner</string> <string name="contact_already_exists">Le contact existe déjà.</string> <string name="join">Rejoindre</string> - <string name="conference_address">Adresse de la conférence</string> - <string name="conference_address_example">groupchat@room.pix-art.de</string> + <string name="channel_full_jid_example">groupchat@room.pix-art.de</string> <string name="save_as_bookmark">Enregistrer comme favori</string> <string name="delete_bookmark">Supprimer le favori</string> <string name="bookmark_already_exists">Le favori existe déjà</string> - <string name="action_edit_subject">Modifier le sujet de la conférence</string> <string name="topic">Sujet</string> <string name="leave">Partir</string> <string name="contact_added_you">Votre correspondant vous a ajouté dans sa liste de contacts</string> @@ -312,9 +300,11 @@ <string name="verified">Vérifié !</string> <string name="smp_requested">Le contact requiert la vérification du SMP</string> <string name="no_otr_session_found">Aucune session OTR valide n\'a été trouvée !</string> - <string name="pref_export_logs">Exporter l’historique</string> - <string name="pref_export_logs_summary">Sauvegarder l’historique des conversations sur la carte SD</string> - <string name="notification_export_logs_title">Sauvegarde des historiques des conversations sur la carte SD</string> + <string name="pref_create_backup">Créer une sauvegarde</string> + <string name="notification_create_backup_title">Création des fichiers de sauvegarde</string> + <string name="restoring_backup">Restauration de la sauvegarde</string> + <string name="notification_restored_backup_title">Votre sauvegarde a été restaurée</string> + <string name="notification_restored_backup_subtitle">N\'oubliez pas d\'activer le compte.</string> <string name="choose_file">Choix du fichier</string> <string name="receiving_x_file">Réception %1$s (%2$d%% complété)</string> <string name="download_x_file">Télécharger %s</string> @@ -376,13 +366,8 @@ <string name="ban_from_conference">Bannir de la conférence</string> <string name="removing_from_public_conference">Vous essayez d’éjecter %s d’une conférence publique. La seule façon de le faire consiste à bannir cet utilisateur définitivement.</string> <string name="ban_now">Bannir maintenant</string> - <string name="could_not_change_role">Impossible de changer le rôle de %s</string> - <string name="public_conference">Conférence accessible publiquement</string> - <string name="private_conference">Privée, accès réservé aux membres de la conférence uniquement</string> <string name="conference_options">Options de la conférence</string> - <string name="members_only">Privé, membres uniquement</string> <string name="non_anonymous">Non anonyme</string> - <string name="moderated">Modéré</string> <string name="you_are_not_participating">Vous ne participez pas</string> <string name="modified_conference_options">Les options de la conférence ont été modifiées !</string> <string name="could_not_modify_conference_options">Impossible de modifier les options de la conférence</string> @@ -399,7 +384,6 @@ <string name="pdf_document">document PDF</string> <string name="apk">Application Android</string> <string name="vcard">Contact</string> - <string name="touch_to_open_conversations">Appuyez pour ouvrir Pix-Art Messenger</string> <string name="avatar_has_been_published">L’avatar a été publié !</string> <string name="sending_x_file">%s en cours d’envoi</string> <string name="offering_x_file">En train de proposer un(e) %s</string> @@ -486,11 +470,7 @@ <item quantity="other">%d messages</item> </plurals> <string name="load_more_messages">Charger plus de messages</string> - <string name="shared_file_with_x">Fichier partagé avec %s</string> - <string name="shared_image_with_x">Image partagée avec %s</string> <string name="no_storage_permission">Pix-Art Messenger a besoin d’accéder au stockage externe</string> - <string name="shared_images_with_x">Image partagée avec %s</string> - <string name="shared_text_with_x">Texte partagé avec %s</string> <string name="sync_with_contacts">Synchroniser avec contacts</string> <string name="sync_with_contacts_long">Pix-Art Messenger souhaite associer vos contacts XMPP avec les contacts de votre appareil, pour utiliser leur nom complet et leur avatar.\n\nPix-Art Messenger va uniquement lire vos contacts et les associer localement, sans les envoyer sur le serveur XMPP.\n\nVotre appareil va maintenant vous demander la permission d’accéder à vos contacts.</string> <string name="notify_on_all_messages">Notifier pour tous les messages</string> @@ -511,19 +491,16 @@ <string name="battery_optimizations_enabled_dialog">Votre appareil applique sur Pix-Art Messenger des optimisations de batterie très strictes qui pourraient provoquer des retards dans les notifications, voire des pertes de messages.\nVous allez maintenant avoir la possibilité de les désactiver.</string> <string name="install_from_unknown_sources_disabled">L’nstallation à partir de sources inconnues a été désactivée</string> <string name="disable">Désactiver</string> - <string name="selection_too_large">La zone sélectionnée est trop grande</string> <string name="cancel_update">Annuler la mise à jour ?</string> <string name="yes">Oui</string> <string name="no">Non</string> <string name="no_accounts">(Aucun compte activé)</string> <string name="this_field_is_required">Ce champ est requis</string> - <string name="no_participants">Aucun participant</string> <string name="correct_message">Corriger le message</string> <string name="send_corrected_message">Envoyer le message corrigé</string> <string name="no_keys_just_confirm">Vous faites déjà confiance à ce contact. En sélectionnant « Terminé » vous confirmerez seulement que %s est membre de cette conférence.</string> <string name="contacts_have_no_pgp_keys">Pix-Art Messenger ne peut pas chiffrer vos messages car vos contacts ne communiquent pas leurs clefs publiques.\n\n</string> <string name="this_account_is_disabled">Vous avez désactivé ce compte</string> - <string name="security_error_invalid_file_access">Erreur de sécurité : accès fichier non-valide</string> <string name="no_application_to_share_uri">Aucune application trouvée pour partager l’URI</string> <string name="share_uri_with">Partager l’URI avec …</string> <string name="mgmt_account_reconnect">Reconnecter</string> @@ -552,8 +529,7 @@ <string name="show_password">Afficher le mot de passe</string> <string name="registration_please_wait">L’enregistrement a échoué : Réessayer plus tard</string> <string name="registration_password_too_weak">Échec de l’enregistrement : le mot de passe n’est pas assez fort</string> - <string name="create_conference">Créer une conférence</string> - <string name="conference_subject">Objet</string> + <string name="create_private_group_chat">Créer une conférence</string> <string name="choose_participants">Choisir les participants</string> <string name="creating_conference">Création de la conférence …</string> <string name="import_text">Votre appareil contient une sauvegarde que vous pouvez importer.\nVotre Messager sera redémarré une fois la sauvegarde restaurée. Désivrez-vous l’importer ? </string> @@ -565,14 +541,8 @@ <string name="pref_broadcast_last_activity_summary">Informer tous vos contacts lorsque vous utitlisez Pix-Art Messenger</string> <string name="invite_user">Inviter à Pix-Art Messenger</string> <string name="unable_to_connect_to_keychain">Connection à OpenKeychain impossible</string> - <string name="send_image">Envoyer l’image ?</string> <string name="this_device_is_no_longer_in_use">Cet appareil n\'est plus utilisé</string> - <string name="import_canceled">Importation annulée</string> - <string name="Import_failed">Échec d’import de la base de données, impossible d’effectuer l’importation</string> - <string name="Password_wrong">Mot de passe erroné, essayez à nouveau</string> - <string name="enter_account_password">Veuillez saisir le mot de passe de votre compte pour pouvoir importer votre sauvegarde.</string> <string name="please_wait">Veuillez patienter …</string> - <string name="databaseimport_started">La sauvegarde sera importée. Ceci peut prendre un peu de temps.</string> <string name="pref_export_plain_text_logs">Exporter les journaux des conversations dans un format lisible</string> <string name="payment_required">Paiement requis</string> <string name="missing_internet_permission">Permission Internet manquante</string> @@ -598,7 +568,6 @@ <string name="data_saver_enabled">Économiseur de données activé</string> <string name="data_saver_enabled_explained">Votre système d’exploitation empêche Pix-Art Messenger d’accéder à Internet lorsque l’application est en arrière plan. Pour recevoir les notifications des nouveaux messages, vous devez autoriser Pix-Art Messenger à accéder au réseau sans restriction quand l’Économiseur de données est activé.\n Pix-Art Messenger tentera de consommer le moins de données possible.</string> <string name="device_does_not_support_data_saver">Votre appareil ne prend pas en charge la désactivation de l’Économiseur de données pour Pix-Art Messenger.</string> - <string name="navigate">Naviguez jusqu’à l’emplacement</string> <string name="add_to_contact_list">Ajouter aux contacts</string> <string name="contact">Contact</string> <string name="unable_to_start_recording">Impossible de démarrer l’enregistrement</string> @@ -625,7 +594,6 @@ <string name="show_inactive_devices">Afficher les inactifs</string> <string name="hide_inactive_devices">Masquer les inactifs</string> <string name="reply">Répondre</string> - <string name="encrypting_message">Chiffrement du message en cours</string> <string name="transcoding_video">Compression de la vidéo en cours</string> <string name="not_fetching_history_retention_period">Aucun message récupéré, en raison de la configuration de la période de rétention.</string> <plurals name="seconds"> @@ -714,7 +682,6 @@ <string name="pref_use_bundled_emoji_summary">Utilise la libraire d’émojis intégrée au lieu de ceux de votre appareil. Les modifications seront prises en considération à la réouverture de l’application.</string> <string name="invalid_muc_nick">Psœudonyme invalide</string> <string name="title_activity_share_via_account">Partager via le compte</string> - <string name="private_messages_are_disabled">Les messages privés ont étés désactivés</string> <string name="destroy_muc">Supprimer complètement la conférence</string> <string name="conference_unknown_error">Vous ne faîtes plus partie de cette conférence</string> <string name="destroy_muc_text">Désirez-vous supprimer complètement la conférence %s ? Ceci supprimera la conférence pour de bon.</string> @@ -723,7 +690,7 @@ <string name="snooze">Silencer</string> <string name="huawei_protected_apps">Applications protégées</string> <string name="pref_enable_multi_accounts_title">Activer les comptes multiples</string> - <string name="pref_enable_multi_accounts_summary">Si vous désirez utiliser plusieurs comptes, vous devez spécifier un mot de passe pour les sauvegardes quotidiennes.</string> + <string name="pref_enable_multi_accounts_summary">Désirez-vous activer l\'utilisation du multi-comptes ?</string> <string name="mtm_accept_cert">Accepter ce certificat inconnu ?</string> <string name="mtm_trust_anchor">Le certificat serveur n’est signé par une Autorité de Certification connue.</string> <string name="mtm_accept_servername">Accepter même non-concordant avec le nom du serveur ?</string> @@ -796,7 +763,6 @@ <string name="pref_show_record_voice_btn_summary">Affiche le bouton d’enregisrement de la voix dans les conversations en tant que bouton d’action rapide</string> <string name="pref_show_record_voice_btn">Afficher le bouton d’enregisrement de la voix</string> <string name="pref_quick_share_attachment_choice">Choix des fichiers attachés</string> - <string name="phone_book">Carnet d’adresses</string> <string name="unable_to_save_recording">Impossible de sauvegarder l’enregistrement</string> <string name="foreground_service_channel_name">Service au premier plan</string> <string name="error_channel_name">Problèmes de connexion</string> @@ -805,14 +771,13 @@ <string name="pref_more_notification_settings">Paramètres des notifications</string> <string name="pref_more_notification_settings_summary">Importance, Sonnerie, Vibreur</string> <string name="video_compression_channel_name">Compression vidéo</string> - <string name="backup_channel_name">Sauvegarde de base de données</string> + <string name="backup_channel_name">Sauvegarde et restauration</string> <string name="app_update_channel_name">Mise à jour de l’application</string> <string name="action_group_details">Détails du groupe</string> <string name="view_media">Afficher le média</string> <string name="media_browser">Gestionnaire de médias</string> <string name="account_status_stream_opening_error">Erreur lors de l’ouverture du flux</string> <string name="action_open">Ouvrir</string> - <string name="action_delete">Supprimer</string> <string name="security_violation_not_attaching_file">Fichier écarté pour des raisons de violations de la sécurité.</string> <string name="delete_file_dialog">Supprimer le fichier</string> <string name="delete_file_dialog_msg">Voulez-vous vraiment supprimer ce fichier ? \n\n<b>Attention :</b> Ceci ne procédera pas à la suppression des copies stockées sur d’autres serveurs ou appareils.</string> @@ -841,4 +806,17 @@ <string name="autojoin_groupchat">Rejoindre automatiquement cette conférence</string> <string name="pref_play_gif_inside">Animer les fichiers GIF dans la discussion</string> <string name="pref_play_gif_inside_summary">L’activation de cette option permet d’animer les images GIF publiées dans la fenêtre de discussion.</string> + <string name="open_with">Ouvrir avec …</string> + <string name="choose_account">Sélectionnez un compte</string> + <string name="set_profile_picture">Image du profil Pix-Art Messenger</string> + <string name="restore_backup">Restaurer une sauvegarde</string> + <string name="restore">Restaurer</string> + <string name="enter_password_to_restore">Entrez votre mot de passe pour le compte %s afin de procéder à la restauration de la sauvegarde.</string> + <string name="unable_to_restore_backup">Impossible de restaurer la sauvegarde.</string> + <string name="unable_to_decrypt_backup">Impossible de déchiffrer la sauvegarde. Vérifiez que le mot de passe est correct.</string> + <string name="pref_prefer_xmpp_avatar_summary">Préférer l\'avatar XMPP des utilisateur·rice·s au lieu de celui figurant sur votre carnet d\'adresses</string> + <string name="pref_prefer_xmpp_avatar">Préférer l\'avatar XMPP</string> + <string name="view_users">Afficher les membres</string> + <string name="group_chat_members">Grouper les membres de la discussion</string> + <string name="message_deleted">Le message a été supprimé</string> </resources> diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index 1101b6a81..650dd48d9 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -68,11 +68,8 @@ <string name="add_contact">Engadir contacto</string> <string name="send_failed">Erro ao enviar</string> <string name="preparing_image">Preparando imaxe para enviar</string> - <string name="preparing_images">Preparando as imaxes para transmitilas</string> - <string name="sharing_files_please_wait">Compartindo ficheiros. Por favor agarde...</string> <string name="action_clear_history">Limpar historial</string> <string name="clear_conversation_history">Limpar historial de conversa</string> - <string name="delete_messages">Borrar mensaxes</string> <string name="also_end_conversation">Finalizar esta conversación tras o proceso</string> <string name="choose_presence">Escoller dispositivo</string> <string name="send_unencrypted_message">Enviar mensaxe non cifrada</string> @@ -88,7 +85,6 @@ <string name="openkeychain_required_long">Conversations emprega unha aplicación de terceiros chamada <b>OpenKeychain</b> para cifrar e descifrar mensaxes e xestionar as túas claves públicas.\n\nOpenKeychain está publicado baixo licencia GPLv3 e disponible en F-Droid e Google Play.\n\n<small>(Por favor, reinicie Conversations despois.)</small></string> <string name="restart">Reiniciar</string> <string name="install">Instalar</string> - <string name="openkeychain_not_installed">Por favor instale OpenKeychain</string> <string name="offering">ofrecendo…</string> <string name="waiting">agardando...</string> <string name="no_pgp_key">Clave OpenPGP non atopada</string> @@ -150,7 +146,6 @@ <string name="mgmt_account_delete_confirm_text">Si elimina a conta perderá todo o historial de conversas</string> <string name="attach_record_voice">Grabar audio</string> <string name="account_settings_jabber_id">ID Jabber</string> - <string name="account_settings_password">Contrasinal</string> <string name="account_settings_example_jabber_id">usuaria@exemplo.com</string> <string name="password">Contrasinal</string> <string name="invalid_jid">O identificador non é un identificador de Jabber válido</string> @@ -191,11 +186,9 @@ <string name="done">Feito</string> <string name="verify">Verificar</string> <string name="decrypt">Descifrar</string> - <string name="conferences">Conversas en grupo</string> <string name="search">Buscar</string> - <string name="create_contact">Crear unha conta</string> <string name="enter_contact">Introducir contacto</string> - <string name="join_conference">Entrar nunha conversa en grupo</string> + <string name="join_public_channel">Entrar nunha conversa en grupo</string> <string name="delete_contact">Eliminar contacto</string> <string name="view_contact_details">Ver os detalles do contacto</string> <string name="block_contact">Bloquear contacto</string> @@ -204,12 +197,10 @@ <string name="select">Selecionar</string> <string name="contact_already_exists">Xa existe o contacto</string> <string name="join">Unirse</string> - <string name="conference_address">Enderezo da conversa en grupo</string> - <string name="conference_address_example">sala@xuntanza.exemplo.com/nick</string> + <string name="channel_full_jid_example">sala@xuntanza.exemplo.com/nick</string> <string name="save_as_bookmark">Gardar como marcador</string> <string name="delete_bookmark">Eliminar marcador</string> <string name="bookmark_already_exists">Este marcador xa existe</string> - <string name="action_edit_subject">Editar o tema da conversa en grupo</string> <string name="joining_conference">Entrando na conversa en grupo</string> <string name="leave">Saír</string> <string name="contact_added_you">Contacto engadido a súa lista de contactos</string> @@ -287,9 +278,6 @@ <string name="verified">Comprobado!</string> <string name="smp_requested">O contacto solicitou a comprobación SMP</string> <string name="no_otr_session_found">Non se atopou unha sesión OTR válida!</string> - <string name="pref_export_logs">Exportar historial</string> - <string name="pref_export_logs_summary">Escribir os rexistros históricos de conversas a tarxeta SD</string> - <string name="notification_export_logs_title">Escribindo os rexistros na tarxeta SD</string> <string name="choose_file">Escoller ficheiro</string> <string name="receiving_x_file">Recibindo %1$s (%2$d %% completado)</string> <string name="download_x_file">Descargar %s</string> @@ -350,13 +338,8 @@ <string name="ban_from_conference">Prohibición da conversa en grupo</string> <string name="removing_from_public_conference">Está intentando eliminar a %s dunha conversa en grupo pública. O único xeito de facelo é prohibir ese usuario para sempre.</string> <string name="ban_now">Rexeitar agora</string> - <string name="could_not_change_role">Non se puido mudar o rol de %s</string> - <string name="public_conference">Conversa en grupo de acceso público</string> - <string name="private_conference">Conversa en grupo privada, só para membros</string> <string name="conference_options">Opcións da conversa en grupo</string> - <string name="members_only">Privada, só para membros</string> <string name="non_anonymous">Non anónima</string> - <string name="moderated">Moderada</string> <string name="you_are_not_participating">Vostede non está a participar</string> <string name="modified_conference_options">¡Opcións da conversa en grupo modificadas!</string> <string name="could_not_modify_conference_options">Non se puideron modificar as opcións da conversa en grupo</string> @@ -373,7 +356,6 @@ <string name="pdf_document">documento PDF</string> <string name="apk">App Android</string> <string name="vcard">Contacto</string> - <string name="touch_to_open_conversations">Toque para abrir Conversations</string> <string name="avatar_has_been_published">Publicouse o avatar</string> <string name="sending_x_file">Enviando %s</string> <string name="offering_x_file">Ofrecendo %s</string> @@ -446,11 +428,7 @@ <item quantity="other">%d mensaxes</item> </plurals> <string name="load_more_messages">Cargar máis mensaxes</string> - <string name="shared_file_with_x">Ficheiro compartido con %s</string> - <string name="shared_image_with_x">Imaxe compartida con %s</string> <string name="no_storage_permission">Conversations precisa acceso ao almacenamento externo</string> - <string name="shared_images_with_x">Imaxes compartidas con %s</string> - <string name="shared_text_with_x">Texto compartido con %s</string> <string name="sync_with_contacts">Sincronice con todos os contactos</string> <string name="sync_with_contacts_long">Conversations quere comparar a súa lista XMPP cos seus contactos para mostrar os seus nomes completos e avatares.\n\nConversations só fará esta comprobación de xeito local sen subilos ao servidor.\n\nAgora pediráselle conceder permiso para acceder aos seus contactos.</string> <string name="notify_on_all_messages">Notificar todas as mensaxes</string> @@ -464,7 +442,6 @@ <string name="battery_optimizations_enabled_explained">O seu dispositivo está a realizar algunhas optimizacións de batería extremas en Conversations que poderían levar a notificacións con retraso o incluso perda dos mesmos.\nÉ recomendado deshabilitadas.</string> <string name="battery_optimizations_enabled_dialog">O seu dispositivo está facendo unhas optimizacións de batería moi fortes en Conversations que poderían facer que se retrasasen algunhas mensaxes ou incluso se perdesen.\n\nAgora pediráselle que se deshabiliten as mismas.</string> <string name="disable">Deshabilitar</string> - <string name="selection_too_large">O área selecionada é demasiado grande</string> <string name="no_accounts">(Contas non activadas)</string> <string name="this_field_is_required">Este campo é requerido</string> <string name="correct_message">Correxir mensaxe</string> @@ -472,7 +449,6 @@ <string name="no_keys_just_confirm">Xa confia neste contacto. Ao seleccionar \'feito\' só está confirmando que %s é parte deste chat grupal.</string> <string name="contacts_have_no_pgp_keys">Conversations non pode cifrar a súa mensaxe porque os seus contactos no publicaron a súa chave pública.\n\n <small>Por favor solicite aos seus contactos que configuren OpenPGP.</small></string> <string name="this_account_is_disabled">Deshabilitou esta conta</string> - <string name="security_error_invalid_file_access">Fallo na seguridade: Acceso non válido ao ficheiro</string> <string name="no_application_to_share_uri">Non se atopou un aplicativo para compartir URI</string> <string name="share_uri_with">Compartir URI con...</string> <string name="welcome_text">XMPP é un protocolo independente do proveedor. Pode utilizar este cliente con calquer servidor XMPP que escolla.\nPara a súa conveniencia facilitamos a creación de unha conta en conversations.im; un proveedor especialmente adaptado ao uso con Conversations.</string> @@ -491,8 +467,7 @@ <string name="show_password">Mostar contrasinal</string> <string name="registration_please_wait">Fallo no rexistro: inténteo de novo</string> <string name="registration_password_too_weak">Fallo no rexistro: contrasinal moi feble</string> - <string name="create_conference">Crear conversa en grupo</string> - <string name="conference_subject">Asunto</string> + <string name="create_private_group_chat">Crear conversa en grupo</string> <string name="choose_participants">Escoller participantes</string> <string name="creating_conference">Creando unha conversa en grupo...</string> <string name="invite_again">Invitar de novo</string> @@ -537,7 +512,6 @@ <string name="i_followed_this_link_from_a_trusted_source">Seguín esta ligazón desde unha fonte de confianza</string> <string name="verifying_omemo_keys_trusted_source">Vai verificar as chaves OMEMO de %1$s despois de pulsar na ligazón. Esto só é seguro si sigueu esta ligazón desde unha fonte de confianza onde só %2$s a podería ter publicado.</string> <string name="verify_omemo_keys">Validar chaves OMEMO</string> - <string name="encrypting_message">Cifrando mensaxe</string> <string name="transcoding_video">Comprimindo vídeo</string> <string name="not_fetching_history_retention_period">Sen obtención de mensaxes debido ao período de retención local.</string> <string name="today">Hoxe</string> diff --git a/src/main/res/values-h360dp/dimens.xml b/src/main/res/values-h360dp/dimens.xml index 5e432c034..bad3dd48a 100644 --- a/src/main/res/values-h360dp/dimens.xml +++ b/src/main/res/values-h360dp/dimens.xml @@ -1,4 +1,3 @@ <resources> <dimen name="publish_avatar_top_margin">16dp</dimen> - <dimen name="publish_avatar_size">128dp</dimen> </resources>
\ No newline at end of file diff --git a/src/main/res/values-h500dp/dimens.xml b/src/main/res/values-h500dp/dimens.xml index deea1124f..09a27ddba 100644 --- a/src/main/res/values-h500dp/dimens.xml +++ b/src/main/res/values-h500dp/dimens.xml @@ -1,4 +1,3 @@ <resources> <dimen name="publish_avatar_top_margin">24dp</dimen> - <dimen name="publish_avatar_size">192dp</dimen> </resources>
\ No newline at end of file diff --git a/src/main/res/values-id/strings.xml b/src/main/res/values-id/strings.xml index 7c0e4190b..96dc68490 100644 --- a/src/main/res/values-id/strings.xml +++ b/src/main/res/values-id/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Buka blokir halaman</string> <string name="title_activity_manage_accounts">Kelola akun</string> <string name="title_activity_settings">Pengaturan</string> - <string name="title_activity_conference_details">Rincian chat kelompok</string> + <string name="conference_details">Rincian chat kelompok</string> <string name="title_activity_contact_details">Rincian kontak</string> <string name="title_activity_sharewith">Berbagi dengan percakapan</string> <string name="title_activity_start_conversation">Mulai percakapan</string> @@ -70,11 +70,8 @@ <string name="add_contact">Tambah kontak</string> <string name="send_failed">pengiriman gagal</string> <string name="preparing_image">Menyiapkan gambar untuk pengiriman</string> - <string name="preparing_images">Menyiapkan banyak gambar untuk pengiriman</string> - <string name="sharing_files_please_wait">Berbagi berkas. Mohon tunggu…</string> <string name="action_clear_history">Hapus riwayat</string> <string name="clear_conversation_history">Hapus Riwayat Percakapan</string> - <string name="delete_messages">Hapus pesan</string> <string name="also_end_conversation">Akhiri pembicaraan setelahnya</string> <string name="choose_presence">Pilih perangkat</string> <string name="send_unencrypted_message">Kirim pesan tidak terenkripsi</string> @@ -90,7 +87,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger menggunakan aplikasi pihak ketiga yang disebut <b>OpenKeychain</b> untuk mengenkripsi dan mendekripsi pesan dan mengelola kunci publik anda.\n\nOpenKeychain memiliki lisensi di bawah GPLv3 dan tersedia di F-Droid dan Google Play.\n\n<small>(Silahkan muat ulang Pix-Art Messenger setelahnya.)</small></string> <string name="restart">Muat ulang</string> <string name="install">Pasang</string> - <string name="openkeychain_not_installed">Silahkan pasang OpenKeychain</string> <string name="offering">penawaran…</string> <string name="waiting">menunggu…</string> <string name="no_pgp_key">Tidak ada kunci OpenPGP yang ditemukan</string> @@ -124,7 +120,6 @@ <string name="receive_presence_updates">Terkirim pembaruan terbaru</string> <string name="ask_for_presence_updates">Mintalah pembaruan terbaru</string> <string name="attach_choose_picture">Pilih gambar</string> - <string name="attach_take_from_camera">Mengambil dari kamera</string> <string name="preemptively_grant">Secara beraturan memberikan permintaan berlangganan</string> <string name="error_not_an_image_file">Berkas yang anda pilih bukan gambar</string> <string name="error_compressing_image">Kesalahan saat mengubah berkas gambar</string> @@ -161,11 +156,8 @@ <string name="mgmt_account_delete_confirm_text">Jika anda menghapus akun anda, semua riwayat percakapan anda akan hilang</string> <string name="attach_record_voice">Rekam suara</string> <string name="account_settings_jabber_id">Obrolan-Data diri</string> - <string name="account_settings_password">Kata sandi</string> <string name="account_settings_example_jabber_id">nama pengguna@pix-art.de</string> <string name="password">Kata sandi</string> - <string name="confirm_password">Konfirmasi sandi</string> - <string name="passwords_do_not_match">Kata sandi tidak cocok</string> <string name="invalid_jid">Ini bukan ID Jabber yang sah</string> <string name="error_out_of_memory">Kehabisan ruang penyimpanan. Gambar terlalu besar</string> <string name="add_phone_book_text">Apakah anda ingin menambahkannya%s ke buku alamat anda?</string> @@ -204,11 +196,9 @@ <string name="done">Selesai</string> <string name="verify">Memeriksa</string> <string name="decrypt">Terdeskripsi</string> - <string name="conferences">Obrolan kelompok</string> <string name="search">Pencarian</string> - <string name="create_contact">Buat kontak</string> <string name="enter_contact">Masukkan kontak</string> - <string name="join_conference">Gabung kelompok obrolan</string> + <string name="join_public_channel">Gabung kelompok obrolan</string> <string name="delete_contact">Menghapus kontak</string> <string name="view_contact_details">Lihat rincian kontak</string> <string name="block_contact">Blokir kontak</string> @@ -217,12 +207,10 @@ <string name="select">Pilih</string> <string name="contact_already_exists">Kontak sudah uda</string> <string name="join">Gabung</string> - <string name="conference_address">Alamat obrolan kelompok</string> - <string name="conference_address_example">obrolankelompok@room.pix-art.de</string> + <string name="channel_full_jid_example">obrolankelompok@room.pix-art.de</string> <string name="save_as_bookmark">Simpan sebagai penanda</string> <string name="delete_bookmark">Hapus penanda</string> <string name="bookmark_already_exists">Penanda ini sudah ada</string> - <string name="action_edit_subject">Ubah subjek obrolan kelompok</string> <string name="joining_conference">Bergabung dengan kelompok obrolan…</string> <string name="leave">Meninggalkan</string> <string name="contact_added_you">Menambahkan kontak anda kedaftar kontak</string> @@ -303,9 +291,6 @@ <string name="verified">Diverifikasi!</string> <string name="smp_requested">Kontak meminta verifikasi SMP</string> <string name="no_otr_session_found">Tidak ada sesi OTR yang valid telah ditemukan!</string> - <string name="pref_export_logs">Ekspor riwayat</string> - <string name="pref_export_logs_summary">Tulis catatan riwayat percakapan ke kartu SD</string> - <string name="notification_export_logs_title">Menulis percakapan ke kartu SD</string> <string name="choose_file">Pilih berkas</string> <string name="receiving_x_file">Mengirimkan %1$s (%2$d%% lengkap)</string> <string name="download_x_file">Unduh %s</string> @@ -366,13 +351,8 @@ <string name="ban_from_conference">Larangan dari obrolan berkelompok</string> <string name="removing_from_public_conference">Anda mencoba menghapusnya s %s dari obrolan kelompok terbuka. Satu-satunya cara untuk melakukannya adalah dengan melarang pengguna itu selamanya.</string> <string name="ban_now">Larang sekarang</string> - <string name="could_not_change_role">Tidak dapat mengganti peran %s</string> - <string name="public_conference">Obrolan kelompok yang dapat diakses publik</string> - <string name="private_conference">Tertutup, obrolan kelompok hanya untuk anggota</string> <string name="conference_options">Pilihan obrolan kelompok</string> - <string name="members_only">Tertutup, anggota saja</string> <string name="non_anonymous">Tidak anonim</string> - <string name="moderated">Penengah</string> <string name="you_are_not_participating">Kamu tidak mengambil bagian</string> <string name="modified_conference_options">Mengubah pilihan obrolan kelompok!</string> <string name="could_not_modify_conference_options">Tidak dapat mengubah pilihan obrolan kelompok</string> @@ -389,7 +369,6 @@ <string name="pdf_document">Dokumen PDF</string> <string name="apk">Aplikasi Android</string> <string name="vcard">Kontak</string> - <string name="touch_to_open_conversations">Sentuh untuk buka Pix-Art Messenger</string> <string name="avatar_has_been_published">Avatar telah diterbitkan!</string> <string name="sending_x_file">Mengirimkan %s</string> <string name="offering_x_file">Penawaran %s</string> @@ -470,11 +449,7 @@ <item quantity="other">%d pesan</item> </plurals> <string name="load_more_messages">Muat lebih banyak pesan</string> - <string name="shared_file_with_x">Berbagi berkas dengan %s</string> - <string name="shared_image_with_x">Berbagi gambar dengan %s</string> <string name="no_storage_permission">Pix-Art Messenger membutuhkan akses ke penyimpanan eksternal</string> - <string name="shared_images_with_x">Berbagi gambar dengan %s</string> - <string name="shared_text_with_x">Berbagi teks dengan %s</string> <string name="sync_with_contacts">Sesuaikan dengan kontak</string> <string name="sync_with_contacts_long">Pix-Art Messenger ingin mencocokkan daftar XMPP anda dengan kontak anda untuk menunjukkan nama lengkap dan avatar mereka.\n\nPix-Art Messenger hanya akan membaca kontak anda dan mencocokkannya secara lokal tanpa mengunggahnya ke server anda.\n\nAnda sekarang diminta izin untuk mengakses kontak anda.</string> <string name="notify_on_all_messages">Hidupkan pemberitahuan untuk semua pesan</string> @@ -495,19 +470,16 @@ <string name="install_from_unknown_sources_disabled">Memasang dari sumber yang tidak dikenal dimatikan</string> <string name="install_from_unknown_sources_disabled_dialog">Perangkat anda diatur untuk tidak mengizinkan pemasangan aplikasi dari sumber yang tidak dipercaya kecuali dari Google PlayStore. Hal ini akan menyebabkan masalah pada pembaruan aplikasi.\n\nAnda sekarang akan diminta untuk mengizinkan pemasangan aplikasi dari sumber yang tidak dipercaya.</string> <string name="disable">Menonaktifkan</string> - <string name="selection_too_large">Area yang dipilih terlalu besar</string> <string name="cancel_update">Batalkan pembaruan?</string> <string name="yes">Ya</string> <string name="no">Tidak</string> <string name="no_accounts">(Tidak ada akun yang diaktifkan)</string> <string name="this_field_is_required">Bagian ini diperlukan</string> - <string name="no_participants">Tidak ada peserta</string> <string name="correct_message">Memeriksa pesan</string> <string name="send_corrected_message">Kirim pesan yang dikoreksi</string> <string name="no_keys_just_confirm">Anda sudah mempercayai kontak ini. Dengan memilih \'selesai\' anda telah memastikan bahwa %s anda adalah bagian dari obrolan kelompok ini.</string> <string name="contacts_have_no_pgp_keys">Pix-Art Messenger tidak dapat mengenkripsi pesan anda karena kontak tidak menampilkan kunci publik mereka.\n\n</string> <string name="this_account_is_disabled">Anda telah menonaktifkan akun ini</string> - <string name="security_error_invalid_file_access">Kesalahan keamanan: Akses berkas tidak sah</string> <string name="no_application_to_share_uri">Tidak ada aplikasi yang ditemukan untuk berbagi URI</string> <string name="share_uri_with">Bagikan URI dengan…</string> <string name="mgmt_account_reconnect">Sambung kembali</string> @@ -532,8 +504,7 @@ <string name="show_password">Tampilkan kata sandi</string> <string name="registration_please_wait">Pendaftaran gagal: Coba lagi nanti</string> <string name="registration_password_too_weak">Pendaftaran gagal: Kata sandi terlalu lemah</string> - <string name="create_conference">Buat obrolan kelompok</string> - <string name="conference_subject">Perihal</string> + <string name="create_private_group_chat">Buat obrolan kelompok</string> <string name="choose_participants">Pilih peserta</string> <string name="creating_conference">Membuat obrolan kelompok…</string> <string name="import_database">Impor cadangan</string> @@ -546,14 +517,8 @@ <string name="request_permissions_message">Pix-Art Messenger akan meminta Anda mengizinkan beberapa izin. Penting bahwa Anda mengizinkan semua hak akses ini untuk menggunakan semua fitur messenger ini. Jika Anda menolak izin ini, aplikasi akan menutup dengan sendiri.</string> <string name="request_permissions_message_again">Anda telah menolak beberapa atau semua izin yang dibutuhkan untuk Pix-Art Messenger. Apakah Anda ingin beralih ke pengaturan dan mengizinkan izin ini? Jika Anda menolak semua izin ini, aplikasi akan menutup dengan sendiri.</string> <string name="unable_to_connect_to_keychain">Tidak dapat terhubung ke OpenKeychain</string> - <string name="send_image">Kirim gambar?</string> <string name="this_device_is_no_longer_in_use">Perangkat ini tidak lagi digunakan</string> - <string name="import_canceled">Impor dibatalkan</string> - <string name="Import_failed">Gagal memasukkan penyimpanan data, masukan tidak mungkin dilakukan</string> - <string name="Password_wrong">Kata sandi salah, coba lagi</string> - <string name="enter_account_password">Masukkan kata sandi akun Anda untuk mengimpor cadangan Anda.</string> <string name="please_wait">Harap tunggu…</string> - <string name="databaseimport_started">Cadangan akan diimpor, ini mungkin memakan waktu sebentar.</string> <string name="pref_export_plain_text_logs_summary">Aktifkan ekspor catatan obrolan sebagai berkas teks yang mudah dibaca</string> <string name="pref_export_plain_text_logs">Ekspor catatan obrolan yang dapat dibaca</string> <string name="payment_required">Pembayaran dibutuhkan</string> @@ -580,7 +545,6 @@ <string name="data_saver_enabled">Penghemat data diaktifkan</string> <string name="data_saver_enabled_explained">Sistem operasi Anda membatasi Pix-Art Messenger untuk mengakses Internet saat berada di latar belakang. Untuk menerima notifikasi pesan baru, Anda harus mengizinkan akses Pix-Art Messenger tidak terbatas saat hemat data aktif. \\n Pix-Art Messenger masih akan berusaha untuk menyimpan data bila memungkinkan.</string> <string name="device_does_not_support_data_saver">Perangkat Anda tidak mendukung penonaktifan hemat data untuk Pix-Art Messenger.</string> - <string name="navigate">Arahkan ke lokasi</string> <string name="add_to_contact_list">Tambahkan ke daftar kontak</string> <string name="contact">Kontak</string> <string name="unable_to_start_recording">Tidak dapat mulai merekam</string> @@ -599,7 +563,6 @@ <string name="verifying_omemo_keys_trusted_source">Anda akan memverifikasi kunci OMEMO dari %1$s setelah mengeklik tautan. Ini hanya aman jika Anda mengikuti tautan ini dari sumber terpercaya yang hanya memiliki %2$s yang bisa menerbitkan tautan ini.</string> <string name="verify_omemo_keys">Memeriksa OMEMO dengan sidik jari</string> <string name="reply">Balas</string> - <string name="encrypting_message">Enkripsi pesan</string> <string name="transcoding_video">Pemampatan video</string> <string name="not_fetching_history_retention_period">Tidak mengambil pesan karena periode penyimpanan lokal.</string> <string name="today">Hari ini</string> @@ -664,7 +627,6 @@ <string name="pref_use_bundled_emoji_summary">Gunakan perpustakaan emoji yang terintegrasi daripada menggunakan perangkat Anda. Perubahan akan tersedia setelah membuka kembali aplikasi.</string> <string name="invalid_muc_nick">Nama panggilan tidak sah</string> <string name="title_activity_share_via_account">Bagikan melalui akun</string> - <string name="private_messages_are_disabled">Pesan pribadi dimatikan</string> <string name="destroy_muc">Menghilangkan obrolan kelompok</string> <string name="conference_unknown_error">Anda tidak lagi di obrolan grup ini</string> <string name="destroy_muc_text">Apakah anda ingin menghilangkan obrolan kelompok %s? Tindakan ini akan menghapus obrolan kelompok secara permanen.</string> diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 6116f0d37..ef078ed07 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Sblocca dominio</string> <string name="title_activity_manage_accounts">Gestisci account</string> <string name="title_activity_settings">Impostazioni</string> - <string name="title_activity_conference_details">Dettagli della conversazione di gruppo</string> + <string name="conference_details">Dettagli della conversazione di gruppo</string> <string name="title_activity_contact_details">Dettagli del Contatto</string> <string name="title_activity_sharewith">Condividi con Conversation</string> <string name="title_activity_start_conversation">Inizia una conversazione</string> @@ -72,12 +72,9 @@ <string name="add_contact">Aggiungi contatto</string> <string name="send_failed">Invio fallito</string> <string name="preparing_image">Preparazione immagine per la trasmissione</string> - <string name="preparing_images">Preparazione immagini per la trasmissione</string> - <string name="sharing_files_please_wait">Condivisione file. Attendere prego...</string> <string name="action_clear_history">Pulisci la cronologia</string> <string name="clear_conversation_history">Pulisci la cronologia della conversazione</string> <string name="clear_histor_msg">Sei sicuro di voler cancellare tutti i messaggi di questa conversazione?\n\n<b>Attenzione:</b>Questo non eliminerà le copie di questi messaggi salvati su altri dispositivi o server.</string> - <string name="delete_messages">Elimina messaggi</string> <string name="also_end_conversation">Termina questa conversazione successivamente</string> <string name="choose_presence">Scegli un dispositivo</string> <string name="send_unencrypted_message">Invia messaggio non cifrato</string> @@ -93,7 +90,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger usa una app di terze parti chiamata <b>OpenKeychain</b> per cifrare e decifrare i messaggi per gestire le tue chiavi pubbliche.\n\nOpenKeychain è rilasciato secondo i termini della GPLv3 ed è disponibile sia su F-Droid, che su Google Play.\n\n<small>(Riavvia Pix-Art Messenger in seguito.)</small></string> <string name="restart">Riavvia</string> <string name="install">Installa</string> - <string name="openkeychain_not_installed">Per favore installa OpenKeychain</string> <string name="offering">offrendo…</string> <string name="waiting">in attesa…</string> <string name="no_pgp_key">Nessuna chiave OpenPGP trovata</string> @@ -127,7 +123,6 @@ <string name="receive_presence_updates">Ricevi aggiornamenti della presenza</string> <string name="ask_for_presence_updates">Chiedi aggiornamenti della presenza</string> <string name="attach_choose_picture">Scegli un’immagine</string> - <string name="attach_take_from_camera">Scatta dalla fotocamera</string> <string name="preemptively_grant">Concedi aggiornamenti della presenza preventivamente</string> <string name="error_not_an_image_file">Il file selezionato non è un’immagine</string> <string name="error_compressing_image">Errore durante la conversione dell’immagine</string> @@ -165,11 +160,8 @@ <string name="mgmt_account_delete_confirm_text">Se elimini il tuo account perderai tutta la cronologia delle conversazioni</string> <string name="attach_record_voice">Registra la voce</string> <string name="account_settings_jabber_id">ID Jabber</string> - <string name="account_settings_password">Password</string> <string name="account_settings_example_jabber_id">utente@esempio.com</string> <string name="password">Password</string> - <string name="confirm_password">Conferma password</string> - <string name="passwords_do_not_match">Le password non corrispondono</string> <string name="invalid_jid">Questo non è un ID Jabber valido</string> <string name="error_out_of_memory">Memoria esaurita. L’immagine è tropppo grande</string> <string name="add_phone_book_text">Vuoi aggiungere %s alla tua rubrica?</string> @@ -210,11 +202,9 @@ <string name="done">Fatto</string> <string name="verify">Verifica</string> <string name="decrypt">Decripta</string> - <string name="conferences">Conversazioni di gruppo</string> <string name="search">Cerca</string> - <string name="create_contact">Crea contatto</string> <string name="enter_contact">Inserisci contatto</string> - <string name="join_conference">Unisciti alla conversazione di gruppo</string> + <string name="join_public_channel">Unisciti alla conversazione di gruppo</string> <string name="delete_contact">Elimina contatto</string> <string name="view_contact_details">Mostra dettagli contatto</string> <string name="block_contact">Blocca contatto</string> @@ -223,12 +213,10 @@ <string name="select">Seleziona</string> <string name="contact_already_exists">Il contatto esiste già</string> <string name="join">Entra</string> - <string name="conference_address">Indirizzo della conversazione di gruppo</string> - <string name="conference_address_example">stanza@conferenza.esempio.com/nick</string> + <string name="channel_full_jid_example">stanza@conferenza.esempio.com/nick</string> <string name="save_as_bookmark">Salva come segnalibro</string> <string name="delete_bookmark">Elimina segnalibro</string> <string name="bookmark_already_exists">Questo segnalibro esiste già</string> - <string name="action_edit_subject">Modifica titolo conversazione di gruppo</string> <string name="topic">Argomento</string> <string name="joining_conference">Ingresso nella conversazione di gruppo…</string> <string name="leave">Abbandona</string> @@ -314,9 +302,12 @@ <string name="verified">Verificato!</string> <string name="smp_requested">Il contatto ha richiesto la verifica SMP</string> <string name="no_otr_session_found">Non è stata trovata nessuna sessione OTR valida!</string> - <string name="pref_export_logs">Esporta la cronologia</string> - <string name="pref_export_logs_summary">Scrivi la cronologia delle conversazioni nella scheda SD</string> - <string name="notification_export_logs_title">Scrittura log su scheda SD</string> + <string name="pref_create_backup">Crea backup</string> + <string name="pref_create_backup_summary">Scrivi file di backup su %s</string> + <string name="notification_create_backup_title">Creazione dei file di backup in corso</string> + <string name="restoring_backup">Ripristino del backup in corso</string> + <string name="notification_restored_backup_title">Il tuo backup è stato ripristinato</string> + <string name="notification_restored_backup_subtitle">Non dimenticarti di abilitare l\'account.</string> <string name="choose_file">Scegli un file</string> <string name="receiving_x_file">Ricezione di %1$s file (%2$d%% completato)</string> <string name="download_x_file">Scarica %s</string> @@ -378,13 +369,8 @@ <string name="ban_from_conference">Bandisci dalla conversazione di gruppo</string> <string name="removing_from_public_conference">Stai cercando di rimuovere %s da una conversazione di gruppo pubblica. L\'unico modo per farlo è bandire per sempre quell\'utente.</string> <string name="ban_now">Bandisci</string> - <string name="could_not_change_role">Impossibile cambiare ruolo di %s</string> - <string name="public_conference">Conversazione di gruppo pubblicamente accessibile</string> - <string name="private_conference">Conversazione di gruppo privata, solo per membri</string> <string name="conference_options">Opzioni conversazione di gruppo</string> - <string name="members_only">Privato, solo membri</string> <string name="non_anonymous">Non anonimo</string> - <string name="moderated">Moderata</string> <string name="you_are_not_participating">Non stai partecipando</string> <string name="modified_conference_options">Opzioni della conversazione di gruppo modificate!</string> <string name="could_not_modify_conference_options">Impossibile modificare le opzioni della conversazione di gruppo</string> @@ -401,7 +387,6 @@ <string name="pdf_document">Documento PDF</string> <string name="apk">Applicazione Android</string> <string name="vcard">Contatto</string> - <string name="touch_to_open_conversations">Tocca per avviare Pix-Art Messenger</string> <string name="avatar_has_been_published">Il tuo avatar è stato pubblicato!</string> <string name="sending_x_file">Invio %s</string> <string name="offering_x_file">Offrendo %s</string> @@ -488,11 +473,7 @@ <item quantity="other">%d messaggi</item> </plurals> <string name="load_more_messages">Carica altri messaggi</string> - <string name="shared_file_with_x">Condividi file con %s</string> - <string name="shared_image_with_x">Immagine condivisa con %s</string> <string name="no_storage_permission">Pix-Art Messenger necessita di accesso alla memoria esterna</string> - <string name="shared_images_with_x">Immagini condivise con %s</string> - <string name="shared_text_with_x">Testo condiviso con %s</string> <string name="sync_with_contacts">Sincronizza con i contatti</string> <string name="sync_with_contacts_long">Pix-Art Messenger vuole confrontare il tuo roster XMPP con i tuoi contatti per mostrare i loro nomi ed avatar.\n\nPix-Art Messenger leggerà solamente i contatti e li confronterà localmente senza caricarli sul server.\n\nTi verrà ora chiesto il permesso di accedere ai tuoi contatti.</string> <string name="notify_on_all_messages">Notifica per tutti i messaggi</string> @@ -514,19 +495,16 @@ <string name="install_from_unknown_sources_disabled">L\'installazione da sorgenti sconosciute è disabilitata</string> <string name="install_from_unknown_sources_disabled_dialog">Il tuo dispositivo è configurato per non consentire installazioni da origini sconosciute ad eccezione di Google PlayStorr. Questo può causare problemi con il nostro servizio di aggiornamento.\n\nOra ti sarà chiesto di consentire l\'installazione da origini sconosciute.</string> <string name="disable">Disabilita</string> - <string name="selection_too_large">L\'area selezionata è troppo grande</string> <string name="cancel_update">Annullare aggiornamento?</string> <string name="yes">Si</string> <string name="no">No</string> <string name="no_accounts">(Nessun account attivo)</string> <string name="this_field_is_required">Questo campo è obbligatorio</string> - <string name="no_participants">Nessun partecipante</string> <string name="correct_message">Correggi messaggio</string> <string name="send_corrected_message">Invia messaggio corretto</string> <string name="no_keys_just_confirm">Ti fidi già di questo contatto. Selezionando \'fatto\' stai solo confermando che %s fa parte di questa conversazione di gruppo.</string> <string name="contacts_have_no_pgp_keys">Pix-Art Messenger non è in grado di criptare i tuoi messaggi perchè i tuoi contatti non stanno annunciando le loro chiavi pubbliche.\n\n</string> <string name="this_account_is_disabled">Hai disabilitato questo account</string> - <string name="security_error_invalid_file_access">Errore di sicurezza: accesso file non valido</string> <string name="no_application_to_share_uri">Nessuna applicazione trovata per condividere l\'URI</string> <string name="share_uri_with">Condividi l\'URI con...</string> <string name="mgmt_account_reconnect">Riconnetti</string> @@ -555,8 +533,7 @@ <string name="show_password">Mostra la password</string> <string name="registration_please_wait">Registrazione fallita: riprova più tardi</string> <string name="registration_password_too_weak">Registrazione fallita: password troppo debole</string> - <string name="create_conference">Crea conversazione di gruppo</string> - <string name="conference_subject">Soggetto</string> + <string name="create_private_group_chat">Crea conversazione di gruppo</string> <string name="choose_participants">Scegli i partecipanti</string> <string name="creating_conference">Creazione conversazione di gruppo…</string> <string name="import_text">C\'è un backup sul tuo dispositivo che può essere importato.\nIl tuo Messenger sarà riavviato durante il processo di backup. Vorresti importare il backup? </string> @@ -570,14 +547,8 @@ <string name="request_permissions_message">Pix-Art Messenger ti chiederà di concedere solo poche autorizzazioni. È importante che tu permetta tutte queste richieste per utilizzare tutte le funzionalità di questa app. Se neghi alcune di queste l\'app si chiuderà da sola.</string> <string name="request_permissions_message_again">Hai negato alcune o tutte le autorizzazioni richieste da Pix-Art Messenger. Vuoi andare nelle impostazioni e concedere questi permessi? Se neghi qualsiasi di queste autorizzazioni, l\'app si chiuderà da sola.</string> <string name="unable_to_connect_to_keychain">Impossibile connettersi a OpenKeychain</string> - <string name="send_image">Inviare immagine?</string> <string name="this_device_is_no_longer_in_use">Questo dispositivo non è più in uso</string> - <string name="import_canceled">Importazione annullata</string> - <string name="Import_failed">Importazione del database fallita, un\'importazione non è possibile</string> - <string name="Password_wrong">Password non corretta, prova di nuovo</string> - <string name="enter_account_password">Per favore inserisci la password del tuo account per importare il tuo backup.</string> <string name="please_wait">Attendere prego…</string> - <string name="databaseimport_started">Il backup sarà importato, potrebbe volerci un po\'.</string> <string name="pref_export_plain_text_logs_summary">Abilita l\'esportazione della cronologia delle conversazioni come file di testo leggibili</string> <string name="pref_export_plain_text_logs">Esporta cronologia delle conversazioni in formato leggibile</string> <string name="payment_required">Necessario pagamento</string> @@ -604,7 +575,6 @@ <string name="data_saver_enabled">Risparmio dati attivato</string> <string name="data_saver_enabled_explained">Il tuo sistema operativo sta limitando l\'accesso internet a Pix-Art Messenger quando è in background. Per ricevere le notifiche di nuovi messaggi dovresti consentire l\'accesso senza limiti a Pix-Art Messenger quando il Risparmio dati è attivo.\nPix-Art Messenger cercherà comunque di risparmiare dati quando possibile.</string> <string name="device_does_not_support_data_saver">Il tuo dispositivo non supporta la disattivazione del Risparmio dati per Pix-Art Messenger.</string> - <string name="navigate">Naviga fino alla posizione</string> <string name="add_to_contact_list">Aggiungi alla lista contatti</string> <string name="contact">Contatto</string> <string name="unable_to_start_recording">Impossibile avviare la registrazione</string> @@ -631,7 +601,6 @@ <string name="show_inactive_devices">Mostra inattivi</string> <string name="hide_inactive_devices">Nascondi inattivi</string> <string name="reply">Rispondi</string> - <string name="encrypting_message">Cifratura del messaggio</string> <string name="transcoding_video">Compressione video in corso</string> <string name="not_fetching_history_retention_period">Nessun recupero di messaggi a causa del periodo di conservazione locale.</string> <plurals name="seconds"> @@ -721,7 +690,6 @@ <string name="pref_use_bundled_emoji_summary">Usa la libreria di emoji integrata invece di quella del dispositivo. Il cambiamento sarà disponibile alla riapertura dell\'app.</string> <string name="invalid_muc_nick">Nickname non valido</string> <string name="title_activity_share_via_account">Condividi via account</string> - <string name="private_messages_are_disabled">I messaggi privati sono disabilitati</string> <string name="destroy_muc">Elimina conversazione di gruppo</string> <string name="conference_unknown_error">Non fai più parte di questa conversazione di gruppo</string> <string name="destroy_muc_text">Vuoi veramente eliminare la conversazione di gruppo %s? Questo rimuoverà questa conversazione di gruppo permanentemente.</string> @@ -732,7 +700,7 @@ <string name="huawei_protected_apps">App Protette</string> <string name="huawei_protected_apps_summary">Per continuare a ricevere notifiche, anche quando lo schermo è spento, è necessario inserire Pix-Art Messenger alla lista delle app protette.</string> <string name="pref_enable_multi_accounts_title">Abilita account multipli</string> - <string name="pref_enable_multi_accounts_summary">Se vuoi utilizzare account multipli devi impostare una password per backup giornalieri.</string> + <string name="pref_enable_multi_accounts_summary">Vuoi utilizzare account multipli?</string> <string name="mtm_accept_cert">Accettare Certificato Sconosciuto?</string> <string name="mtm_trust_anchor">Il certificato del server non è firmato da un\'Autorità di Certificazione conosciuta.</string> <string name="mtm_accept_servername">Accettare nome server non corrispondente?</string> @@ -809,7 +777,6 @@ <string name="pref_show_record_voice_btn">Mostra pulsante di registrazione vocale</string> <string name="pref_use_quick_share_attachment_choice_summary">Sostituisci il pulsante di invio con quello della scelta dell\'allegato se non è stato scritto nulla. Altrimenti mostra scelte rapide, che possono essere configurate nelle prossime impostazioni.</string> <string name="pref_quick_share_attachment_choice">Scelta dell\'allegato</string> - <string name="phone_book">Rubrica</string> <string name="unable_to_save_recording">Impossibile salvare la registrazione</string> <string name="foreground_service_channel_name">Servizio in primo piano</string> <string name="foreground_service_channel_description">Questa categoria di notifiche è utilizzata per mostrare una notifica permanente indicando che Pix-Art Messenger è in esecuzione.</string> @@ -823,14 +790,13 @@ <string name="pref_more_notification_settings">Impostazioni delle notifiche</string> <string name="pref_more_notification_settings_summary">Importanza, Suono, Vibrazione</string> <string name="video_compression_channel_name">Compressione video</string> - <string name="backup_channel_name">Backup del database</string> + <string name="backup_channel_name">Backup & Ripristino</string> <string name="app_update_channel_name">Aggiornamento app</string> <string name="action_group_details">Dettagli del gruppo</string> <string name="view_media">Vedi media</string> <string name="media_browser">Visualizzatore media</string> <string name="account_status_stream_opening_error">Errore di apertura dello stream</string> <string name="action_open">Apri</string> - <string name="action_delete">Elimina</string> <string name="security_violation_not_attaching_file">File omesso a causa di una violazione di sicurezza.</string> <string name="delete_file_dialog">Elimina file</string> <string name="delete_file_dialog_msg">Sei sicuro di voler eliminare questo file?\n\n<b>Attenzione:</b> Questo non eliminerà le copie di questo file che sono salvate su altri dispositivi o server.</string> @@ -859,4 +825,18 @@ <string name="autojoin_groupchat">Unisciti automaticamente a questa conversazione di gruppo</string> <string name="pref_play_gif_inside">Riproduci file GIF nelle conversazioni</string> <string name="pref_play_gif_inside_summary">Attivando questa opzione, i file GIF verranno riprodotti direttamente nella conversazione.</string> + <string name="open_with">Apri con…</string> + <string name="choose_account">Scegli account</string> + <string name="set_profile_picture">Immagine di profilo Pix-Art Messenger</string> + <string name="restore_backup">Ripristino backup</string> + <string name="restore">Ripristino</string> + <string name="enter_password_to_restore">Inserisci la password per l\'account %s per ripristinare il backup.</string> + <string name="restore_warning">Non usare il ripristino del backup nel tentativo di clonare (avviare simultaneamente) un\'installazione. Il ripristino del backup è inteso solo per migrazioni o nel caso tu abbia perso il dispositivo originale.</string> + <string name="unable_to_restore_backup">Impossibile ripristinare il backup.</string> + <string name="unable_to_decrypt_backup">Impossibile decriptare il backup. La password è corretta?</string> + <string name="pref_prefer_xmpp_avatar_summary">Preferisci l\'immagine del profilo dell\'utente XMPP invece di quella dalla tua rubrica</string> + <string name="pref_prefer_xmpp_avatar">Preferisci l\'avatar XMPP</string> + <string name="view_users">Vedi membri</string> + <string name="group_chat_members">Membri della conversazione di gruppo</string> + <string name="message_deleted">Il messaggio è stato eliminato</string> </resources> diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 226fc4eb0..db7c4f308 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -68,11 +68,8 @@ <string name="add_contact">Contact toevoegen</string> <string name="send_failed">afleveren mislukt</string> <string name="preparing_image">Bezig met voorbereiden van versturen van afbeelding</string> - <string name="preparing_images">Bezig met voorbereiden van versturen van afbeeldingen</string> - <string name="sharing_files_please_wait">Bestanden delen. Even geduld…</string> <string name="action_clear_history">Geschiedenis wissen</string> <string name="clear_conversation_history">Gespreksgeschiedenis wissen</string> - <string name="delete_messages">Berichten verwijderen</string> <string name="also_end_conversation">Dit gesprek na afloop beëindigen</string> <string name="choose_presence">Apparaat kiezen</string> <string name="send_unencrypted_message">Verstuur onversleuteld bericht</string> @@ -88,7 +85,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger gebruikt een derde partij app genaamd <b>OpenKeychain</b> om berichten te versleutelen en ontsleutelen, en om publieke sleutels te beheren.\n\nOpenKeychain is beschikbaar onder de GPLv3 en beschikbaar op F-Droid en Google Play.\n\n<small>(Herstart Pix-Art Messenger na installatie.)</small></string> <string name="restart">Herstarten</string> <string name="install">Installeren</string> - <string name="openkeychain_not_installed">Gelieve OpenKeychain te installeren</string> <string name="offering">bezig met aanbieden…</string> <string name="waiting">wachten…</string> <string name="no_pgp_key">Geen OpenPGP-sleutel gevonden</string> @@ -150,7 +146,6 @@ <string name="mgmt_account_delete_confirm_text">Als je je account verwijdert, wordt je volledige gespreksgeschiedenis gewist</string> <string name="attach_record_voice">Stem opnemen</string> <string name="account_settings_jabber_id">Jabber-ID:</string> - <string name="account_settings_password">Wachtwoord:</string> <string name="account_settings_example_jabber_id">gebruikersnaam@voorbeeld.nl</string> <string name="password">Wachtwoord</string> <string name="invalid_jid">Dit is geen geldige Jabber-ID</string> @@ -191,11 +186,9 @@ <string name="done">Klaar</string> <string name="verify">Verifiëren</string> <string name="decrypt">Ontsleutelen</string> - <string name="conferences">Groepsgesprekken</string> <string name="search">Zoeken</string> - <string name="create_contact">Contact aanmaken</string> <string name="enter_contact">Contact invoeren</string> - <string name="join_conference">Deelnemen aan groepsgesprek</string> + <string name="join_public_channel">Deelnemen aan groepsgesprek</string> <string name="delete_contact">Contact verwijderen</string> <string name="view_contact_details">Contactgegevens bekijken</string> <string name="block_contact">Contact blokkeren</string> @@ -204,12 +197,10 @@ <string name="select">Selecteren</string> <string name="contact_already_exists">Het contact bestaat al</string> <string name="join">Deelnemen</string> - <string name="conference_address">Gespreksadres</string> - <string name="conference_address_example">ruimte@groepsgesprek.voorbeeld.be/naam</string> + <string name="channel_full_jid_example">ruimte@groepsgesprek.voorbeeld.be/naam</string> <string name="save_as_bookmark">Opslaan als bladwijzer</string> <string name="delete_bookmark">Bladwijzer verwijderen</string> <string name="bookmark_already_exists">Deze bladwijzer bestaat al</string> - <string name="action_edit_subject">Gespreksonderwerp bewerken</string> <string name="joining_conference">Deelnemen aan groepsgesprek…</string> <string name="leave">Verlaten</string> <string name="contact_added_you">Contact heeft je toegevoegd aan zijn/haar contacten</string> @@ -287,9 +278,6 @@ <string name="verified">Geverifieerd!</string> <string name="smp_requested">Contact vraagt SMP-bevestiging</string> <string name="no_otr_session_found">Geen geldige OTR-sessie gevonden!</string> - <string name="pref_export_logs">Geschiedenis exporteren</string> - <string name="pref_export_logs_summary">Gespreksgeschiedenis schrijven naar SD-kaart</string> - <string name="notification_export_logs_title">Logs schrijven naar SD-kaart</string> <string name="choose_file">Bestand kiezen</string> <string name="receiving_x_file">Ontvangen van %1$s (%2$d%% voltooid)</string> <string name="download_x_file">%s downloaden</string> @@ -350,13 +338,8 @@ <string name="ban_from_conference">Verbannen uit groepsgesprek</string> <string name="removing_from_public_conference">Je probeert %s te verwijderen uit een publiek groepsgesprek. De enige manier om dat te doen is door hem/haar voorgoed te verbannen.</string> <string name="ban_now">Nu verbannen</string> - <string name="could_not_change_role">Kon rol van %s niet wijzigen</string> - <string name="public_conference">Openbaar groepsgesprek</string> - <string name="private_conference">Privé groepsgesprek, enkel toegankelijk voor leden</string> <string name="conference_options">Gespreksopties</string> - <string name="members_only">Privé, enkel leden</string> <string name="non_anonymous">Niet anoniem</string> - <string name="moderated">Gemodereerd</string> <string name="you_are_not_participating">Je neemt geen deel</string> <string name="modified_conference_options">Gespreksopties aangepast!</string> <string name="could_not_modify_conference_options">Kon gespreksopties niet aanpassen</string> @@ -373,7 +356,6 @@ <string name="pdf_document">PDF-document</string> <string name="apk">Android-applicatie</string> <string name="vcard">Contact</string> - <string name="touch_to_open_conversations">Tik om Pix-Art Messenger te openen</string> <string name="avatar_has_been_published">Avatar is gepubliceerd!</string> <string name="sending_x_file">Bezig met versturen van %s</string> <string name="offering_x_file">Bezig met aanbieden van %s</string> @@ -446,11 +428,7 @@ <item quantity="other">%d berichten</item> </plurals> <string name="load_more_messages">Laad meer berichten</string> - <string name="shared_file_with_x">Bestand gedeeld met %s</string> - <string name="shared_image_with_x">Afbeelding gedeeld met %s</string> <string name="no_storage_permission">Pix-Art Messenger heeft toegang nodig tot de externe opslag</string> - <string name="shared_images_with_x">Afbeeldingen gedeeld met %s</string> - <string name="shared_text_with_x">Tekst gedeeld met %s</string> <string name="sync_with_contacts">Synchroniseer met contacten</string> <string name="sync_with_contacts_long">Pix-Art Messenger wil je XMPP-contacten met je systeemcontacten vergelijken om hun volledige namen en avatars te tonen.\n\nPix-Art Messenger zal je contacten enkel lokaal lezen en vergelijken zonder ze te uploaden naar je server.\n\nJe zal nu gevraagd worden Pix-Art Messenger toegang te verlenen tot je contacten.</string> <string name="notify_on_all_messages">Melding bij alle berichten</string> @@ -464,14 +442,12 @@ <string name="battery_optimizations_enabled_explained">Je apparaat voert sterke batterij-optimalisaties uit op Pix-Art Messenger, die kunnen leiden tot vertraagde meldingen of zelfs verlies van berichten.\nHet is aangeraden deze optimalisaties uit te schakelen.</string> <string name="battery_optimizations_enabled_dialog">Je apparaat voert sterke batterij-optimalisaties uit op Pix-Art Messenger, die kunnen leiden tot vertraagde meldingen of zelfs verlies van berichten.\n\nJe zal nu gevraagd worden deze optimalisaties uit te schakelen.</string> <string name="disable">Uitschakelen</string> - <string name="selection_too_large">Het gekozen vlak is te groot</string> <string name="no_accounts">(Geen actieve accounts)</string> <string name="this_field_is_required">Dit veld is vereist</string> <string name="correct_message">Bericht corrigeren</string> <string name="send_corrected_message">Gecorrigeerd bericht versturen</string> <string name="no_keys_just_confirm">Je vertrouwt dit contact al. Door \'klaar\' te kiezen bevestig je enkel dat %s deel uitmaakt van dit groepsgesprek.</string> <string name="this_account_is_disabled">Je hebt deze account uitgeschakeld</string> - <string name="security_error_invalid_file_access">Beveiligingsfout: Ongeldige bestandstoegang</string> <string name="no_application_to_share_uri">Geen applicatie om URI te delen</string> <string name="share_uri_with">URI delen met…</string> <string name="your_full_jid_will_be">Je volledige Jabber-ID zal %s zijn</string> @@ -488,8 +464,7 @@ <string name="show_password">Wachtwoord weergeven</string> <string name="registration_please_wait">Registratie mislukt: probeer later opnieuw</string> <string name="registration_password_too_weak">Registratie mislukt: wachtwoord te zwak</string> - <string name="create_conference">Groepsgesprek aanmaken</string> - <string name="conference_subject">Onderwerp</string> + <string name="create_private_group_chat">Groepsgesprek aanmaken</string> <string name="choose_participants">Kies deelnemers</string> <string name="creating_conference">Groepsgesprek aanmaken…</string> <string name="invite_again">Opnieuw uitnodigen</string> @@ -534,7 +509,6 @@ <string name="i_followed_this_link_from_a_trusted_source">Ik heb deze link gevolgd vanuit een betrouwbare bron</string> <string name="verifying_omemo_keys_trusted_source">Je staat op het punt de OMEMO-sleutels van %1$s te verifiëren door op een link te klikken. Dit is enkel veilig als je de link van een betrouwbare bron hebt gevolgd, waarbij enkel %2$s de link gepubliceerd kan hebben.</string> <string name="verify_omemo_keys">OMEMO-sleutels verifiëren</string> - <string name="encrypting_message">Bericht wordt versleuteld</string> <string name="transcoding_video">Video wordt gecomprimeerd</string> <string name="not_fetching_history_retention_period">Berichten worden niet opgehaald wegens lokale bewaarperiode.</string> <string name="today">Vandaag</string> diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 8ac5220b5..2b6008d8b 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -17,9 +17,12 @@ <string name="action_unblock_domain">Odblokuj domenę</string> <string name="title_activity_manage_accounts">Zarządzaj kontami</string> <string name="title_activity_settings">Ustawienia</string> + <string name="conference_details">Szczegóły czatu grupowego</string> + <string name="title_activity_contact_details">Szczegóły kontaktu</string> <string name="title_activity_sharewith">Udostępnij w konwersacji</string> <string name="title_activity_start_conversation">Rozpocznij konwersację</string> <string name="title_activity_choose_contact">Wybierz kontakt</string> + <string name="title_activity_choose_contacts">Wybierz kontakt</string> <string name="title_activity_block_list">Czarna lista</string> <string name="just_now">przed chwilą</string> <string name="minute_ago">minutę temu</string> @@ -40,10 +43,12 @@ <string name="block_domain_text">Zablokować wszystkie kontakty z %s?</string> <string name="unblock_domain_text">Odblokować wszystkie kontakty z %s?</string> <string name="contact_blocked">Kontakt zablokowany</string> + <string name="blocked">Zablokowane</string> <string name="remove_bookmark_text">Czy usunąć zakładkę %s? Konwersacja powiązana z zakładką nie zostanie usunięta.</string> <string name="register_account">Zarejestruj nowe konto na serwerze</string> <string name="change_password_on_server">Zmień hasło na serwerze</string> <string name="share_with">Udostępnij...</string> + <string name="start_conversation">Rozpocznij konwersację</string> <string name="invite_contact">Zaproś kontakt</string> <string name="contacts">Kontakty</string> <string name="cancel">Anuluj</string> @@ -67,11 +72,10 @@ <string name="add_contact">Dodaj kontakt</string> <string name="send_failed">wysyłanie nie powiodło się</string> <string name="preparing_image">Przygotowywanie obrazu do wysłania</string> - <string name="preparing_images">Przygotowywanie obrazów do wysłania</string> - <string name="sharing_files_please_wait">Udostępnianie plików. Proszę czekać...</string> <string name="action_clear_history">Wyczyść historię</string> <string name="clear_conversation_history">Wyczyść historię konwersacji</string> - <string name="delete_messages">Usuń wiadomości</string> + <string name="clear_histor_msg">Czy na pewno usunąć wszystkie wiadomości z wybranej konwersacji?\n\n<b>Uwaga:</b> +Nie spowoduje to usunięcia kopii wiadomości przechowywanych na innych urządzeniach lub serwerach.</string> <string name="also_end_conversation">po czym zakończ tę rozmowę</string> <string name="choose_presence">Wybierz urządzenie</string> <string name="send_unencrypted_message">Wyślij wiadomość bez szyfrowania</string> @@ -87,13 +91,18 @@ <string name="openkeychain_required_long">Pix-Art Messenger używa zewnętrznej aplikacji <b>OpenKeychain</b> do szyfrowania wiadomości i zarządzania kluczami publicznymi.\n\nOpenKeychain rozpowszechniany jest na licencji GPLv3 przez F-Droid lub Google Play.\n\n<small>(Zrestartuj Pix-Art Messenger po instalacji).</small></string> <string name="restart">Zrestartuj</string> <string name="install">Zainstaluj</string> - <string name="openkeychain_not_installed">Proszę zainstalować OpenKeychain</string> <string name="offering">oferowanie...</string> <string name="waiting">oczekiwanie...</string> <string name="no_pgp_key">Nie znaleziono klucza OpenPGP</string> <string name="contact_has_no_pgp_key">Pix-Art Messenger nie może zaszyfrować wiadomości, ponieważ kontakt nie udostępnia klucza publicznego.\n\n<small>Zasugeruj rozmówcy instalację OpenPGP.</small></string> <string name="no_pgp_keys">Nie znaleziono kluczy OpenPGP</string> <string name="pref_general">Główne</string> + <string name="pref_accept_files_wifi">Akceptuj pliki w połączeniach WiFi</string> + <string name="pref_accept_files_summary_wifi">W połączeniu WiFi automatycznie akceptować pliki mniejsze niż…</string> + <string name="pref_accept_files_mobile">Akceptuj pliki w połączeniach mobilnych</string> + <string name="pref_accept_files_summary_mobile">W połączeniu mobilnym automatycznie akceptować pliki mniejsze niż…</string> + <string name="pref_accept_files_mobileroaming">Akceptuj pliki w połączeniu roamingowym</string> + <string name="pref_accept_files_summary_mobileroaming">W połączeniu roamingowym automatycznie akceptować pliki mniejsze niż…</string> <string name="pref_attachments">Załączniki</string> <string name="pref_notification_settings">Powiadomienie</string> <string name="pref_vibrate">Wibracje</string> @@ -102,6 +111,8 @@ <string name="pref_led_summary">Migaj lampką powiadamiającą gdy nadejdzie wiadomość</string> <string name="pref_sound">Dzwonek</string> <string name="pref_sound_summary">Odtwórz dźwięk gdy nadejdzie wiadomość</string> + <string name="pref_send_crash">Wyślij raporty o awariach</string> + <string name="pref_send_crash_summary">Przesyłając raporty o awariach, pomagasz stale ulepszać Pix-Art Messenger</string> <string name="pref_confirm_messages">Potwierdzenia wiadomości</string> <string name="pref_confirm_messages_summary">Zezwól na wysyłanie do osób z twojej listy kontaktów informacji o tym, kiedy otrzymałeś i przeczytałeś wiadomość od nich</string> <string name="pref_ui_options">UI</string> @@ -139,6 +150,7 @@ <string name="encryption_choice_otr">OTR</string> <string name="encryption_choice_pgp">OpenPGP</string> <string name="encryption_choice_omemo">OMEMO</string> + <string name="action_account">Edytuj konto</string> <string name="mgmt_account_delete">Usuń konto</string> <string name="mgmt_account_publish_avatar">Publikuj awatar</string> <string name="mgmt_account_publish_pgp">Udostępnij klucz publiczny OpenPGP</string> @@ -149,7 +161,6 @@ <string name="mgmt_account_delete_confirm_text">Jeśli usuniesz swoje konto, cała historia wiadomości zostanie stracona.</string> <string name="attach_record_voice">Nagraj głos</string> <string name="account_settings_jabber_id">Jabber ID</string> - <string name="account_settings_password">Hasło</string> <string name="account_settings_example_jabber_id">username@example.com</string> <string name="password">Hasło</string> <string name="invalid_jid">Wprowadzono niepoprawny Jabber ID</string> @@ -165,6 +176,8 @@ <string name="server_info_pep">XEP-0163: PEP (Awatary / OMEMO)</string> <string name="server_info_http_upload">XEP-0363: Przesyłanie plików przez HTTP</string> <string name="server_info_push">XEP-0357: Push</string> + <string name="server_info_available">tak</string> + <string name="server_info_unavailable">nie</string> <string name="missing_public_keys">Brak informacji o kluczu publicznym</string> <string name="last_seen_now">widziany chwilę temu</string> <string name="last_seen_min">widziany minutę temu</string> @@ -192,6 +205,8 @@ <string name="decrypt">Odszyfruj</string> <string name="search">Szukaj</string> <string name="enter_contact">Wpisz kontakt</string> + <string name="join_public_channel">Dołącz do czatu grupowego</string> + <string name="delete_contact">Usuń kontakt</string> <string name="view_contact_details">Szczegóły kontaktu</string> <string name="block_contact">Zablokuj kontakt</string> <string name="unblock_contact">Odblokuj kontakt</string> @@ -199,10 +214,12 @@ <string name="select">Wybierz</string> <string name="contact_already_exists">Kontakt już istnieje</string> <string name="join">Dołącz</string> - <string name="conference_address_example">konferencja@konferencje.example.com/nick</string> + <string name="channel_full_jid_example">konferencja@konferencje.example.com/nick</string> <string name="save_as_bookmark">Dodaj jako zakładkę</string> <string name="delete_bookmark">Usuń zakładkę</string> <string name="bookmark_already_exists">Zakładka już istnieje</string> + <string name="topic">Temat</string> + <string name="joining_conference">Dołączenie do czatu grupowego…</string> <string name="leave">Opuść pokój</string> <string name="contact_added_you">Kontakt został dodany do listy</string> <string name="add_back">Dodaj ponownie</string> @@ -224,6 +241,7 @@ <string name="skip">Pomiń</string> <string name="disable_notifications">Wyłącz powiadomienia</string> <string name="enable">Włącz</string> + <string name="conference_requires_password">Czat grupowy wymaga hasła</string> <string name="enter_password">Wprowadź hasło</string> <string name="request_presence_updates">Poproś kontakt o udostępnienie powiadomień o obecności.\n\n<small>Pozwoli to na ustalenie klienta, z którego korzysta rozmówca.</small></string> <string name="request_now">Zażądaj teraz</string> @@ -249,6 +267,10 @@ <string name="pref_expert_options_other">Inne opcje</string> <string name="toast_message_otr_fingerprint">Odcisk klucza OTR został skopiowany do schowka</string> <string name="toast_message_omemo_fingerprint">Odcisk klucza OMEMO został skopiowany do schowka!</string> + <string name="conference_banned">Zostałeś zbanowany z tego czatu grupowego</string> + <string name="conference_members_only">Ten czat jest tylko dla członków grupy</string> + <string name="conference_kicked">Zostałeś wyrzucony z danego czatu grupowego</string> + <string name="conference_shutdown">Czat grupowy został zamknięty</string> <string name="using_account">używając konta %s</string> <string name="checking_x">Sprawdzanie %s na hoście HTTP</string> <string name="not_connected_try_again">Brak połączenia. Spróbuj ponownie później</string> @@ -259,6 +281,8 @@ <string name="send_again">Wyślij ponownie</string> <string name="file_url">URL pliku</string> <string name="url_copied_to_clipboard">URL obrazu został skopiowany do schowka</string> + <string name="scan_qr_code">Zeskanuj kod QR</string> + <string name="show_qr_code">Pokaż kod QR</string> <string name="show_block_list">Wyświetl listę banów</string> <string name="account_details">Szczegóły konta</string> <string name="verify_otr">Weryfikuj OTR</string> @@ -268,15 +292,13 @@ <string name="confirm">Potwierdź</string> <string name="in_progress">W toku</string> <string name="respond">Odpowiedz</string> + <string name="failed">Nie powiodło się</string> <string name="secrets_do_not_match">Sekrety są niezgodne</string> <string name="try_again">Spróbuj ponownie</string> <string name="finish">Zakończ</string> <string name="verified">Weryfikacja udana!</string> <string name="smp_requested">Kontakt zażądał weryfikacji SMP</string> <string name="no_otr_session_found">Brak ważnej sesji OTR!</string> - <string name="pref_export_logs">Wyeksportuj historię rozmów</string> - <string name="pref_export_logs_summary">Zapisz historię rozmów na karcie SD</string> - <string name="notification_export_logs_title">Zapisywanie historii na karcie SD...</string> <string name="choose_file">Wybierz plik</string> <string name="receiving_x_file">Odbieranie %1$s (ukończono %2$d%%)</string> <string name="download_x_file">Pobierz %s</string> @@ -295,6 +317,8 @@ <string name="are_you_sure_verify_fingerprint">Czy na pewno chcesz zweryfikować odcisk klucza OTR kontaktu?</string> <string name="pref_show_dynamic_tags">Etykiety kontaktów</string> <string name="pref_show_dynamic_tags_summary">Wyświetlaj etykiety pod kontaktami</string> + <string name="no_conference_server_found">Nie znaleziono serwera grupowych rozmów</string> + <string name="conference_creation_failed">Utworzenie grupy nie powiodło się!</string> <string name="account_image_description">Awatar konta</string> <string name="copy_otr_clipboard_description">Skopiuj odcisk klucza OTR do schowka</string> <string name="copy_omemo_clipboard_description">Skopiuj odcisk klucza OMEMO do schowka</string> @@ -304,6 +328,7 @@ <string name="distrust_omemo_key">Przestań ufać urządzeniu</string> <string name="distrust_omemo_key_text">Czy jesteś pewien, że chcesz cofnąć weryfikację tego urządzenia?\nUrządzenie to, i wiadomości z niego przychodzące będą oznaczane jako niezaufane.</string> <string name="error_no_keys_to_trust_server_error">Nie ma dostępnych kluczy dla tego kontaktu.\nPobieranie nowych kluczy z serwera nie powiodło się. Być może jest coś nie tak z serwerem kontaktu?</string> + <string name="error_trustkeys_title">Coś poszło nie tak</string> <string name="fetching_history_from_server">Pobieranie historii z serwera</string> <string name="no_more_history_on_server">Koniec historii na serwerze</string> <string name="updating">Aktualizowanie...</string> @@ -330,12 +355,13 @@ <string name="remove_membership">Cofnij członkostwo</string> <string name="grant_admin_privileges">Przyznaj uprawnienia administratora</string> <string name="remove_admin_privileges">Odbierz uprawnienia administratora</string> + <string name="remove_from_room">Usuń z czatu grupowego</string> <string name="could_not_change_affiliation">Nie udało się zmienić stanowiska dla %s</string> + <string name="ban_from_conference">Wyklucz z czatu grupowego</string> + <string name="removing_from_public_conference">Próbujesz usunąć %s z publicznego czatu grupowego. Możliwe jest tylko zablokowanie tego użytkownika na zawsze.</string> <string name="ban_now">Zbanuj teraz</string> - <string name="could_not_change_role">Nie udało się zmienić funkcji %s</string> - <string name="members_only">Prywatne, tylko dla członków.</string> + <string name="conference_options">Opcje czatu grupowego</string> <string name="non_anonymous">Nieanonimowa</string> - <string name="moderated">Moderowany</string> <string name="you_are_not_participating">Nie bierzesz udziału</string> <string name="never">Nigdy</string> <string name="until_further_notice">Ręcznie</string> @@ -350,11 +376,11 @@ <string name="pdf_document">Dokument PDF</string> <string name="apk">Aplikacja Androida</string> <string name="vcard">Kontakt</string> - <string name="touch_to_open_conversations">Dotknij, aby otworzyć Pix-Art Messenger</string> <string name="avatar_has_been_published">Avatar został pomyślnie opublikowany!</string> <string name="sending_x_file">Wysyłanie %s</string> <string name="offering_x_file">Oferowanie %s</string> <string name="hide_offline">Ukryj niedostępnych</string> + <string name="is_typing">pisze…</string> <string name="pref_chat_states">Powiadomienia pisania</string> <string name="pref_chat_states_summary">Powiadamiaj rozmówcę, kiedy rozpoczynasz nową wiadomość</string> <string name="send_location">Wyślij lokalizację</string> @@ -369,12 +395,20 @@ <string name="dialog_manage_certs_title">Usuń certyfikaty</string> <string name="dialog_manage_certs_positivebutton">Usuń zaznaczone</string> <string name="dialog_manage_certs_negativebutton">Anuluj</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">Usunięto %d certyfikat</item> + <item quantity="few">Usunięto %d certyfikaty</item> + <item quantity="many">Usunięto %d certyfikatów</item> + <item quantity="other">Usunięto %d certyfikatów</item> + </plurals> <string name="pref_quick_action_summary">Zastąp przycisk wysyłania szybką akcją</string> <string name="pref_quick_action">Szybka akcja</string> <string name="none">Brak</string> <string name="recently_used">Ostatnio używana</string> + <string name="search_contacts">Wyszukiwanie kontaktów</string> <string name="choose_quick_action">Wybierz szybką akcję</string> <string name="send_private_message">Wyślij wiadomość prywatną</string> + <string name="user_has_left_conference">%1$s opuścił/a grupę!</string> <string name="username">Nazwa użytkownika</string> <string name="username_hint">Nazwa użytkownika</string> <string name="invalid_username">Błędna nazwa użytkownika</string> @@ -382,13 +416,22 @@ <string name="download_failed_file_not_found">Pobieranie nieudane: Nie odnaleziono pliku</string> <string name="download_failed_could_not_connect">Pobieranie nieudane: Nie można połączyć z hostem</string> <string name="download_failed_could_not_write_file">Pobieranie niepowiodło się: brak możliwości zapisu pliku</string> + <string name="action_check_update">Sprawdź aktualizacje</string> + <string name="title_activity_updater">Usługa aktualizacji</string> + <string name="update_available">Wersja %1$s jest udostępniona.\n\nWielkość pliku: %2$s\n\nZaktualizować na wersje %1$s?</string> + <string name="remind_later">później</string> + <string name="update">aktualizacja</string> + <string name="no_update_available">Brak dostępnych aktualizacji</string> + <string name="download_started">Rozpoczęto pobieranie</string> <string name="account_status_tor_unavailable">Sieć TOR jest niedostepna</string> <string name="account_status_bind_failure">Błąd połączenia (zasób)</string> <string name="account_status_host_unknown">Serwer nie odpowiada domenie</string> <string name="server_info_broken">Zepsute</string> + <string name="pref_presence_settings">Dostępność</string> <string name="pref_away_when_screen_off">Status \"Oddalony\" gdy wyświetlacz jest wyłączony</string> <string name="pref_away_when_screen_off_summary">Oznacza Twój zasób jako \"Oddalony\", gdy wyświetlacz jest wyłączony</string> <string name="pref_dnd_on_silent_mode">\"Nie przeszkadzać\" w trybie cichym</string> + <string name="update_info">Pix-Art Messenger pobiera i instaluje automatycznie nową wersję.\n\nPotrwa to chwilkę…</string> <string name="pref_dnd_on_silent_mode_summary">Oznacza Twój zasób jako \"Nie przeszkadzać\", gdy urządzenie jest w trybie cichym</string> <string name="pref_treat_vibrate_as_silent">Traktuj tryb wibracji jak tryb cichy</string> <string name="pref_treat_vibrate_as_dnd_summary">Oznacza Twój zasób jako \"Nie przeszkadzać\", gdy urządzenie jest w trybie wibracji</string> @@ -408,18 +451,21 @@ <string name="error_fetching_omemo_key">Błąd pobierania klucza OMEMO!</string> <string name="verified_omemo_key_with_certificate">Zweryfikowano klucz OMEMO z certyfikatem</string> <string name="device_does_not_support_certificates">Twoje urządzenie nie wspiera wyboru certyfikatów klienckich</string> + <string name="changelog">Zmiany</string> <string name="account_settings_hostname">Nazwa hosta</string> <string name="account_settings_port">Port</string> <string name="hostname_or_onion">Adres serwera lub \".onion\" adres</string> <string name="not_a_valid_port">To nie jest prawidłowy numer portu</string> <string name="not_valid_hostname">To nie jest prawidłowa nazwa hosta</string> <string name="connected_accounts">%1$d z %2$d kont połączonych</string> + <plurals name="x_messages"> + <item quantity="one">%d wiadomość</item> + <item quantity="few">%d wiadomości</item> + <item quantity="many">%d wiadomości</item> + <item quantity="other">%d wiadomości</item> + </plurals> <string name="load_more_messages">Załaduj wiecej wiadomości</string> - <string name="shared_file_with_x">Dzielony plik z %s</string> - <string name="shared_image_with_x">Dzielony obraz z %s</string> <string name="no_storage_permission">Pix-Art Messenger potrzebuje dostęp do zewnętrznego magazynu</string> - <string name="shared_images_with_x">Obraz udostępniony %s</string> - <string name="shared_text_with_x">Tekst udostępniony %s</string> <string name="sync_with_contacts">Synchronizuj z kontaktami</string> <string name="sync_with_contacts_long">Pix-Art Messenger chce dopasować Twoje kontakty XMPP z listą kontaktów w telefonie, by uzupełnić ich pełne imiona oraz awatary.\n\nPix-Art Messenger jedynie przeczyta Twoje kontakty i dopasuje je lokalnie, bez wysyłania na Twój serwer.\n\nZostaniesz teraz poproszony o przydzielenie pozwolenia na odczyt Twoich kontaktów.</string> <string name="notify_on_all_messages">Powiadom o wszystkich wiadomościach</string> @@ -428,20 +474,29 @@ <string name="notify_paused">Powiadomienia wstrzymane</string> <string name="pref_picture_compression">Kompresuj obrazki</string> <string name="pref_picture_compression_summary">Zmień rozmiar i kompresuj obrazki</string> + <string name="pref_video_compression">Skompresuj filmik</string> + <string name="pref_video_compression_summary">Zmień rozmiar i kompresuj wideo</string> + <string name="video_low">niski (144p)</string> + <string name="video_mid">średnie (360p)</string> + <string name="video_high">wysokie (720p)</string> + <string name="video_original">Oryginał (nieskompresowany)</string> <string name="always">Zawsze</string> <string name="battery_optimizations_enabled">Optymalizacje zużycia baterii włączone</string> <string name="battery_optimizations_enabled_explained">Twoje urządzenie wykonuje poważnie optymalizacje zużycia baterii przez Pix-Art Messenger, które mogą powodować opóźnienie powiadomień lub nawet utratę wiadomości.\nZaleca się ich wyłączenie.</string> <string name="battery_optimizations_enabled_dialog">Twoje urządzenie wykonuje poważnie optymalizacje zużycia baterii przez Pix-Art Messenger, które mogą powodować opóźnienie powiadomień lub nawet utratę wiadomości.\n\nZostaniesz teraz poproszony o ich wyłączenie.</string> + <string name="install_from_unknown_sources_disabled">Instalowanie z nieznanych źródeł wyłączone</string> <string name="disable">Wyłącz</string> - <string name="selection_too_large">Zaznaczony obszar jest zbyt duży</string> + <string name="cancel_update">Anulować aktualizację?</string> + <string name="yes">Tak</string> + <string name="no">Nie</string> <string name="no_accounts">(Brak aktywynych kont)</string> <string name="this_field_is_required">To pole jest wymagane</string> <string name="correct_message">Popraw wiadomość</string> <string name="send_corrected_message">Wyślij poprawioną wiadomość</string> <string name="this_account_is_disabled">Wyłączyłeś to konto</string> - <string name="security_error_invalid_file_access">Błąd bezpieczeństwa: nieprawidłowy dostęp do pliku</string> <string name="no_application_to_share_uri">Nie odnaleziono aplikacji skojarzonej z URI</string> <string name="share_uri_with">Udostępnij URI za pomocą...</string> + <string name="mgmt_account_reconnect">Połącz ponownie</string> <string name="your_full_jid_will_be">Twój pełen Jabber ID będzie następujący: %s</string> <string name="create_account">Utwórz Konto</string> <string name="pick_your_username">Wybierz nazwę użytkownika</string> @@ -453,16 +508,25 @@ <string name="presence_dnd">Zajęty</string> <string name="secure_password_generated">Zostało wygenerowane bezpieczne hasło</string> <string name="device_does_not_support_battery_op">Twoje urządzenie nie pozwala na wyłączenie optymalizacji baterii</string> + <string name="share">Udostępnić</string> + <string name="share_location">Udostępnij lokalizację</string> + <string name="location_sharing_disabled">Udostępnienie lokalizacji jest wyłączone w ustawieniach</string> + <string name="locating">Lokowanie…</string> + <string name="action_end_conversation_muc">Opuść grupę</string> <string name="show_password">Pokaż hasło</string> <string name="registration_please_wait">Rejestracja nie powiodła się. Spróbuj później</string> <string name="registration_password_too_weak">Rejestracja nie powiodła się: hasło zbyt słabe</string> - <string name="conference_subject">Temat</string> - <string name="choose_participants">Wybierz członków</string> + <string name="create_private_group_chat">Utwórz czat grupowy</string> + <string name="choose_participants">Wybierz uczestnika</string> + <string name="import_database">Importować backup</string> <string name="invite_again">Zaproś ponownie</string> + <string name="inviteUser_Subject">zaprosił/a Cie poprzez</string> <string name="pref_broadcast_last_activity">Dziel się ostatnią interakcją.</string> <string name="pref_broadcast_last_activity_summary">Powiadamiaj kontakty, że używasz Pix-Art Messenger</string> + <string name="invite_user">Zaproś do Pix-Art Messengera</string> <string name="unable_to_connect_to_keychain">Nie można połączyć się z OpenKeychain</string> <string name="this_device_is_no_longer_in_use">Urządzenie to nie jest już używane</string> + <string name="please_wait">Proszę czekać…</string> <string name="payment_required">Płatność wymagana</string> <string name="missing_internet_permission">Brak pozwolenia na korzystanie z Internetu</string> <string name="me">Ja</string> @@ -470,8 +534,10 @@ <string name="allow">Pozwól</string> <string name="no_permission_to_access_x">Brak pozwolenia na dostęp do %s</string> <string name="remote_server_not_found">Nie znaleziono serwera</string> + <string name="error_file_corrupt">Plik wygląda na uszkodzony.</string> <string name="unable_to_update_account">Nie można zaktualizować konta</string> <string name="report_jid_as_spammer">Raportuj ten JID jako rozsyłający spam</string> + <string name="create_issue">Zgłoś problem</string> <string name="pref_delete_omemo_identities">Usuń tożsamości OMEMO</string> <string name="pref_delete_omemo_identities_summary">Wygeneruj jeszcze raz klucze OMEMO. Wszystkie Twoje kontakty będą musiały zweryfikować Twoje nowe klucze. Użyj tego tylko w ostateczności.</string> <string name="delete_selected_keys">Usuń zaznaczone klucze</string> @@ -486,6 +552,9 @@ <string name="data_saver_enabled_explained">Twój system operacyjny blokuje dostęp do internetu Pix-Art Messenger, kiedy jest w tle. Aby dostawać powiadomienia dla nowych wiadomości powinieneś/powinnaś dać Pix-Art Messenger nieograniczony dostęp do internetu, kiedy Oszczędzanie danych jest włączone. Pix-Art Messenger będzie wciąż ograniczał transfer danych, kiedy tylko to jest możliwe.</string> <string name="device_does_not_support_data_saver">Twoje urządzenie nie wspiera wyłączenia Oszczędzania danych dla Pix-Art Messenger.</string> + <string name="add_to_contact_list">Dodaj do listy kontaktów</string> + <string name="contact">Kontakt</string> + <string name="unable_to_start_recording">Nie można rozpocząć nagrywania</string> <string name="error_unable_to_create_temporary_file">Niemożna utworzyć pliku tymczasowego</string> <string name="this_device_has_been_verified">To urządzenie zostało zweryfikowane</string> <string name="copy_fingerprint">Skopiuj odcisk</string> @@ -501,9 +570,47 @@ Pix-Art Messenger będzie wciąż ograniczał transfer danych, kiedy tylko to je <string name="i_followed_this_link_from_a_trusted_source">Trafiłem na ten link w zaufanym źródle</string> <string name="verifying_omemo_keys_trusted_source">Zaraz zweryfikujesz klucz OMEMO %1$s klikając w link. Jest to bezpieczne jedynie, kiedy link pochodzi z zaufanego źródła gdzie tylko %2$s mógł go opublikować.</string> <string name="verify_omemo_keys">Zweryfikuj klucze OMEMO</string> - <string name="encrypting_message">Szyfrowanie wiadomości</string> + <string name="show_inactive_devices">Pokaż nieaktywne</string> + <string name="hide_inactive_devices">Ukryj nieaktywne</string> + <string name="reply">Odpowiedzieć</string> <string name="transcoding_video">Kompresuję film</string> <string name="not_fetching_history_retention_period">Nie pobieram wiadomości przez lokalny okres retencji</string> + <plurals name="seconds"> + <item quantity="one">%d sekundy</item> + <item quantity="few">%d sekundy</item> + <item quantity="many">%d sekund</item> + <item quantity="other">%d sekundy</item> + </plurals> + <plurals name="minutes"> + <item quantity="one">%d minuta</item> + <item quantity="few">%d minut</item> + <item quantity="many">%d minut</item> + <item quantity="other">%d minuty</item> + </plurals> + <plurals name="hours"> + <item quantity="one">%d godzina</item> + <item quantity="few">%d godzin</item> + <item quantity="many">%d godzin</item> + <item quantity="other">%d godziny</item> + </plurals> + <plurals name="days"> + <item quantity="one">%d dzień</item> + <item quantity="few">%d dni</item> + <item quantity="many">%d dni</item> + <item quantity="other">after %d days - po %d dniach</item> + </plurals> + <plurals name="weeks"> + <item quantity="one">%d tydzień</item> + <item quantity="few">%d tygodnie</item> + <item quantity="many">%d tygodni</item> + <item quantity="other">after %d weeks - po %d tygodniach</item> + </plurals> + <plurals name="months"> + <item quantity="one">%d miesiąc</item> + <item quantity="few">%d miesięcy</item> + <item quantity="many">%d miesięcy</item> + <item quantity="other">%d miesięcy</item> + </plurals> <string name="today">Dzisiaj</string> <string name="block_jabber_id">Zablokuj Jabber ID</string> <string name="corresponding_conversations_closed">Odpowiadające rozmowy zostały zamknięte.</string> @@ -513,17 +620,27 @@ Pix-Art Messenger będzie wciąż ograniczał transfer danych, kiedy tylko to je <string name="received_message_from_stranger">Odebrano wiadomość od nieznajomego</string> <string name="contacts_are_typing">1%s pisze...</string> <string name="contact_is_typing">%s pisze...</string> + <string name="one_participant">jeden uczestnik</string> + <string name="more_participants">%d uczestników</string> + <string name="presence_offline">Nieaktywny</string> <string name="block_stranger">Zablokuj nieznajomego</string> <string name="block_entire_domain">Zablokuj całą domenę</string> <string name="pref_use_tor">Połącz przez sieć TOR</string> <string name="pref_use_tor_summary">Tuneluj wszystkie połączenia przez sieć TOR. Wymaga zainstalowania aplikacji \"Orbot\"</string> <string name="pref_connection_options">Połączenie</string> + <string name="image_low">niski (720p)</string> + <string name="image_mid">średnie (1920p)</string> + <string name="image_high">wysokie (3840p)</string> + <string name="image_original">oryginał (nieskompresowany)</string> + <string name="last_seen_just_away">ostatnio</string> <string name="send_message">Wyślij wiadomość</string> <string name="pref_show_connection_options_summary">Pokaż nazwę hosta i ustawienia portu przy dodawaniu konta</string> <string name="pref_show_connection_options">Rozszerzone ustawienia połączenia</string> <string name="retry_decryption">Ponownie spróbuj odszyfrować</string> <string name="session_failure">Błąd sesji</string> <string name="sasl_downgrade">Starszy mechanizm SASL</string> + <string name="action_take_photo">zrób zdjęcie</string> + <string name="action_take_video">nagraj wideo</string> <string name="account_status_regis_web">Serwer wymaga rejestracji na stronie</string> <string name="application_found_to_open_website">Nie znaleziono aplikacji do otwarcia strony</string> <string name="open_website">Otwórz stronę</string> @@ -534,6 +651,80 @@ Pix-Art Messenger będzie wciąż ograniczał transfer danych, kiedy tylko to je <string name="pref_validate_hostname_summary">Certyfikaty serwera posiadające prawidłową nazwę hosta są uznawane za zweryfikowane</string> <string name="certificate_does_not_contain_jid">Certyfikat nie zawiera Jabber ID</string> <string name="error_no_keys_to_trust_presence">Brak dostępnych kluczy dla tego kontaktu.\nUpewnij się, że wzajemnie powiadamiacie się o obecności.</string> + <string name="mark_as_read">oznacz jako przeczytane</string> <string name="server_info_partial">częściowo</string> + <string name="update_service">Usługa aktualizacji Messengera Pix-Art</string> + <string name="install_update">Zainstalować aktualizację?</string> + <string name="highlight_in_muc">wymień użytkownika</string> + <string name="contacts_have_read_up_to_this_point">%s przeczytał do tego miejsca</string> <string name="contact_has_read_up_to_this_point">%s przeczytał do tego miejsca</string> + <string name="pref_warn_unencrypted_chat">Ostrzec, jeśli czat nie jest szyfrowany</string> + <string name="pref_warn_unencrypted_chat_summary">Jeśli dostępne jest szyfrowanie wiadomości, powinieneś go użyć. Jeśli nie używasz szyfrowania, wyświetl komunikat ostrzegawczy na czacie.</string> + <string name="pref_use_bundled_emoji">Użyj zintegrowanych emoji</string> + <string name="title_activity_share_via_account">Udostępnij przez konto</string> + <string name="conference_unknown_error">Nie jesteś już w tym czacie grupowym</string> + <string name="huawei_protected_apps">Chronione aplikacje</string> + <string name="huawei_protected_apps_summary">Aby otrzymywać powiadomienia, nawet jeśli ekran jest wyłączony, musisz dodać Pix-Art Messenger do listy chronionych aplikacji.</string> + <string name="mtm_accept_cert">Zaakceptować nieznany certyfikat?</string> + <string name="mtm_connect_anyway">Czy połączyć mimo to?</string> + <string name="mtm_cert_details">Szczegóły certyfikatu:</string> + <string name="once">Jednorazowo</string> + <string name="pref_scroll_to_bottom_summary">Przewiń w dół po wysłaniu wiadomości</string> + <string name="pref_scroll_to_bottom">Przewiń do dołu</string> + <string name="no_camera_permission">Pix-Art Messenger potrzebuje dostęp do kamery</string> + <string name="pref_automatically_delete_messages">Automatyczne usuwanie wiadomości</string> + <string name="disable_encryption">Wyłącz szyfrowanie</string> + <string name="error_trustkey_general">Pix-Art Messenger nie może wysyłać zaszyfrowanych wiadomości do %1$s. Może to być spowodowane nieaktualnym serwerem lub klientem użytkownika, który nie może obsłużyć OMEMO.</string> + <string name="error_trustkey_device_list">Nie można pobrać listy urządzeń</string> + <string name="disable_encryption_message">Szyfrowanie OMEMO na pewno wyłączyć dla tej rozmowy?\nTo pozwoli administratorowi serwera na odczyt twoich wiadomości, jednak może to być jedyną możliwością aby komunikować z ludźmi używającymi nieaktualnych klientów.</string> + <string name="disable_now">Wyłącz teraz</string> + <string name="pref_omemo_setting">OMEMO szyfrowanie</string> + <string name="chooce_account">Wybierz konto</string> + <string name="small">Mały</string> + <string name="medium">Średni</string> + <string name="large">Duży</string> + <string name="create_shortcut">Utwórz skrót</string> + <string name="search_messages">Znajdź Wiadomość</string> + <string name="gif">GIF</string> + <string name="view_conversation">Wyświetl komunikację</string> + <string name="web_address">adres internetowy</string> + <string name="copy_jabber_id">Skopiuj JID</string> + <string name="copy_link">Skopiuj adres interenetowy</string> + <string name="server_hint">Wybierz dostawce</string> + <string name="choose_your_server">Wybierz dostawcę Jabber</string> + <string name="group_chat_avatar">Awatara czatu grupowego</string> + <string name="host_does_not_support_group_chat_avatars">Host nie obsługuje awatarów grupowych</string> + <string name="only_the_owner_can_change_group_chat_avatar">Tylko właściciel grupy może zmienić awatar</string> + <string name="contact_name">Nazwa kontaktu</string> + <string name="nickname">Pseudonim czatu grupowego</string> + <string name="group_chat_name">Nazwa</string> + <string name="providing_a_name_is_optional">Podanie nazwy jest opcjonalne</string> + <string name="create_dialog_group_chat_name">Nazwa czatu grupowego</string> + <string name="delete_message">Usuń wiadomość</string> + <string name="no_location_permission">Pix-Art Messenger potrzebuje dostęp do usług lokalizacyjnych</string> + <string name="pref_show_record_voice_btn_summary">Pokaż przycisk nagrywania głosu w czatach, jako szybką akcję</string> + <string name="pref_show_record_voice_btn">Pokaż przycisk nagrywania głosu</string> + <string name="notification_group_messages">Wiadomości</string> + <string name="messages_channel_name">Wiadomości</string> + <string name="silent_messages_channel_name">Wycisz wiadomości</string> + <string name="pref_more_notification_settings_summary">Ważność, Dźwięk, Wibracja</string> + <string name="video_compression_channel_name">Kompresja wideo</string> + <string name="app_update_channel_name">Aktualizacja aplikacji</string> + <string name="action_group_details">Szczegóły grupy</string> + <string name="action_open">Otwórz</string> + <string name="delete_file_dialog">Usuń plik</string> + <string name="cancelled">anulowane</string> + <string name="bad_key_for_encryption">Zły klucz do szyfrowania.</string> + <string name="delete_message_dialog">Usuń wiadomość</string> + <string name="delete_message_dialog_msg">Czy na pewno chcesz usunąć tą wiadomość?\n\n<b>Uwaga:</b> Nie spowoduje to usunięcia kopii tej wiadomości przechowywanej na innych urządzeniach lub serwerach.</string> + <string name="feature_not_implemented">Funkcja nie została zaimplementowana</string> + <string name="install_orbot">Zainstaluj Orbot</string> + <string name="start_orbot">Uruchom Orbot</string> + <string name="group_chat_will_make_your_jabber_id_public">Ten czat grupowy udostępnia publicznie Twój Jabber ID</string> + <string name="your_name">Twoje miano</string> + <string name="enter_your_name">Wprowadź swoje imię</string> + <string name="enter_your_name_instructions">Wprowadź swój pseudonim, który będzie widoczny dla Twoich kontaktów.</string> + <string name="no_name_set_instructions">Brak ustawionego pseudonimu.</string> + <string name="autojoin_groupchat">Automatycznie dołącz do tego czatu grupowego</string> + <string name="pref_play_gif_inside">Odtwarzaj pliki GIF w czacie</string> </resources> diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index 62fc1886f..e4a7a0332 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -65,11 +65,8 @@ <string name="add_contact">Adicionar contato</string> <string name="send_failed">a entrega falhou</string> <string name="preparing_image">Preparando a imagem para envio</string> - <string name="preparing_images">Preparando as imagens para o envio</string> - <string name="sharing_files_please_wait">Compartilhando arquivos. Por favor aguarde...</string> <string name="action_clear_history">Limpar histórico</string> <string name="clear_conversation_history">Limpar o histórico de conversas</string> - <string name="delete_messages">Remover mensagens</string> <string name="also_end_conversation">Terminar esta conversa depois</string> <string name="choose_presence">Escolher dispositivo</string> <string name="send_unencrypted_message">Enviar mensagem não encriptada</string> @@ -85,7 +82,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger utiliza uma aplicação de terceiros chamada <b>OpenKeychain</b> para encriptad e decriptar mensagens e gerenciar as suas chaves públicas.\n\nOpenKeychain é licenciado sob a licença GPLv3 e está disponível no F-Droid e Google Play.\n\n<small>(Por favor reinicie Pix-Art Messenger em seguida)</small></string> <string name="restart">Reiniciar</string> <string name="install">Instalar</string> - <string name="openkeychain_not_installed">Por favor instale o OpenKeychain</string> <string name="offering">oferecendo...</string> <string name="waiting">aguardando...</string> <string name="no_pgp_key">Nenhuma chave OpenPGP encontrada</string> @@ -139,7 +135,6 @@ <string name="mgmt_account_are_you_sure">Tem a certeza?</string> <string name="attach_record_voice">Gravar voz</string> <string name="account_settings_jabber_id">ID Jabber</string> - <string name="account_settings_password">Senha</string> <string name="account_settings_example_jabber_id">nomedeutilizador@exemplo.com</string> <string name="password">Senha</string> <string name="invalid_jid">Esse não é um ID Jabber válido</string> @@ -263,8 +258,6 @@ <string name="verified">Verificado!</string> <string name="smp_requested">O contato pediu uma verificação SMP</string> <string name="no_otr_session_found">Não foi encontrada nenhuma sessão OTR válida!</string> - <string name="pref_export_logs">Exportar histórico</string> - <string name="notification_export_logs_title">A escrever os logs para o cartão SD</string> <string name="choose_file">Escolher arquivo</string> <string name="receiving_x_file">Recebendo %1$s (%2$d%% concluído)</string> <string name="download_x_file">Transferir %s</string> @@ -315,10 +308,7 @@ <string name="remove_admin_privileges">Revogar privilégios de administração</string> <string name="could_not_change_affiliation">Não foi possível alterar a afiliação de %s</string> <string name="ban_now">Banir agora</string> - <string name="could_not_change_role">Não foi possível alterar o papel de %s</string> - <string name="members_only">Privado, apenas membros</string> <string name="non_anonymous">Não-anónimo</string> - <string name="moderated">Moderado</string> <string name="you_are_not_participating">Não está participando</string> <string name="never">Nunca</string> <string name="until_further_notice">Até segunda ordem</string> @@ -333,7 +323,6 @@ <string name="pdf_document">Documento PDF</string> <string name="apk">Aplicação Android</string> <string name="vcard">Contato</string> - <string name="touch_to_open_conversations">Toque para abrir Pix-Art Messenger</string> <string name="avatar_has_been_published">O avatar foi publicado!</string> <string name="sending_x_file">Enviando %s</string> <string name="offering_x_file">Oferecendo %s</string> @@ -398,11 +387,7 @@ <item quantity="other">%d mensagens</item> </plurals> <string name="load_more_messages">Carregar mais mensagens</string> - <string name="shared_file_with_x">Arquivo compartilhado com %s</string> - <string name="shared_image_with_x">Imagem compartilhada com %s</string> <string name="no_storage_permission">Pix-Art Messenger necessita de acesso ao armazenamento externo</string> - <string name="shared_images_with_x">Imagens compartilhadas com %s</string> - <string name="shared_text_with_x">Mensagem compartilhada com %s</string> <string name="sync_with_contacts">Sincronizar com contatos</string> <string name="sync_with_contacts_long">A aplicação Pix-Art Messenger deseja fazer a correspondência entre a sua lista de contatos XMPP e os seus contatos para mostrar os seus nomes completos e os seus avatares.\n\nA aplicação Pix-Art Messenger irá apenas ler os seus contatos e fazer a correspondência localmente sem enviar os dados para o seu servidor.\n\nAgora será pedida permissão para acessar os seus contatos.</string> <string name="notify_on_all_messages">Notificar em todas as mensagens</string> @@ -415,13 +400,11 @@ <string name="battery_optimizations_enabled_explained">O seu dispositivo está fazendo otimizações de bateria bastante agressivas na aplicação Pix-Art Messenger que poderão levar a notificações atrasadas ou até mesmo a perda de mensagens.\nÉ recomendado que desative.</string> <string name="battery_optimizations_enabled_dialog">O seu dispositivo está fazendo otimizações de bateria bastantes agressivas na aplicação Pix-Art Messenger que poderão levar a notificações atrasadas ou até mesmo perda de mensagens.\n\nAgora será pedida permissão para desativá-las.</string> <string name="disable">Desativar</string> - <string name="selection_too_large">A área selecionada é muito grande</string> <string name="no_accounts">(Nenhuma conta ativada)</string> <string name="this_field_is_required">Este campo é obrigatório</string> <string name="correct_message">Corrigir mensagem</string> <string name="send_corrected_message">Enviar mensagem corrigida</string> <string name="this_account_is_disabled">Desativou esta conta</string> - <string name="security_error_invalid_file_access">Erro de segurança: Acesso ao arquivo inválido</string> <string name="no_application_to_share_uri">Não foi encontrada nenhuma aplicação para compartilhar o URI</string> <string name="share_uri_with">Compartilhar URI com...</string> <string name="create_account">Criar conta</string> diff --git a/src/main/res/values-ro/strings.xml b/src/main/res/values-ro/strings.xml index 622d3e990..9816ad0ae 100644 --- a/src/main/res/values-ro/strings.xml +++ b/src/main/res/values-ro/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Deblochează domeniu</string> <string name="title_activity_manage_accounts">Configurează conturile</string> <string name="title_activity_settings">Setări</string> - <string name="title_activity_conference_details">Detalii discuție de grup</string> + <string name="conference_details">Detalii discuție de grup</string> <string name="title_activity_contact_details">Detalii contact</string> <string name="title_activity_sharewith">Partajează într-o conversație</string> <string name="title_activity_start_conversation">Pornește o conversație</string> @@ -70,11 +70,8 @@ <string name="add_contact">Adaugă contact</string> <string name="send_failed">trimitere eșuată</string> <string name="preparing_image">Pregătesc imaginea pentru transmisie</string> - <string name="preparing_images">Pregătesc imaginile pentru transmisie</string> - <string name="sharing_files_please_wait">Trimitere fișiere. Te rog asteaptă...</string> <string name="action_clear_history">Șterge istoric</string> <string name="clear_conversation_history">Șterge istoricul conversației</string> - <string name="delete_messages">Șterge mesajele</string> <string name="also_end_conversation">Închide conversația după ștergere</string> <string name="choose_presence">Alege dispozitiv</string> <string name="send_unencrypted_message">Trimite mesaje necriptate</string> @@ -90,7 +87,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger utilizează o aplicație externă <b>OpenKeychain</b> pentru a cripta și decripta mesaje și a administra cheile publice.\n\nOpenKeychain este licențiat sub GPLv3 și se poate instala din F-Droid și Google Play.\n\n<small>(Vă rugăm să reporniți Pix-Art Messenger după)</small></string> <string name="restart">Repornește</string> <string name="install">Instalare</string> - <string name="openkeychain_not_installed">Va rugăm să instalați OpenKeychain</string> <string name="offering">transmit...</string> <string name="waiting">în așteptare...</string> <string name="no_pgp_key">Nu am găsit cheia OpenPGP</string> @@ -153,7 +149,6 @@ <string name="mgmt_account_delete_confirm_text">Dacă vă ștergeți contul, întregul istoric de conversații va fi pierdut</string> <string name="attach_record_voice">Înregistrare voce</string> <string name="account_settings_jabber_id">ID-ul Jabber</string> - <string name="account_settings_password">Parolă</string> <string name="account_settings_example_jabber_id">username@pix-art.de</string> <string name="password">Parolă</string> <string name="invalid_jid">Acesta nu este un ID Jabber valabil</string> @@ -194,11 +189,9 @@ <string name="done">Gata</string> <string name="verify">Verifică</string> <string name="decrypt">Decriptează</string> - <string name="conferences">Discuții de grup</string> <string name="search">Caută</string> - <string name="create_contact">Creează contact</string> <string name="enter_contact">Introdu contact</string> - <string name="join_conference">Alătură-te unei discuții de grup</string> + <string name="join_public_channel">Alătură-te unei discuții de grup</string> <string name="delete_contact">Șterge contact</string> <string name="view_contact_details">Arată detalii contact</string> <string name="block_contact">Blochează contact</string> @@ -207,12 +200,10 @@ <string name="select">Selecteaza</string> <string name="contact_already_exists">Contactul exista deja</string> <string name="join">Alatura-te</string> - <string name="conference_address">Adresă discuție de grup</string> - <string name="conference_address_example">grupchat@room.pix-art.de</string> + <string name="channel_full_jid_example">grupchat@room.pix-art.de</string> <string name="save_as_bookmark">Salvează semn de carte</string> <string name="delete_bookmark">Șterge semn de carte</string> <string name="bookmark_already_exists">Acest semn de carte exista</string> - <string name="action_edit_subject">Editează subiectul discuției de grup</string> <string name="joining_conference">Vă alăturați discuției de grup...</string> <string name="leave">Paraseste</string> <string name="contact_added_you">Contactul a fost adăugat în listă</string> @@ -291,9 +282,6 @@ <string name="verified">Verificat!</string> <string name="smp_requested">Contactul a cerut verificare SMP</string> <string name="no_otr_session_found">Nu s-a găsit nici o sesiune OTR validă</string> - <string name="pref_export_logs">Exportă istoric</string> - <string name="pref_export_logs_summary">Scrie istoricul conversațiilor pe cardul SD</string> - <string name="notification_export_logs_title">Se scrie jurnal pe card SD</string> <string name="choose_file">Alege un fișier</string> <string name="receiving_x_file">Primesc %1$s (%2$d%% complet)</string> <string name="download_x_file">Descarca %s</string> @@ -354,13 +342,8 @@ <string name="ban_from_conference">Interzice accesul la discuția de grup</string> <string name="removing_from_public_conference">Încercați să înlăturați pe %s dintr-o discuție de grup publică. Singurul mod în care puteți face asta este sa blocați contactul pentru totdeauna.</string> <string name="ban_now">Interzice accesul acum</string> - <string name="could_not_change_role">Nu s-a putut schimba rolul lui %s</string> - <string name="public_conference">Discuție de grup accesibilă public</string> - <string name="private_conference">Discuție de grup privată, accesibilă numai membrilor</string> <string name="conference_options">Opțiuni discuție de grup</string> - <string name="members_only">Privat, numai pentru membri</string> <string name="non_anonymous">Ne anonim</string> - <string name="moderated">Monitorizata</string> <string name="you_are_not_participating">Nu participați</string> <string name="modified_conference_options">Opțiuni discuție de grup modificate!</string> <string name="could_not_modify_conference_options">Nu s-au putut modifica opțiunile discuției de grup</string> @@ -377,7 +360,6 @@ <string name="pdf_document">document PDF</string> <string name="apk">Aplicație Android</string> <string name="vcard">Contact</string> - <string name="touch_to_open_conversations">Atingeți pentru a deschide Pix-Art Messenger</string> <string name="avatar_has_been_published">Avatarul a fost publicat!</string> <string name="sending_x_file">Trimit %s</string> <string name="offering_x_file">Ofer %s</string> @@ -456,11 +438,7 @@ <item quantity="other">%d mesaje</item> </plurals> <string name="load_more_messages">Încarcă mai multe mesaje</string> - <string name="shared_file_with_x">Partajează fișierul cu %s...</string> - <string name="shared_image_with_x">Partajează imaginea cu %s.</string> <string name="no_storage_permission">Pix-Art Messenger are nevoie de acces la stocarea externa</string> - <string name="shared_images_with_x">Partajează imaginile cu %s.</string> - <string name="shared_text_with_x">Partajează textul cu %s</string> <string name="sync_with_contacts">Sincronizează cu contactele</string> <string name="sync_with_contacts_long">Pix-Art Messenger dorește să potrivească lista de contacte XMPP cu cea din dispozitiv pentru a putea afișa numele lor complete și avatarele.\n\nPix-Art Messenger doar va citi și potrivi local fără să le încarce pe vreun server.\n\nUrmează să fiți întrebați dacă doriți să permiteți accesul la contacte.</string> <string name="notify_on_all_messages">Notifică la toate mesajele</string> @@ -476,7 +454,6 @@ bateriei pentru Conversations, asta poate duce la notificări întârziate sau chiar pierderea mesajelor.\nÎn continuare veți fi rugați să dezactivați aceste optimizări.</string> <string name="disable">Dezactivează</string> - <string name="selection_too_large">Zona selectată este prea mare</string> <string name="yes">Da</string> <string name="no">Nu</string> <string name="no_accounts">(Nici un cont activat)</string> @@ -486,7 +463,6 @@ sau chiar pierderea mesajelor.\nÎn continuare veți fi rugați să dezactivați <string name="no_keys_just_confirm">Deja aveți încredere în acest contact. Selectând \'gata\' doar confirmați că %s ia parte la această discuție de grup.</string> <string name="contacts_have_no_pgp_keys">Pix-Art Messenger nu poate cripta mesajele tale către contactele tale care nu își anunță cheia publică.\n\n</string> <string name="this_account_is_disabled">Ați dezactivat acest cont</string> - <string name="security_error_invalid_file_access">Eroare de securitate.: Acces fișier invalid</string> <string name="no_application_to_share_uri">Nu s-a găsit nici o aplicație care să partajeze adresa</string> <string name="share_uri_with">Partajează adresa cu...</string> <string name="mgmt_account_reconnect">Reconectare</string> @@ -509,8 +485,7 @@ sau chiar pierderea mesajelor.\nÎn continuare veți fi rugați să dezactivați <string name="show_password">Arată parola</string> <string name="registration_please_wait">Înregistrare eșuată: Încercați din nou mai târziu</string> <string name="registration_password_too_weak">Înregistrare eșuată: Parolă nesigură</string> - <string name="create_conference">Creează discuție de grup</string> - <string name="conference_subject">Subiect</string> + <string name="create_private_group_chat">Creează discuție de grup</string> <string name="choose_participants">Alege participanți</string> <string name="creating_conference">Se creează discuția de grup...</string> <string name="invite_again">Trimite din nou invitația</string> @@ -555,7 +530,6 @@ sau chiar pierderea mesajelor.\nÎn continuare veți fi rugați să dezactivați <string name="i_followed_this_link_from_a_trusted_source">Am urmat aceasta legătură de la o sursă de încredere</string> <string name="verifying_omemo_keys_trusted_source">Urmează să verificați cheile OMEMO pentru %1$s după ce veți deschide legătura. Acest lucru se poate face în siguranță doar dacă ați primit legătura de la o sursă de încredere unde doar %2$s putea publica.</string> <string name="verify_omemo_keys">Verifica cheile OMEMO</string> - <string name="encrypting_message">Mesajul se criptează</string> <string name="transcoding_video">Se comprimă clipul video</string> <string name="not_fetching_history_retention_period">Politica de retenție locală împiedică descărcarea altor mesaje.</string> <string name="today">Azi</string> diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index f91147ede..d22a21dc4 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Разблокировать домен</string> <string name="title_activity_manage_accounts">Управление аккаунтами</string> <string name="title_activity_settings">Настройки</string> - <string name="title_activity_conference_details">Информация о конференции</string> + <string name="conference_details">Информация о конференции</string> <string name="title_activity_contact_details">Сведения о контакте</string> <string name="title_activity_sharewith">Поделиться с разговором</string> <string name="title_activity_start_conversation">Начать Разговор</string> @@ -72,11 +72,8 @@ <string name="add_contact">Добавить контакт</string> <string name="send_failed">доставка не удалась</string> <string name="preparing_image">Подготовка изображения к передаче</string> - <string name="preparing_images">Подготовка изображений к передаче</string> - <string name="sharing_files_please_wait">Расшаривание файлов. Пожалуйста, подождите…</string> <string name="action_clear_history">Очистить историю</string> <string name="clear_conversation_history">Очистить Историю Разговора</string> - <string name="delete_messages">Удалить сообщения</string> <string name="also_end_conversation">Потом закончить этот разговор</string> <string name="choose_presence">Выбрать устройство</string> <string name="send_unencrypted_message">Отправить сообщение без шифрования</string> @@ -92,7 +89,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger использует стороннее приложение <b>OpenKeychain</b> для шифрования и расшифровки сообщений, а также для управления вашими публичными ключами.\n\nOpenKeychain лицензирован под GPLv3 и доступен в F-Droid и Google Play.\n\n<small>(Пожалуйста, перезапустите Pix-Art Messenger впоследствии.)</small></string> <string name="restart">Перезапуск</string> <string name="install">Установка</string> - <string name="openkeychain_not_installed">Пожалуйста, установите OpenKeychain</string> <string name="offering">предложение…</string> <string name="waiting">ожидание…</string> <string name="no_pgp_key">Не найден ключ OpenPGP</string> @@ -126,7 +122,6 @@ <string name="receive_presence_updates">Принимать обновления присутствия</string> <string name="ask_for_presence_updates">Запрашивать обновления присутствия</string> <string name="attach_choose_picture">Выберите изображение</string> - <string name="attach_take_from_camera">Снять на камеру</string> <string name="error_not_an_image_file">Выбранный файл не является изображением</string> <string name="error_compressing_image">Ошибка конвертации изображения</string> <string name="error_file_not_found">Файл не найден</string> @@ -162,11 +157,8 @@ <string name="mgmt_account_delete_confirm_text">Если вы удалите свой аккаунт, то история разговора будет потеряна</string> <string name="attach_record_voice">Записать голосовое сообщение</string> <string name="account_settings_jabber_id">Jabber-ID</string> - <string name="account_settings_password">Пароль</string> <string name="account_settings_example_jabber_id">username@pix-art.de</string> <string name="password">Пароль</string> - <string name="confirm_password">Подтверждение пароля</string> - <string name="passwords_do_not_match">Пароли не совпадают</string> <string name="invalid_jid">Недействительный Jabber-ID</string> <string name="error_out_of_memory">Недостаточно памяти. Изображение слишком большое</string> <string name="add_phone_book_text">Хотите добавить %s в вашу адресную книгу?</string> @@ -207,11 +199,9 @@ <string name="done">Готово</string> <string name="verify">Подтверждение</string> <string name="decrypt">Расшифровать</string> - <string name="conferences">Групповые чаты</string> <string name="search">Поиск</string> - <string name="create_contact">Создать Контакт</string> <string name="enter_contact">Введите Контакт</string> - <string name="join_conference">Присоединиться к групповому чату</string> + <string name="join_public_channel">Присоединиться к групповому чату</string> <string name="delete_contact">Удалить Контакт</string> <string name="view_contact_details">Просмотреть детали контакта</string> <string name="block_contact">Заблокировать контакт</string> @@ -220,12 +210,10 @@ <string name="select">Выбрать</string> <string name="contact_already_exists">Контакт уже существует</string> <string name="join">Присоединиться</string> - <string name="conference_address">Адрес группового чата</string> - <string name="conference_address_example">groupchat@room.pix-art.de</string> + <string name="channel_full_jid_example">groupchat@room.pix-art.de</string> <string name="save_as_bookmark">Сохранить как закладку</string> <string name="delete_bookmark">Удалить закладку</string> <string name="bookmark_already_exists">Эта закладка уже создана</string> - <string name="action_edit_subject">Изменить тему группового чата</string> <string name="topic">Тема</string> <string name="joining_conference">Подключение к групповому чату…</string> <string name="leave">Покинуть</string> @@ -307,9 +295,6 @@ <string name="finish">Завершить</string> <string name="verified">Подтверждён!</string> <string name="no_otr_session_found">Не найдена действительная OTR сессия!</string> - <string name="pref_export_logs">Экспорт истории</string> - <string name="pref_export_logs_summary">Записать логи истории разговоров на SD карту</string> - <string name="notification_export_logs_title">Записываются разговоры на SD карту</string> <string name="choose_file">Выберите файл</string> <string name="barcode_does_not_contain_fingerprints_for_this_conversation">QR код не содержит отпечатки для этого разговора.</string> <string name="use_camera_icon_to_scan_barcode">Используйте камеру для сканирования QR кода контактов</string> @@ -320,7 +305,6 @@ <string name="media_browser">Медиа браузер</string> <string name="account_status_stream_opening_error">Ошибка открытия потока</string> <string name="action_open">Открыть</string> - <string name="action_delete">Удалить</string> <string name="delete_file_dialog">Удалить файл</string> <string name="cancelled">отменён</string> <string name="bad_key_for_encryption">Неправильный ключ для шифрования.</string> diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 06d3181eb..e640b12bf 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -65,11 +65,8 @@ <string name="add_contact">Lägg till kontakt</string> <string name="send_failed">sändning misslyckades</string> <string name="preparing_image">Förbereder bild för sändning</string> - <string name="preparing_images">Förbereder bilder för överföring</string> - <string name="sharing_files_please_wait">Delar filer. Vänta...</string> <string name="action_clear_history">Rensa historik</string> <string name="clear_conversation_history">Rensa konversationshistorik</string> - <string name="delete_messages">Ta bort meddelanden</string> <string name="also_end_conversation">Avsluta denna konversation efteråt</string> <string name="choose_presence">Välj enhet</string> <string name="send_unencrypted_message">Skicka okrypterat meddelande</string> @@ -85,7 +82,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger använder en tredjeparts-applikation som heter <b>OpenKeychain</b> för att kryptera och avkryptera meddelanden och hantera dina publika nycklar.\n\nOpenKeychain är licensierad under GPLv3 och tillgänglig på F-Droid och Google Play.\n\n<small>(Starta om Pix-Art Messenger efter installation.)</small></string> <string name="restart">Starta om</string> <string name="install">Installera</string> - <string name="openkeychain_not_installed">Installera OpenKeychain</string> <string name="offering">erbjuder…</string> <string name="waiting">väntar…</string> <string name="no_pgp_key">Ingen OpenPGP-nyckel funnen</string> @@ -146,7 +142,6 @@ <string name="mgmt_account_delete_confirm_text">Om du tar bort ditt konto kommer hela konversationshistoriken att försvinna</string> <string name="attach_record_voice">Spela in röst</string> <string name="account_settings_jabber_id">Jabber-ID</string> - <string name="account_settings_password">Lösenord</string> <string name="account_settings_example_jabber_id">användarnamn@exempel.se</string> <string name="password">Lösenord</string> <string name="invalid_jid">Detta är inte ett korrekt Jabber-ID</string> @@ -196,7 +191,7 @@ <string name="select">Välj</string> <string name="contact_already_exists">Kontakten finns redan</string> <string name="join">Gå med</string> - <string name="conference_address_example">room@conference.example.com/nick</string> + <string name="channel_full_jid_example">room@conference.example.com/nick</string> <string name="save_as_bookmark">Spara som bokmärke</string> <string name="delete_bookmark">Ta bort bokmärke</string> <string name="bookmark_already_exists">Detta bokmärke finns redan</string> @@ -271,9 +266,6 @@ <string name="verified">Verifierad</string> <string name="smp_requested">Kontakt begärde SMP-verifikation</string> <string name="no_otr_session_found">Ingen giltig OTR-session kunde hittas!</string> - <string name="pref_export_logs">Exportera historik</string> - <string name="pref_export_logs_summary">Skriv loggar av konversationer till SD-kort</string> - <string name="notification_export_logs_title">Skriver loggar till SD-kort</string> <string name="choose_file">Välj fil</string> <string name="receiving_x_file">Tar emot %1$s (%2$d%% klart)</string> <string name="download_x_file">Ladda ner %s</string> @@ -328,10 +320,7 @@ <string name="remove_admin_privileges">Återkalla administratörsbehörighet</string> <string name="could_not_change_affiliation">Kunde inte ändra tillhörigheten för %s</string> <string name="ban_now">Bannlys nu</string> - <string name="could_not_change_role">Kunde inte ändra rollen för %s</string> - <string name="members_only">Privat, medlemsskap krävs</string> <string name="non_anonymous">Icke-anonymt</string> - <string name="moderated">Modererad</string> <string name="you_are_not_participating">Du deltar ej</string> <string name="never">Aldrig</string> <string name="until_further_notice">Tills vidare</string> @@ -346,7 +335,6 @@ <string name="pdf_document">PDF-dokument</string> <string name="apk">Android-app</string> <string name="vcard">Kontakt</string> - <string name="touch_to_open_conversations">Tryck för att öppna Pix-Art Messenger</string> <string name="avatar_has_been_published">Avatarbild har publicerats!</string> <string name="sending_x_file">Skickar %s</string> <string name="offering_x_file">Erbjuder %s</string> @@ -416,11 +404,7 @@ <item quantity="other">%d meddelanden</item> </plurals> <string name="load_more_messages">Ladda fler meddelanden</string> - <string name="shared_file_with_x">Delade fil med %s</string> - <string name="shared_image_with_x">Delade bild med %s</string> <string name="no_storage_permission">Pix-Art Messenger behöver access till extern lagring</string> - <string name="shared_images_with_x">Delade bilder med %s</string> - <string name="shared_text_with_x">Delade text med %s</string> <string name="sync_with_contacts">Synkronisera med kontakter</string> <string name="sync_with_contacts_long">Pix-Art Messenger vill matcha din XMPP-kontaktlista med dina kontakter för att visa deras namn och profilbild.\n\nPix-Art Messenger läser endast dina kontakter för att matcha dem lokalt utan att ladda upp dem till din server.\n\nDu kommer nu få frågan om tillåtelse för att använda kontakterna.</string> <string name="notify_on_all_messages">Notifiera för alla meddelanden</string> @@ -433,13 +417,11 @@ <string name="battery_optimizations_enabled_explained">Din enhet har kraftiga batterioptimeringar som påverkar Pix-Art Messenger på så sätt att inkommande meddelanden kan försenas eller kan till och med gå förlorade.\nDet är rekommenderat att deaktivera batterioptimeringarna.</string> <string name="battery_optimizations_enabled_dialog">Din enhet har kraftiga batterioptimeringar som påverkar Pix-Art Messenger på så sätt att inkommande meddelanden kan försenas eller kan till och med gå förlorade.\n\nDu kommer nu att bli ombedd att stänga av batterioptimeringarna för Pix-Art Messenger.</string> <string name="disable">Deaktivera</string> - <string name="selection_too_large">The valda området är för stort</string> <string name="no_accounts">(Inget konto aktiverat)</string> <string name="this_field_is_required">Detta fält måste fyllas i</string> <string name="correct_message">Korrigera meddelanden</string> <string name="send_corrected_message">Skicka korrigerat meddelande</string> <string name="this_account_is_disabled">Du har deaktiverat detta konto</string> - <string name="security_error_invalid_file_access">Säkerhetsfel: Ogiltig filaccess</string> <string name="no_application_to_share_uri">Ingen applikation kunde hittas för att dela URI</string> <string name="share_uri_with">Dela URI med...</string> <string name="your_full_jid_will_be">Ditt jabber ID blir: %s</string> @@ -456,7 +438,6 @@ <string name="show_password">Visa lösenord</string> <string name="registration_please_wait">Registreringfel: Försök igen senare</string> <string name="registration_password_too_weak">Registreringsfel: Lösenordet är för svagt</string> - <string name="conference_subject">Ämne</string> <string name="choose_participants">Välj deltagare</string> <string name="invite_again">Bjud in igen</string> <string name="pref_broadcast_last_activity">Meddela din senaste aktivit</string> @@ -497,7 +478,6 @@ <string name="i_followed_this_link_from_a_trusted_source">Jag följde denna länk från en trovärdig källa</string> <string name="verifying_omemo_keys_trusted_source">Du håller på att verifiera OMEMO-nyckeln för %1$s efter att du följt en länk. Detta är endast säkert om du följde länken från en trovärdig källa där endast %2$s kan ha publiserat denna länk.</string> <string name="verify_omemo_keys">Verifiera OMEMO-nycklar</string> - <string name="encrypting_message">Krypterar meddelande</string> <string name="not_fetching_history_retention_period">Hämtar inte meddelanden på grund av inställningen för borttagning av gamla meddelanden.</string> <string name="block_jabber_id">Blockera Jabber-ID</string> <string name="corresponding_conversations_closed">Motsvarande konversationer är stängda.</string> diff --git a/src/main/res/values-tl/strings.xml b/src/main/res/values-tl/strings.xml index 3bbb58321..7b8b789d4 100644 --- a/src/main/res/values-tl/strings.xml +++ b/src/main/res/values-tl/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Walang harang na domain</string> <string name="title_activity_manage_accounts">Ang mga account ay pamahalaan</string> <string name="title_activity_settings">Settings</string> - <string name="title_activity_conference_details">Mga detalye ng kagrupo sa chat</string> + <string name="conference_details">Mga detalye ng kagrupo sa chat</string> <string name="title_activity_contact_details">Mga detalye ng pagkontak</string> <string name="title_activity_sharewith">Pag-uusap ay binahagi</string> <string name="title_activity_start_conversation">Simula na pag-uusap</string> @@ -70,11 +70,8 @@ <string name="add_contact">Magdagdag ng contact</string> <string name="send_failed">nabigo ang paghahatid</string> <string name="preparing_image">Paghahanda ng imahe para sa pagpapadala</string> - <string name="preparing_images">Paghahanda ng mga imahe para sa paghahatid</string> - <string name="sharing_files_please_wait">Pagbabahagi ng mga file. Mangyaring maghintay…</string> <string name="action_clear_history">Linisin ang kasaysayan</string> <string name="clear_conversation_history">I-clear ang Kasaysayan ng Pag-uusap</string> - <string name="delete_messages">Tanggalin ang mga mensahe</string> <string name="also_end_conversation">Tapusin ang pag-uusap na ito pagkatapos</string> <string name="choose_presence">Pumili ng device</string> <string name="send_unencrypted_message">Magpadala ng hindi naka-encrypt na mensahe</string> @@ -89,7 +86,6 @@ <string name="openkeychain_required">OpenKeychain</string> <string name="restart">I-restart</string> <string name="install">I-instal</string> - <string name="openkeychain_not_installed">Paki-instal ang OpenKeychain</string> <string name="offering">Hain…</string> <string name="waiting">Hintayin…</string> <string name="no_pgp_key">Walang OpenPGP key ang nakita</string> diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml index 4a0720b41..8edc8dbf6 100644 --- a/src/main/res/values-tr/strings.xml +++ b/src/main/res/values-tr/strings.xml @@ -17,7 +17,7 @@ <string name="action_unblock_domain">Domainin engelini kaldır</string> <string name="title_activity_manage_accounts">Hesapları Yönet</string> <string name="title_activity_settings">Ayarlar</string> - <string name="title_activity_conference_details">Grup sohbeti Detayları</string> + <string name="conference_details">Grup sohbeti Detayları</string> <string name="title_activity_contact_details">Kişi Detayları</string> <string name="title_activity_sharewith">Sohbetle Paylaş</string> <string name="title_activity_start_conversation">Konuşmayı Başlat</string> @@ -70,11 +70,8 @@ <string name="add_contact">Kişi ekle</string> <string name="send_failed">gönderme başarısız</string> <string name="preparing_image">Aktarım için resim hazırlanıyor</string> - <string name="preparing_images">Aktarım için resimler hazırlanıyor</string> - <string name="sharing_files_please_wait">Dosyalar paylaşılıyor. Lütfen bekleyin…</string> <string name="action_clear_history">Geçmişi sil</string> <string name="clear_conversation_history">Konuşma Geçmişini Sil</string> - <string name="delete_messages">Mesajları silin</string> <string name="also_end_conversation">Bu sohbeti daha sonra sonlandır</string> <string name="choose_presence">Cihazı seçin</string> <string name="send_unencrypted_message">Şifresiz mesaj gönder</string> @@ -90,7 +87,6 @@ <string name="openkeychain_required_long">Pix-Art messenger ileti şifreleme ile şifre çözme ve genel anahtarlarınzı yönetmek için üçüncü parti <b>OpenKeychain</b> denen bir uygulama kullanır.\n\nOpenKeychain GPLv3 lisansı altındadır ve F-Droid ile Google Play\'de mevcuttur.\n\n<small>(Lürfen Pix-Art messenger\'ı daha sonra yeniden başlatın.)</small></string> <string name="restart">Yeniden başlat</string> <string name="install">Yükle</string> - <string name="openkeychain_not_installed">Lütfen OpenKeychain\'i yükleyin</string> <string name="offering">teklif yapılıyor…</string> <string name="waiting">Bekleniyor…</string> <string name="no_pgp_key">Hiç OpenPGP anahtarı bulunamadı</string> @@ -124,7 +120,6 @@ <string name="receive_presence_updates">Durum güncellemelerini alın</string> <string name="ask_for_presence_updates">Durum güncellemelerini isteyin</string> <string name="attach_choose_picture">Resim seç</string> - <string name="attach_take_from_camera">Kameradan al</string> <string name="preemptively_grant">Önceden abone talep isteğinde bulunun</string> <string name="error_not_an_image_file">Seçtiğiniz dosya bir görüntü dosyası değil</string> <string name="error_compressing_image">Görüntü dosyası dönüştürürken hata oluştu</string> @@ -161,11 +156,8 @@ <string name="mgmt_account_delete_confirm_text">Hesabınızı silerseniz, tüm görüşme geçmişiniz kaybolur</string> <string name="attach_record_voice">Kayıt sesi</string> <string name="account_settings_jabber_id">Jabber-ID</string> - <string name="account_settings_password">Şifre</string> <string name="account_settings_example_jabber_id">kullanıcıadı@pix-art.de</string> <string name="password">Parola</string> - <string name="confirm_password">Parolayı onayla</string> - <string name="passwords_do_not_match">Parolalar uyuşmuyor</string> <string name="invalid_jid">Bu geçerli bir Jabber-ID değil</string> <string name="error_out_of_memory">Bellek yetersiz. Görüntü çok büyük</string> <string name="add_phone_book_text">Adres defterinize %s\'i eklemek istiyor musunuz?</string> @@ -204,11 +196,9 @@ <string name="done">Bitti</string> <string name="verify">Doğrula</string> <string name="decrypt">Şifreyi çöz</string> - <string name="conferences">Grup sohbeti</string> <string name="search">Ara</string> - <string name="create_contact">Kişi oluştur</string> <string name="enter_contact">İletişim Kur</string> - <string name="join_conference">Grup sohbetine katıl</string> + <string name="join_public_channel">Grup sohbetine katıl</string> <string name="delete_contact">Kişiyi sil</string> <string name="view_contact_details">Kişi ayrıntılarını görün</string> <string name="block_contact">Kişiyi engelleyin</string> @@ -217,12 +207,10 @@ <string name="select">Seç</string> <string name="contact_already_exists">Kişi zaten mevcut</string> <string name="join">Katıl</string> - <string name="conference_address">Grup sohbeti adresi</string> - <string name="conference_address_example">groupchat@room.pix-art.de</string> + <string name="channel_full_jid_example">groupchat@room.pix-art.de</string> <string name="save_as_bookmark">Yer imi olarak kaydet</string> <string name="delete_bookmark">Yer imini sil</string> <string name="bookmark_already_exists">Bu yer işareti zaten var</string> - <string name="action_edit_subject">Grup sohbetinin konusunu düzenle</string> <string name="joining_conference">Sohbet Grubuna bağlanılıyor…</string> <string name="leave">Ayrıl</string> <string name="contact_added_you">Kişi sizi listesine ekledi</string> @@ -303,9 +291,6 @@ <string name="verified">Doğrulandı!</string> <string name="smp_requested">İstenilen SMP doğrulamayla bağlantıyı kurun</string> <string name="no_otr_session_found">Geçerli bir OTR oturumu bulunamadı!</string> - <string name="pref_export_logs">Dışa aktarım geçmişi</string> - <string name="pref_export_logs_summary">Sohbet geçmişini SD karta yazın</string> - <string name="notification_export_logs_title">Sohbet geçmişi SD karta yazılıyor</string> <string name="choose_file">Dosya seç</string> <string name="receiving_x_file">%1$s alındığında (%2$d%% tamamlandı)</string> <string name="download_x_file">%s indir</string> @@ -366,13 +351,8 @@ <string name="ban_from_conference">Grup sohbetini engelle</string> <string name="removing_from_public_conference">%s adlı kullanıcıyı bir genel grup sohbetinden kaldırılmaya çalışılıyorsunuz. Bunu yapmanın tek yolu kullanıcıyı sonsuza dek yasaklamaktır.</string> <string name="ban_now">Şimdi engelle</string> - <string name="could_not_change_role">%s görevini değiştiremedim</string> - <string name="public_conference">Herkese açık sohbet grubu</string> - <string name="private_conference">Özel, sadece grup üyeleri</string> <string name="conference_options">Grup sohbeti seçenekleri</string> - <string name="members_only">Özel, yalnızca üyeler</string> <string name="non_anonymous">İsimsiz</string> - <string name="moderated">Denetlenen</string> <string name="you_are_not_participating">Katılım sağlamıyorsun</string> <string name="modified_conference_options">Grup sohbeti seçeneklerini düzenle!</string> <string name="could_not_modify_conference_options">Grup sohbeti seçenekleri değiştirilemedi</string> @@ -389,7 +369,6 @@ <string name="pdf_document">PDF belgesi</string> <string name="apk">Android Uygulaması</string> <string name="vcard">Bağlantı</string> - <string name="touch_to_open_conversations">Pix-Art Messenger\'ı açmak için dokunun</string> <string name="avatar_has_been_published">Avatar yayınlandı!</string> <string name="sending_x_file">Gönderiliyor %s</string> <string name="offering_x_file">Öneri %s</string> @@ -472,11 +451,7 @@ <item quantity="other">%d mesajlar</item> </plurals> <string name="load_more_messages">Daha fazla ileti yükle</string> - <string name="shared_file_with_x">%s ile paylaşılan dosya</string> - <string name="shared_image_with_x">%s ile paylaşılan resim</string> <string name="no_storage_permission">Pix-Art Messenger\'ın harici depolama alanına erişim izni gerekiyor</string> - <string name="shared_images_with_x">%s ile paylaşılan resimler</string> - <string name="shared_text_with_x">%s ile paylaşılan metin</string> <string name="sync_with_contacts">Bağlantıyı senkronize et</string> <string name="sync_with_contacts_long">Pix-Art Messenger, tam adlarını ve avatarlarını göstermek için XMPP listenizi kişilerinizle eşleştirmek istiyor\n\nPix-Art Messenger yalnızca kişilerinizi okuyacak ve bunları sunucunuza yüklemeden yerel olarak eşleştirecektir.\n\nKişilerinize erişmek için izin vermeniz istenecektir.</string> <string name="notify_on_all_messages">Tüm mesajları bildir</string> @@ -497,19 +472,16 @@ <string name="install_from_unknown_sources_disabled">Bilinmeyen açık kaynaklardan yüklemeleri devre dışı bırak</string> <string name="install_from_unknown_sources_disabled_dialog">Cihazınız, Google PlayStore dışında bilinmeyen kaynaklardan uygulama yüklemelerine izin vermeyecek şekilde yapılandırıldı. Bu, uygulama güncelleyicisinde sorunlara neden olur. \n\n Bundan sonra bilinmeyen kaynaklardan uygulama yüklemelerine izin vermeniz istenecektir.</string> <string name="disable">Devre dışı</string> - <string name="selection_too_large">Seçilen alan çok büyük</string> <string name="cancel_update">Güncellemeyi iptal et?</string> <string name="yes">Evet</string> <string name="no">Hayır</string> <string name="no_accounts">(Aktif hesap yok)</string> <string name="this_field_is_required">Bu alan gerekli</string> - <string name="no_participants">Katılımcı yok</string> <string name="correct_message">Doğru mesaj</string> <string name="send_corrected_message">Düzeltilmiş mesaj gönder</string> <string name="no_keys_just_confirm">Bu kişiye zaten güvenin. \'tamam\' seçeneğini seçerseniz, yalnızca%s, bu grup sohbetinin bir parçası olduğunu onaylıyorsunuz demektir.</string> <string name="contacts_have_no_pgp_keys">Kişileriniz genel anahtarlarını duyurmadığı için Pix-Art Messenger mesajlarınızı şifreleyemiyor.\n\n</string> <string name="this_account_is_disabled">Bu hesabı devre dışı bıraktınız</string> - <string name="security_error_invalid_file_access">Güvenlik hatası: Geçersiz dosya erişimi</string> <string name="no_application_to_share_uri">URI\'yi paylaşacak hiçbir uygulama bulunamadı</string> <string name="share_uri_with">Ile URI paylaş…</string> <string name="mgmt_account_reconnect">Yeniden bağlan</string> @@ -535,8 +507,7 @@ <string name="show_password">Parolayı göster</string> <string name="registration_please_wait">Kayıt başarısız: Daha sonra tekrar deneyin</string> <string name="registration_password_too_weak">Kayıt başarısız oldu: Şifre çok zayıf</string> - <string name="create_conference">Grup sohbeti oluştur</string> - <string name="conference_subject">Konu</string> + <string name="create_private_group_chat">Grup sohbeti oluştur</string> <string name="choose_participants">Katılımcıları seç</string> <string name="creating_conference">Grup sohbeti oluşturuluyor…</string> <string name="import_database">Yedeği içeri aktar</string> @@ -549,14 +520,8 @@ <string name="request_permissions_message">Pix-Art Messenger birkaç izin vermenizi isteyecektir. Tüm bu izinlerin bu mesajlaşma aracının tüm özelliklerine sahip olmasına izin vermeniz önemlidir. Bu izinlerden herhangi birini reddederseniz, uygulama kendiliğinden kapanacaktır.</string> <string name="request_permissions_message_again">Pix-Art Messenger için gerekli bazı veya tüm izinleri reddettiniz. Ayarlara atlamak ve bu izinlere izin vermek ister misiniz? Bu izinlerden herhangi birine siz karar verirseniz, uygulama kendiliğinden kapanacaktır.</string> <string name="unable_to_connect_to_keychain">OpenKeyChain\'e bağlanılamıyor</string> - <string name="send_image">Resim gönder?</string> <string name="this_device_is_no_longer_in_use">Bu cihaz artık kullanılmıyor</string> - <string name="import_canceled">İçe aktarım iptal edildi</string> - <string name="Import_failed">Veritabanı içe aktarma başarısız, içe aktarma mümkün değil</string> - <string name="Password_wrong">Yanlış parola, yeniden dene</string> - <string name="enter_account_password">Yedeklemenizi içe aktarmak için lütfen hesap şifrenizi girin.</string> <string name="please_wait">Lütfen bekleyin…</string> - <string name="databaseimport_started">Yedekleme içe aktarılacaktır, bu biraz zaman alabilir.</string> <string name="pref_export_plain_text_logs_summary">Sohbet günlüklerini insan tarafından okunabilir metin dosyaları olarak dışa aktarmayı etkinleştir</string> <string name="pref_export_plain_text_logs">Insanlar tarafından okunabilir metin dosyalarını dışa aktarın</string> <string name="payment_required">Ödeme gerekli</string> @@ -583,7 +548,6 @@ <string name="data_saver_enabled">Veri kaydedici etkin</string> <string name="data_saver_enabled_explained">İşletim sisteminiz Pix-Art Messenger\'ın arka planda olduğunda Internet\'e erişmesini kısıtlıyor. Yeni mesaj bildirimlerini almak için, Veri koruyucu açıkken Pix-Art Messenger\'a sınırsız erişime izin vermelisiniz.\n Pix-Art Messenger mümkün olduğunca veri kaydetmek için çaba gösterecektir.</string> <string name="device_does_not_support_data_saver">Cihazınız Pix-Art messenger\'ın veri koruyucusunu devre dışı bırakmayı desteklemiyor.</string> - <string name="navigate">Konuma gidin</string> <string name="add_to_contact_list">Bağlantı listesine ekle</string> <string name="contact">Bağlantı</string> <string name="unable_to_start_recording">Kayıt başlatılamıyor</string> @@ -603,7 +567,6 @@ <string name="verifying_omemo_keys_trusted_source">Bir bağlantıyı tıkladıktan sonra %1$s ürününün OMEMO tuşlarını doğrulamak üzeresin. Yalnızca %2$s tarafından bu bağlantıyı yayınlayabildiğiniz güvenilir bir kaynaktan bu bağlantıyı izlerseniz güvenli olur.</string> <string name="verify_omemo_keys">OMEMO parmak izlerini doğrulayın</string> <string name="reply">Yanıtla</string> - <string name="encrypting_message">Mesajı şifrele</string> <string name="transcoding_video">Video sıkıştırılıyor</string> <string name="not_fetching_history_retention_period">Yerel koruma süresi nedeniyle ileti getirilemiyor.</string> <string name="today">Bugün</string> @@ -668,7 +631,6 @@ <string name="pref_use_bundled_emoji_summary">Cihazlarınızın yerine emoji kitaplığını kullanın. Değişiklikler, uygulamayı yeniden açtıktan sonra kullanılabilir olacaktır.</string> <string name="invalid_muc_nick">Geçersiz kullanıcı adı</string> <string name="title_activity_share_via_account">Hesap yoluyla paylaş</string> - <string name="private_messages_are_disabled">Özel mesajlar devre dışı</string> <string name="destroy_muc">Grup sohbetini sil</string> <string name="conference_unknown_error">Artık bu grupta değilsiniz</string> <string name="destroy_muc_text">%s grubunu gerçekten silmek istiyor musunuz? Grup sohbeti kalıcı olarak silinecektir.</string> diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml index e3f355814..5a5bbc62a 100644 --- a/src/main/res/values/attrs.xml +++ b/src/main/res/values/attrs.xml @@ -20,6 +20,7 @@ <attr name="color_background_tertiary" format="reference|color" /> <attr name="color_background_secondary" format="reference|color" /> <attr name="color_background_primary" format="reference|color" /> + <attr name="color_background_overlay" format="reference|color" /> <attr name="edit_text_color" format="reference|color" /> <attr name="color_warning" format="reference|color" /> <attr name="EmojiColor" format="reference|color" /> @@ -78,7 +79,6 @@ <attr name="icon_settings" format="reference" /> <attr name="icon_share" format="reference" /> <attr name="icon_delete" format="reference" /> - <attr name="icon_import_export" format="reference" /> <attr name="icon_scan_qr_code" format="reference" /> <attr name="icon_enable_undecided_device" format="reference" /> <attr name="icon_scroll_down" format="reference" /> diff --git a/src/main/res/values/bools.xml b/src/main/res/values/bools.xml new file mode 100644 index 000000000..4807728ca --- /dev/null +++ b/src/main/res/values/bools.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <bool name="prefer_xmpp_avatar">true</bool> +</resources>
\ No newline at end of file diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index 936c5b36c..1d4c78b78 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <color name="realgreen">#388e3c</color> <!-- green 700 --> - <color name="primary">#0D47A1</color> <!-- blue 900 --> - <color name="primary_dark">#1A237E</color> <!-- indigo 900 --> + <color name="realgreen">#ff388e3c</color> <!-- green 700 --> + <color name="primary">#ff0D47A1</color> <!-- blue 900 --> + <color name="primary_dark">#ff1A237E</color> <!-- indigo 900 --> <color name="accent">#ff0091ea</color> <!-- light blue accent --> <color name="black87">#de000309</color> <color name="black54">#8a000309</color> @@ -18,36 +18,38 @@ <color name="white70">#b2e2e7f1</color> <color name="white87">#deffffff</color> <color name="green500">#ff4CAF50</color> <!-- green 500 --> - <color name="grey50">#fafafa</color> <!-- grey 50 --> + <color name="grey50">#fffafafa</color> <!-- grey 50 --> <color name="grey200">#ffeeeeee</color> <!-- grey 200 --> <color name="grey300">#ffe0e0e0</color> <!-- grey 300 --> + <color name="grey300_40">#66e0e0e0</color> <color name="grey500">#ff9e9e9e</color> <!-- grey 500 --> <color name="grey700">#ff616161</color> <!-- grey 700 --> <color name="grey800">#ff424242</color> <!-- grey 800 --> - <color name="grey900">#212121</color> <!-- grey 900 --> + <color name="grey900">#ff212121</color> <!-- grey 900 --> + <color name="grey900_40">#66212121</color> <color name="red800">#ffc62828</color> <!-- red 800 --> <color name="red500">#fff44336</color> <!-- red 500 --> - <color name="red700">#d32f2f</color> <!-- red 700 --> + <color name="red700">#ffd32f2f</color> <!-- red 700 --> <color name="orange500">#ffff9800</color> <!-- orange 500 --> - <color name="bubble">#37474f</color> <!-- blue grey 800 --> + <color name="bubble">#ff37474f</color> <!-- blue grey 800 --> <color name="realwhite">#ffffffff</color> <!-- white --> <!-- bubbles --> <color name="lightwhite">#fafafa</color> <!-- green 50 --> <color name="lightblue">#bbdefb</color> <!-- blue 100 --> - <color name="lightred">#ffcdd2</color> <!-- red 100 --> - <color name="lightgreen">#c8e6c9</color> <!-- green 100 --> + <color name="lightred">#ffffcdd2</color> <!-- red 100 --> + <color name="lightgreen">#ffc8e6c9</color> <!-- green 100 --> <color name="darkwhite">#ff424242</color> <!-- grey 800 --> - <color name="darkblue">#37474f</color> <!-- blue grey 800 --> - <color name="darkred">#b71c1c</color> <!-- red 900 --> - <color name="darkgreen">#1b5e20</color> <!-- green 900 --> + <color name="darkblue">#ff37474f</color> <!-- blue grey 800 --> + <color name="darkred">#ffb71c1c</color> <!-- red 900 --> + <color name="darkgreen">#ff1b5e20</color> <!-- green 900 --> - <color name="online">#388e3c</color> <!-- green 700 --> + <color name="online">#ff388e3c</color> <!-- green 700 --> <color name="away">#ffff9800</color> <!-- orange 500 --> <color name="notavailable">#fff44336</color> <!-- red 500 --> <color name="warning_button">#ffcdd2</color> <!-- red 100 --> - <color name="warning_button_dark">#C62828</color> <!-- red 800 --> + <color name="warning_button_dark">#ffC62828</color> <!-- red 800 --> <!-- scanner --> - <color name="scan_result_dots">#4caf50</color> <!-- green 500 --> + <color name="scan_result_dots">#ff4caf50</color> <!-- green 500 --> </resources> diff --git a/src/main/res/values/defaults.xml b/src/main/res/values/defaults.xml index 116081eb0..fadf26bbd 100644 --- a/src/main/res/values/defaults.xml +++ b/src/main/res/values/defaults.xml @@ -7,7 +7,7 @@ <string name="type_web" translatable="false">Web browser</string> <string name="type_console" translatable="false">Console</string> <string name="pref_about_message" translatable="false"><b>Pix-Art Messenger</b> - \n\nCopyright © 2014-2018 Christian Schneppe + \n\nCopyright © 2014-2019 Christian Schneppe \n\nhttp://jabber.pix-art.de \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ GNU General Public License for more details. \n\nYou should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses - \n\nDownload the full original source code at https://github.com/siacs/Conversations (Copyright © 2014-2018 Daniel Gultsch) + \n\nDownload the full original source code at https://github.com/siacs/Conversations (Copyright © 2014-2019 Daniel Gultsch) \n\nDownload the modified source code at https://github.com/kriztan/Pix-Art-Messenger \n\nPix-Art Messenger recommends blabber.im as provider. More information can be found at https://blabber.im \n\nYou can support me by making a donation in form of a gift in sense of § 516 ff BGB i.e. a gratuitous donation. @@ -54,6 +54,7 @@ \n\nhttps://www.openstreetmap.org/\n(Open Database License) \n\nhttp://xmpp.rocks/\n(The MIT License (MIT)) \n\nhttps://github.com/drakeet/ToastCompat/\n(Apache License, Version 2.0) + \n\nhttps://github.com/leinardi/FloatingActionButtonSpeedDial/\n(Apache License, Version 2.0) </string> <string name="default_resource" translatable="false">Phone</string> @@ -67,7 +68,7 @@ <bool name="vibrate_on_notification">true</bool> <bool name="led">true</bool> <string name="notification_ringtone">content://settings/system/notification_sound</string> - <bool name="notifications_from_strangers">false</bool> + <bool name="notifications_from_strangers">true</bool> <bool name="headsup_notifications">true</bool> <bool name="enable_quiet_hours">false</bool> <bool name="manually_change_presence">true</bool> diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml index 0c6ef41fb..d12dd39f2 100644 --- a/src/main/res/values/dimens.xml +++ b/src/main/res/values/dimens.xml @@ -16,7 +16,6 @@ <dimen name="toolbar_elevation">4dp</dimen> <dimen name="publish_avatar_top_margin">8dp</dimen> - <dimen name="publish_avatar_size">96dp</dimen> <dimen name="avatar_on_details_screen_size">56dp</dimen> <dimen name="input_label_vertical_spacing">4dp</dimen> @@ -28,4 +27,8 @@ <dimen name="background_image_opacity">0.12</dimen> <dimen name="rounded_image_border">5dp</dimen> + <dimen name="avatar">48dp</dimen> + <dimen name="avatar_on_conversation_overview">56dp</dimen> + <dimen name="avatar_on_status_message">32dp</dimen> + <dimen name="avatar_big">480dp</dimen> </resources> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index c7b8f70c7..0678c4710 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -14,19 +14,19 @@ <string name="action_unblock_contact">Unblock contact</string> <string name="action_block_domain">Block domain</string> <string name="action_unblock_domain">Unblock domain</string> - <string name="title_activity_manage_accounts">Manage Accounts</string> + <string name="title_activity_manage_accounts">Manage accounts</string> <string name="title_activity_settings">Settings</string> - <string name="title_activity_conference_details">Group chat Details</string> - <string name="title_activity_contact_details">Contact Details</string> - <string name="title_activity_sharewith">Share with Conversation</string> - <string name="title_activity_start_conversation">Start Conversation</string> + <string name="conference_details">Group chat details</string> + <string name="title_activity_contact_details">Contact details</string> + <string name="title_activity_sharewith">Share with conversation</string> + <string name="title_activity_start_conversation">Start conversation</string> <string name="title_activity_choose_contact">Choose contact</string> - <string name="title_activity_choose_contacts">Choose Contacts</string> + <string name="title_activity_choose_contacts">Choose contacts</string> <string name="title_activity_block_list">Block list</string> <string name="just_now">just now</string> <string name="minute_ago">1 min ago</string> <string name="minutes_ago">%d mins ago</string> - <string name="unread_conversations">unread Conversations</string> + <string name="unread_conversations">unread conversations</string> <string name="sending">sending…</string> <string name="message_decrypting">Decrypting message. Please wait…</string> <string name="pgp_message">OpenPGP encrypted message</string> @@ -71,12 +71,9 @@ <string name="add_contact">Add contact</string> <string name="send_failed">delivery failed</string> <string name="preparing_image">Preparing image for transmission</string> - <string name="preparing_images">Preparing images for transmission</string> - <string name="sharing_files_please_wait">Sharing files. Please wait…</string> <string name="action_clear_history">Clear history</string> <string name="clear_conversation_history">Clear Conversation History</string> <string name="clear_histor_msg">Are you sure you want to delete all messages within this conversation?\n\n<b>Warning:</b> This will not delete copies of those messages that are stored on other devices or servers.</string> - <string name="delete_messages">Delete messages</string> <string name="also_end_conversation">End this conversation afterwards</string> <string name="choose_presence">Choose device</string> <string name="send_unencrypted_message">Send unencrypted message</string> @@ -92,7 +89,6 @@ <string name="openkeychain_required_long">Pix-Art Messenger utilizes a third party app called <b>OpenKeychain</b> to encrypt and decrypt messages and to manage your public keys.\n\nOpenKeychain is licensed under GPLv3 and available on F-Droid and Google Play.\n\n<small>(Please restart Pix-Art Messenger afterwards.)</small></string> <string name="restart">Restart</string> <string name="install">Install</string> - <string name="openkeychain_not_installed">Please install OpenKeychain</string> <string name="offering">offering…</string> <string name="waiting">waiting…</string> <string name="no_pgp_key">No OpenPGP Key found</string> @@ -126,7 +122,6 @@ <string name="receive_presence_updates">Receive presence updates</string> <string name="ask_for_presence_updates">Ask for presence updates</string> <string name="attach_choose_picture">Choose picture</string> - <string name="attach_take_from_camera">Take from camera</string> <string name="preemptively_grant">Preemptively grant subscription request</string> <string name="error_not_an_image_file">The file you selected is not an image</string> <string name="error_compressing_image">Error while converting the image file</string> @@ -164,11 +159,8 @@ <string name="mgmt_account_delete_confirm_text">If you delete your account, your entire conversation history will be lost</string> <string name="attach_record_voice">Record voice</string> <string name="account_settings_jabber_id">Jabber-ID</string> - <string name="account_settings_password">Password</string> <string name="account_settings_example_jabber_id">username@pix-art.de</string> <string name="password">Password</string> - <string name="confirm_password">Confirm password</string> - <string name="passwords_do_not_match">Passwords do not match</string> <string name="invalid_jid">This is not a valid Jabber-ID</string> <string name="error_out_of_memory">Out of memory. Image is too large</string> <string name="add_phone_book_text">Do you want to add %s to your address book?</string> @@ -209,11 +201,9 @@ <string name="done">Done</string> <string name="verify">Verify</string> <string name="decrypt">Decrypt</string> - <string name="conferences">Group chats</string> + <string name="bookmarks">Bookmarks</string> <string name="search">Search</string> - <string name="create_contact">Create Contact</string> <string name="enter_contact">Enter Contact</string> - <string name="join_conference">Join group chat</string> <string name="delete_contact">Delete Contact</string> <string name="view_contact_details">View contact details</string> <string name="block_contact">Block contact</string> @@ -222,12 +212,11 @@ <string name="select">Select</string> <string name="contact_already_exists">The contact already exists</string> <string name="join">Join</string> - <string name="conference_address">Group chat address</string> - <string name="conference_address_example">groupchat@room.pix-art.de</string> + <string name="channel_full_jid_example">channel@conference.example.com/nick</string> + <string name="channel_bare_jid_example">channel@conference.example.com</string> <string name="save_as_bookmark">Save as bookmark</string> <string name="delete_bookmark">Delete bookmark</string> <string name="bookmark_already_exists">This bookmark already exists</string> - <string name="action_edit_subject">Edit group chat subject</string> <string name="topic">Topic</string> <string name="joining_conference">Joining group chat…</string> <string name="leave">Leave</string> @@ -314,9 +303,12 @@ <string name="smp_requested">Contact requested SMP verification</string> <string name="no_otr_session_found">No valid OTR session has been found!</string> <string name="conversations_foreground_service" translatable="false">Pix-Art Messenger</string> - <string name="pref_export_logs">Export history</string> - <string name="pref_export_logs_summary">Write conversations history logs to SD card</string> - <string name="notification_export_logs_title">Writing conversations to SD card</string> + <string name="pref_create_backup">Create backup</string> + <string name="pref_create_backup_summary">Write backup files to %s</string> + <string name="notification_create_backup_title">Creating backup files</string> + <string name="restoring_backup">Restoring backup</string> + <string name="notification_restored_backup_title">Your backup has been restored</string> + <string name="notification_restored_backup_subtitle">Do not forget to enable the account.</string> <string name="choose_file">Choose file</string> <string name="receiving_x_file">Receiving %1$s (%2$d%% completed)</string> <string name="download_x_file">Download %s</string> @@ -378,13 +370,7 @@ <string name="ban_from_conference">Ban from group chat</string> <string name="removing_from_public_conference">You are trying to remove %s from a public group chat. The only way to do that is to ban that user for ever.</string> <string name="ban_now">Ban now</string> - <string name="could_not_change_role">Could not change role of %s</string> - <string name="public_conference">Publicly accessible group chat</string> - <string name="private_conference">Private, members only group chat</string> - <string name="conference_options">Group chat options</string> - <string name="members_only">Private, members only</string> - <string name="non_anonymous">Non-anonymous</string> - <string name="moderated">Moderated</string> + <string name="non_anonymous">Make Jabber IDs visible to anyone</string> <string name="you_are_not_participating">You are not participating</string> <string name="modified_conference_options">Modified group chat options!</string> <string name="could_not_modify_conference_options">Could not modify group chat options</string> @@ -401,7 +387,6 @@ <string name="pdf_document">PDF document</string> <string name="apk">Android App</string> <string name="vcard">Contact</string> - <string name="touch_to_open_conversations">Touch to open Pix-Art Messenger</string> <string name="avatar_has_been_published">Avatar has been published!</string> <string name="sending_x_file">Sending %s</string> <string name="offering_x_file">Offering %s</string> @@ -488,11 +473,7 @@ <item quantity="other">%d messages</item> </plurals> <string name="load_more_messages">Load more messages</string> - <string name="shared_file_with_x">Shared file with %s</string> - <string name="shared_image_with_x">Shared image with %s</string> <string name="no_storage_permission">Pix-Art Messenger needs access to external storage</string> - <string name="shared_images_with_x">Shared images with %s</string> - <string name="shared_text_with_x">Shared text with %s</string> <string name="sync_with_contacts">Synchronize with contacts</string> <string name="sync_with_contacts_long">Pix-Art Messenger wants to match your XMPP roster with your contacts to show their full names and avatars.\n\nPix-Art Messenger will only read your contacts and match them locally without uploading them to your server.\n\nYou will now be asked to grant permission to access your contacts.</string> <string name="notify_on_all_messages">Notify on all messages</string> @@ -506,7 +487,7 @@ <string name="video_low">low (144p)</string> <string name="video_mid">middle (360p)</string> <string name="video_high">high (720p)</string> - <string name="video_original">Original (uncompressed)</string> + <string name="video_original">original (uncompressed)</string> <string name="always">Always</string> <string name="battery_optimizations_enabled">Battery optimizations enabled</string> <string name="battery_optimizations_enabled_explained">Your device is doing some heavy battery optimizations on Pix-Art Messenger that might lead to delayed notifications or even message loss.\nIt is recommended to disable those.</string> @@ -514,19 +495,16 @@ <string name="install_from_unknown_sources_disabled">Install from unknown sources disabled</string> <string name="install_from_unknown_sources_disabled_dialog">Your device is configured to not allow app installs from unknown sources except from Google PlayStore. This will cause problems with the in app updater.\n\nYou will now be asked to allow app installs from unknown sources.</string> <string name="disable">Disable</string> - <string name="selection_too_large">The selected area is too large</string> <string name="cancel_update">Cancel update?</string> <string name="yes">Yes</string> <string name="no">No</string> <string name="no_accounts">(No activated accounts)</string> <string name="this_field_is_required">This field is required</string> - <string name="no_participants">No participants</string> <string name="correct_message">Correct message</string> <string name="send_corrected_message">Send corrected message</string> <string name="no_keys_just_confirm">You already trust this contact. By selecting ’done’ you are just confirming that %s is part of this group chat.</string> <string name="contacts_have_no_pgp_keys">Pix-Art Messenger is unable to encrypt your messages because your contacts are not announcing their public keys.\n\n</string> <string name="this_account_is_disabled">You have disabled this account</string> - <string name="security_error_invalid_file_access">Security error: Invalid file access</string> <string name="no_application_to_share_uri">No application found to share URI</string> <string name="share_uri_with">Share URI with…</string> <string name="mgmt_account_reconnect">Reconnect</string> @@ -555,8 +533,6 @@ <string name="show_password">Show password</string> <string name="registration_please_wait">Registration failed: Try again later</string> <string name="registration_password_too_weak">Registration failed: Password too weak</string> - <string name="create_conference">Create group chat</string> - <string name="conference_subject">Subject</string> <string name="choose_participants">Choose participants</string> <string name="creating_conference">Creating group chat…</string> <string name="import_text">There is a backup on your device which can be imported.\nYour Messenger will be restarted during backup process. Shall the backup be imported? </string> @@ -570,14 +546,8 @@ <string name="request_permissions_message">Pix-Art Messenger will ask you to allow a few permissions. It is important that you allow all these permissions to use all features of this messenger. If you deny any of these permissions the app will close itself.</string> <string name="request_permissions_message_again">You have denied some or all permissions needed for Pix-Art Messenger. Would you like to jump to the settings and allow these permissions? If you denie any of these permissions, the app will close itself.</string> <string name="unable_to_connect_to_keychain">Unable to connect to OpenKeychain</string> - <string name="send_image">Send image?</string> <string name="this_device_is_no_longer_in_use">This device is no longer in use</string> - <string name="import_canceled">Import canceled</string> - <string name="Import_failed">Database import failed, an import is not possible</string> - <string name="Password_wrong">Wrong password, try again</string> - <string name="enter_account_password">Please enter your account password to import your backup.</string> <string name="please_wait">Please wait…</string> - <string name="databaseimport_started">Backup will be imported, this may take awhile.</string> <string name="pref_export_plain_text_logs_summary">Enable an export of chat logs as human readable text files</string> <string name="pref_export_plain_text_logs">Export human readable chat logs</string> <string name="payment_required">Payment required</string> @@ -604,7 +574,6 @@ <string name="data_saver_enabled">Data saver enabled</string> <string name="data_saver_enabled_explained">Your operating system is restricting Pix-Art Messenger from accessing the Internet when in background. To receive notifications of new messages you should allow Pix-Art Messenger unrestricted access when Data saver is on.\\nPix-Art Messenger will still make an effort to save data when possible.</string> <string name="device_does_not_support_data_saver">Your device does not supporting disabling data saver for Pix-Art Messenger.</string> - <string name="navigate">Navigate to location</string> <string name="add_to_contact_list">Add to contact list</string> <string name="contact">Contact</string> <string name="unable_to_start_recording">Unable to start recording</string> @@ -631,7 +600,6 @@ <string name="show_inactive_devices">Show inactive</string> <string name="hide_inactive_devices">Hide inactive</string> <string name="reply">Reply</string> - <string name="encrypting_message">Encrypting message</string> <string name="transcoding_video">Compressing video</string> <string name="not_fetching_history_retention_period">Not fetching messages due to local retention period.</string> <plurals name="seconds"> @@ -721,7 +689,6 @@ <string name="pref_use_bundled_emoji_summary">Use the integrated emoji library instead of using your devices ones. Changes will be available after reopening the app.</string> <string name="invalid_muc_nick">Invalid nickname</string> <string name="title_activity_share_via_account">Share via account</string> - <string name="private_messages_are_disabled">Private messages are disabled</string> <string name="destroy_muc">Destroy group chat</string> <string name="conference_unknown_error">You are no longer in this group chat</string> <string name="destroy_muc_text">Would you like to destroy the group chat %s? This will remove this group chat permanently.</string> @@ -732,7 +699,7 @@ <string name="huawei_protected_apps">Protected Apps</string> <string name="huawei_protected_apps_summary">To keep receiving notifications, even when the screen is turned off, you need to add Pix-Art Messenger to the list of protected apps.</string> <string name="pref_enable_multi_accounts_title">Enable multiple accounts</string> - <string name="pref_enable_multi_accounts_summary">You want to use multiple accounts, so you have to set a password for daily backups.</string> + <string name="pref_enable_multi_accounts_summary">Do you want to use multiple accounts?</string> <string name="mtm_accept_cert">Accept Unknown Certificate?</string> <string name="mtm_trust_anchor">The server certificate is not signed by a known Certificate Authority.</string> <string name="mtm_accept_servername">Accept Mismatching Server Name?</string> @@ -809,7 +776,6 @@ <string name="pref_show_record_voice_btn">Show record voice button</string> <string name="pref_use_quick_share_attachment_choice_summary">Replace send button with attachment choice if no message is typed. Otherwise show quick actions, which can be cofigured in the following setting.</string> <string name="pref_quick_share_attachment_choice">Attachment choice</string> - <string name="phone_book">Address book</string> <string name="unable_to_save_recording">Unable to save recording</string> <string name="foreground_service_channel_name">Foreground service</string> <string name="foreground_service_channel_description">This notification category is used to display a permanent notification indicating that Pix-Art Messenger is running.</string> @@ -823,14 +789,13 @@ <string name="pref_more_notification_settings">Notification Settings</string> <string name="pref_more_notification_settings_summary">Importance, Sound, Vibrate</string> <string name="video_compression_channel_name">Video compression</string> - <string name="backup_channel_name">Database backup</string> + <string name="backup_channel_name">Backup & Restore</string> <string name="app_update_channel_name">App update</string> <string name="action_group_details">Group details</string> <string name="view_media">View media</string> <string name="media_browser">Media browser</string> <string name="account_status_stream_opening_error">Stream opening error</string> <string name="action_open">Open</string> - <string name="action_delete">Delete</string> <string name="security_violation_not_attaching_file">File omitted due to security violation.</string> <string name="delete_file_dialog">Delete file</string> <string name="delete_file_dialog_msg">Are you sure you want to delete this file?\n\n<b>Warning:</b> This will not delete copies of this file that are stored on other devices or servers.</string> @@ -850,7 +815,7 @@ <string name="install_orbot">Install Orbot</string> <string name="start_orbot">Start Orbot</string> <string name="no_market_app_installed">No market app installed.</string> - <string name="group_chat_will_make_your_jabber_id_public">This group chat will make your Jabber ID public</string> + <string name="group_chat_will_make_your_jabber_id_public">This channel will make your Jabber ID public</string> <string name="ebook">e-book</string> <string name="your_name">Your name</string> <string name="enter_your_name">Enter your name</string> @@ -859,5 +824,51 @@ <string name="autojoin_groupchat">Automatically join this group chat</string> <string name="pref_play_gif_inside">Play GIF files in chat</string> <string name="pref_play_gif_inside_summary">Setting this to true plays GIF files directly inside the chat view.</string> + <string name="open_with">Open with…</string> <string name="server_info_adhoc_invite">XEP-0050: Ad-Hoc Commands: user invite</string> + <string name="choose_account">Choose account</string> + <string name="set_profile_picture">Pix-Art Messenger profile picture</string> + <string name="restore_backup">Restore backup</string> + <string name="restore">Restore</string> + <string name="enter_password_to_restore">Enter your password for the account %s to restore the backup.</string> + <string name="restore_warning">Do not use the restore backup feature in an attempt to clone (run simultaneously) an installation. Restoring a backup is only meant for migrations or in case you have lost the original device.</string> + <string name="unable_to_restore_backup">Unable to restore backup.</string> + <string name="unable_to_decrypt_backup">Unable to decrypt backup. Is the password correct?</string> + <string name="pref_prefer_xmpp_avatar_summary">Prefer the user\'s XMPP avatar instead of the one from your address book</string> + <string name="pref_prefer_xmpp_avatar">Prefer XMPP avatar</string> + <string name="view_users">View participants</string> + <string name="group_chat_members">Participants</string> + <string name="message_deleted">Message was deleted</string> + <string name="channel_details">Channel details</string> + <string name="join_public_channel">Join public channel</string> + <string name="create_private_group_chat">Create private group chat</string> + <string name="create_public_channel">Create public channel</string> + <string name="create_dialog_channel_name">Channel name</string> + <string name="xmpp_address">XMPP address</string> + <string name="please_enter_name">Please provide a name for the channel</string> + <string name="please_enter_xmpp_address">Please provide an XMPP address</string> + <string name="this_is_an_xmpp_address">This is an XMPP address. Please provide a name.</string> + <string name="creating_channel">Creating public channel…</string> + <string name="channel_already_exists">This channel already exits</string> + <string name="joined_an_existing_channel">You\'ve joined an existing channel</string> + <string name="unable_to_set_channel_configuration">Unable to set channel configuration</string> + <string name="back">Back</string> + <string name="conference_options">Private group chat configuration</string> + <string name="channel_options">Public channel configuration</string> + <string name="allow_participants_to_edit_subject">Allow anyone to edit the topic</string> + <string name="allow_participants_to_invite_others">Allow anyone to invite others</string> + <string name="anyone_can_edit_subject">Anyone can edit the topic.</string> + <string name="owners_can_edit_subject">Owners can edit the topic.</string> + <string name="admins_can_edit_subject">Admins can edit the topic.</string> + <string name="owners_can_invite_others">Owners can invite others.</string> + <string name="anyone_can_invite_others">Anyone can invite others.</string> + <string name="jabber_ids_are_visible_to_admins">Jabber IDs are visible to admins.</string> + <string name="jabber_ids_are_visible_to_anyone">Jabber IDs are visible to anyone.</string> + <string name="no_users_hint_channel">This public channel has no participants. Invite your contacts or use the share button to distribute its XMPP address.</string> + <string name="no_users_hint_group_chat">This private group chat has no participants.</string> + <string name="grant_owner_privileges">Grant owner privileges</string> + <string name="remove_owner_privileges">Revoke owner privileges</string> + <string name="remove_from_channel">Remove from channel</string> + <string name="ban_from_channel">Ban from channel</string> + <string name="manage_permission">Manage permissions</string> </resources> diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index 92e7cd49c..6f9898a75 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -13,6 +13,7 @@ <item name="color_background_primary">@color/grey50</item> <item name="color_background_secondary">@color/grey200</item> <item name="color_background_tertiary">@color/grey300</item> + <item name="color_background_overlay">@color/grey300_40</item> <item name="color_warning">@color/red700</item> <item name="TextColorOnline">@color/green500</item> <item name="TextColorError">@color/red800</item> @@ -66,7 +67,8 @@ </item> <item name="ic_send_voice_offline" type="reference">@drawable/ic_send_voice_offline</item> - <item name="media_preview_document" type="reference">@drawable/ic_description_black_48dp</item> + <item name="media_preview_document" type="reference">@drawable/ic_description_black_48dp + </item> <item name="media_preview_recording" type="reference">@drawable/ic_mic_black_48dp</item> <item name="media_preview_audio" type="reference">@drawable/ic_headset_black_48dp</item> <item name="media_preview_location" type="reference">@drawable/ic_room_black_48dp</item> @@ -99,8 +101,6 @@ <item name="icon_search" type="reference">@drawable/ic_search_white_24dp</item> <item name="icon_secure" type="reference">@drawable/ic_lock_open_white_24dp</item> <item name="icon_settings" type="reference">@drawable/ic_settings_black_24dp</item> - <item name="icon_import_export" type="reference">@drawable/ic_import_export_white_24dp - </item> <item name="icon_delete" type="reference">@drawable/ic_delete_black_24dp</item> <item name="icon_share" type="reference">@drawable/ic_share_black_24dp</item> <item name="icon_scan_qr_code" type="reference">@drawable/ic_qrcode_scan_white_24dp</item> @@ -123,7 +123,7 @@ <item name="delete_icon_alpha" type="float">0.70</item> <item name="ic_attach_camera" type="reference">@drawable/ic_attach_camera</item> - <item type="reference" name="ic_attach_videocam">@drawable/ic_attach_videocam</item> + <item name="ic_attach_videocam" type="reference">@drawable/ic_attach_videocam</item> <item name="ic_attach_document" type="reference">@drawable/ic_attach_document</item> <item name="ic_attach_location" type="reference">@drawable/ic_attach_location</item> <item name="ic_attach_photo" type="reference">@drawable/ic_attach_photo</item> @@ -169,6 +169,7 @@ <item name="color_background_primary">@color/grey700</item> <item name="color_background_secondary">@color/grey800</item> <item name="color_background_tertiary">@color/grey900</item> + <item name="color_background_overlay">@color/grey900_40</item> <item name="color_warning">@color/red700</item> <item name="TextColorOnline">@color/green500</item> <item name="TextColorError">@color/red500</item> @@ -242,7 +243,8 @@ <item name="dialog_horizontal_padding">24dp</item> <item name="dialog_vertical_padding">16dp</item> - <item name="media_preview_document" type="reference">@drawable/ic_description_white_48dp</item> + <item name="media_preview_document" type="reference">@drawable/ic_description_white_48dp + </item> <item name="media_preview_recording" type="reference">@drawable/ic_mic_white_48dp</item> <item name="media_preview_audio" type="reference">@drawable/ic_headset_white_48dp</item> <item name="media_preview_location" type="reference">@drawable/ic_room_white_48dp</item> @@ -274,8 +276,6 @@ <item name="icon_search" type="reference">@drawable/ic_search_white_24dp</item> <item name="icon_secure" type="reference">@drawable/ic_lock_open_white_24dp</item> <item name="icon_settings" type="reference">@drawable/ic_settings_white_24dp</item> - <item name="icon_import_export" type="reference">@drawable/ic_import_export_white_24dp - </item> <item name="icon_delete" type="reference">@drawable/ic_delete_white_24dp</item> <item name="icon_share" type="reference">@drawable/ic_share_white_24dp</item> <item name="icon_scan_qr_code" type="reference">@drawable/ic_qrcode_scan_white_24dp</item> diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 747338435..52a957f98 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -76,6 +76,11 @@ android:key="play_gif_inside" android:summary="@string/pref_play_gif_inside_summary" android:title="@string/pref_play_gif_inside" /> + <CheckBoxPreference + android:defaultValue="@bool/prefer_xmpp_avatar" + android:key="prefer_xmpp_avatar" + android:summary="@string/pref_prefer_xmpp_avatar_summary" + android:title="@string/pref_prefer_xmpp_avatar" /> </PreferenceScreen> <!--Notifications--> <PreferenceScreen @@ -307,11 +312,11 @@ android:key="crashreport" android:summary="@string/pref_send_crash_summary" android:title="@string/pref_send_crash" /> - <PreferenceCategory android:title="@string/pref_export_logs"> + <PreferenceCategory android:title="@string/pref_create_backup"> <Preference - android:key="export_logs" - android:summary="@string/pref_export_logs_summary" - android:title="@string/pref_export_logs" /> + android:key="create_backup" + android:summary="@string/pref_create_backup_summary" + android:title="@string/pref_create_backup" /> <CheckBoxPreference android:defaultValue="@bool/plain_text_logs" android:key="export_plain_text_logs" diff --git a/src/standardPush/AndroidManifest.xml b/src/standardPush/AndroidManifest.xml index 08aafe900..a54f8bee1 100644 --- a/src/standardPush/AndroidManifest.xml +++ b/src/standardPush/AndroidManifest.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" package="de.pixart.messenger"> - <application> + <application tools:ignore="GoogleAppIndexingWarning"> <meta-data android:name="firebase_analytics_collection_deactivated" @@ -16,7 +17,7 @@ android:exported="true" android:permission="android.permission.CHANGE_CONFIGURATION"> <intent-filter> - <action android:name="eu.siacs.conversations.RENEW_INSTANCE_ID" /> + <action android:name="de.pixart.messenger.RENEW_INSTANCE_ID" /> </intent-filter> </receiver> @@ -27,11 +28,5 @@ <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> - - <service android:name=".services.InstanceIdService"> - <intent-filter> - <action android:name="com.google.firebase.INSTANCE_ID_EVENT" /> - </intent-filter> - </service> </application> </manifest> diff --git a/src/standardPush/java/de/pixart/messenger/services/InstanceIdService.java b/src/standardPush/java/de/pixart/messenger/services/InstanceIdService.java deleted file mode 100644 index 9a3309be4..000000000 --- a/src/standardPush/java/de/pixart/messenger/services/InstanceIdService.java +++ /dev/null @@ -1,29 +0,0 @@ -package de.pixart.messenger.services; - -import android.content.Intent; -import android.support.v4.content.ContextCompat; -import android.util.Log; - -import com.google.firebase.iid.FirebaseInstanceIdService; - -import de.pixart.messenger.Config; -import de.pixart.messenger.utils.Compatibility; - -public class InstanceIdService extends FirebaseInstanceIdService { - - @Override - public void onTokenRefresh() { - final Intent intent = new Intent(this, XmppConnectionService.class); - intent.setAction(XmppConnectionService.ACTION_FCM_TOKEN_REFRESH); - try { - if (Compatibility.runsAndTargetsTwentySix(this)) { - intent.putExtra(EventReceiver.EXTRA_NEEDS_FOREGROUND_SERVICE, true); - ContextCompat.startForegroundService(this, intent); - } else { - startService(intent); - } - } catch (IllegalStateException e) { - Log.e(Config.LOGTAG, "InstanceIdService is not allowed to start service"); - } - } -}
\ No newline at end of file diff --git a/src/standardPush/java/de/pixart/messenger/services/MaintenanceReceiver.java b/src/standardPush/java/de/pixart/messenger/services/MaintenanceReceiver.java index 5706a487a..31ed95a88 100644 --- a/src/standardPush/java/de/pixart/messenger/services/MaintenanceReceiver.java +++ b/src/standardPush/java/de/pixart/messenger/services/MaintenanceReceiver.java @@ -3,7 +3,6 @@ package de.pixart.messenger.services; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.support.v4.content.ContextCompat; import android.util.Log; import com.google.firebase.iid.FirebaseInstanceId; @@ -17,7 +16,7 @@ public class MaintenanceReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.d(Config.LOGTAG, "received intent in maintenance receiver"); - if ("eu.siacs.conversations.RENEW_INSTANCE_ID".equals(intent.getAction())) { + if ("de.pixart.messenger.RENEW_INSTANCE_ID".equals(intent.getAction())) { renewInstanceToken(context); } @@ -29,12 +28,7 @@ public class MaintenanceReceiver extends BroadcastReceiver { FirebaseInstanceId.getInstance().deleteInstanceId(); final Intent intent = new Intent(context, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_FCM_TOKEN_REFRESH); - if (Compatibility.runsAndTargetsTwentySix(context)) { - intent.putExtra(EventReceiver.EXTRA_NEEDS_FOREGROUND_SERVICE, true); - ContextCompat.startForegroundService(context, intent); - } else { - context.startService(intent); - } + Compatibility.startService(context, intent); } catch (IOException e) { Log.d(Config.LOGTAG, "unable to renew instance token", e); } diff --git a/src/standardPush/java/de/pixart/messenger/services/PushMessageReceiver.java b/src/standardPush/java/de/pixart/messenger/services/PushMessageReceiver.java index f07167f8d..c6d9b96be 100644 --- a/src/standardPush/java/de/pixart/messenger/services/PushMessageReceiver.java +++ b/src/standardPush/java/de/pixart/messenger/services/PushMessageReceiver.java @@ -1,7 +1,6 @@ package de.pixart.messenger.services; import android.content.Intent; -import android.support.v4.content.ContextCompat; import android.util.Log; import com.google.firebase.messaging.FirebaseMessagingService; @@ -24,15 +23,17 @@ public class PushMessageReceiver extends FirebaseMessagingService { final Intent intent = new Intent(this, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_FCM_MESSAGE_RECEIVED); intent.putExtra("account", data.get("account")); - try { - if (Compatibility.runsAndTargetsTwentySix(this)) { - intent.putExtra(EventReceiver.EXTRA_NEEDS_FOREGROUND_SERVICE, true); - ContextCompat.startForegroundService(this, intent); - } else { - startService(intent); - } - } catch (IllegalStateException e) { - Log.e(Config.LOGTAG, "PushMessageReceiver is not allowed to start service"); + Compatibility.startService(this, intent); + } + + @Override + public void onNewToken(String token) { + if (!EventReceiver.hasEnabledAccounts(this)) { + Log.d(Config.LOGTAG, "PushMessageReceiver ignored new token because no accounts are enabled"); + return; } + final Intent intent = new Intent(this, XmppConnectionService.class); + intent.setAction(XmppConnectionService.ACTION_FCM_TOKEN_REFRESH); + Compatibility.startService(this, intent); } }
\ No newline at end of file |