diff --git a/.travis.yml b/.travis.yml index 247e8fd9b..239637ba4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ android: - '.+' before_script: - mkdir libs - - wget -O libs/libwebrtc-m84.aar http://gultsch.de/files/libwebrtc-m84.aar + - wget -O libs/libwebrtc-m85.aar https://gultsch.de/files/libwebrtc-m85.aar script: - ./gradlew assembleConversationsFreeSystemRelease - ./gradlew assembleQuicksyFreeCompatRelease diff --git a/CHANGELOG.md b/CHANGELOG.md index 89f2e0d30..532d3c0b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +### Version 2.9.0 + +* Search individual conversations +* Notify user if message delivery fails +* Remember display names (nicks) from Quicksy users across restarts +* Add button to start Orbot (Tor) from notification if necessary + ### Version 2.8.10 * Handle GPX files diff --git a/build.gradle b/build.gradle index 5f99f8268..c63d1a14f 100644 --- a/build.gradle +++ b/build.gradle @@ -80,7 +80,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:3.12.12' implementation 'com.google.guava:guava:27.1-android' quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.11.1' - //implementation fileTree(include: ['libwebrtc-m83.aar'], dir: 'libs') + //implementation fileTree(include: ['libwebrtc-m85.aar'], dir: 'libs') implementation 'org.webrtc:google-webrtc:1.0.32006' } @@ -96,8 +96,8 @@ android { defaultConfig { minSdkVersion 21 targetSdkVersion 28 - versionCode 398 - versionName "2.8.10.1" + versionCode 399 + versionName "2.9.0" archivesBaseName += "-$versionName" applicationId "eu.sum7.conversations" resValue "string", "applicationId", applicationId diff --git a/src/conversations/res/values-pl/strings.xml b/src/conversations/res/values-pl/strings.xml index 85a833b15..9108588df 100644 --- a/src/conversations/res/values-pl/strings.xml +++ b/src/conversations/res/values-pl/strings.xml @@ -8,4 +8,5 @@ Zostałeś zaproszony do %1$s. Poprowadzimy ciebie przez proces tworzenia konta.\nWybierając %1$s jako dostawcę będziesz mógł komunikować się z innymi użytkownikami podając swój pełny adres XMPP. Zostałeś zaproszony do %1$s. Nazwa użytkownika została już dla ciebie wybrana. Poprowadzimy ciebie przez proces tworzenia konta.\nBęziesz mógł komunikować się z innymi użytkownikami podając swój adres XMPP. Zaproszenie twojego serwera - + Niepoprawnie sformatowany kod zaopatrywania + diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index c78d32a98..ffd06ec0f 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + @@ -36,10 +37,6 @@ - - { return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : name); } + @Override + public String getAvatarName() { + return name; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java index 2227e4a80..0701afcfe 100644 --- a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java @@ -13,81 +13,81 @@ import eu.siacs.conversations.xmpp.stanzas.PresencePacket; public class PresenceGenerator extends AbstractGenerator { - public PresenceGenerator(XmppConnectionService service) { - super(service); - } + public PresenceGenerator(XmppConnectionService service) { + super(service); + } - private PresencePacket subscription(String type, Contact contact) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("type", type); - packet.setTo(contact.getJid()); - packet.setFrom(contact.getAccount().getJid().asBareJid()); - return packet; - } + private PresencePacket subscription(String type, Contact contact) { + PresencePacket packet = new PresencePacket(); + packet.setAttribute("type", type); + packet.setTo(contact.getJid()); + packet.setFrom(contact.getAccount().getJid().asBareJid()); + return packet; + } - public PresencePacket requestPresenceUpdatesFrom(Contact contact) { - PresencePacket packet = subscription("subscribe", contact); - String displayName = contact.getAccount().getDisplayName(); - if (!TextUtils.isEmpty(displayName)) { - packet.addChild("nick",Namespace.NICK).setContent(displayName); - } - return packet; - } + public PresencePacket requestPresenceUpdatesFrom(Contact contact) { + PresencePacket packet = subscription("subscribe", contact); + String displayName = contact.getAccount().getDisplayName(); + if (!TextUtils.isEmpty(displayName)) { + packet.addChild("nick", Namespace.NICK).setContent(displayName); + } + return packet; + } - public PresencePacket stopPresenceUpdatesFrom(Contact contact) { - return subscription("unsubscribe", contact); - } + public PresencePacket stopPresenceUpdatesFrom(Contact contact) { + return subscription("unsubscribe", contact); + } - public PresencePacket stopPresenceUpdatesTo(Contact contact) { - return subscription("unsubscribed", contact); - } + public PresencePacket stopPresenceUpdatesTo(Contact contact) { + return subscription("unsubscribed", contact); + } - public PresencePacket sendPresenceUpdatesTo(Contact contact) { - return subscription("subscribed", contact); - } + public PresencePacket sendPresenceUpdatesTo(Contact contact) { + return subscription("subscribed", contact); + } - public PresencePacket selfPresence(Account account, Presence.Status status) { - return selfPresence(account, status, true); - } + public PresencePacket selfPresence(Account account, Presence.Status status) { + return selfPresence(account, status, true); + } - public PresencePacket selfPresence(final Account account, final Presence.Status status, final boolean personal) { - final PresencePacket packet = new PresencePacket(); - if (personal) { - final String sig = account.getPgpSignature(); - final String message = account.getPresenceStatusMessage(); - if(status.toShowString() != null) { - packet.addChild("show").setContent(status.toShowString()); - } - if (!TextUtils.isEmpty(message)) { - packet.addChild(new Element("status").setContent(message)); - } - if (sig != null && mXmppConnectionService.getPgpEngine() != null) { - packet.addChild("x", "jabber:x:signed").setContent(sig); - } - } - final String capHash = getCapHash(account); - if (capHash != null) { - Element cap = packet.addChild("c", - "http://jabber.org/protocol/caps"); - cap.setAttribute("hash", "sha-1"); - cap.setAttribute("node", "https://sum7.eu"); - cap.setAttribute("ver", capHash); - } - return packet; - } + public PresencePacket selfPresence(final Account account, final Presence.Status status, final boolean personal) { + final PresencePacket packet = new PresencePacket(); + if (personal) { + final String sig = account.getPgpSignature(); + final String message = account.getPresenceStatusMessage(); + if (status.toShowString() != null) { + packet.addChild("show").setContent(status.toShowString()); + } + if (!TextUtils.isEmpty(message)) { + packet.addChild(new Element("status").setContent(message)); + } + if (sig != null && mXmppConnectionService.getPgpEngine() != null) { + packet.addChild("x", "jabber:x:signed").setContent(sig); + } + } + final String capHash = getCapHash(account); + if (capHash != null) { + Element cap = packet.addChild("c", + "http://jabber.org/protocol/caps"); + cap.setAttribute("hash", "sha-1"); + cap.setAttribute("node", "http://conversations.im"); + cap.setAttribute("ver", capHash); + } + return packet; + } - public PresencePacket leave(final MucOptions mucOptions) { - PresencePacket presencePacket = new PresencePacket(); - presencePacket.setTo(mucOptions.getSelf().getFullJid()); - presencePacket.setFrom(mucOptions.getAccount().getJid()); - presencePacket.setAttribute("type", "unavailable"); - return presencePacket; - } + public PresencePacket leave(final MucOptions mucOptions) { + PresencePacket presencePacket = new PresencePacket(); + presencePacket.setTo(mucOptions.getSelf().getFullJid()); + presencePacket.setFrom(mucOptions.getAccount().getJid()); + presencePacket.setAttribute("type", "unavailable"); + return presencePacket; + } - public PresencePacket sendOfflinePresence(Account account) { - PresencePacket packet = new PresencePacket(); - packet.setFrom(account.getJid()); - packet.setAttribute("type","unavailable"); - return packet; - } + public PresencePacket sendOfflinePresence(Account account) { + PresencePacket packet = new PresencePacket(); + packet.setFrom(account.getJid()); + packet.setAttribute("type", "unavailable"); + return packet; + } } diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 7622d3071..07541e7cb 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -300,6 +300,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } else { Contact contact = account.getRoster().getContact(user); if (contact.setPresenceName(nick)) { + mXmppConnectionService.syncRoster(account); mXmppConnectionService.getAvatarService().clear(contact); } } @@ -307,8 +308,14 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece mXmppConnectionService.updateAccountUi(); } - private boolean handleErrorMessage(Account account, MessagePacket packet) { + private boolean handleErrorMessage(final Account account, final MessagePacket packet) { if (packet.getType() == MessagePacket.TYPE_ERROR) { + if (packet.fromServer(account)) { + final Pair forwarded = packet.getForwardedMessagePacket("received", "urn:xmpp:carbons:2"); + if (forwarded != null) { + return handleErrorMessage(account, forwarded.first); + } + } final Jid from = packet.getFrom(); final String id = packet.getId(); if (from != null && id != null) { @@ -362,7 +369,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece final Element result = MessageArchiveService.Version.findResult(original); final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); if (query != null && query.validFrom(original.getFrom())) { - Pair f = original.getForwardedMessagePacket("result", query.version.namespace); + final Pair f = original.getForwardedMessagePacket("result", query.version.namespace); if (f == null) { return; } @@ -370,6 +377,9 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece packet = f.first; serverMsgId = result.getAttribute("id"); query.incrementMessageCount(); + if (handleErrorMessage(account, packet)) { + return; + } } else if (query != null) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received mam result from invalid sender"); return; @@ -1011,8 +1021,9 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece final String nick = packet.findChildContent("nick", Namespace.NICK); if (nick != null && InvalidJid.hasValidFrom(original)) { - Contact contact = account.getRoster().getContact(from); + final Contact contact = account.getRoster().getContact(from); if (contact.setPresenceName(nick)) { + mXmppConnectionService.syncRoster(account); mXmppConnectionService.getAvatarService().clear(contact); } } diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index 4e862ddf6..7f57ef733 100644 --- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java @@ -337,6 +337,7 @@ public class PresenceParser extends AbstractParser implements mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, false); } else if (type.equals("subscribe")) { if (contact.setPresenceName(packet.findChildContent("nick", Namespace.NICK))) { + mXmppConnectionService.syncRoster(account); mXmppConnectionService.getAvatarService().clear(contact); } if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) { diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index f2a59348e..dce2f8e20 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -63,11 +63,12 @@ import eu.siacs.conversations.xmpp.Jid; public class DatabaseBackend extends SQLiteOpenHelper { private static final String DATABASE_NAME = "history"; - private static final int DATABASE_VERSION = 47; + private static final int DATABASE_VERSION = 48; private static DatabaseBackend instance = null; private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " + Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT," + + Contact.PRESENCE_NAME + " TEXT," + Contact.JID + " TEXT," + Contact.KEYS + " TEXT," + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER," + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, " @@ -559,6 +560,9 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL(CREATE_RESOLVER_RESULTS_TABLE); } + if (oldVersion < 48 && newVersion >= 48) { + db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + Contact.PRESENCE_NAME + " TEXT"); + } } private void canonicalizeJids(SQLiteDatabase db) { @@ -577,7 +581,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { continue; } - String updateArgs[] = { + final String[] updateArgs = { newJid, cursor.getString(cursor.getColumnIndex(Conversation.UUID)), }; @@ -780,11 +784,20 @@ public class DatabaseBackend extends SQLiteOpenHelper { return list; } - public Cursor getMessageSearchCursor(List term) { - SQLiteDatabase db = this.getReadableDatabase(); - String SQL = "SELECT " + Message.TABLENAME + ".*," + Conversation.TABLENAME + '.' + Conversation.CONTACTJID + ',' + Conversation.TABLENAME + '.' + Conversation.ACCOUNT + ',' + Conversation.TABLENAME + '.' + Conversation.MODE + " FROM " + Message.TABLENAME + " join " + Conversation.TABLENAME + " on " + Message.TABLENAME + '.' + Message.CONVERSATION + '=' + Conversation.TABLENAME + '.' + Conversation.UUID + " join messages_index ON messages_index.uuid=messages.uuid where " + Message.ENCRYPTION + " NOT IN(" + Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE + ',' + Message.ENCRYPTION_PGP + ',' + Message.ENCRYPTION_DECRYPTION_FAILED + ',' + Message.ENCRYPTION_AXOLOTL_FAILED + ") AND " + Message.TYPE + " IN(" + Message.TYPE_TEXT + ',' + Message.TYPE_PRIVATE + ") AND messages_index.body MATCH ? ORDER BY " + Message.TIME_SENT + " DESC limit " + Config.MAX_SEARCH_RESULTS; + public Cursor getMessageSearchCursor(final List term, final String uuid) { + final SQLiteDatabase db = this.getReadableDatabase(); + final StringBuilder SQL = new StringBuilder(); + final String[] selectionArgs; + SQL.append("SELECT " + Message.TABLENAME + ".*," + Conversation.TABLENAME + '.' + Conversation.CONTACTJID + ',' + Conversation.TABLENAME + '.' + Conversation.ACCOUNT + ',' + Conversation.TABLENAME + '.' + Conversation.MODE + " FROM " + Message.TABLENAME + " join " + Conversation.TABLENAME + " on " + Message.TABLENAME + '.' + Message.CONVERSATION + '=' + Conversation.TABLENAME + '.' + Conversation.UUID + " join messages_index ON messages_index.uuid=messages.uuid where " + Message.ENCRYPTION + " NOT IN(" + Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE + ',' + Message.ENCRYPTION_PGP + ',' + Message.ENCRYPTION_DECRYPTION_FAILED + ',' + Message.ENCRYPTION_AXOLOTL_FAILED + ") AND " + Message.TYPE + " IN(" + Message.TYPE_TEXT + ',' + Message.TYPE_PRIVATE + ") AND messages_index.body MATCH ?"); + if (uuid == null) { + selectionArgs = new String[]{FtsUtils.toMatchString(term)}; + } else { + selectionArgs = new String[]{FtsUtils.toMatchString(term), uuid}; + SQL.append(" AND "+Conversation.TABLENAME+'.'+Conversation.UUID+"=?"); + } + SQL.append(" ORDER BY " + Message.TIME_SENT + " DESC limit " + Config.MAX_SEARCH_RESULTS); Log.d(Config.LOGTAG, "search term: " + FtsUtils.toMatchString(term)); - return db.rawQuery(SQL, new String[]{FtsUtils.toMatchString(term)}); + return db.rawQuery(SQL.toString(), selectionArgs); } public List markFileAsDeleted(final File file, final boolean internal) { @@ -1006,7 +1019,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { final SQLiteDatabase db = this.getWritableDatabase(); db.beginTransaction(); for (Contact contact : roster.getContacts()) { - if (contact.getOption(Contact.Options.IN_ROSTER) || contact.getAvatarFilename() != null || contact.getOption(Contact.Options.SYNCED_VIA_OTHER)) { + if (contact.getOption(Contact.Options.IN_ROSTER) || contact.hasAvatarOrPresenceName() || contact.getOption(Contact.Options.SYNCED_VIA_OTHER)) { db.insert(Contact.TABLENAME, null, contact.getContentValues()); } else { String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?"; diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index 92835b8a9..1b29194e6 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -685,5 +685,6 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { public interface Avatarable { @ColorInt int getAvatarBackgroundColor(); + String getAvatarName(); } } diff --git a/src/main/java/eu/siacs/conversations/services/EventReceiver.java b/src/main/java/eu/siacs/conversations/services/EventReceiver.java index 985effc3a..500eb25e5 100644 --- a/src/main/java/eu/siacs/conversations/services/EventReceiver.java +++ b/src/main/java/eu/siacs/conversations/services/EventReceiver.java @@ -8,6 +8,8 @@ import android.preference.PreferenceManager; import android.support.v4.content.ContextCompat; import android.util.Log; +import com.google.common.base.Strings; + import eu.siacs.conversations.Config; import eu.siacs.conversations.utils.Compatibility; @@ -19,17 +21,13 @@ public class EventReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, final Intent originalIntent) { final Intent intentForService = new Intent(context, XmppConnectionService.class); - if (originalIntent.getAction() != null) { - intentForService.setAction(originalIntent.getAction()); - final Bundle extras = originalIntent.getExtras(); - if (extras != null) { - intentForService.putExtras(extras); - } - } else { - intentForService.setAction("other"); - } final String action = originalIntent.getAction(); - if (action.equals("ui") || hasEnabledAccounts(context)) { + intentForService.setAction(Strings.isNullOrEmpty(action) ? "other" : action); + final Bundle extras = originalIntent.getExtras(); + if (extras != null) { + intentForService.putExtras(extras); + } + if ("ui".equals(action) || hasEnabledAccounts(context)) { Compatibility.startService(context, intentForService); } else { Log.d(Config.LOGTAG, "EventReceiver ignored action " + intentForService.getAction()); diff --git a/src/main/java/eu/siacs/conversations/services/MessageSearchTask.java b/src/main/java/eu/siacs/conversations/services/MessageSearchTask.java index 6fd2413f8..c18c94cf2 100644 --- a/src/main/java/eu/siacs/conversations/services/MessageSearchTask.java +++ b/src/main/java/eu/siacs/conversations/services/MessageSearchTask.java @@ -56,18 +56,20 @@ public class MessageSearchTask implements Runnable, Cancellable { private final XmppConnectionService xmppConnectionService; private final List term; + private final String uuid; private final OnSearchResultsAvailable onSearchResultsAvailable; private boolean isCancelled = false; - private MessageSearchTask(XmppConnectionService xmppConnectionService, List term, OnSearchResultsAvailable onSearchResultsAvailable) { + private MessageSearchTask(XmppConnectionService xmppConnectionService, List term, final String uuid, OnSearchResultsAvailable onSearchResultsAvailable) { this.xmppConnectionService = xmppConnectionService; this.term = term; + this.uuid = uuid; this.onSearchResultsAvailable = onSearchResultsAvailable; } - public static void search(XmppConnectionService xmppConnectionService, List term, OnSearchResultsAvailable onSearchResultsAvailable) { - new MessageSearchTask(xmppConnectionService, term, onSearchResultsAvailable).executeInBackground(); + public static void search(XmppConnectionService xmppConnectionService, List term, final String uuid, OnSearchResultsAvailable onSearchResultsAvailable) { + new MessageSearchTask(xmppConnectionService, term, uuid, onSearchResultsAvailable).executeInBackground(); } public static void cancelRunningTasks() { @@ -86,7 +88,7 @@ public class MessageSearchTask implements Runnable, Cancellable { try { final HashMap conversationCache = new HashMap<>(); final List result = new ArrayList<>(); - cursor = xmppConnectionService.databaseBackend.getMessageSearchCursor(term); + cursor = xmppConnectionService.databaseBackend.getMessageSearchCursor(term, uuid); long dbTimer = SystemClock.elapsedRealtime(); if (isCancelled) { Log.d(Config.LOGTAG, "canceled search task"); diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index c53dbd7d5..3c2a093bc 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -61,6 +61,7 @@ import eu.siacs.conversations.ui.TimePreference; import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.Compatibility; import eu.siacs.conversations.utils.GeoHelper; +import eu.siacs.conversations.utils.TorServiceUtils; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection; @@ -82,6 +83,7 @@ public class NotificationService { private static final int ERROR_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 6; private static final int INCOMING_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 8; public static final int ONGOING_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 10; + private static final int DELIVERY_FAILED_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 12; private final XmppConnectionService mXmppConnectionService; private final LinkedHashMap> notifications = new LinkedHashMap<>(); private final HashMap mBacklogMessageCounter = new HashMap<>(); @@ -220,9 +222,20 @@ public class NotificationService { quietHoursChannel.setSound(null, null); notificationManager.createNotificationChannel(quietHoursChannel); + + final NotificationChannel deliveryFailedChannel = new NotificationChannel("delivery_failed", + c.getString(R.string.delivery_failed_channel_name), + NotificationManager.IMPORTANCE_DEFAULT); + deliveryFailedChannel.setShowBadge(false); + deliveryFailedChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) + .build()); + deliveryFailedChannel.setGroup("chats"); + notificationManager.createNotificationChannel(deliveryFailedChannel); } - public boolean notify(final Message message) { + private boolean notify(final Message message) { final Conversation conversation = (Conversation) message.getConversation(); return message.getStatus() == Message.STATUS_RECEIVED && !conversation.isMuted() @@ -342,6 +355,37 @@ public class NotificationService { } } + public void pushFailedDelivery(final Message message) { + final Conversation conversation = (Conversation) message.getConversation(); + final boolean isScreenOn = mXmppConnectionService.isInteractive(); + if (this.mIsInForeground && isScreenOn && this.mOpenConversation == message.getConversation()) { + Log.d(Config.LOGTAG, message.getConversation().getAccount().getJid().asBareJid() + ": suppressing failed delivery notification because conversation is open"); + return; + } + final PendingIntent pendingIntent = createContentIntent(conversation); + final int notificationId = generateRequestCode(conversation, 0) + DELIVERY_FAILED_NOTIFICATION_ID; + final int failedDeliveries = conversation.countFailedDeliveries(); + final Notification notification = + new Builder(mXmppConnectionService, "delivery_failed") + .setContentTitle(conversation.getName()) + .setAutoCancel(true) + .setSmallIcon(R.drawable.ic_error_white_24dp) + .setContentText(mXmppConnectionService.getResources().getQuantityText(R.plurals.some_messages_could_not_be_delivered, failedDeliveries)) + .setGroup("delivery_failed") + .setContentIntent(pendingIntent).build(); + final Notification summaryNotification = + new Builder(mXmppConnectionService, "delivery_failed") + .setContentTitle(mXmppConnectionService.getString(R.string.failed_deliveries)) + .setContentText(mXmppConnectionService.getResources().getQuantityText(R.plurals.some_messages_could_not_be_delivered, 1024)) + .setSmallIcon(R.drawable.ic_error_white_24dp) + .setGroup("delivery_failed") + .setGroupSummary(true) + .setAutoCancel(true) + .build(); + notify(notificationId, notification); + notify(DELIVERY_FAILED_NOTIFICATION_ID, summaryNotification); + } + public void showIncomingCallNotification(final AbstractJingleConnection.Id id, final Set media) { final Intent fullScreenIntent = new Intent(mXmppConnectionService, RtpSessionActivity.class); fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toEscapedString()); @@ -1092,9 +1136,11 @@ public class NotificationService { } final boolean showAllErrors = QuickConversationsService.isConversations(); final List errors = new ArrayList<>(); + boolean torNotAvailable = false; for (final Account account : mXmppConnectionService.getAccounts()) { if (account.hasErrorStatus() && account.showErrorNotification() && (showAllErrors || account.getLastErrorStatus() == Account.State.UNAUTHORIZED)) { errors.add(account); + torNotAvailable |= account.getStatus() == Account.State.TOR_NOT_AVAILABLE; } } if (mXmppConnectionService.foregroundNotificationNeedsUpdatingWhenErrorStateChanges()) { @@ -1113,7 +1159,23 @@ public class NotificationService { } mBuilder.addAction(R.drawable.ic_autorenew_white_24dp, mXmppConnectionService.getString(R.string.try_again), - createTryAgainIntent()); + createTryAgainIntent() + ); + if (torNotAvailable) { + if (TorServiceUtils.isOrbotInstalled(mXmppConnectionService)) { + mBuilder.addAction( + R.drawable.ic_play_circle_filled_white_48dp, + mXmppConnectionService.getString(R.string.start_orbot), + PendingIntent.getActivity(mXmppConnectionService, 147, TorServiceUtils.LAUNCH_INTENT, 0) + ); + } else { + mBuilder.addAction( + R.drawable.ic_file_download_white_24dp, + mXmppConnectionService.getString(R.string.install_orbot), + PendingIntent.getActivity(mXmppConnectionService, 146, TorServiceUtils.INSTALL_INTENT, 0) + ); + } + } mBuilder.setDeleteIntent(createDismissErrorIntent()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mBuilder.setVisibility(Notification.VISIBILITY_PRIVATE); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 8bc43c99f..3225be67d 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -36,6 +36,8 @@ import android.support.annotation.BoolRes; import android.support.annotation.IntegerRes; import android.support.v4.app.RemoteInput; import android.support.v4.content.ContextCompat; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; @@ -127,6 +129,7 @@ import eu.siacs.conversations.utils.ReplacingTaskManager; import eu.siacs.conversations.utils.Resolver; import eu.siacs.conversations.utils.SerialSingleThreadExecutor; import eu.siacs.conversations.utils.StringUtils; +import eu.siacs.conversations.utils.TorServiceUtils; import eu.siacs.conversations.utils.WakeLockHelper; import eu.siacs.conversations.utils.XmppUri; import eu.siacs.conversations.xml.Element; @@ -261,6 +264,16 @@ public class XmppConnectionService extends Service { return false; } }; + private final AtomicBoolean isPhoneInCall = new AtomicBoolean(false); + private final PhoneStateListener phoneStateListener = new PhoneStateListener() { + @Override + public void onCallStateChanged(final int state, final String phoneNumber) { + isPhoneInCall.set(state != TelephonyManager.CALL_STATE_IDLE); + if (state == TelephonyManager.CALL_STATE_OFFHOOK) { + mJingleConnectionManager.notifyPhoneCallStarted(); + } + } + }; private boolean destroyed = false; @@ -602,8 +615,8 @@ public class XmppConnectionService extends Service { return c != null && c.getMode() == Conversational.MODE_MULTI; } - public void search(List term, OnSearchResultsAvailable onSearchResultsAvailable) { - MessageSearchTask.search(this, term, onSearchResultsAvailable); + public void search(final List term, final String uuid, final OnSearchResultsAvailable onSearchResultsAvailable) { + MessageSearchTask.search(this, term, uuid, onSearchResultsAvailable); } @Override @@ -652,8 +665,15 @@ public class XmppConnectionService extends Service { final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID); Log.d(Config.LOGTAG, "received intent to dismiss call with session id " + sessionId); mJingleConnectionManager.rejectRtpSession(sessionId); + break; } - break; + case TorServiceUtils.ACTION_STATUS: + final String status = intent.getStringExtra(TorServiceUtils.EXTRA_STATUS); + if ("ON".equals(status)) { + handleOrbotStartedEvent(); + return START_STICKY; + } + break; case ACTION_END_CALL: { final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID); Log.d(Config.LOGTAG, "received intent to end call with session id " + sessionId); @@ -787,6 +807,14 @@ public class XmppConnectionService extends Service { return START_STICKY; } + private void handleOrbotStartedEvent() { + for (final Account account : accounts) { + if (account.getStatus() == Account.State.TOR_NOT_AVAILABLE) { + reconnectAccount(account, true, false); + } + } + } + private boolean processAccountState(Account account, boolean interactive, boolean isUiAction, boolean isAccountPushed, HashSet pingCandidates) { boolean pingNow = false; if (account.getStatus().isAttemptReconnect()) { @@ -1128,17 +1156,31 @@ public class XmppConnectionService extends Service { toggleForegroundService(); updateUnreadCountBadge(); toggleScreenEventReceiver(); + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(TorServiceUtils.ACTION_STATUS); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { scheduleNextIdlePing(); - IntentFilter intentFilter = new IntentFilter(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); } intentFilter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED); - registerReceiver(this.mInternalEventReceiver, intentFilter); } + registerReceiver(this.mInternalEventReceiver, intentFilter); mForceDuringOnCreate.set(false); toggleForegroundService(); + setupPhoneStateListener(); + } + + + private void setupPhoneStateListener() { + final TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + if (telephonyManager != null) { + telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + } + } + + public boolean isPhoneInCall() { + return isPhoneInCall.get(); } private void checkForDeletedFiles() { @@ -1192,7 +1234,7 @@ public class XmppConnectionService extends Service { public void onDestroy() { try { unregisterReceiver(this.mInternalEventReceiver); - } catch (IllegalArgumentException e) { + } catch (final IllegalArgumentException e) { //ignored } destroyed = false; @@ -2193,7 +2235,7 @@ public class XmppConnectionService extends Service { final Jid jid = Jid.ofEscaped(address); final Account account = new Account(jid, password); account.setOption(Account.OPTION_DISABLED, true); - Log.d(Config.LOGTAG,jid.asBareJid().toEscapedString()+": provisioning account"); + Log.d(Config.LOGTAG, jid.asBareJid().toEscapedString() + ": provisioning account"); createAccount(account); } @@ -3880,7 +3922,7 @@ public class XmppConnectionService extends Service { } - public void markMessage(Message message, int status, String errorMessage) { + public void markMessage(final Message message, final int status, final String errorMessage) { final int oldStatus = message.getStatus(); if (status == Message.STATUS_SEND_FAILED && (oldStatus == Message.STATUS_SEND_RECEIVED || oldStatus == Message.STATUS_SEND_DISPLAYED)) { return; @@ -3892,6 +3934,9 @@ public class XmppConnectionService extends Service { message.setStatus(status); databaseBackend.updateMessage(message, false); updateConversationUi(); + if (oldStatus != status && status == Message.STATUS_SEND_FAILED) { + mNotificationService.pushFailedDelivery(message); + } } private SharedPreferences getPreferences() { diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 0136e99e0..845c601e1 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1240,6 +1240,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke case R.id.attach_location: handleAttachmentSelection(item); break; + case R.id.action_search: + startSearch(); + break; case R.id.action_archive: activity.xmppConnectionService.archiveConversation(conversation); break; @@ -1289,6 +1292,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke return super.onOptionsItemSelected(item); } + private void startSearch() { + final Intent intent = new Intent(getActivity(), SearchActivity.class); + intent.putExtra(SearchActivity.EXTRA_CONVERSATION_UUID, conversation.getUuid()); + startActivity(intent); + } + private void returnToOngoingCall() { final Optional ongoingRtpSession = activity.xmppConnectionService.getJingleConnectionManager().getOngoingRtpConnection(conversation.getContact()); if (ongoingRtpSession.isPresent()) { @@ -2034,9 +2043,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke toggleInputMethod(); } - public void reInit(Conversation conversation, Bundle extras) { + public void reInit(final Conversation conversation, final Bundle extras) { QuickLoader.set(conversation.getUuid()); - this.saveMessageDraftStopAudioPlayer(); + final boolean changedConversation = this.conversation != conversation; + if (changedConversation) { + this.saveMessageDraftStopAudioPlayer(); + } this.clearPending(); if (this.reInit(conversation, extras != null)) { if (extras != null) { @@ -2753,7 +2765,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke correctMessage(lastEditableMessage); return true; } else { - Toast.makeText(getActivity(),R.string.could_not_correct_message, Toast.LENGTH_LONG).show(); + Toast.makeText(getActivity(), R.string.could_not_correct_message, Toast.LENGTH_LONG).show(); return false; } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java index 261907d5b..049c0f27e 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java @@ -266,8 +266,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio } private boolean processViewIntent(Intent intent) { - String uuid = intent.getStringExtra(EXTRA_CONVERSATION); - Conversation conversation = uuid != null ? xmppConnectionService.findConversationByUuid(uuid) : null; + final String uuid = intent.getStringExtra(EXTRA_CONVERSATION); + final Conversation conversation = uuid != null ? xmppConnectionService.findConversationByUuid(uuid) : null; if (conversation == null) { Log.d(Config.LOGTAG, "unable to view conversation with uuid:" + uuid); return false; @@ -380,8 +380,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_conversations, menu); - AccountUtils.showHideMenuItems(menu); - MenuItem qrCodeScanMenuItem = menu.findItem(R.id.action_scan_qr_code); + final MenuItem qrCodeScanMenuItem = menu.findItem(R.id.action_scan_qr_code); if (qrCodeScanMenuItem != null) { if (isCameraFeatureAvailable()) { Fragment fragment = getFragmentManager().findFragmentById(R.id.main_fragment); @@ -489,6 +488,18 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio case R.id.action_scan_qr_code: UriHandlerActivity.scan(this); return true; + case R.id.action_search_all_conversations: + startActivity(new Intent(this, SearchActivity.class)); + return true; + case R.id.action_search_this_conversation: + final Conversation conversation = ConversationFragment.getConversation(this); + if (conversation == null) { + return true; + } + final Intent intent = new Intent(this, SearchActivity.class); + intent.putExtra(SearchActivity.EXTRA_CONVERSATION_UUID, conversation.getUuid()); + startActivity(intent); + return true; } return super.onOptionsItemSelected(item); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java index 829785073..ffbf6f7d9 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java @@ -64,6 +64,7 @@ import eu.siacs.conversations.ui.util.PendingActionHelper; import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.ScrollState; import eu.siacs.conversations.ui.util.StyledAttributes; +import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.ThemeHelper; import static android.support.v7.widget.helper.ItemTouchHelper.LEFT; @@ -284,6 +285,7 @@ public class ConversationsOverviewFragment extends XmppFragment { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) { menuInflater.inflate(R.menu.fragment_conversations_overview, menu); + AccountUtils.showHideMenuItems(menu); } @Override diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index 92d6f2093..dcccec104 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -89,7 +89,6 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat private static final int REQUEST_CHANGE_STATUS = 0xee11; private static final int REQUEST_ORBOT = 0xff22; private final PendingItem mPendingPresenceTemplate = new PendingItem<>(); - private final AtomicBoolean mPendingReconnect = new AtomicBoolean(false); private AlertDialog mCaptchaDialog = null; private Jid jidToEdit; private boolean mInitMode = false; @@ -475,13 +474,6 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat Log.d(Config.LOGTAG, "pgp result not ok"); } } - if (requestCode == REQUEST_ORBOT) { - if (xmppConnectionService != null && mAccount != null) { - xmppConnectionService.reconnectAccountInBackground(mAccount); - } else { - mPendingReconnect.set(true); - } - } } @Override @@ -781,11 +773,6 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat } if (mAccount != null) { - - if (mPendingReconnect.compareAndSet(true, false)) { - xmppConnectionService.reconnectAccountInBackground(mAccount); - } - this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER); this.mUsernameMode |= mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && mAccount.isOptionSet(Account.OPTION_REGISTER); if (mPendingFingerprintVerificationUri != null) { diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 0fd9ee187..0cf2d6f33 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -677,18 +677,22 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe this.binding.endCall.setVisibility(View.INVISIBLE); this.binding.acceptCall.setVisibility(View.INVISIBLE); } else if (state == RtpEndUserState.INCOMING_CALL) { + this.binding.rejectCall.setContentDescription(getString(R.string.dismiss_call)); this.binding.rejectCall.setOnClickListener(this::rejectCall); this.binding.rejectCall.setImageResource(R.drawable.ic_call_end_white_48dp); this.binding.rejectCall.setVisibility(View.VISIBLE); this.binding.endCall.setVisibility(View.INVISIBLE); + this.binding.acceptCall.setContentDescription(getString(R.string.answer_call)); this.binding.acceptCall.setOnClickListener(this::acceptCall); this.binding.acceptCall.setImageResource(R.drawable.ic_call_white_48dp); this.binding.acceptCall.setVisibility(View.VISIBLE); } else if (state == RtpEndUserState.DECLINED_OR_BUSY) { + this.binding.rejectCall.setContentDescription(getString(R.string.exit)); this.binding.rejectCall.setOnClickListener(this::exit); this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp); this.binding.rejectCall.setVisibility(View.VISIBLE); this.binding.endCall.setVisibility(View.INVISIBLE); + this.binding.acceptCall.setContentDescription(getString(R.string.record_voice_mail)); this.binding.acceptCall.setOnClickListener(this::recordVoiceMail); this.binding.acceptCall.setImageResource(R.drawable.ic_voicemail_white_24dp); this.binding.acceptCall.setVisibility(View.VISIBLE); @@ -698,15 +702,18 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.RETRACTED ).contains(state)) { + this.binding.rejectCall.setContentDescription(getString(R.string.exit)); this.binding.rejectCall.setOnClickListener(this::exit); this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp); this.binding.rejectCall.setVisibility(View.VISIBLE); this.binding.endCall.setVisibility(View.INVISIBLE); + this.binding.acceptCall.setContentDescription(getString(R.string.try_again)); this.binding.acceptCall.setOnClickListener(this::retry); this.binding.acceptCall.setImageResource(R.drawable.ic_replay_white_48dp); this.binding.acceptCall.setVisibility(View.VISIBLE); } else { this.binding.rejectCall.setVisibility(View.INVISIBLE); + this.binding.endCall.setContentDescription(getString(R.string.hang_up)); this.binding.endCall.setOnClickListener(this::endCall); this.binding.endCall.setImageResource(R.drawable.ic_call_end_white_48dp); this.binding.endCall.setVisibility(View.VISIBLE); diff --git a/src/main/java/eu/siacs/conversations/ui/SearchActivity.java b/src/main/java/eu/siacs/conversations/ui/SearchActivity.java index 3b337e0c2..d73249f4b 100644 --- a/src/main/java/eu/siacs/conversations/ui/SearchActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/SearchActivity.java @@ -29,6 +29,7 @@ package eu.siacs.conversations.ui; +import android.content.Intent; import android.databinding.DataBindingUtil; import android.os.Bundle; import android.support.v7.widget.Toolbar; @@ -42,6 +43,8 @@ import android.view.View; import android.widget.AdapterView; import android.widget.EditText; +import com.google.common.base.Strings; + import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -70,17 +73,21 @@ import static eu.siacs.conversations.ui.util.SoftKeyboardUtils.showKeyboard; public class SearchActivity extends XmppActivity implements TextWatcher, OnSearchResultsAvailable, MessageAdapter.OnContactPictureClicked { private static final String EXTRA_SEARCH_TERM = "search-term"; + public static final String EXTRA_CONVERSATION_UUID = "uuid"; private ActivitySearchBinding binding; private MessageAdapter messageListAdapter; private final List messages = new ArrayList<>(); private WeakReference selectedMessageReference = new WeakReference<>(null); + private String uuid; private final ChangeWatcher> currentSearch = new ChangeWatcher<>(); private final PendingItem pendingSearchTerm = new PendingItem<>(); private final PendingItem> pendingSearch = new PendingItem<>(); @Override public void onCreate(final Bundle bundle) { + final Intent intent = getIntent(); + this.uuid = intent == null ? null : Strings.emptyToNull(intent.getStringExtra(EXTRA_CONVERSATION_UUID)); final String searchTerm = bundle == null ? null : bundle.getString(EXTRA_SEARCH_TERM); if (searchTerm != null) { pendingSearchTerm.push(searchTerm); @@ -103,10 +110,10 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc final String term = pendingSearchTerm.pop(); if (term != null) { searchField.append(term); - List searchTerm = FtsUtils.parse(term); + final List searchTerm = FtsUtils.parse(term); if (xmppConnectionService != null) { if (currentSearch.watch(searchTerm)) { - xmppConnectionService.search(searchTerm, this); + xmppConnectionService.search(searchTerm, uuid, this); } } else { pendingSearch.push(searchTerm); @@ -114,6 +121,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc } searchField.addTextChangedListener(this); searchField.setHint(R.string.search_messages); + searchField.setContentDescription(getString(R.string.search_messages)); searchField.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); if (term == null) { showKeyboard(searchField); @@ -206,7 +214,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc void onBackendConnected() { final List searchTerm = pendingSearch.pop(); if (searchTerm != null && currentSearch.watch(searchTerm)) { - xmppConnectionService.search(searchTerm, this); + xmppConnectionService.search(searchTerm, uuid,this); } } @@ -239,7 +247,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc return; } if (term.size() > 0) { - xmppConnectionService.search(term, this); + xmppConnectionService.search(term, uuid,this); } else { MessageSearchTask.cancelRunningTasks(); this.messages.clear(); diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index 4d31d74d6..fa0d5ada9 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -621,8 +621,10 @@ public class StartConversationActivity extends XmppActivity implements XmppConne } if (binding.startConversationViewPager.getCurrentItem() == 0) { mSearchEditText.setHint(R.string.search_contacts); + mSearchEditText.setContentDescription(getString(R.string.search_contacts)); } else { mSearchEditText.setHint(R.string.search_bookmarks); + mSearchEditText.setContentDescription(getString(R.string.search_bookmarks)); } } diff --git a/src/main/java/eu/siacs/conversations/ui/service/AudioPlayer.java b/src/main/java/eu/siacs/conversations/ui/service/AudioPlayer.java index 785f0ac01..5d3766452 100644 --- a/src/main/java/eu/siacs/conversations/ui/service/AudioPlayer.java +++ b/src/main/java/eu/siacs/conversations/ui/service/AudioPlayer.java @@ -112,17 +112,21 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti } viewHolder.playPause.setAlpha(viewHolder.darkBackground ? 0.7f : 0.57f); viewHolder.playPause.setOnClickListener(this); + final Context context = viewHolder.playPause.getContext(); if (message == currentlyPlayingMessage) { if (AudioPlayer.player != null && AudioPlayer.player.isPlaying()) { viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_pause_white_36dp : R.drawable.ic_pause_black_36dp); + viewHolder.playPause.setContentDescription(context.getString(R.string.pause_audio)); viewHolder.progress.setEnabled(true); } else { + viewHolder.playPause.setContentDescription(context.getString(R.string.play_audio)); viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp); viewHolder.progress.setEnabled(false); } return true; } else { viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp); + viewHolder.playPause.setContentDescription(context.getString(R.string.play_audio)); viewHolder.runtime.setText(formatTime(message.getFileParams().runtime)); viewHolder.progress.setProgress(0); viewHolder.progress.setEnabled(false); @@ -156,7 +160,8 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti } } - private boolean playPauseCurrent(ViewHolder viewHolder) { + private boolean playPauseCurrent(final ViewHolder viewHolder) { + final Context context = viewHolder.playPause.getContext(); viewHolder.playPause.setAlpha(viewHolder.darkBackground ? 0.7f : 0.57f); if (player.isPlaying()) { viewHolder.progress.setEnabled(false); @@ -164,6 +169,7 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti messageAdapter.flagScreenOff(); releaseProximityWakeLock(); viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp); + viewHolder.playPause.setContentDescription(context.getString(R.string.play_audio)); } else { viewHolder.progress.setEnabled(true); player.start(); @@ -171,6 +177,7 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti acquireProximityWakeLock(); this.stopRefresher(true); viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_pause_white_36dp : R.drawable.ic_pause_black_36dp); + viewHolder.playPause.setContentDescription(context.getString(R.string.pause_audio)); } return false; } @@ -194,6 +201,7 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti acquireProximityWakeLock(); viewHolder.progress.setEnabled(true); viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_pause_white_36dp : R.drawable.ic_pause_black_36dp); + viewHolder.playPause.setContentDescription(viewHolder.playPause.getContext().getString(R.string.pause_audio)); sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); return true; } catch (Exception e) { @@ -248,6 +256,7 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti } final ViewHolder viewHolder = ViewHolder.get(audioPlayer); final Message message = (Message) audioPlayer.getTag(); + viewHolder.playPause.setContentDescription(viewHolder.playPause.getContext().getString(R.string.play_audio)); viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp); if (message != null) { viewHolder.runtime.setText(formatTime(message.getFileParams().runtime)); diff --git a/src/main/java/eu/siacs/conversations/ui/util/AvatarWorkerTask.java b/src/main/java/eu/siacs/conversations/ui/util/AvatarWorkerTask.java index 4e23f14f1..ad7da7189 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/AvatarWorkerTask.java +++ b/src/main/java/eu/siacs/conversations/ui/util/AvatarWorkerTask.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.ui.util; +import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; @@ -11,11 +12,10 @@ import android.widget.ImageView; import java.lang.ref.WeakReference; import java.util.concurrent.RejectedExecutionException; +import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.ui.XmppActivity; -import eu.siacs.conversations.ui.adapter.AccountAdapter; -import eu.siacs.conversations.utils.UIHelper; public class AvatarWorkerTask extends AsyncTask { private final WeakReference imageViewReference; @@ -80,6 +80,7 @@ public class AvatarWorkerTask extends AsyncTask avatarWorkerTaskReference; diff --git a/src/main/java/eu/siacs/conversations/ui/util/ConversationMenuConfigurator.java b/src/main/java/eu/siacs/conversations/ui/util/ConversationMenuConfigurator.java index 98986e03a..24bbdd7c7 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/ConversationMenuConfigurator.java +++ b/src/main/java/eu/siacs/conversations/ui/util/ConversationMenuConfigurator.java @@ -112,16 +112,16 @@ public class ConversationMenuConfigurator { none.setVisible(Config.supportUnencrypted() || conversation.getMode() == Conversation.MODE_MULTI); axolotl.setVisible(Config.supportOmemo()); switch (conversation.getNextEncryption()) { - case Message.ENCRYPTION_NONE: - none.setChecked(true); - break; case Message.ENCRYPTION_PGP: + menuSecure.setTitle(R.string.encrypted_with_openpgp); pgp.setChecked(true); break; case Message.ENCRYPTION_AXOLOTL: + menuSecure.setTitle(R.string.encrypted_with_omemo); axolotl.setChecked(true); break; default: + menuSecure.setTitle(R.string.not_encrypted); none.setChecked(true); break; } diff --git a/src/main/java/eu/siacs/conversations/utils/StylingHelper.java b/src/main/java/eu/siacs/conversations/utils/StylingHelper.java index c26a86ea4..4d4339a42 100644 --- a/src/main/java/eu/siacs/conversations/utils/StylingHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/StylingHelper.java @@ -129,7 +129,7 @@ public class StylingHelper { int start = indexOfIgnoreCase(string, needle, 0); while (start != -1) { int end = start + length; - editable.setSpan(new BackgroundColorSpan(ContextCompat.getColor(context, dark ? R.color.blue_a100 : R.color.blue_a200)), start, end, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); + editable.setSpan(new BackgroundColorSpan(ContextCompat.getColor(context, dark ? R.color.blue_a100 : R.color.blue_a400)), start, end, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); editable.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, dark ? R.color.black87 : R.color.white)), start, end, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); start = indexOfIgnoreCase(string, needle, start + length); } diff --git a/src/main/java/eu/siacs/conversations/utils/TorServiceUtils.java b/src/main/java/eu/siacs/conversations/utils/TorServiceUtils.java index 3e333483b..9fc9f5082 100644 --- a/src/main/java/eu/siacs/conversations/utils/TorServiceUtils.java +++ b/src/main/java/eu/siacs/conversations/utils/TorServiceUtils.java @@ -16,6 +16,12 @@ public class TorServiceUtils { private static final Uri ORBOT_PLAYSTORE_URI = Uri.parse("market://details?id=" + URI_ORBOT); private final static String ACTION_START_TOR = "org.torproject.android.START_TOR"; + public static final Intent INSTALL_INTENT = new Intent(Intent.ACTION_VIEW, ORBOT_PLAYSTORE_URI); + public static final Intent LAUNCH_INTENT = new Intent(ACTION_START_TOR); + + public final static String ACTION_STATUS = "org.torproject.android.intent.action.STATUS"; + public final static String EXTRA_STATUS = "org.torproject.android.intent.extra.STATUS"; + public static boolean isOrbotInstalled(Context context) { try { context.getPackageManager().getPackageInfo(URI_ORBOT, PackageManager.GET_ACTIVITIES); @@ -27,17 +33,14 @@ public class TorServiceUtils { public static void downloadOrbot(Activity activity, int requestCode) { - final Intent intent = new Intent(Intent.ACTION_VIEW, ORBOT_PLAYSTORE_URI); try { - activity.startActivityForResult(intent, requestCode); + activity.startActivityForResult(INSTALL_INTENT, requestCode); } catch (ActivityNotFoundException e) { ToastCompat.makeText(activity, R.string.no_market_app_installed, ToastCompat.LENGTH_SHORT).show(); } } public static void startOrbot(Activity activity, int requestCode) { - final Intent launchIntent = new Intent(URI_ORBOT); - launchIntent.setAction(ACTION_START_TOR); - activity.startActivityForResult(launchIntent, requestCode); + activity.startActivityForResult(LAUNCH_INTENT, requestCode); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 67ff221b7..178ac659c 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -14,6 +14,7 @@ import com.google.common.collect.ImmutableSet; import java.lang.ref.WeakReference; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -90,7 +91,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { final AbstractJingleConnection connection; if (FileTransferDescription.NAMESPACES.contains(descriptionNamespace)) { connection = new JingleFileTransferConnection(this, id, from); - } else if (Namespace.JINGLE_APPS_RTP.equals(descriptionNamespace) && !usesTor(account)) { + } else if (Namespace.JINGLE_APPS_RTP.equals(descriptionNamespace) && isUsingClearNet(account)) { final boolean sessionEnded = this.terminatedSessions.asMap().containsKey(PersistableSessionId.of(id)); final boolean stranger = isWithStrangerAndStrangerNotificationsAreOff(account, id.with); if (isBusy() || sessionEnded || stranger) { @@ -116,11 +117,14 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } - private boolean usesTor(final Account account) { - return account.isOnion() || mXmppConnectionService.useTorToConnect(); + private boolean isUsingClearNet(final Account account) { + return !account.isOnion() && !mXmppConnectionService.useTorToConnect(); } public boolean isBusy() { + if (mXmppConnectionService.isPhoneInCall()) { + return true; + } for (AbstractJingleConnection connection : this.connections.values()) { if (connection instanceof JingleRtpConnection) { if (((JingleRtpConnection) connection).isTerminated()) { @@ -134,6 +138,18 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } + public void notifyPhoneCallStarted() { + for (AbstractJingleConnection connection : connections.values()) { + if (connection instanceof JingleRtpConnection) { + final JingleRtpConnection rtpConnection = (JingleRtpConnection) connection; + if (rtpConnection.isTerminated()) { + continue; + } + rtpConnection.notifyPhoneCall(); + } + } + } + private Optional findMatchingSessionProposal(final Account account, final Jid with, final Set media) { synchronized (this.rtpSessionProposals) { for (Map.Entry entry : this.rtpSessionProposals.entrySet()) { @@ -257,7 +273,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { Collections2.filter(descriptions, d -> d instanceof RtpDescription), input -> (RtpDescription) input ); - if (rtpDescriptions.size() > 0 && rtpDescriptions.size() == descriptions.size() && !usesTor(account)) { + if (rtpDescriptions.size() > 0 && rtpDescriptions.size() == descriptions.size() && isUsingClearNet(account)) { final Collection media = Collections2.transform(rtpDescriptions, RtpDescription::getMedia); if (media.contains(Media.UNKNOWN)) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": encountered unknown media in session proposal. " + propose); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index bc3487ad6..d2c340639 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -371,8 +371,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web if (transition(State.SESSION_ACCEPTED)) { respondOk(jinglePacket); receiveSessionAccept(contentMap); - final List identificationTags = contentMap.group == null ? contentMap.getNames() : contentMap.group.getIdentificationTags(); - processCandidates(identificationTags, contentMap.contents.entrySet()); } else { Log.d(Config.LOGTAG, String.format("%s: received session-accept while in state %s", id.account.getJid().asBareJid(), state)); respondOk(jinglePacket); @@ -390,7 +388,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage()); return; } - org.webrtc.SessionDescription answer = new org.webrtc.SessionDescription( + final org.webrtc.SessionDescription answer = new org.webrtc.SessionDescription( org.webrtc.SessionDescription.Type.ANSWER, sessionDescription.toString() ); @@ -400,7 +398,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to set remote description after receiving session-accept", Throwables.getRootCause(e)); webRTCWrapper.close(); sendSessionTerminate(Reason.FAILED_APPLICATION); + return; } + final List identificationTags = contentMap.group == null ? contentMap.getNames() : contentMap.group.getIdentificationTags(); + processCandidates(identificationTags, contentMap.contents.entrySet()); } private void sendSessionAccept() { @@ -900,6 +901,16 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web } } + + public void notifyPhoneCall() { + Log.d(Config.LOGTAG, "a phone call has just been started. killing jingle rtp connections"); + if (Arrays.asList(State.PROPOSED, State.SESSION_INITIALIZED).contains(this.state)) { + rejectCall(); + } else { + endCall(); + } + } + public synchronized void rejectCall() { if (isTerminated()) { Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": received rejectCall() when session has already been terminated. nothing to do"); diff --git a/src/main/res/drawable-hdpi/ic_error_white_24dp.png b/src/main/res/drawable-hdpi/ic_error_white_24dp.png new file mode 100644 index 000000000..69cbb1e0b Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_error_white_24dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_play_circle_filled_white_48dp.png b/src/main/res/drawable-hdpi/ic_play_circle_filled_white_48dp.png new file mode 100644 index 000000000..30330cfad Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_play_circle_filled_white_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_error_white_24dp.png b/src/main/res/drawable-mdpi/ic_error_white_24dp.png new file mode 100644 index 000000000..ca148fc7c Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_error_white_24dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_play_circle_filled_white_48dp.png b/src/main/res/drawable-mdpi/ic_play_circle_filled_white_48dp.png new file mode 100644 index 000000000..5dcdf0d7a Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_play_circle_filled_white_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_error_white_24dp.png b/src/main/res/drawable-xhdpi/ic_error_white_24dp.png new file mode 100644 index 000000000..9829698dd Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_error_white_24dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_play_circle_filled_white_48dp.png b/src/main/res/drawable-xhdpi/ic_play_circle_filled_white_48dp.png new file mode 100644 index 000000000..9dc082586 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_play_circle_filled_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_error_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_error_white_24dp.png new file mode 100644 index 000000000..abe2573b1 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_error_white_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_play_circle_filled_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_play_circle_filled_white_48dp.png new file mode 100644 index 000000000..1d73fb5f4 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_play_circle_filled_white_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_error_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_error_white_24dp.png new file mode 100644 index 000000000..830fb7e1e Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_error_white_24dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_play_circle_filled_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_play_circle_filled_white_48dp.png new file mode 100644 index 000000000..166f5c5ce Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_play_circle_filled_white_48dp.png differ diff --git a/src/main/res/layout/activity_start_conversation.xml b/src/main/res/layout/activity_start_conversation.xml index b83f595b9..1fd915bd6 100644 --- a/src/main/res/layout/activity_start_conversation.xml +++ b/src/main/res/layout/activity_start_conversation.xml @@ -50,6 +50,7 @@ app:sdMainFabClosedBackgroundColor="?colorPrimary" app:sdMainFabOpenedBackgroundColor="?colorPrimaryDark" app:sdUseReverseAnimationOnClose="true" + android:contentDescription="@string/add_contact_or_create_or_join_group_chat" app:sdOverlayLayout="@id/overlay"/> diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml index f63f612b1..75b1eb340 100644 --- a/src/main/res/layout/message_received.xml +++ b/src/main/res/layout/message_received.xml @@ -37,7 +37,9 @@ android:orientation="vertical" android:padding="2dp"> - + diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml index 82d122ec8..f84d07e34 100644 --- a/src/main/res/layout/message_sent.xml +++ b/src/main/res/layout/message_sent.xml @@ -51,7 +51,9 @@ android:orientation="vertical" android:padding="2dp"> - + diff --git a/src/main/res/menu/activity_conversations.xml b/src/main/res/menu/activity_conversations.xml index 3275fdc6d..bcf227ee5 100644 --- a/src/main/res/menu/activity_conversations.xml +++ b/src/main/res/menu/activity_conversations.xml @@ -1,26 +1,25 @@ + xmlns:app="http://schemas.android.com/apk/res-auto"> + + + + + + - - - - + app:showAsAction="always" /> \ No newline at end of file diff --git a/src/main/res/menu/fragment_conversation.xml b/src/main/res/menu/fragment_conversation.xml index 5eb0b313b..1e12f91c1 100644 --- a/src/main/res/menu/fragment_conversation.xml +++ b/src/main/res/menu/fragment_conversation.xml @@ -28,7 +28,7 @@ android:id="@+id/action_security" android:icon="?attr/icon_not_secure" android:orderInCategory="20" - android:title="@string/action_secure" + android:title="@string/encrypted_with_omemo" app:showAsAction="always"> @@ -99,6 +99,12 @@ android:orderInCategory="45" android:title="@string/invite_contact" app:showAsAction="never" /> + + android:title="@string/more_options"> + + + + + + + - - \ No newline at end of file diff --git a/src/main/res/menu/fragment_conversations_overview.xml b/src/main/res/menu/fragment_conversations_overview.xml index 39daf7d8c..52937ebd1 100644 --- a/src/main/res/menu/fragment_conversations_overview.xml +++ b/src/main/res/menu/fragment_conversations_overview.xml @@ -28,10 +28,26 @@ --> + xmlns:app="http://schemas.android.com/apk/res-auto"> + android:visible="@bool/show_individual_search_options" + app:showAsAction="never" /> + + + \ 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 0da16ab13..c8079d2ad 100644 --- a/src/main/res/values-ar/strings.xml +++ b/src/main/res/values-ar/strings.xml @@ -4,11 +4,9 @@ محادثة جديدة إدارة الحسابات إدارة الحساب - أغلق هذه المحادثة بيانات جهة الإتصال تفاصيل مجموعة المحادثة تفاصيل القناة - تشفير المحادثة إضافة حساب تعديل الإسم أضف إلى دفتر العناوين diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index 00e7b3c3b..915ff2951 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -4,11 +4,9 @@ Нов разговор Управление на профилите Управление на профила - Затваряне на този разговор Подробности за контакта Подробности за груповия разговор Подробности за канала - Защитен разговор Добавяне на профил Редактиране на името Добавяне към адресния указател diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index f5a3db49d..2749e83d9 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -4,10 +4,8 @@ Nova conversa Gestiona els comptes Administrar compte - Tancar aquesta conversa Detalls del contacte Detalls del xat de grup - Conversa segura Afegeix un compte Edita el nom Afegeix a la llibreta d\'adreces diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index 103c03430..4b0ce3f36 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -4,9 +4,7 @@ Nová konverzace Nastavení účtů Nastavení účtu - Zavřít tuto konverzaci Detaily kontaktu - Zabezpečená konverzace Přidat účet Upravit jméno Přidat do adresáře diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index e79d6e9ed..ce06c6380 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -4,11 +4,10 @@ Neue Unterhaltung Konten verwalten Konto verwalten - Diese Unterhaltung beenden + Unterhaltung beenden Kontaktdetails Gruppenchatdetails Channeldetails - Verschlüsselte Unterhaltung Konto hinzufügen Namen bearbeiten Zum Telefonbuch hinzufügen @@ -754,6 +753,7 @@ Laufende Anrufe Lautlose Nachrichten Diese Benachrichtigungsart wird verwendet, um Benachrichtigungen anzuzeigen, die keinen Ton auslösen sollen. Zum Beispiel, wenn du auf einem anderen Gerät aktiv bist (Schonfrist). + Fehlgeschlagene Zustellungen Benachrichtigungseinstellungen Anrufeinstellungen Wichtigkeit, Klang, Vibrationen @@ -921,12 +921,30 @@ Du kannst immer nur einen Anruf zur gleichen Zeit machen. Zurück zum laufenden Aufruf Kamera konnte nicht gewechselt werden - Zu Favoriten hinzufügen - Von Favoriten entfernen + Oben anheften + Von oben ablösen GPX-Strecke Nachricht konnte nicht korrigiert werden + Alle Unterhaltungen + Diese Unterhaltung + Dein Avatar + Avatar für %s + Verschlüsselt mit OMEMO + Verschlüsselt mit OpenPGP + Nicht verschlüsselt + Beenden + Sprachnachricht aufzeichnen + Audio abspielen + Audio anhalten + Kontakt hinzufügen, Gruppenchat erstellen oder beitreten oder Channels entdecken %1$d Teilnehmer anzeigen %1$d Teilnehmer anzeigen + + Eine Nachricht konnte nicht zugestellt werden + Einige Nachrichten konnten nicht zugestellt werden + + Fehlgeschlagene Zustellungen + Weitere Optionen diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index 6948212ed..640d82e2d 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -4,11 +4,9 @@ Νέα συζήτηση Διαχείριση λογαριασμών Διαχείριση λογαριασμού - Κλείσιμο συζήτησης Λεπτομέρειες επαφής Λεπτομέρειες ομαδικής συζήτησης Λεπτομέρειες καναλιού - Ασφαλής συζήτηση Προσθήκη λογαριασμού Επεξεργασία ονόματος Προσθήκη στην ατζέντα diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 1daf200c5..31301b574 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -4,11 +4,10 @@ Nueva conversación Gestionar cuentas Gestionar cuenta - Cerrar esta conversación + Cerrar conversación Detalles del contacto Detalles de conversación Detalles del canal - Conversación segura Añadir cuenta Editar contacto Añadir a contactos @@ -921,10 +920,20 @@ Solo puedes hacer una llamada a la vez Volver a la llamada en curso No se ha podido cambiar de cámara - Añadir a los favoritos - Eliminar de favoritos Recorrido GPX No se pudo corregir el mensaje + Todas las conversaciones + Esta conversación + Tu imagen de perfil + Imagen de perfil de %s + Encriptado con OMEMO + Encriptado con OpenPGP + Sin encriptar + Salir + Grabar mensaje de voz + Reproducir audio + Pausar audio + Añadir contacto, crear o unirse a un grupo de chat, o descubrir canales Ver %1$d Participante Ver %1$d Participantes diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index bf595e4a5..e3dc29fb4 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -4,11 +4,9 @@ Elkarrizketa berria Kontuak kudeatu Kontua kudeatu - Elkarrizketa hau itxi Kontaktuaren xehetasunak Taldearen xehetasunak Kanalaren xehetasunak - Elkarrizketa segurua Kontua gehitu Izena editatu Helbideen liburura gehitu diff --git a/src/main/res/values-fa-rIR/strings.xml b/src/main/res/values-fa-rIR/strings.xml index 682185323..728ec8e9c 100644 --- a/src/main/res/values-fa-rIR/strings.xml +++ b/src/main/res/values-fa-rIR/strings.xml @@ -5,7 +5,6 @@ مدیریت حساب های کاربری جزییات مخاطب جزئیات چت گروهی - مکالمه امن اضافه کردن حساب کاربری تغییر نام اضافه کردن به لیست ادرس ها diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index a6ebebacf..523ec019f 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -4,11 +4,9 @@ Nouvelle conversation Gérer les comptes Gérer le compte - Fermer cette conversation Détails du contact Détails du groupe Détails du canal - Conversation sécurisée Ajouter un compte Modifier le nom Ajouter au carnet d\'adresses @@ -913,8 +911,6 @@ Vous ne pouvez prendre qu\'un appel à la fois. Reprendre l\'appel en cours Impossible de changer de caméra - Ajouter aux favoris - Enlever des favoris Voir %1$d participant Voir %1$d participants diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index a3c7a63e7..7f38f1f4a 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -4,11 +4,10 @@ Nova conversa Xestionar contas Xestionar conta - Pechar esta conversa + Pechar conversa Detalles do contacto Detalles da conversa de grupo Detalles do canal - Conversa segura Engadir conta Editar contacto Engadir a libreta de enderezos @@ -754,6 +753,7 @@ Chamadas realizadas Mensaxes acalados Este grupo de notificacións é utilizado para mostrar notificacións que non debería activar ningún son. Por exemplo, cando está activo en outro dispositivo (Período de Graza). + Entregas fallidas Axustes de notificación das mensaxes Axustes da notificación de chamadas Importancia, Son, Vibrar @@ -921,12 +921,30 @@ Só podes manter unha chamada en cada momento. Voltar á chamada activa Non se puido activar a cámara - Engadir a favoritas - Eliminar das favoritas + Fixar enriba + Desafixar de enriba Ruta GPX No se pode correxir a mensaxe + Todas as conversas + Esta conversa + O teu avatar + Avatar para %s + Cifrado con OMEMO + Cifrado con OpenPGP + Sen cifrar + Saír + Gravar correo de voz + Reproducir audio + Pausar audio + Engade un contacto, crea o únete a unha conversa en grupo ou descubre canles. Ver %1$d Participante Ver %1$d Participantes + + Unha mensaxe non se entregou + Algunhas mensaxes non se entregaron + + Entregas fallidas + Máis opcións diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml index 0aff10aa7..2201a659f 100644 --- a/src/main/res/values-hu/strings.xml +++ b/src/main/res/values-hu/strings.xml @@ -4,11 +4,9 @@ Új beszélgetés Fiókok kezelése Fiók kezelése - Beszélgetés bezárása Partner részletei Csoportos csevegés részletei Csatorna részletei - Biztonságos beszélgetés Fiók hozzáadása Név szerkesztése Hozzáadás a címjegyzékhez @@ -883,8 +881,6 @@ Egyszerre csak egy hívásban vehet részt. Visszatérés a kimenő híváshoz Nem sikerült átváltani a kamerát - Hozzáadás a kedvencekhez - Eltávolítás a kedvencekből %1$d résztvevő megtekintése %1$d résztvevő megtekintése diff --git a/src/main/res/values-id/strings.xml b/src/main/res/values-id/strings.xml index 78fa9411f..08fb2bc34 100644 --- a/src/main/res/values-id/strings.xml +++ b/src/main/res/values-id/strings.xml @@ -4,7 +4,6 @@ Percakapan Baru Pengaturan Akun Detil Kontak - Amankan Percakapan Tambah Akun Ubah Nama Tambahkan ke daftar kontak diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index c42c050d0..b7b8e356a 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -4,11 +4,9 @@ Nuova conversazione Gestisci account Gestisci account - Chiudi questa conversazione Dettagli del contatto Dettagli chat di gruppo Dettagli canale - Conversazione sicura Aggiungi account Modifica il nome Aggiungi alla rubrica @@ -461,7 +459,11 @@ Rotto Disponibilità \"Non disponibile\" a schermo spento + Imposta come non disponibile quando lo schermo è spento + \"Occupato\" in modalità silenziosa + Imposta come occupato quando il dispositivo è in modalità silenziosa Tratta vibrazione come modalità silenziosa + Imposta come occupato quando il dispositivo è in modalità vibrazione Impostazioni estese di connessione Mostra nome host e impostazioni della porta quando configuri un account xmpp.esempio.it @@ -917,8 +919,8 @@ Puoi fare solo una chiamata alla volta. Torna alla chiamata in corso Impossibile cambiare fotocamera - Aggiungi ai preferiti - Rimuovi dai preferiti + Traccia GPX + Impossibile correggere il messaggio Vedi %1$d partecipante Vedi %1$d partecipanti diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml index e12b2b5b3..228704ef3 100644 --- a/src/main/res/values-iw/strings.xml +++ b/src/main/res/values-iw/strings.xml @@ -4,7 +4,6 @@ שיחה חדשה נהל חשבונות פרטי איש קשר - דיון מאובטח הוסף חשבון ערוך שם מחק מרשימת אנשי הקשר diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index 870ac2ac2..92bf10639 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -4,11 +4,9 @@ 新しい会話 アカウントの管理 アカウントの管理 - この会話を閉じる 連絡先の詳細 談話室の詳細 チャンネルの詳細 - 安全に会話 アカウントを追加 名前の編集 アドレス帳に追加 diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index b8ae856be..d00ee1940 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -4,7 +4,6 @@ 새 대화 계정 연락처 정보 - 안전한 대화 계정 추가 이름 편집 주소록에 추가 diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml index c237a8c79..2c94fcbba 100644 --- a/src/main/res/values-nb-rNO/strings.xml +++ b/src/main/res/values-nb-rNO/strings.xml @@ -5,7 +5,6 @@ Kontobehandling Kontaktdetaljer Gruppesludringsdetaljer - Sikret samtale Legg til samtale Rediger navn Legg til i kontaktliste diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 23bea3fa6..2f2b64cab 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -4,11 +4,9 @@ Nieuw gesprek Accounts beheren Account beheren - Dit gesprek sluiten Contactgegevens Gespreksgegevens Kanaalinformatie - Beveiligd gesprek Account toevoegen Naam veranderen Toevoegen aan adresboek diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index c27c4b484..07e572ae9 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -4,11 +4,10 @@ Nowa konwersacja Zarządzaj kontami Zarządzaj kontem - Zamknij konwersację + Zamknij rozmowę Szczegóły kontaktu Szczegóły konferencji Szczegóły kanału - Konwersacja szyfrowana Dodaj konto Edytuj nazwę Dodaj do kontaktów @@ -401,6 +400,7 @@ Oznacz jako przeczytane Ustawienia wprowadzania Enter wysyła + Użyj klawisza Enter aby wysłać wiadomość. Możesz zawsze użyć Ctrl+Enter do wysyłania wiadomości, nawet jeśli ta opcja jest wyłączona. Pokaż klawisz Enter Zamień klawisz emotikon na klawisz Enter plik audio @@ -424,8 +424,8 @@ Nie odnaleziono aplikacji do wyświetlenia lokalizacji Lokalizacja Zamknięto konwersację - Opuść prywatną rozmowę grupową - Opuść publiczny kanał + Opuszczono prywatną rozmowę grupową + Opuszczono publiczny kanał Nie ufaj certyfikatom systemowym Wymagaj ręcznego potwierdzania certyfikatów Usuń certyfikaty @@ -462,7 +462,11 @@ Zepsute Dostępność Status \"Oddalony\" gdy wyświetlacz jest wyłączony + Pokaż jako Oddalony kiedy ekran jest wyłączony. + Zajęty w trybie cichym + Pokaż jako Zajęty jeśli urządzenie jest w trybie cichym Traktuj tryb wibracji jak tryb cichy + Pokaż jako Zajęty kiedy urządzenie jest w trybie wibracji Rozszerzone ustawienia połączenia Pokaż nazwę hosta i ustawienia portu przy dodawaniu konta xmpp.example.com @@ -767,6 +771,7 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż Połączenia wychodzące Ciche wiadomości Ta kategoria powiadomień jest używana aby wyświetlać powiadomienia które nie powodują żadnych dźwięków. Na przykład w ciągu aktywności na innym urządzeniu (okres karencji). + Nie dostarczone wiadomości Ustawienia powiadomień wiadomości Ustawienia powiadomień dla przychodzących połączeń Ważność, Dźwięk, Wibracja @@ -929,16 +934,39 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż Połączenie audio Połączenie wideo Pomoc + Przełącz do rozmowy Twój mikrofon jest niedostępny Możesz mieć tylko jedno połączenie na raz. Powróć do trwającego połączenia Nie można zmienić aparatu - Dodaj do ulubionych - Usuń z ulubionych + Przypnij + Odepnij + Ścieżka GPX + Nie można poprawić wiadomości + Wszystkie rozmowy + Ta rozmowa + Twój awatar + Awatar dla %s + Zaszyfrowane OMEMO + Zaszyfrowane OpenPGP + Niezaszyfrowane + Wyjście + Zapisz pocztę głosową + Odtwórz audio + Spauzuj audio + Dodaj kontakt, stwórz lub dołącz do rozmowy grupowej lub odkryj kanały Pokaż %1$d uczestnika Pokaż %1$d uczestników Pokaż %1$d uczestników Pokaż %1$d uczestników + + Wiadomość nie mogła zostać dostarczona + Niektóre wiadomości nie mogły być dostarczone + Niektóre wiadomości nie mogły być dostarczone + Niektóre wiadomości nie mogły być dostarczone + + Nie dostarczone wiadomości + Więcej ustawień diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index aaadafa17..a8d56e895 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -4,11 +4,9 @@ Nova conversa Gerenciar contas Gerenciar conta - Encerrar essa conversa Detalhes do contato Detalhes da conversa em grupo Detalhes do canal - Conversa segura Adicionar conta Editar o nome Adicionar ao livro de endereços @@ -921,8 +919,6 @@ Você só pode ter uma chamada de cada vez Retornar para a chamada em andamento Não foi possível trocar a câmera - Adicionar aos favoritos - Remover dos favoritos Trilha GPX Não foi possível corrigir a mensagem diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index 48ac1af22..aa40d9cc8 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -4,7 +4,6 @@ Nova conversa Gerir contas Detalhes do contacto - Conversa segura Adicionar conta Editar nome Adicionar ao livro de endereços diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index ac36077c6..e17bc8fff 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -8,7 +8,6 @@ Detalii contact Detalii discuție de grup Detalii canal - Securizează conferința Adaugă cont Editează nume Adaugă la lista de contacte @@ -762,6 +761,7 @@ Apeluri în curs Mesaje silențioase Acest grup de notificări este folosit pentru a arăta notificări care nu emit sunete. De exemplu atunci când sunteți activi pe un alt dispozitiv (Perioada de grație). + Trimiteri eșuate Setări de notificare ale mesajelor Setări de notificare ale apelurilor primite Importanță, sunete, vibrații @@ -929,13 +929,32 @@ Puteți avea un singur apel simultan. Reveniți la apelul în curs Nu s-a putut face comutarea camerei foto - Adaugă la favorite - Înlătură din favorite + Fixează sus + Anulează fixarea Traseu GPX Nu s-a putut corecta mesajul + Toate conversațiile + Această conversație + Avatarul dumneavoastră + Avatar pentru %s + Criptare OMEMO + Criptare OpenPGP + Fără criptare + Ieșire + Înregistrare mesaj vocal + Redare audio + Pauză audio + Adaugă contact, creează sau alătură-te discuției de grup, sau descoperă canale Arată %1$d participant Arată %1$d participanți Arată %1$d de participanți + + Un mesaj nu a putut fi livrat + Niște mesaje nu au putut fi livrate + Niște mesaje nu au putut fi livrate + + Trimiteri eșuate + Mai multe diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index b4328ad26..0942bbae8 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -4,11 +4,9 @@ Новая беседа Управление аккаунтами Управление аккаунтом - Закрыть текущую беседу Сведения о контакте Подробности конференции Сведения о канале - Защищённая беседа Добавить аккаунт Редактировать контакт Добавить в адресную книгу @@ -933,8 +931,6 @@ Нельзя одновременно совершать больше одного звонка. Вернуться к текущему звонку Не удалось переключить камеру - Добавить в избранные - Убрать из избранных Просмотр %1$d участника Просмотр %1$d участников diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index cd8643ff1..6cee3cc8e 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -4,7 +4,6 @@ Nová konverzácia Nastavenie účtov Detaily kontaktu - Zabezpečená konverzácia Pridať účet Upraviť meno Vymazať zo zoznamu diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index b89041668..0c5ef2675 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -5,7 +5,6 @@ Управљај налозима Детаљи контакта Детаљи групног ћаскања - Безбедна преписка Додај налог Уреди име Додај у именик diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 95c39ba06..0f371f3ef 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -4,11 +4,9 @@ Ny konversation Kontoinställningar Hantera konto - Stäng denna konversation Kontaktdetaljer Gruppchattdetaljer Kanaldetaljer - Säker konversation Lägg till konto Ändra namn Lägg till i kontakter diff --git a/src/main/res/values-tr-rTR/strings.xml b/src/main/res/values-tr-rTR/strings.xml index 00fe5d929..d23a9f999 100644 --- a/src/main/res/values-tr-rTR/strings.xml +++ b/src/main/res/values-tr-rTR/strings.xml @@ -4,11 +4,9 @@ Yeni konuşma Hesapları yönet Hesabı yönet - Bu konuşmayı kapat Kişi bilgileri Küme konuşması ayrıntıları Kanal ayrıntıları - Güvenli konuşma Hesap ekle İsmi düzenle Telefon rehberine ekle diff --git a/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml index 9d02b5ff9..34361619d 100644 --- a/src/main/res/values-uk/strings.xml +++ b/src/main/res/values-uk/strings.xml @@ -4,11 +4,9 @@ Нова розмова Мої облікові записи Мій обліковий запис - Завершити розмову Деталі контакту Деталі групи Деталі каналу - Захищена розмова Додати обліковий запис Редагувати ім\'я Додати до контактів @@ -933,8 +931,6 @@ Водночас можливо здійснювати лише один виклик. Назад до активного виклику Неможливо перемкнути камеру - Додати до обраного - Вилучити з обраного Перегляд %1$d учасника Перегляд %1$d учасників diff --git a/src/main/res/values-vi/strings.xml b/src/main/res/values-vi/strings.xml index adcd79e96..57d506c2d 100644 --- a/src/main/res/values-vi/strings.xml +++ b/src/main/res/values-vi/strings.xml @@ -4,7 +4,6 @@ Hội thoại mới Quản lý tài khoản Thông tin liên hệ - Bảo mật hội thoại Thêm tài khoản Chỉnh sửa tên Thêm vào danh bạ diff --git a/src/main/res/values-w945dp/defaults.xml b/src/main/res/values-w945dp/defaults.xml index 855f5b520..f17a59a91 100644 --- a/src/main/res/values-w945dp/defaults.xml +++ b/src/main/res/values-w945dp/defaults.xml @@ -1,5 +1,7 @@ false + false + true diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index cdd7e736a..0e44707be 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -4,11 +4,9 @@ 新对话 管理账户 管理账户 - 关闭对话 联系人详情 群聊详情 频道详情 - 加密对话 添加账户 编辑名称 添加到通讯录 @@ -460,7 +458,11 @@ 损坏 可用性 锁屏时显示离开 + 屏幕关闭时显示为“离开” + 在静音模式显示为忙碌 + 设备处于静音模式时显示为忙碌 将振动看作静音 + 设备振动时显示为忙碌 高级连接设置 注册账户时显示主机名和端口 xmpp.example.com @@ -909,8 +911,8 @@ 只能同时打一通电话 返回正在进行的通话 无法切换摄像头 - 添加到收藏夹 - 从收藏夹删除 + GPX轨迹 + 无法更正消息 查看%1$d成员 diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index 94e26a277..d1dfd6e6b 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -4,7 +4,6 @@ 新對話 管理帳戶 聯絡人詳情 - 安全對話 新增帳戶 編輯姓名 添加到地址薄 diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index 3b52f4c24..37a8fe124 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -26,6 +26,8 @@ #ff4CAF50 #ff82B1FF #ff448AFF + #ff2979FF + #ff2962FF #ffff9800 #fffb8c00 #ffF57C00 diff --git a/src/main/res/values/defaults.xml b/src/main/res/values/defaults.xml index 9f07f3f34..8fef0fa5c 100644 --- a/src/main/res/values/defaults.xml +++ b/src/main/res/values/defaults.xml @@ -34,6 +34,8 @@ false false true + true + false true default_on small diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index eb624be38..a16bf406c 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -4,11 +4,10 @@ New conversation Manage accounts Manage account - Close this conversation + Close conversation Contact details Group chat details Channel details - Secure conversation Add account Edit name Add to address book @@ -756,6 +755,7 @@ Ongoing calls Silent messages This notification group is used to display notifications that should not trigger any sound. For example when being active on another device (Grace Period). + Failed deliveries Message notification settings Incoming calls notification settings Importance, Sound, Vibrate @@ -923,12 +923,30 @@ You can only have one call at a time. Return to ongoing call Could not switch camera - Add to favorites - Remove from favorites + Pin to top + Unpin from top GPX track Could not correct message + All conversations + This conversation + Your avatar + Avatar for %s + Encrypted with OMEMO + Encrypted with OpenPGP + Not encrypted + Exit + Record voice mail + Play audio + Pause audio + Add contact, create or join group chat, or discover channels View %1$d Participant View %1$d Participants + + A message could not be delivered + Some messages could not be delivered + + Failed deliveries + More options diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index 201cecab5..881a21037 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -4,7 +4,7 @@