diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index 4da07af9f..faef2e098 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -708,11 +708,11 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { } public void deleteOmemoIdentity() { - final String node = AxolotlService.PEP_BUNDLES + ":" + getOwnDeviceId(); - final IqPacket deleteBundleNode = mXmppConnectionService.getIqGenerator().deleteNode(node); - mXmppConnectionService.sendIqPacket(account, deleteBundleNode, null); + mXmppConnectionService.deletePepNode( + account, AxolotlService.PEP_BUNDLES + ":" + getOwnDeviceId()); final Set ownDeviceIds = getOwnDeviceIds(); - publishDeviceIdsAndRefineAccessModel(ownDeviceIds == null ? Collections.emptySet() : ownDeviceIds); + publishDeviceIdsAndRefineAccessModel( + ownDeviceIds == null ? Collections.emptySet() : ownDeviceIds); } public List getCryptoTargets(Conversation conversation) { diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index 6b87cb36d..52a19eaa4 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -156,9 +156,9 @@ public class IqGenerator extends AbstractGenerator { public IqPacket publishAvatar(Avatar avatar, Bundle options) { final Element item = new Element("item"); item.setAttribute("id", avatar.sha1sum); - final Element data = item.addChild("data", "urn:xmpp:avatar:data"); + final Element data = item.addChild("data", Namespace.AVATAR_DATA); data.setContent(avatar.image); - return publish("urn:xmpp:avatar:data", item, options); + return publish(Namespace.AVATAR_DATA, item, options); } public IqPacket publishElement(final String namespace, final Element element, String id, final Bundle options) { @@ -172,20 +172,20 @@ public class IqGenerator extends AbstractGenerator { final Element item = new Element("item"); item.setAttribute("id", avatar.sha1sum); final Element metadata = item - .addChild("metadata", "urn:xmpp:avatar:metadata"); + .addChild("metadata", Namespace.AVATAR_METADATA); final Element info = metadata.addChild("info"); info.setAttribute("bytes", avatar.size); info.setAttribute("id", avatar.sha1sum); info.setAttribute("height", avatar.height); info.setAttribute("width", avatar.height); info.setAttribute("type", avatar.type); - return publish("urn:xmpp:avatar:metadata", item, options); + return publish(Namespace.AVATAR_METADATA, item, options); } public IqPacket retrievePepAvatar(final Avatar avatar) { final Element item = new Element("item"); item.setAttribute("id", avatar.sha1sum); - final IqPacket packet = retrieve("urn:xmpp:avatar:data", item); + final IqPacket packet = retrieve(Namespace.AVATAR_DATA, item); packet.setTo(avatar.owner); return packet; } @@ -197,6 +197,13 @@ public class IqGenerator extends AbstractGenerator { return packet; } + public IqPacket retrieveVcardAvatar(final Jid to) { + final IqPacket packet = new IqPacket(IqPacket.TYPE.GET); + packet.setTo(to); + packet.addChild("vCard", "vcard-temp"); + return packet; + } + public IqPacket retrieveAvatarMetaData(final Jid to) { final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null); if (to != null) { diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 5c66451ce..50743312c 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -279,6 +279,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } else if (Namespace.BOOKMARKS2.equals(node) && account.getJid().asBareJid().equals(from)) { account.setBookmarks(Collections.emptyMap()); Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": deleted bookmarks node"); + } else if (Namespace.AVATAR_METADATA.equals(node) && account.getJid().asBareJid().equals(from)) { + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": deleted avatar metadata node"); } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 43d0e769f..79da6d551 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -38,7 +38,6 @@ import android.preference.PreferenceManager; import android.provider.ContactsContract; import android.security.KeyChain; import android.telephony.PhoneStateListener; -import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.DisplayMetrics; @@ -48,6 +47,7 @@ import android.util.Pair; import androidx.annotation.BoolRes; import androidx.annotation.IntegerRes; +import androidx.annotation.NonNull; import androidx.core.app.RemoteInput; import androidx.core.content.ContextCompat; @@ -2792,7 +2792,6 @@ public class XmppConnectionService extends Service { } }); } - public void joinMuc(Conversation conversation) { joinMuc(conversation, null, false); } @@ -3010,6 +3009,71 @@ public class XmppConnectionService extends Service { } } + public void deleteAvatar(final Account account) { + final AtomicBoolean executed = new AtomicBoolean(false); + final Runnable onDeleted = + () -> { + if (executed.compareAndSet(false, true)) { + account.setAvatar(null); + databaseBackend.updateAccount(account); + getAvatarService().clear(account); + updateAccountUi(); + } + }; + deleteVcardAvatar(account, onDeleted); + deletePepNode(account, Namespace.AVATAR_DATA); + deletePepNode(account, Namespace.AVATAR_METADATA, onDeleted); + } + + public void deletePepNode(final Account account, final String node) { + deletePepNode(account, node, null); + } + + private void deletePepNode(final Account account, final String node, final Runnable runnable) { + final IqPacket request = mIqGenerator.deleteNode(node); + sendIqPacket(account, request, (a, packet) -> { + if (packet.getType() == IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG,a.getJid().asBareJid()+": successfully deleted pep node "+node); + if (runnable != null) { + runnable.run(); + } + } else { + Log.d(Config.LOGTAG,a.getJid().asBareJid()+": failed to delete "+ packet); + } + }); + } + + private void deleteVcardAvatar(final Account account, @NonNull final Runnable runnable) { + final IqPacket retrieveVcard = mIqGenerator.retrieveVcardAvatar(account.getJid().asBareJid()); + sendIqPacket(account, retrieveVcard, (a, response) -> { + if (response.getType() != IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG,a.getJid().asBareJid()+": no vCard set. nothing to do"); + return; + } + final Element vcard = response.findChild("vCard", "vcard-temp"); + if (vcard == null) { + Log.d(Config.LOGTAG,a.getJid().asBareJid()+": no vCard set. nothing to do"); + return; + } + Element photo = vcard.findChild("PHOTO"); + if (photo == null) { + photo = vcard.addChild("PHOTO"); + } + photo.clearChildren(); + IqPacket publication = new IqPacket(IqPacket.TYPE.SET); + publication.setTo(a.getJid().asBareJid()); + publication.addChild(vcard); + sendIqPacket(account, publication, (a1, publicationResponse) -> { + if (publicationResponse.getType() == IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG,a1.getJid().asBareJid()+": successfully deleted vcard avatar"); + runnable.run(); + } else { + Log.d(Config.LOGTAG, "failed to publish vcard " + publicationResponse.getErrorCondition()); + } + }); + }); + } + private boolean hasEnabledAccounts() { if (this.accounts == null) { return false; @@ -3598,7 +3662,7 @@ public class XmppConnectionService extends Service { if (result.getType() == IqPacket.TYPE.RESULT) { publishAvatarMetadata(account, avatar, options, true, callback); } else if (retry && PublishOptions.preconditionNotMet(result)) { - pushNodeConfiguration(account, "urn:xmpp:avatar:data", options, new OnConfigurationPushed() { + pushNodeConfiguration(account, Namespace.AVATAR_DATA, options, new OnConfigurationPushed() { @Override public void onPushSucceeded() { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": changed node configuration for avatar node"); @@ -3638,7 +3702,7 @@ public class XmppConnectionService extends Service { callback.onAvatarPublicationSucceeded(); } } else if (retry && PublishOptions.preconditionNotMet(result)) { - pushNodeConfiguration(account, "urn:xmpp:avatar:metadata", options, new OnConfigurationPushed() { + pushNodeConfiguration(account, Namespace.AVATAR_METADATA, options, new OnConfigurationPushed() { @Override public void onPushSucceeded() { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": changed node configuration for avatar meta data node"); diff --git a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java index cb1d0ad31..0e14fcc8f 100644 --- a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java @@ -7,6 +7,8 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.View.OnLongClickListener; import android.widget.Button; @@ -14,6 +16,7 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.annotation.StringRes; import com.theartofdev.edmodo.cropper.CropImage; @@ -120,7 +123,25 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC } @Override - public void onSaveInstanceState(Bundle outState) { + public boolean onCreateOptionsMenu(@NonNull final Menu menu) { + getMenuInflater().inflate(R.menu.activity_publish_profile_picture, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + if (item.getItemId() == R.id.action_delete_avatar) { + if (xmppConnectionService != null && account != null) { + xmppConnectionService.deleteAvatar(account); + } + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { if (this.avatarUri != null) { outState.putParcelable("uri", this.avatarUri); } diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java index 09bbda4cd..72c35a92f 100644 --- a/src/main/java/eu/siacs/conversations/xml/Namespace.java +++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java @@ -26,6 +26,8 @@ public final class Namespace { public static final String BOOKMARKS_CONVERSION = "urn:xmpp:bookmarks-conversion:0"; public static final String BOOKMARKS = "storage:bookmarks"; public static final String SYNCHRONIZATION = "im.quicksy.synchronization:0"; + public static final String AVATAR_DATA = "urn:xmpp:avatar:data"; + public static final String AVATAR_METADATA = "urn:xmpp:avatar:metadata"; public static final String AVATAR_CONVERSION = "urn:xmpp:pep-vcard-conversion:0"; public static final String JINGLE = "urn:xmpp:jingle:1"; public static final String JINGLE_ERRORS = "urn:xmpp:jingle:errors:1"; diff --git a/src/main/res/menu/activity_publish_profile_picture.xml b/src/main/res/menu/activity_publish_profile_picture.xml new file mode 100644 index 000000000..bcfb99ae8 --- /dev/null +++ b/src/main/res/menu/activity_publish_profile_picture.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 299c57b33..53fb4871d 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -979,5 +979,6 @@ Account registrations are not supported No XMPP address found Temporary authentication failure + Delete avatar