diff options
Diffstat (limited to 'src')
20 files changed, 276 insertions, 121 deletions
diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java index ac99b1709..e2914f9d3 100644 --- a/src/main/java/de/pixart/messenger/Config.java +++ b/src/main/java/de/pixart/messenger/Config.java @@ -77,6 +77,8 @@ public final class Config { public static final int CONNECT_DISCO_TIMEOUT = 30; public static final int MINI_GRACE_PERIOD = 750; + public static final boolean XEP_0392 = false; //enables a variant of XEP-0392 that is based on HSLUV + public static final int FILE_SIZE = 1048576; // 1 MiB public static final int AVATAR_SIZE = 480; @@ -86,8 +88,6 @@ public final class Config { public static final Bitmap.CompressFormat IMAGE_FORMAT = Bitmap.CompressFormat.JPEG; public static final int IMAGE_QUALITY = 75; - public static final boolean QUICK_SHARE_ATTACHMENT_CHOICE = true; // set to true to use attachment choice instead of quick share for send button - public static final int DEFAULT_ZOOM = 15; //for locations public final static long LOCATION_FIX_TIME_DELTA = 1000 * 10; // ms public final static float LOCATION_FIX_SPACE_DELTA = 10; // m diff --git a/src/main/java/de/pixart/messenger/entities/Account.java b/src/main/java/de/pixart/messenger/entities/Account.java index a8c72cdf9..46d036419 100644 --- a/src/main/java/de/pixart/messenger/entities/Account.java +++ b/src/main/java/de/pixart/messenger/entities/Account.java @@ -638,8 +638,10 @@ public class Account extends AbstractEntity { return this.avatar; } - public void activateGracePeriod(long duration) { - this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration; + public void activateGracePeriod(final long duration) { + if (duration > 0) { + this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration; + } } public void deactivateGracePeriod() { diff --git a/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java b/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java index 37a098300..507c0c39a 100644 --- a/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java +++ b/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java @@ -36,6 +36,7 @@ public abstract class AbstractGenerator { "http://jabber.org/protocol/disco#info", "urn:xmpp:avatar:metadata+notify", "http://jabber.org/protocol/nick+notify", + Namespace.BOOKMARKS + "+notify", "urn:xmpp:ping", "jabber:iq:version", "http://jabber.org/protocol/chatstates" @@ -59,11 +60,11 @@ public abstract class AbstractGenerator { protected XmppConnectionService mXmppConnectionService; - protected AbstractGenerator(XmppConnectionService service) { + AbstractGenerator(XmppConnectionService service) { this.mXmppConnectionService = service; } - protected String getIdentityVersion() { + String getIdentityVersion() { if (mVersion == null) { this.mVersion = PhoneHelper.getVersionName(mXmppConnectionService); } diff --git a/src/main/java/de/pixart/messenger/generator/IqGenerator.java b/src/main/java/de/pixart/messenger/generator/IqGenerator.java index 918c95e6f..86f58af69 100644 --- a/src/main/java/de/pixart/messenger/generator/IqGenerator.java +++ b/src/main/java/de/pixart/messenger/generator/IqGenerator.java @@ -138,6 +138,13 @@ public class IqGenerator extends AbstractGenerator { return publish("urn:xmpp:avatar:data", item); } + public IqPacket publishElement(final String namespace, final Element element, final Bundle options) { + final Element item = new Element("item"); + item.setAttribute("id", "current"); + item.addChild(element); + return publish(namespace, item, options); + } + public IqPacket publishAvatarMetadata(final Avatar avatar) { final Element item = new Element("item"); item.setAttribute("id", avatar.sha1sum); @@ -197,6 +204,7 @@ public class IqGenerator extends AbstractGenerator { public IqPacket publishDeviceIds(final Set<Integer> ids, final Bundle publishOptions) { final Element item = new Element("item"); + item.setAttribute("id", "current"); final Element list = item.addChild("list", AxolotlService.PEP_PREFIX); for (Integer id : ids) { final Element device = new Element("device"); @@ -209,6 +217,7 @@ public class IqGenerator extends AbstractGenerator { public IqPacket publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey, final Set<PreKeyRecord> preKeyRecords, final int deviceId, Bundle publishOptions) { final Element item = new Element("item"); + item.setAttribute("id", "current"); final Element bundle = item.addChild("bundle", AxolotlService.PEP_PREFIX); final Element signedPreKeyPublic = bundle.addChild("signedPreKeyPublic"); signedPreKeyPublic.setAttribute("signedPreKeyId", signedPreKeyRecord.getId()); @@ -231,6 +240,7 @@ public class IqGenerator extends AbstractGenerator { public IqPacket publishVerification(byte[] signature, X509Certificate[] certificates, final int deviceId) { final Element item = new Element("item"); + item.setAttribute("id", "current"); final Element verification = item.addChild("verification", AxolotlService.PEP_PREFIX); final Element chain = verification.addChild("chain"); for (int i = 0; i < certificates.length; ++i) { diff --git a/src/main/java/de/pixart/messenger/parser/MessageParser.java b/src/main/java/de/pixart/messenger/parser/MessageParser.java index 7cfd69e8b..df85f2f25 100644 --- a/src/main/java/de/pixart/messenger/parser/MessageParser.java +++ b/src/main/java/de/pixart/messenger/parser/MessageParser.java @@ -304,6 +304,13 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece AxolotlService axolotlService = account.getAxolotlService(); axolotlService.registerDevices(from, deviceIds); mXmppConnectionService.updateAccountUi(); + } else if (Namespace.BOOKMARKS.equals(node)) { + Log.d(Config.LOGTAG, "received bookmarks from " + from); + if (account.getJid().asBareJid().equals(from)) { + final Element i = items.findChild("item"); + final Element storage = i == null ? null : i.findChild("storage", Namespace.BOOKMARKS); + mXmppConnectionService.processBookmarks(account, storage); + } } } diff --git a/src/main/java/de/pixart/messenger/persistance/FileBackend.java b/src/main/java/de/pixart/messenger/persistance/FileBackend.java index 3bb1e5c4e..6b35219e4 100644 --- a/src/main/java/de/pixart/messenger/persistance/FileBackend.java +++ b/src/main/java/de/pixart/messenger/persistance/FileBackend.java @@ -641,11 +641,12 @@ public class FileBackend { bitmap = BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(uri)); return getPepAvatar(bitmap, Bitmap.CompressFormat.PNG, 100); } catch (Exception e) { + return null; + } finally { if (bitmap != null) { bitmap.recycle(); } } - return null; } private Avatar getPepAvatar(Bitmap bitmap, Bitmap.CompressFormat format, int quality) { @@ -679,6 +680,9 @@ public class FileBackend { avatar.width = bitmap.getWidth(); avatar.height = bitmap.getHeight(); return avatar; + } catch (OutOfMemoryError e) { + Log.d(Config.LOGTAG, "unable to convert avatar to base64 due to low memory"); + return null; } catch (Exception e) { return null; } diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java index e50f0d9e2..f4e0bf877 100644 --- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java +++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java @@ -150,6 +150,7 @@ import de.pixart.messenger.xmpp.jingle.OnJinglePacketReceived; import de.pixart.messenger.xmpp.jingle.stanzas.JinglePacket; import de.pixart.messenger.xmpp.mam.MamReference; import de.pixart.messenger.xmpp.pep.Avatar; +import de.pixart.messenger.xmpp.pep.PublishOptions; import de.pixart.messenger.xmpp.stanzas.IqPacket; import de.pixart.messenger.xmpp.stanzas.MessagePacket; import de.pixart.messenger.xmpp.stanzas.PresencePacket; @@ -167,11 +168,11 @@ 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_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; - private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp"; public static final String FDroid = "org.fdroid.fdroid"; public static final String PlayStore = "com.android.vending"; public static final String Yalp = "com.github.yeriomin.yalpstore"; + private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; + private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp"; static { URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory()); @@ -198,7 +199,45 @@ public class XmppConnectionService extends Service { } } }; + //Ui callback listeners + private final Set<OnConversationUpdate> mOnConversationUpdates = Collections.newSetFromMap(new WeakHashMap<OnConversationUpdate, Boolean>()); + private final Set<OnShowErrorToast> mOnShowErrorToasts = Collections.newSetFromMap(new WeakHashMap<OnShowErrorToast, Boolean>()); + private final Set<OnAccountUpdate> mOnAccountUpdates = Collections.newSetFromMap(new WeakHashMap<OnAccountUpdate, Boolean>()); + private final Set<OnCaptchaRequested> mOnCaptchaRequested = Collections.newSetFromMap(new WeakHashMap<OnCaptchaRequested, Boolean>()); + private final Set<OnRosterUpdate> mOnRosterUpdates = Collections.newSetFromMap(new WeakHashMap<OnRosterUpdate, Boolean>()); + private final Set<OnUpdateBlocklist> mOnUpdateBlocklist = Collections.newSetFromMap(new WeakHashMap<OnUpdateBlocklist, Boolean>()); + private final Set<OnMucRosterUpdate> mOnMucRosterUpdate = Collections.newSetFromMap(new WeakHashMap<OnMucRosterUpdate, Boolean>()); + private final Set<OnKeyStatusUpdated> mOnKeyStatusUpdated = Collections.newSetFromMap(new WeakHashMap<OnKeyStatusUpdated, Boolean>()); + private final Object LISTENER_LOCK = new Object(); public DatabaseBackend databaseBackend; + private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() { + + @Override + public boolean onMessageAcknowledged(Account account, String uuid) { + for (final Conversation conversation : getConversations()) { + if (conversation.getAccount() == account) { + Message message = conversation.findUnsentMessageWithUuid(uuid); + if (message != null) { + message.setStatus(Message.STATUS_SEND); + message.setErrorMessage(null); + databaseBackend.updateMessage(message, false); + return true; + } + } + } + return false; + } + }; + public FileBackend fileBackend = new FileBackend(this); + private final ConversationsFileObserver fileObserver = new ConversationsFileObserver( + Environment.getExternalStorageDirectory().getAbsolutePath() + ) { + @Override + public void onEvent(int event, String path) { + markFileDeleted(path); + } + }; + public HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(this); private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true); private long mLastActivity = 0; private ContentObserver contactObserver = new ContentObserver(null) { @@ -211,7 +250,6 @@ public class XmppConnectionService extends Service { startService(intent); } }; - public FileBackend fileBackend = new FileBackend(this); private MemorizingTrustManager mMemorizingTrustManager; private NotificationService mNotificationService = new NotificationService(this); private ShortcutService mShortcutService = new ShortcutService(this); @@ -221,6 +259,9 @@ public class XmppConnectionService extends Service { private OnPresencePacketReceived mPresenceParser = new PresenceParser(this); private IqParser mIqParser = new IqParser(this); private MessageGenerator mMessageGenerator = new MessageGenerator(this); + private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this); + private List<Account> accounts; + private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(this); public OnContactStatusChanged onContactStatusChanged = (contact, online) -> { Conversation conversation = find(getConversations(), contact); if (conversation != null) { @@ -240,9 +281,6 @@ public class XmppConnectionService extends Service { } } }; - private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this); - private List<Account> accounts; - private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(this); private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() { @Override @@ -250,51 +288,9 @@ public class XmppConnectionService extends Service { mJingleConnectionManager.deliverPacket(account, packet); } }; - public HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(this); private AvatarService mAvatarService = new AvatarService(this); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private PushManagementService mPushManagementService = new PushManagementService(this); - private final ConversationsFileObserver fileObserver = new ConversationsFileObserver( - Environment.getExternalStorageDirectory().getAbsolutePath() - ) { - @Override - public void onEvent(int event, String path) { - markFileDeleted(path); - } - }; - private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() { - - @Override - public boolean onMessageAcknowledged(Account account, String uuid) { - for (final Conversation conversation : getConversations()) { - if (conversation.getAccount() == account) { - Message message = conversation.findUnsentMessageWithUuid(uuid); - if (message != null) { - message.setStatus(Message.STATUS_SEND); - message.setErrorMessage(null); - databaseBackend.updateMessage(message, false); - return true; - } - } - } - return false; - } - }; - - private int unreadCount = -1; - - //Ui callback listeners - private final Set<OnConversationUpdate> mOnConversationUpdates = Collections.newSetFromMap(new WeakHashMap<OnConversationUpdate, Boolean>()); - private final Set<OnShowErrorToast> mOnShowErrorToasts = Collections.newSetFromMap(new WeakHashMap<OnShowErrorToast, Boolean>()); - private final Set<OnAccountUpdate> mOnAccountUpdates = Collections.newSetFromMap(new WeakHashMap<OnAccountUpdate, Boolean>()); - private final Set<OnCaptchaRequested> mOnCaptchaRequested = Collections.newSetFromMap(new WeakHashMap<OnCaptchaRequested, Boolean>()); - private final Set<OnRosterUpdate> mOnRosterUpdates = Collections.newSetFromMap(new WeakHashMap<OnRosterUpdate, Boolean>()); - private final Set<OnUpdateBlocklist> mOnUpdateBlocklist = Collections.newSetFromMap(new WeakHashMap<OnUpdateBlocklist, Boolean>()); - private final Set<OnMucRosterUpdate> mOnMucRosterUpdate = Collections.newSetFromMap(new WeakHashMap<OnMucRosterUpdate, Boolean>()); - private final Set<OnKeyStatusUpdated> mOnKeyStatusUpdated = Collections.newSetFromMap(new WeakHashMap<OnKeyStatusUpdated, Boolean>()); - - private final Object LISTENER_LOCK = new Object(); - private final OnBindListener mOnBindListener = new OnBindListener() { @Override @@ -315,7 +311,9 @@ public class XmppConnectionService extends Service { account.getRoster().clearPresences(); mJingleConnectionManager.cancelInTransmission(); fetchRosterFromServer(account); - fetchBookmarks(account); + if (!account.getXmppConnection().getFeatures().bookmarksConversion()) { + fetchBookmarks(account); + } final boolean flexible = account.getXmppConnection().getFeatures().flexibleOfflineMessageRetrieval(); final boolean catchup = getMessageArchiveService().inCatchup(account); if (flexible && catchup) { @@ -333,6 +331,7 @@ public class XmppConnectionService extends Service { syncDirtyContacts(account); } }; + private int unreadCount = -1; private AtomicLong mLastExpiryRun = new AtomicLong(0); private SecureRandom mRandom; private LruCache<Pair<String, String>, ServiceDiscoveryResult> discoCache = new LruCache<>(20); @@ -925,6 +924,10 @@ public class XmppConnectionService extends Service { } } + public boolean getAttachmentChoicePreference() { + return getBooleanPreference(SettingsActivity.QUICK_SHARE_ATTACHMENT_CHOICE, R.bool.quick_share_attachment_choice); + } + private Presence.Status getTargetPresence() { if (dndOnSilentMode() && isPhoneSilenced()) { return Presence.Status.DND; @@ -1494,7 +1497,7 @@ public class XmppConnectionService extends Service { @Override public void onMessageFound(Message message) { if (mHttpConnectionManager.getAutoAcceptFileSize() >= message.getFileParams().size) { - Log.d(Config.LOGTAG, "Resend failed message with size " + message.getFileParams().size + " bytes for " + conversation.getJid()); + Log.d(Config.LOGTAG, "Resend failed message with size " + message.getFileParams().size + " bytes for " + conversation.getJid()); resendMessage(message, true); } } @@ -1520,45 +1523,53 @@ public class XmppConnectionService extends Service { public void fetchBookmarks(final Account account) { final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET); final Element query = iqPacket.query("jabber:iq:private"); - query.addChild("storage", "storage:bookmarks"); - final OnIqPacketReceived callback = new OnIqPacketReceived() { + query.addChild("storage", Namespace.BOOKMARKS); + final OnIqPacketReceived callback = (a, response) -> { + if (response.getType() == IqPacket.TYPE.RESULT) { + final Element query1 = response.query(); + final Element storage = query1.findChild("storage", "storage:bookmarks"); + processBookmarks(a, storage); + } else { + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": could not fetch bookmarks"); + } + }; + sendIqPacket(account, iqPacket, callback); + } - @Override - public void onIqPacketReceived(final Account account, final IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - final Element query = packet.query(); - final HashMap<Jid, Bookmark> bookmarks = new HashMap<>(); - final Element storage = query.findChild("storage", "storage:bookmarks"); - final boolean autojoin = respectAutojoin(); - if (storage != null) { - for (final Element item : storage.getChildren()) { - if (item.getName().equals("conference")) { - final Bookmark bookmark = Bookmark.parse(item, account); - Bookmark old = bookmarks.put(bookmark.getJid(), bookmark); - if (old != null && old.getBookmarkName() != null && bookmark.getBookmarkName() == null) { - bookmark.setBookmarkName(old.getBookmarkName()); - } - Conversation conversation = find(bookmark); - if (conversation != null) { - bookmark.setConversation(conversation); - } else if (bookmark.autojoin() && bookmark.getJid() != null && autojoin) { - conversation = findOrCreateConversation(account, bookmark.getJid(), true, true, false); - bookmark.setConversation(conversation); - } - } - } + public void processBookmarks(Account account, Element storage) { + final HashMap<Jid, Bookmark> bookmarks = new HashMap<>(); + final boolean autojoin = respectAutojoin(); + if (storage != null) { + for (final Element item : storage.getChildren()) { + if (item.getName().equals("conference")) { + final Bookmark bookmark = Bookmark.parse(item, account); + Bookmark old = bookmarks.put(bookmark.getJid(), bookmark); + if (old != null && old.getBookmarkName() != null && bookmark.getBookmarkName() == null) { + bookmark.setBookmarkName(old.getBookmarkName()); + } + Conversation conversation = find(bookmark); + if (conversation != null) { + bookmark.setConversation(conversation); + } else if (bookmark.autojoin() && bookmark.getJid() != null && autojoin) { + conversation = findOrCreateConversation(account, bookmark.getJid(), true, true, false); + bookmark.setConversation(conversation); } - account.setBookmarks(new CopyOnWriteArrayList<>(bookmarks.values())); - } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not fetch bookmarks"); } } - }; - sendIqPacket(account, iqPacket, callback); + } + account.setBookmarks(new CopyOnWriteArrayList<>(bookmarks.values())); } public void pushBookmarks(Account account) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": pushing bookmarks"); + if (account.getXmppConnection().getFeatures().bookmarksConversion()) { + pushBookmarksPep(account); + } else { + pushBookmarksPrivateXml(account); + } + } + + private void pushBookmarksPrivateXml(Account account) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": pushing bookmarks via private xml"); IqPacket iqPacket = new IqPacket(IqPacket.TYPE.SET); Element query = iqPacket.query("jabber:iq:private"); Element storage = query.addChild("storage", "storage:bookmarks"); @@ -1568,6 +1579,46 @@ public class XmppConnectionService extends Service { sendIqPacket(account, iqPacket, mDefaultIqHandler); } + private void pushBookmarksPep(Account account) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": pushing bookmarks via pep"); + Element storage = new Element("storage", "storage:bookmarks"); + for (Bookmark bookmark : account.getBookmarks()) { + storage.addChild(bookmark); + } + pushNodeAndEnforcePublishOptions(account, Namespace.BOOKMARKS, storage, PublishOptions.persistentWhitelistAccess()); + } + + private void pushNodeAndEnforcePublishOptions(final Account account, final String node, final Element element, final Bundle options) { + pushNodeAndEnforcePublishOptions(account, node, element, options, true); + } + + private void pushNodeAndEnforcePublishOptions(final Account account, final String node, final Element element, final Bundle options, final boolean retry) { + IqPacket packet = mIqGenerator.publishElement(node, element, options); + Log.d(Config.LOGTAG, packet.toString()); + sendIqPacket(account, packet, (a, response) -> { + if (response.getType() == IqPacket.TYPE.RESULT) { + return; + } + final Element error = response.getType() == IqPacket.TYPE.ERROR ? response.findChild("error") : null; + final boolean preconditionNotMet = error != null && error.hasChild("precondition-not-met", Namespace.PUBSUB_ERROR); + if (retry && preconditionNotMet) { + pushNodeConfiguration(account, node, options, new OnConfigurationPushed() { + @Override + public void onPushSucceeded() { + pushNodeAndEnforcePublishOptions(account, node, element, options, false); + } + + @Override + public void onPushFailed() { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to push node configuration (" + node + ")"); + } + }); + } else { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error publishing bookmarks (retry=" + Boolean.toString(retry) + ") " + response); + } + }); + } + private void restoreFromDatabase() { synchronized (this.conversations) { final Map<String, Account> accountLookupTable = new Hashtable<>(); @@ -1860,7 +1911,7 @@ public class XmppConnectionService extends Service { conversation.setContactJid(jid.asBareJid()); } databaseBackend.updateConversation(conversation); - loadMessagesFromDb = conversation.messagesLoaded.compareAndSet(true,false); + loadMessagesFromDb = conversation.messagesLoaded.compareAndSet(true, false); } else { String conversationName; Contact contact = account.getRoster().getContact(jid); @@ -1934,7 +1985,7 @@ public class XmppConnectionService extends Service { conversation.setContactJid(jid.asBareJid()); } databaseBackend.updateConversation(conversation); - loadMessagesFromDb = conversation.messagesLoaded.compareAndSet(true,false); + loadMessagesFromDb = conversation.messagesLoaded.compareAndSet(true, false); } else { String conversationName; Contact contact = account.getRoster().getContact(jid); @@ -2147,7 +2198,7 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnConversationUpdates.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as ConversationListChangedListener"); + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as ConversationListChangedListener"); } this.mNotificationService.setIsInForeground(this.mOnConversationUpdates.size() > 0); } @@ -2173,7 +2224,7 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnShowErrorToasts.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnShowErrorToastListener"); + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnShowErrorToastListener"); } } if (remainingListeners) { @@ -2197,7 +2248,7 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnAccountUpdates.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnAccountListChangedtListener"); + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnAccountListChangedtListener"); } } if (remainingListeners) { @@ -2221,7 +2272,7 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnCaptchaRequested.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnCaptchaRequestListener"); + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnCaptchaRequestListener"); } } if (remainingListeners) { @@ -2245,7 +2296,7 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnRosterUpdates.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnRosterUpdateListener"); + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnRosterUpdateListener"); } } if (remainingListeners) { @@ -2269,7 +2320,7 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnUpdateBlocklist.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnUpdateBlocklistListener"); + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnUpdateBlocklistListener"); } } if (remainingListeners) { @@ -2293,7 +2344,7 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnKeyStatusUpdated.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnKeyStatusUpdateListener"); + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnKeyStatusUpdateListener"); } } if (remainingListeners) { @@ -2317,7 +2368,7 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnMucRosterUpdate.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnMucRosterListener"); + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnMucRosterListener"); } } if (remainingListeners) { @@ -2581,7 +2632,7 @@ public class XmppConnectionService extends Service { } private boolean hasEnabledAccounts() { - for(Account account : this.accounts) { + for (Account account : this.accounts) { if (account.isEnabled()) { return true; } @@ -2744,13 +2795,13 @@ public class XmppConnectionService extends Service { if (callback != null) { callback.error(R.string.conference_creation_failed, null); } - return true; + return false; } } else { if (callback != null) { callback.error(R.string.not_connected_try_again, null); } - return true; + return false; } } @@ -2796,6 +2847,7 @@ public class XmppConnectionService extends Service { } public void pushNodeConfiguration(Account account, final String node, final Bundle options, final OnConfigurationPushed callback) { + Log.d(Config.LOGTAG, "pushing node configuration"); pushNodeConfiguration(account, account.getJid().asBareJid(), node, options, callback); } @@ -3172,7 +3224,7 @@ public class XmppConnectionService extends Service { getAvatarService().clear(account); databaseBackend.updateAccount(account); } - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": published avatar "+(avatar.size/1024)+"KiB"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": published avatar " + (avatar.size / 1024) + "KiB"); if (callback != null) { callback.onAvatarPublicationSucceeded(); } @@ -3185,7 +3237,7 @@ public class XmppConnectionService extends Service { }); } else { Element error = result.findChild("error"); - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": server rejected avatar "+(avatar.size/1024)+"KiB "+(error!=null?error.toString():"")); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": server rejected avatar " + (avatar.size / 1024) + "KiB " + (error != null ? error.toString() : "")); if (callback != null) { callback.onAvatarPublicationFailed(R.string.error_publish_avatar_server_reject); } @@ -3539,7 +3591,7 @@ public class XmppConnectionService extends Service { public long getLongPreference(String name, @IntegerRes int res) { long defaultValue = getResources().getInteger(res); try { - return Long.parseLong(getPreferences().getString(name,String.valueOf(defaultValue))); + return Long.parseLong(getPreferences().getString(name, String.valueOf(defaultValue))); } catch (NumberFormatException e) { return defaultValue; } @@ -3777,7 +3829,7 @@ public class XmppConnectionService extends Service { public void updateMemorizingTrustmanager() { final MemorizingTrustManager tm; - final boolean dontTrustSystemCAs = getPreferences().getBoolean("dont_trust_system_cas", getResources().getBoolean(R.bool.dont_trust_system_cas)); + final boolean dontTrustSystemCAs = getBooleanPreference("dont_trust_system_cas", R.bool.dont_trust_system_cas); if (dontTrustSystemCAs) { tm = new MemorizingTrustManager(getApplicationContext(), null); } else { @@ -4270,11 +4322,11 @@ public class XmppConnectionService extends Service { } public boolean blindTrustBeforeVerification() { - return getPreferences().getBoolean(SettingsActivity.BLIND_TRUST_BEFORE_VERIFICATION, getResources().getBoolean(R.bool.btbv)); + return getBooleanPreference(SettingsActivity.BLIND_TRUST_BEFORE_VERIFICATION, R.bool.btbv); } public boolean showForegroundService() { - return getPreferences().getBoolean(SettingsActivity.SHOW_FOREGROUND_SERVICE, getResources().getBoolean(R.bool.show_foreground_service)); + return getBooleanPreference(SettingsActivity.SHOW_FOREGROUND_SERVICE, R.bool.show_foreground_service); } public void ScheduleAutomaticExport() { @@ -4293,7 +4345,7 @@ public class XmppConnectionService extends Service { if (timetoexport.before(now)) { SimpleDateFormat newDate = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US); timetoexport.add(Calendar.DAY_OF_YEAR, 1); //DATE or DAY_OF_MONTH - Log.d(Config.LOGTAG, "Schedule automatic export logs, for today, the export time is in the past, scheduling first export run for the next day ("+ newDate.format(timetoexport.getTimeInMillis()) +")."); + Log.d(Config.LOGTAG, "Schedule automatic export logs, for today, the export time is in the past, scheduling first export run for the next day (" + newDate.format(timetoexport.getTimeInMillis()) + ")."); } final PendingIntent ScheduleExportIntent = PendingIntent.getBroadcast(this, AlarmReceiver.SCHEDULE_ALARM_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT); ((AlarmManager) getSystemService(ALARM_SERVICE)).setInexactRepeating(AlarmManager.RTC_WAKEUP, timetoexport.getTimeInMillis(), AlarmManager.INTERVAL_DAY, ScheduleExportIntent); diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java index 0374e643c..ec9dcf8f0 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java @@ -126,6 +126,7 @@ 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.SendButtonAction.TEXT; import static de.pixart.messenger.ui.util.SoftKeyboardUtils.hideSoftKeyboard; import static de.pixart.messenger.xmpp.Patches.ENCRYPTION_EXCEPTIONS; @@ -1186,7 +1187,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } menuNeedHelp.setVisible(true); menuSearchUpdates.setVisible(false); - ConversationMenuConfigurator.configureAttachmentMenu(conversation, menu, Config.QUICK_SHARE_ATTACHMENT_CHOICE); + ConversationMenuConfigurator.configureAttachmentMenu(conversation, menu, activity.xmppConnectionService.getAttachmentChoicePreference()); ConversationMenuConfigurator.configureEncryptionMenu(conversation, menu); } else { menuNeedHelp.setVisible(false); @@ -1223,11 +1224,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke final boolean received = message.getStatus() <= Message.STATUS_RECEIVED; if (received) { if (message.getConversation() instanceof Conversation && message.getConversation().getMode() == Conversation.MODE_MULTI) { + Jid tcp = message.getTrueCounterpart(); Jid user = message.getCounterpart(); if (user != null && !user.isBareJid()) { final MucOptions mucOptions = ((Conversation) message.getConversation()).getMucOptions(); if (mucOptions.participating() || ((Conversation) message.getConversation()).getNextCounterpart() != null) { - if (!mucOptions.isUserInRoom(user)) { + if (!mucOptions.isUserInRoom(user) && mucOptions.findUserByRealJid(tcp == null ? null : tcp.asBareJid()) == null) { Toast.makeText(getActivity(), activity.getString(R.string.user_has_left_conference, user.getResource()), Toast.LENGTH_SHORT).show(); } highlightInConference(user.getResource()); @@ -2411,7 +2413,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke showSnackbar(R.string.conference_kicked, R.string.join, joinMuc); break; case UNKNOWN: - showSnackbar(R.string.conference_unknown_error, R.string.leave, joinMuc); + showSnackbar(R.string.conference_unknown_error, R.string.join, joinMuc); break; case INVALID_NICK: showSnackbar(R.string.invalid_muc_nick, R.string.edit, clickToMuc); @@ -2556,7 +2558,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke final Conversation c = this.conversation; final Presence.Status status; final String text = this.binding.textinput == null ? "" : this.binding.textinput.getText().toString(); - final SendButtonAction action = SendButtonTool.getAction(getActivity(), c, text); + SendButtonAction action = SendButtonTool.getAction(getActivity(), c, text); if (useSendButtonToIndicateStatus && c.getAccount().getStatus() == Account.State.ONLINE) { if (activity.xmppConnectionService != null && activity.xmppConnectionService.getMessageArchiveService().isCatchingUp(c)) { status = Presence.Status.OFFLINE; @@ -2568,6 +2570,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } else { status = Presence.Status.OFFLINE; } + if (action.toString().equals("CHOOSE_ATTACHMENT") && !activity.xmppConnectionService.getAttachmentChoicePreference()) { + action = TEXT; + } this.binding.textSendButton.setTag(action); this.binding.textSendButton.setImageResource(SendButtonTool.getSendButtonImageResource(getActivity(), action, status)); } diff --git a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java index 9ee11bd0b..1ace03e5e 100644 --- a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java @@ -58,12 +58,15 @@ public class SettingsActivity extends XmppActivity implements public static final String SHOW_FOREGROUND_SERVICE = "show_foreground_service"; public static final String USE_BUNDLED_EMOJIS = "use_bundled_emoji"; public static final String USE_MULTI_ACCOUNTS = "use_multi_accounts"; + public static final String QUICK_SHARE_ATTACHMENT_CHOICE = "quick_share_attachment_choice"; public static final int REQUEST_WRITE_LOGS = 0xbf8701; Preference multiAccountPreference; Preference BundledEmojiPreference; + Preference QuickShareAttachmentChoicePreference; boolean isMultiAccountChecked = false; boolean isBundledEmojiChecked; + boolean isQuickShareAttachmentChoiceChecked = false; private SettingsFragment mSettingsFragment; @Override @@ -105,6 +108,15 @@ public class SettingsActivity extends XmppActivity implements isBundledEmojiChecked = ((CheckBoxPreference) BundledEmojiPreference).isChecked(); } + QuickShareAttachmentChoicePreference = mSettingsFragment.findPreference("quick_share_attachment_choice"); + if (QuickShareAttachmentChoicePreference != null) { + QuickShareAttachmentChoicePreference.setOnPreferenceChangeListener((preference, newValue) -> { + recreate(); + return true; + }); + isQuickShareAttachmentChoiceChecked = ((CheckBoxPreference) QuickShareAttachmentChoicePreference).isChecked(); + } + changeOmemoSettingSummary(); if (Config.FORCE_ORBOT) { @@ -173,7 +185,7 @@ public class SettingsActivity extends XmppActivity implements quickAction.setEntryValues(entryValues.toArray(new CharSequence[entryValues.size()])); } - if (Config.QUICK_SHARE_ATTACHMENT_CHOICE) { + if (isQuickShareAttachmentChoiceChecked) { if (UIPreferenceScreen != null && quickAction != null) { UIPreferenceScreen.removePreference(quickAction); } diff --git a/src/main/java/de/pixart/messenger/ui/XmppActivity.java b/src/main/java/de/pixart/messenger/ui/XmppActivity.java index f52b2d72c..3ab6bfc6a 100644 --- a/src/main/java/de/pixart/messenger/ui/XmppActivity.java +++ b/src/main/java/de/pixart/messenger/ui/XmppActivity.java @@ -943,7 +943,11 @@ public abstract class XmppActivity extends ActionBarActivity { } public void inviteUser() { - if (xmppConnectionServiceBound && !xmppConnectionService.multipleAccounts()) { + if (!xmppConnectionServiceBound) { + Toast.makeText(this, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show(); + return; + } + if (!xmppConnectionService.multipleAccounts()) { Account mAccount = xmppConnectionService.getAccounts().get(0); String user = Jid.of(mAccount.getJid()).getLocal(); String domain = Jid.of(mAccount.getJid()).getDomain(); diff --git a/src/main/java/de/pixart/messenger/ui/util/SendButtonTool.java b/src/main/java/de/pixart/messenger/ui/util/SendButtonTool.java index 291edb822..b4b0ecc28 100644 --- a/src/main/java/de/pixart/messenger/ui/util/SendButtonTool.java +++ b/src/main/java/de/pixart/messenger/ui/util/SendButtonTool.java @@ -38,10 +38,9 @@ import de.pixart.messenger.R; import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.entities.Presence; import de.pixart.messenger.ui.ConversationFragment; +import de.pixart.messenger.ui.SettingsActivity; import de.pixart.messenger.utils.UIHelper; -import static de.pixart.messenger.Config.QUICK_SHARE_ATTACHMENT_CHOICE; - public class SendButtonTool { public static SendButtonAction getAction(Activity activity, Conversation c, String text) { @@ -61,12 +60,13 @@ public class SendButtonTool { if (conference && c.getNextCounterpart() != null) { return SendButtonAction.CANCEL; } else { - if (QUICK_SHARE_ATTACHMENT_CHOICE && AttachmentsVisible(c)) { + boolean quickShareChoice = preferences.getBoolean(SettingsActivity.QUICK_SHARE_ATTACHMENT_CHOICE, activity.getResources().getBoolean(R.bool.quick_share_attachment_choice)); + String setting = preferences.getString("quick_action", activity.getResources().getString(R.string.quick_action)); + if (quickShareChoice && AttachmentsVisible(c)) { return SendButtonAction.CHOOSE_ATTACHMENT; - } else if (QUICK_SHARE_ATTACHMENT_CHOICE && !AttachmentsVisible(c)) { + } else if (quickShareChoice && !AttachmentsVisible(c)) { return SendButtonAction.TEXT; } else { - String setting = preferences.getString("quick_action", activity.getResources().getString(R.string.quick_action)); if (!setting.equals("none") && UIHelper.receivedLocationQuestion(c.getLatestMessage())) { return SendButtonAction.SEND_LOCATION; } else { @@ -198,5 +198,4 @@ public class SendButtonTool { return res; } - }
\ 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 d76e4fede..146a9784d 100644 --- a/src/main/java/de/pixart/messenger/utils/Namespace.java +++ b/src/main/java/de/pixart/messenger/utils/Namespace.java @@ -20,4 +20,6 @@ public final class Namespace { public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL = "http://jabber.org/protocol/offline"; public static final String BIND = "urn:ietf:params:xml:ns:xmpp-bind"; public static final String P1_S3_FILE_TRANSFER = "p1:s3filetransfer"; + public static final String BOOKMARKS_CONVERSION = "urn:xmpp:bookmarks-conversion:0"; + public static final String BOOKMARKS = "storage:bookmarks"; } diff --git a/src/main/java/de/pixart/messenger/utils/UIHelper.java b/src/main/java/de/pixart/messenger/utils/UIHelper.java index f5730e981..b603f68ee 100644 --- a/src/main/java/de/pixart/messenger/utils/UIHelper.java +++ b/src/main/java/de/pixart/messenger/utils/UIHelper.java @@ -222,6 +222,9 @@ public class UIHelper { } public static int getColorForName(String name, boolean safe) { + if (Config.XEP_0392) { + return XEP0392Helper.rgbFromNick(name); + } if (name == null || name.isEmpty()) { return 0xFF202020; } diff --git a/src/main/java/de/pixart/messenger/utils/XEP0392Helper.java b/src/main/java/de/pixart/messenger/utils/XEP0392Helper.java new file mode 100644 index 000000000..bbfe50898 --- /dev/null +++ b/src/main/java/de/pixart/messenger/utils/XEP0392Helper.java @@ -0,0 +1,30 @@ +package de.pixart.messenger.utils; + +import android.graphics.Color; + +import org.hsluv.HUSLColorConverter; + +import java.security.MessageDigest; + +public class XEP0392Helper { + + private static double angle(String nickname) { + try { + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + byte[] digest = sha1.digest(nickname.getBytes("UTF-8")); + int angle = ((int) (digest[0]) & 0xff) + ((int) (digest[1]) & 0xff) * 256; + return angle / 65536.; + } catch (Exception e) { + return 0.0; + } + } + + public static int rgbFromNick(String name) { + double[] hsluv = new double[3]; + hsluv[0] = angle(name) * 360; + hsluv[1] = 100; + hsluv[2] = 50; + double[] rgb = HUSLColorConverter.hsluvToRgb(hsluv); + return Color.rgb((int) Math.round(rgb[0] * 255), (int) Math.round(rgb[1] * 255), (int) Math.round(rgb[2] * 255)); + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/xmpp/InvalidJid.java b/src/main/java/de/pixart/messenger/xmpp/InvalidJid.java index ce381abdc..fdaadeab2 100644 --- a/src/main/java/de/pixart/messenger/xmpp/InvalidJid.java +++ b/src/main/java/de/pixart/messenger/xmpp/InvalidJid.java @@ -69,6 +69,11 @@ public class InvalidJid implements Jid { } @Override + public boolean isDomainJid() { + throw new AssertionError("Not implemented"); + } + + @Override public Jid asBareJid() { throw new AssertionError("Not implemented"); } diff --git a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java index a6fe3756a..7373291fd 100644 --- a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java +++ b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java @@ -1789,6 +1789,10 @@ public class XmppConnection implements Runnable { return hasDiscoFeature(Jid.of(account.getServer()), "urn:xmpp:carbons:2"); } + public boolean bookmarksConversion() { + return hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS_CONVERSION) && pepPublishOptions(); + } + public boolean blocking() { return hasDiscoFeature(Jid.of(account.getServer()), Namespace.BLOCKING); } diff --git a/src/main/java/de/pixart/messenger/xmpp/pep/PublishOptions.java b/src/main/java/de/pixart/messenger/xmpp/pep/PublishOptions.java index 534f4005c..56b85169c 100644 --- a/src/main/java/de/pixart/messenger/xmpp/pep/PublishOptions.java +++ b/src/main/java/de/pixart/messenger/xmpp/pep/PublishOptions.java @@ -14,4 +14,11 @@ public class PublishOptions { return options; } + public static Bundle persistentWhitelistAccess() { + final Bundle options = new Bundle(); + options.putString("pubsub#persist_items", "true"); + options.putString("pubsub#access_model", "whitelist"); + return options; + } + }
\ No newline at end of file diff --git a/src/main/res/values/defaults.xml b/src/main/res/values/defaults.xml index 98509a48e..214dbe642 100644 --- a/src/main/res/values/defaults.xml +++ b/src/main/res/values/defaults.xml @@ -108,6 +108,7 @@ <string name="default_font_size">small</string> <bool name="start_searching">false</bool> <bool name="show_record_voice_btn">true</bool> + <bool name="quick_share_attachment_choice">true</bool> <string-array name="domains"> <item>pix-art.de</item> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 47a01d3c9..f5dabc615 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -810,4 +810,6 @@ <string name="no_location_permission">Pix-Art Messenger needs access to location services</string> <string name="pref_show_record_voice_btn_summary">Show record voice button in chats as quick action</string> <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> </resources> diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 31ca6158b..b4edb7a34 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -38,6 +38,11 @@ android:key="presence_colored_names" android:summary="@string/pref_use_colored_names_to_indicate_status_summary" android:title="@string/pref_use_colored_names_to_indicate_status" /> + <CheckBoxPreference + android:defaultValue="@bool/quick_share_attachment_choice" + android:key="quick_share_attachment_choice" + android:summary="@string/pref_use_quick_share_attachment_choice_summary" + android:title="@string/pref_quick_share_attachment_choice" /> <ListPreference android:defaultValue="@string/quick_action" android:dialogTitle="@string/choose_quick_action" |