aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/pixart
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/pixart')
-rw-r--r--src/main/java/de/pixart/messenger/Config.java2
-rw-r--r--src/main/java/de/pixart/messenger/OmemoActivity.java16
-rw-r--r--src/main/java/de/pixart/messenger/crypto/OtrService.java486
-rw-r--r--src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java24
-rw-r--r--src/main/java/de/pixart/messenger/crypto/PgpEngine.java536
-rw-r--r--src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java188
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java2090
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/CryptoFailedException.java6
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java6
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/NoSessionsCreatedException.java2
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/OnMessageCreatedCallback.java2
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/SQLiteAxolotlStore.java834
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java446
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java218
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/Anonymous.java30
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java140
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/External.java36
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/Plain.java32
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/SaslMechanism.java107
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/ScramSha1.java423
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/Tokenizer.java120
-rw-r--r--src/main/java/de/pixart/messenger/entities/AbstractEntity.java18
-rw-r--r--src/main/java/de/pixart/messenger/entities/Account.java1258
-rw-r--r--src/main/java/de/pixart/messenger/entities/Blockable.java14
-rw-r--r--src/main/java/de/pixart/messenger/entities/Bookmark.java312
-rw-r--r--src/main/java/de/pixart/messenger/entities/Contact.java1059
-rw-r--r--src/main/java/de/pixart/messenger/entities/Conversation.java1888
-rw-r--r--src/main/java/de/pixart/messenger/entities/DownloadableFile.java148
-rw-r--r--src/main/java/de/pixart/messenger/entities/ListItem.java38
-rw-r--r--src/main/java/de/pixart/messenger/entities/Message.java5
-rw-r--r--src/main/java/de/pixart/messenger/entities/MucOptions.java1320
-rw-r--r--src/main/java/de/pixart/messenger/entities/Presence.java170
-rw-r--r--src/main/java/de/pixart/messenger/entities/PresenceTemplate.java134
-rw-r--r--src/main/java/de/pixart/messenger/entities/Presences.java212
-rw-r--r--src/main/java/de/pixart/messenger/entities/Roster.java148
-rw-r--r--src/main/java/de/pixart/messenger/entities/ServiceDiscoveryResult.java648
-rw-r--r--src/main/java/de/pixart/messenger/entities/Transferable.java2
-rw-r--r--src/main/java/de/pixart/messenger/entities/TransferablePlaceholder.java59
-rw-r--r--src/main/java/de/pixart/messenger/generator/AbstractGenerator.java182
-rw-r--r--src/main/java/de/pixart/messenger/generator/IqGenerator.java687
-rw-r--r--src/main/java/de/pixart/messenger/generator/MessageGenerator.java400
-rw-r--r--src/main/java/de/pixart/messenger/generator/PresenceGenerator.java94
-rw-r--r--src/main/java/de/pixart/messenger/http/HttpConnectionManager.java142
-rw-r--r--src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java662
-rw-r--r--src/main/java/de/pixart/messenger/http/HttpUploadConnection.java388
-rw-r--r--src/main/java/de/pixart/messenger/parser/AbstractParser.java130
-rw-r--r--src/main/java/de/pixart/messenger/parser/IqParser.java652
-rw-r--r--src/main/java/de/pixart/messenger/parser/MessageParser.java1218
-rw-r--r--src/main/java/de/pixart/messenger/parser/PresenceParser.java454
-rw-r--r--src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java2510
-rw-r--r--src/main/java/de/pixart/messenger/persistance/FileBackend.java1828
-rw-r--r--src/main/java/de/pixart/messenger/persistance/OnPhoneContactsMerged.java2
-rw-r--r--src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java188
-rw-r--r--src/main/java/de/pixart/messenger/services/AlarmReceiver.java2
-rw-r--r--src/main/java/de/pixart/messenger/services/AvatarService.java802
-rw-r--r--src/main/java/de/pixart/messenger/services/CheckAppVersionService.java2
-rw-r--r--src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java104
-rw-r--r--src/main/java/de/pixart/messenger/services/EventReceiver.java28
-rw-r--r--src/main/java/de/pixart/messenger/services/ExportLogsService.java328
-rw-r--r--src/main/java/de/pixart/messenger/services/MessageArchiveService.java764
-rw-r--r--src/main/java/de/pixart/messenger/services/NotificationService.java8
-rw-r--r--src/main/java/de/pixart/messenger/services/XmppConnectionService.java7565
-rw-r--r--src/main/java/de/pixart/messenger/ui/AboutPreference.java20
-rw-r--r--src/main/java/de/pixart/messenger/ui/AbstractSearchableListItemActivity.java202
-rw-r--r--src/main/java/de/pixart/messenger/ui/BlockContactDialog.java84
-rw-r--r--src/main/java/de/pixart/messenger/ui/BlocklistActivity.java102
-rw-r--r--src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java194
-rw-r--r--src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java428
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java1238
-rw-r--r--src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java996
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationActivity.java3394
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationFragment.java2630
-rw-r--r--src/main/java/de/pixart/messenger/ui/EditAccountActivity.java2200
-rw-r--r--src/main/java/de/pixart/messenger/ui/EditMessage.java161
-rw-r--r--src/main/java/de/pixart/messenger/ui/EnterJidDialog.java214
-rw-r--r--src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java192
-rw-r--r--src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java688
-rw-r--r--src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java547
-rw-r--r--src/main/java/de/pixart/messenger/ui/RecordingActivity.java254
-rw-r--r--src/main/java/de/pixart/messenger/ui/SetPresenceActivity.java400
-rw-r--r--src/main/java/de/pixart/messenger/ui/SettingsActivity.java468
-rw-r--r--src/main/java/de/pixart/messenger/ui/SettingsFragment.java80
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShareLocationActivity.java90
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShareWithActivity.java602
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java100
-rw-r--r--src/main/java/de/pixart/messenger/ui/StartConversationActivity.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/StartUI.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/TimePreference.java178
-rw-r--r--src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java606
-rw-r--r--src/main/java/de/pixart/messenger/ui/UiCallback.java6
-rw-r--r--src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java770
-rw-r--r--src/main/java/de/pixart/messenger/ui/WelcomeActivity.java24
-rw-r--r--src/main/java/de/pixart/messenger/ui/XmppActivity.java2410
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java74
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java116
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java108
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java302
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java1874
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormBooleanFieldWrapper.java114
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormFieldFactory.java35
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java152
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormJidSingleFieldWrapper.java54
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormTextFieldWrapper.java138
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormWrapper.java98
-rw-r--r--src/main/java/de/pixart/messenger/ui/widget/ClickableMovementMethod.java56
-rw-r--r--src/main/java/de/pixart/messenger/ui/widget/Switch.java102
-rw-r--r--src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java14
-rw-r--r--src/main/java/de/pixart/messenger/utils/CryptoHelper.java370
-rw-r--r--src/main/java/de/pixart/messenger/utils/DNSHelper.java464
-rw-r--r--src/main/java/de/pixart/messenger/utils/ExceptionHandler.java32
-rw-r--r--src/main/java/de/pixart/messenger/utils/ExceptionHelper.java195
-rw-r--r--src/main/java/de/pixart/messenger/utils/ExifHelper.java4
-rw-r--r--src/main/java/de/pixart/messenger/utils/FileUtils.java282
-rw-r--r--src/main/java/de/pixart/messenger/utils/GeoHelper.java120
-rw-r--r--src/main/java/de/pixart/messenger/utils/MimeUtils.java15
-rw-r--r--src/main/java/de/pixart/messenger/utils/OnPhoneContactsLoadedListener.java2
-rw-r--r--src/main/java/de/pixart/messenger/utils/PRNGFixes.java519
-rw-r--r--src/main/java/de/pixart/messenger/utils/PhoneHelper.java238
-rw-r--r--src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java102
-rw-r--r--src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java78
-rw-r--r--src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java84
-rw-r--r--src/main/java/de/pixart/messenger/utils/UIHelper.java556
-rw-r--r--src/main/java/de/pixart/messenger/utils/XmlHelper.java18
-rw-r--r--src/main/java/de/pixart/messenger/utils/Xmlns.java10
-rw-r--r--src/main/java/de/pixart/messenger/utils/XmppUri.java271
-rw-r--r--src/main/java/de/pixart/messenger/utils/video/MediaController.java2
-rw-r--r--src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java28
-rw-r--r--src/main/java/de/pixart/messenger/utils/video/Track.java6
-rw-r--r--src/main/java/de/pixart/messenger/xml/Element.java348
-rw-r--r--src/main/java/de/pixart/messenger/xml/Tag.java186
-rw-r--r--src/main/java/de/pixart/messenger/xml/TagWriter.java162
-rw-r--r--src/main/java/de/pixart/messenger/xml/XmlReader.java202
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnAdvancedStreamFeaturesLoaded.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnBindListener.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnContactStatusChanged.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnIqPacketReceived.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnKeyStatusUpdated.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnMessagePacketReceived.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnPresencePacketReceived.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnStatusChanged.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/OnUpdateBlocklist.java16
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/XmppConnection.java3142
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/chatstate/ChatState.java46
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/forms/Data.java174
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/forms/Field.java116
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jid/InvalidJidException.java4
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jid/Jid.java398
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java250
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java2038
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java290
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java424
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java374
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleTransport.java12
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/OnFileTransmissionStatusChanged.java4
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/OnJinglePacketReceived.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/OnPrimaryCandidateFound.java2
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/OnTransportConnected.java4
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Content.java242
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/JinglePacket.java150
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Reason.java12
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/pep/Avatar.java172
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractAcknowledgeableStanza.java40
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractStanza.java82
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/IqPacket.java110
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/MessagePacket.java164
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/PresencePacket.java6
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/csi/ActivePacket.java8
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/csi/InactivePacket.java8
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/AckPacket.java10
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/EnablePacket.java10
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/RequestPacket.java8
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/ResumePacket.java12
174 files changed, 33762 insertions, 33724 deletions
diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java
index 61422938c..f44ab661b 100644
--- a/src/main/java/de/pixart/messenger/Config.java
+++ b/src/main/java/de/pixart/messenger/Config.java
@@ -116,7 +116,7 @@ public final class Config {
public static final boolean PARSE_REAL_JID_FROM_MUC_MAM = false; //dangerous if server doesn’t filter
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
- public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
+ public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
public static final int MAM_MAX_MESSAGES = 500;
public static final long FREQUENT_RESTARTS_DETECTION_WINDOW = 8 * 60 * 60 * 1000; // 8 hours
diff --git a/src/main/java/de/pixart/messenger/OmemoActivity.java b/src/main/java/de/pixart/messenger/OmemoActivity.java
index f60b064cf..dcf420442 100644
--- a/src/main/java/de/pixart/messenger/OmemoActivity.java
+++ b/src/main/java/de/pixart/messenger/OmemoActivity.java
@@ -28,7 +28,7 @@ public abstract class OmemoActivity extends XmppActivity {
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu,v,menuInfo);
+ super.onCreateContextMenu(menu, v, menuInfo);
Object account = v.getTag(R.id.TAG_ACCOUNT);
Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT);
if (account != null && fingerprint != null && account instanceof Account && fingerprint instanceof String) {
@@ -42,7 +42,7 @@ public abstract class OmemoActivity extends XmppActivity {
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.purge_omemo_key:
- showPurgeKeyDialog(mSelectedAccount,mSelectedFingerprint);
+ showPurgeKeyDialog(mSelectedAccount, mSelectedFingerprint);
break;
case R.id.copy_omemo_key:
copyOmemoFingerprint(mSelectedFingerprint);
@@ -88,7 +88,7 @@ public abstract class OmemoActivity extends XmppActivity {
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
- showX509Certificate(account,fingerprint);
+ showX509Certificate(account, fingerprint);
}
};
key.setOnClickListener(listener);
@@ -98,13 +98,13 @@ public abstract class OmemoActivity extends XmppActivity {
ImageView verifiedFingerprintSymbol = (ImageView) view.findViewById(R.id.verified_fingerprint);
trustToggle.setVisibility(View.VISIBLE);
registerForContextMenu(view);
- view.setTag(R.id.TAG_ACCOUNT,account);
- view.setTag(R.id.TAG_FINGERPRINT,fingerprint);
+ view.setTag(R.id.TAG_ACCOUNT, account);
+ view.setTag(R.id.TAG_FINGERPRINT, fingerprint);
boolean x509 = Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
final View.OnClickListener toast;
trustToggle.setChecked(status.isTrusted(), false);
- if (status.isActive()){
+ if (status.isActive()) {
key.setTextColor(getPrimaryTextColor());
keyType.setTextColor(getSecondaryTextColor());
if (status.isVerified()) {
@@ -126,7 +126,7 @@ public abstract class OmemoActivity extends XmppActivity {
trustToggle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- account.getAxolotlService().setFingerprintTrust(fingerprint,FingerprintStatus.createActive(false));
+ account.getAxolotlService().setFingerprintTrust(fingerprint, FingerprintStatus.createActive(false));
v.setEnabled(true);
v.setOnClickListener(null);
}
@@ -211,7 +211,7 @@ public abstract class OmemoActivity extends XmppActivity {
if (x509Certificate != null) {
showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate));
} else {
- Toast.makeText(this,R.string.certificate_not_found, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.certificate_not_found, Toast.LENGTH_SHORT).show();
}
}
diff --git a/src/main/java/de/pixart/messenger/crypto/OtrService.java b/src/main/java/de/pixart/messenger/crypto/OtrService.java
index 18f9670d8..459f49c38 100644
--- a/src/main/java/de/pixart/messenger/crypto/OtrService.java
+++ b/src/main/java/de/pixart/messenger/crypto/OtrService.java
@@ -38,25 +38,25 @@ import de.pixart.messenger.xmpp.stanzas.MessagePacket;
public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
- private Account account;
- private OtrPolicy otrPolicy;
- private KeyPair keyPair;
- private XmppConnectionService mXmppConnectionService;
-
- public OtrService(XmppConnectionService service, Account account) {
- this.account = account;
- this.otrPolicy = new OtrPolicyImpl();
- this.otrPolicy.setAllowV1(false);
- this.otrPolicy.setAllowV2(true);
- this.otrPolicy.setAllowV3(true);
- this.keyPair = loadKey(account.getKeys());
- this.mXmppConnectionService = service;
- }
-
- private KeyPair loadKey(final JSONObject keys) {
- if (keys == null) {
- return null;
- }
+ private Account account;
+ private OtrPolicy otrPolicy;
+ private KeyPair keyPair;
+ private XmppConnectionService mXmppConnectionService;
+
+ public OtrService(XmppConnectionService service, Account account) {
+ this.account = account;
+ this.otrPolicy = new OtrPolicyImpl();
+ this.otrPolicy.setAllowV1(false);
+ this.otrPolicy.setAllowV2(true);
+ this.otrPolicy.setAllowV3(true);
+ this.keyPair = loadKey(account.getKeys());
+ this.mXmppConnectionService = service;
+ }
+
+ private KeyPair loadKey(final JSONObject keys) {
+ if (keys == null) {
+ return null;
+ }
synchronized (keys) {
try {
BigInteger x = new BigInteger(keys.getString("otr_x"), 16);
@@ -78,232 +78,232 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
return null;
}
}
- }
-
- private void saveKey() {
- PublicKey publicKey = keyPair.getPublic();
- PrivateKey privateKey = keyPair.getPrivate();
- KeyFactory keyFactory;
- try {
- keyFactory = KeyFactory.getInstance("DSA");
- DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(
- privateKey, DSAPrivateKeySpec.class);
- DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey,
- DSAPublicKeySpec.class);
- this.account.setKey("otr_x", privateKeySpec.getX().toString(16));
- this.account.setKey("otr_g", privateKeySpec.getG().toString(16));
- this.account.setKey("otr_p", privateKeySpec.getP().toString(16));
- this.account.setKey("otr_q", privateKeySpec.getQ().toString(16));
- this.account.setKey("otr_y", publicKeySpec.getY().toString(16));
- } catch (final NoSuchAlgorithmException | InvalidKeySpecException e) {
- e.printStackTrace();
- }
+ }
+
+ private void saveKey() {
+ PublicKey publicKey = keyPair.getPublic();
+ PrivateKey privateKey = keyPair.getPrivate();
+ KeyFactory keyFactory;
+ try {
+ keyFactory = KeyFactory.getInstance("DSA");
+ DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(
+ privateKey, DSAPrivateKeySpec.class);
+ DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey,
+ DSAPublicKeySpec.class);
+ this.account.setKey("otr_x", privateKeySpec.getX().toString(16));
+ this.account.setKey("otr_g", privateKeySpec.getG().toString(16));
+ this.account.setKey("otr_p", privateKeySpec.getP().toString(16));
+ this.account.setKey("otr_q", privateKeySpec.getQ().toString(16));
+ this.account.setKey("otr_y", publicKeySpec.getY().toString(16));
+ } catch (final NoSuchAlgorithmException | InvalidKeySpecException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @Override
+ public void askForSecret(SessionID id, InstanceTag instanceTag, String question) {
+ try {
+ final Jid jid = Jid.fromSessionID(id);
+ Conversation conversation = this.mXmppConnectionService.find(this.account, jid);
+ if (conversation != null) {
+ conversation.smp().hint = question;
+ conversation.smp().status = Conversation.Smp.STATUS_CONTACT_REQUESTED;
+ mXmppConnectionService.updateConversationUi();
+ }
+ } catch (InvalidJidException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": smp in invalid session " + id.toString());
+ }
+ }
+
+ @Override
+ public void finishedSessionMessage(SessionID arg0, String arg1)
+ throws OtrException {
+
+ }
+
+ @Override
+ public String getFallbackMessage(SessionID arg0) {
+ return MessageGenerator.OTR_FALLBACK_MESSAGE;
+ }
+
+ @Override
+ public byte[] getLocalFingerprintRaw(SessionID arg0) {
+ try {
+ return getFingerprintRaw(getPublicKey());
+ } catch (OtrCryptoException e) {
+ return null;
+ }
+ }
+
+ public PublicKey getPublicKey() {
+ if (this.keyPair == null) {
+ return null;
+ }
+ return this.keyPair.getPublic();
+ }
+
+ @Override
+ public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException {
+ if (this.keyPair == null) {
+ KeyPairGenerator kg;
+ try {
+ kg = KeyPairGenerator.getInstance("DSA");
+ this.keyPair = kg.genKeyPair();
+ this.saveKey();
+ mXmppConnectionService.databaseBackend.updateAccount(account);
+ } catch (NoSuchAlgorithmException e) {
+ Log.d(Config.LOGTAG,
+ "error generating key pair " + e.getMessage());
+ }
+ }
+ return this.keyPair;
+ }
+
+ @Override
+ public String getReplyForUnreadableMessage(SessionID arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public OtrPolicy getSessionPolicy(SessionID arg0) {
+ return otrPolicy;
+ }
+
+ @Override
+ public void injectMessage(SessionID session, String body)
+ throws OtrException {
+ MessagePacket packet = new MessagePacket();
+ packet.setFrom(account.getJid());
+ if (session.getUserID().isEmpty()) {
+ packet.setAttribute("to", session.getAccountID());
+ } else {
+ packet.setAttribute("to", session.getAccountID() + "/" + session.getUserID());
+ }
+ packet.setBody(body);
+ MessageGenerator.addMessageHints(packet);
+ try {
+ Jid jid = Jid.fromSessionID(session);
+ Conversation conversation = mXmppConnectionService.find(account, jid);
+ if (conversation != null && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+ if (mXmppConnectionService.sendChatStates()) {
+ packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
+ }
+ }
+ } catch (final InvalidJidException ignored) {
+
+ }
+
+ packet.setType(MessagePacket.TYPE_CHAT);
+ account.getXmppConnection().sendMessagePacket(packet);
+ }
+
+ @Override
+ public void messageFromAnotherInstanceReceived(SessionID session) {
+ sendOtrErrorMessage(session, "Message from another OTR-instance received");
+ }
+
+ @Override
+ public void multipleInstancesDetected(SessionID arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void requireEncryptedMessage(SessionID arg0, String arg1)
+ throws OtrException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void showError(SessionID arg0, String arg1) throws OtrException {
+ Log.d(Config.LOGTAG, "show error");
+ }
+
+ @Override
+ public void smpAborted(SessionID id) throws OtrException {
+ setSmpStatus(id, Conversation.Smp.STATUS_NONE);
+ }
+
+ private void setSmpStatus(SessionID id, int status) {
+ try {
+ final Jid jid = Jid.fromSessionID(id);
+ Conversation conversation = this.mXmppConnectionService.find(this.account, jid);
+ if (conversation != null) {
+ conversation.smp().status = status;
+ mXmppConnectionService.updateConversationUi();
+ }
+ } catch (final InvalidJidException ignored) {
+
+ }
+ }
+
+ @Override
+ public void smpError(SessionID id, int arg1, boolean arg2)
+ throws OtrException {
+ setSmpStatus(id, Conversation.Smp.STATUS_NONE);
+ }
+
+ @Override
+ public void unencryptedMessageReceived(SessionID arg0, String arg1)
+ throws OtrException {
+ throw new OtrException(new Exception("unencrypted message received"));
+ }
+
+ @Override
+ public void unreadableMessageReceived(SessionID session) throws OtrException {
+ Log.d(Config.LOGTAG, "unreadable message received");
+ sendOtrErrorMessage(session, "You sent me an unreadable OTR-encrypted message");
+ }
+
+ public void sendOtrErrorMessage(SessionID session, String errorText) {
+ try {
+ Jid jid = Jid.fromSessionID(session);
+ Conversation conversation = mXmppConnectionService.find(account, jid);
+ String id = conversation == null ? null : conversation.getLastReceivedOtrMessageId();
+ if (id != null) {
+ MessagePacket packet = mXmppConnectionService.getMessageGenerator()
+ .generateOtrError(jid, id, errorText);
+ packet.setFrom(account.getJid());
+ mXmppConnectionService.sendMessagePacket(account, packet);
+ Log.d(Config.LOGTAG, packet.toString());
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ + ": unreadable OTR message in " + conversation.getName());
+ }
+ } catch (InvalidJidException e) {
+ return;
+ }
+ }
+ @Override
+ public void unverify(SessionID id, String arg1) {
+ setSmpStatus(id, Conversation.Smp.STATUS_FAILED);
}
- @Override
- public void askForSecret(SessionID id, InstanceTag instanceTag, String question) {
- try {
- final Jid jid = Jid.fromSessionID(id);
- Conversation conversation = this.mXmppConnectionService.find(this.account,jid);
- if (conversation!=null) {
- conversation.smp().hint = question;
- conversation.smp().status = Conversation.Smp.STATUS_CONTACT_REQUESTED;
- mXmppConnectionService.updateConversationUi();
- }
- } catch (InvalidJidException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": smp in invalid session "+id.toString());
- }
- }
-
- @Override
- public void finishedSessionMessage(SessionID arg0, String arg1)
- throws OtrException {
-
- }
-
- @Override
- public String getFallbackMessage(SessionID arg0) {
- return MessageGenerator.OTR_FALLBACK_MESSAGE;
- }
-
- @Override
- public byte[] getLocalFingerprintRaw(SessionID arg0) {
- try {
- return getFingerprintRaw(getPublicKey());
- } catch (OtrCryptoException e) {
- return null;
- }
- }
-
- public PublicKey getPublicKey() {
- if (this.keyPair == null) {
- return null;
- }
- return this.keyPair.getPublic();
- }
-
- @Override
- public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException {
- if (this.keyPair == null) {
- KeyPairGenerator kg;
- try {
- kg = KeyPairGenerator.getInstance("DSA");
- this.keyPair = kg.genKeyPair();
- this.saveKey();
- mXmppConnectionService.databaseBackend.updateAccount(account);
- } catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG,
- "error generating key pair " + e.getMessage());
- }
- }
- return this.keyPair;
- }
-
- @Override
- public String getReplyForUnreadableMessage(SessionID arg0) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public OtrPolicy getSessionPolicy(SessionID arg0) {
- return otrPolicy;
- }
-
- @Override
- public void injectMessage(SessionID session, String body)
- throws OtrException {
- MessagePacket packet = new MessagePacket();
- packet.setFrom(account.getJid());
- if (session.getUserID().isEmpty()) {
- packet.setAttribute("to", session.getAccountID());
- } else {
- packet.setAttribute("to", session.getAccountID() + "/" + session.getUserID());
- }
- packet.setBody(body);
- MessageGenerator.addMessageHints(packet);
- try {
- Jid jid = Jid.fromSessionID(session);
- Conversation conversation = mXmppConnectionService.find(account,jid);
- if (conversation != null && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
- if (mXmppConnectionService.sendChatStates()) {
- packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
- }
- }
- } catch (final InvalidJidException ignored) {
-
- }
-
- packet.setType(MessagePacket.TYPE_CHAT);
- account.getXmppConnection().sendMessagePacket(packet);
- }
-
- @Override
- public void messageFromAnotherInstanceReceived(SessionID session) {
- sendOtrErrorMessage(session, "Message from another OTR-instance received");
- }
-
- @Override
- public void multipleInstancesDetected(SessionID arg0) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void requireEncryptedMessage(SessionID arg0, String arg1)
- throws OtrException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void showError(SessionID arg0, String arg1) throws OtrException {
- Log.d(Config.LOGTAG,"show error");
- }
-
- @Override
- public void smpAborted(SessionID id) throws OtrException {
- setSmpStatus(id, Conversation.Smp.STATUS_NONE);
- }
-
- private void setSmpStatus(SessionID id, int status) {
- try {
- final Jid jid = Jid.fromSessionID(id);
- Conversation conversation = this.mXmppConnectionService.find(this.account,jid);
- if (conversation!=null) {
- conversation.smp().status = status;
- mXmppConnectionService.updateConversationUi();
- }
- } catch (final InvalidJidException ignored) {
-
- }
- }
-
- @Override
- public void smpError(SessionID id, int arg1, boolean arg2)
- throws OtrException {
- setSmpStatus(id, Conversation.Smp.STATUS_NONE);
- }
-
- @Override
- public void unencryptedMessageReceived(SessionID arg0, String arg1)
- throws OtrException {
- throw new OtrException(new Exception("unencrypted message received"));
- }
-
- @Override
- public void unreadableMessageReceived(SessionID session) throws OtrException {
- Log.d(Config.LOGTAG,"unreadable message received");
- sendOtrErrorMessage(session, "You sent me an unreadable OTR-encrypted message");
- }
-
- public void sendOtrErrorMessage(SessionID session, String errorText) {
- try {
- Jid jid = Jid.fromSessionID(session);
- Conversation conversation = mXmppConnectionService.find(account, jid);
- String id = conversation == null ? null : conversation.getLastReceivedOtrMessageId();
- if (id != null) {
- MessagePacket packet = mXmppConnectionService.getMessageGenerator()
- .generateOtrError(jid, id, errorText);
- packet.setFrom(account.getJid());
- mXmppConnectionService.sendMessagePacket(account,packet);
- Log.d(Config.LOGTAG,packet.toString());
- Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()
- +": unreadable OTR message in "+conversation.getName());
- }
- } catch (InvalidJidException e) {
- return;
- }
- }
-
- @Override
- public void unverify(SessionID id, String arg1) {
- setSmpStatus(id, Conversation.Smp.STATUS_FAILED);
- }
-
- @Override
- public void verify(SessionID id, String fingerprint, boolean approved) {
- Log.d(Config.LOGTAG,"OtrService.verify("+id.toString()+","+fingerprint+","+String.valueOf(approved)+")");
- try {
- final Jid jid = Jid.fromSessionID(id);
- Conversation conversation = this.mXmppConnectionService.find(this.account,jid);
- if (conversation!=null) {
- if (approved) {
- conversation.getContact().addOtrFingerprint(fingerprint);
- }
- conversation.smp().hint = null;
- conversation.smp().status = Conversation.Smp.STATUS_VERIFIED;
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.syncRosterToDisk(conversation.getAccount());
- }
- } catch (final InvalidJidException ignored) {
- }
- }
-
- @Override
- public FragmenterInstructions getFragmenterInstructions(SessionID sessionID) {
- return null;
- }
+ @Override
+ public void verify(SessionID id, String fingerprint, boolean approved) {
+ Log.d(Config.LOGTAG, "OtrService.verify(" + id.toString() + "," + fingerprint + "," + String.valueOf(approved) + ")");
+ try {
+ final Jid jid = Jid.fromSessionID(id);
+ Conversation conversation = this.mXmppConnectionService.find(this.account, jid);
+ if (conversation != null) {
+ if (approved) {
+ conversation.getContact().addOtrFingerprint(fingerprint);
+ }
+ conversation.smp().hint = null;
+ conversation.smp().status = Conversation.Smp.STATUS_VERIFIED;
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.syncRosterToDisk(conversation.getAccount());
+ }
+ } catch (final InvalidJidException ignored) {
+ }
+ }
+
+ @Override
+ public FragmenterInstructions getFragmenterInstructions(SessionID sessionID) {
+ return null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java b/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java
index ffa853fda..4b0e78764 100644
--- a/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java
+++ b/src/main/java/de/pixart/messenger/crypto/PgpDecryptionService.java
@@ -28,9 +28,9 @@ public class PgpDecryptionService {
private final XmppConnectionService mXmppConnectionService;
private OpenPgpApi openPgpApi = null;
- protected final ArrayDeque<Message> messages = new ArrayDeque();
+ protected final ArrayDeque<Message> messages = new ArrayDeque();
protected final HashSet<Message> pendingNotifications = new HashSet<>();
- Message currentMessage;
+ Message currentMessage;
private PendingIntent pendingIntent;
@@ -38,7 +38,7 @@ public class PgpDecryptionService {
this.mXmppConnectionService = service;
}
- public synchronized boolean decrypt(final Message message, boolean notify) {
+ public synchronized boolean decrypt(final Message message, boolean notify) {
messages.add(message);
if (notify && pendingIntent == null) {
pendingNotifications.add(message);
@@ -48,10 +48,10 @@ public class PgpDecryptionService {
continueDecryption();
return notify;
}
- }
+ }
public synchronized void decrypt(final List<Message> list) {
- for(Message message : list) {
+ for (Message message : list) {
if (message.getEncryption() == Message.ENCRYPTION_PGP) {
messages.add(message);
}
@@ -69,19 +69,19 @@ public class PgpDecryptionService {
this.pendingNotifications.remove(message);
}
- protected synchronized void decryptNext() {
- if (pendingIntent == null
+ protected synchronized void decryptNext() {
+ if (pendingIntent == null
&& getOpenPgpApi() != null
- && (currentMessage = messages.poll()) != null) {
- new Thread(new Runnable() {
+ && (currentMessage = messages.poll()) != null) {
+ new Thread(new Runnable() {
@Override
public void run() {
executeApi(currentMessage);
decryptNext();
}
}).start();
- }
- }
+ }
+ }
public synchronized void continueDecryption(boolean resetPending) {
if (resetPending) {
@@ -200,7 +200,7 @@ public class PgpDecryptionService {
if (pendingIntent == null) {
return false;
} else {
- for(Message message : messages) {
+ for (Message message : messages) {
if (message.getConversation() == conversation) {
return true;
}
diff --git a/src/main/java/de/pixart/messenger/crypto/PgpEngine.java b/src/main/java/de/pixart/messenger/crypto/PgpEngine.java
index 38d097ec3..c906c7514 100644
--- a/src/main/java/de/pixart/messenger/crypto/PgpEngine.java
+++ b/src/main/java/de/pixart/messenger/crypto/PgpEngine.java
@@ -29,290 +29,290 @@ import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.ui.UiCallback;
public class PgpEngine {
- private OpenPgpApi api;
- private XmppConnectionService mXmppConnectionService;
+ private OpenPgpApi api;
+ private XmppConnectionService mXmppConnectionService;
- public PgpEngine(OpenPgpApi api, XmppConnectionService service) {
- this.api = api;
- this.mXmppConnectionService = service;
- }
+ public PgpEngine(OpenPgpApi api, XmppConnectionService service) {
+ this.api = api;
+ this.mXmppConnectionService = service;
+ }
- public void encrypt(final Message message, final UiCallback<Message> callback) {
- Intent params = new Intent();
- params.setAction(OpenPgpApi.ACTION_ENCRYPT);
- final Conversation conversation = message.getConversation();
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- long[] keys = {
- conversation.getContact().getPgpKeyId(),
- conversation.getAccount().getPgpId()
- };
- params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys);
- } else {
- params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, conversation.getMucOptions().getPgpKeyIds());
- }
+ public void encrypt(final Message message, final UiCallback<Message> callback) {
+ Intent params = new Intent();
+ params.setAction(OpenPgpApi.ACTION_ENCRYPT);
+ final Conversation conversation = message.getConversation();
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ long[] keys = {
+ conversation.getContact().getPgpKeyId(),
+ conversation.getAccount().getPgpId()
+ };
+ params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys);
+ } else {
+ params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, conversation.getMucOptions().getPgpKeyIds());
+ }
- if (!message.needsUploading()) {
- params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- String body;
- if (message.hasFileOnRemoteHost()) {
- body = message.getFileParams().url.toString();
- } else {
- body = message.getBody();
- }
- InputStream is = new ByteArrayInputStream(body.getBytes());
- final OutputStream os = new ByteArrayOutputStream();
- api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
+ if (!message.needsUploading()) {
+ params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
+ String body;
+ if (message.hasFileOnRemoteHost()) {
+ body = message.getFileParams().url.toString();
+ } else {
+ body = message.getBody();
+ }
+ InputStream is = new ByteArrayInputStream(body.getBytes());
+ final OutputStream os = new ByteArrayOutputStream();
+ api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
- @Override
- public void onReturn(Intent result) {
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
- OpenPgpApi.RESULT_CODE_ERROR)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- try {
- os.flush();
- StringBuilder encryptedMessageBody = new StringBuilder();
- String[] lines = os.toString().split("\n");
- for (int i = 2; i < lines.length - 1; ++i) {
- if (!lines[i].contains("Version")) {
- encryptedMessageBody.append(lines[i].trim());
- }
- }
- message.setEncryptedBody(encryptedMessageBody
- .toString());
- callback.success(message);
- } catch (IOException e) {
- callback.error(R.string.openpgp_error, message);
- }
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
+ OpenPgpApi.RESULT_CODE_ERROR)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ try {
+ os.flush();
+ StringBuilder encryptedMessageBody = new StringBuilder();
+ String[] lines = os.toString().split("\n");
+ for (int i = 2; i < lines.length - 1; ++i) {
+ if (!lines[i].contains("Version")) {
+ encryptedMessageBody.append(lines[i].trim());
+ }
+ }
+ message.setEncryptedBody(encryptedMessageBody
+ .toString());
+ callback.success(message);
+ } catch (IOException e) {
+ callback.error(R.string.openpgp_error, message);
+ }
- break;
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- callback.userInputRequried((PendingIntent) result
- .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
- message);
- break;
- case OpenPgpApi.RESULT_CODE_ERROR:
- logError(conversation.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- callback.error(R.string.openpgp_error, message);
- break;
- }
- }
- });
- } else {
- try {
- DownloadableFile inputFile = this.mXmppConnectionService
- .getFileBackend().getFile(message, true);
- DownloadableFile outputFile = this.mXmppConnectionService
- .getFileBackend().getFile(message, false);
- outputFile.getParentFile().mkdirs();
- outputFile.createNewFile();
- final InputStream is = new FileInputStream(inputFile);
- final OutputStream os = new FileOutputStream(outputFile);
- api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
+ break;
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ callback.userInputRequried((PendingIntent) result
+ .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+ message);
+ break;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ logError(conversation.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+ callback.error(R.string.openpgp_error, message);
+ break;
+ }
+ }
+ });
+ } else {
+ try {
+ DownloadableFile inputFile = this.mXmppConnectionService
+ .getFileBackend().getFile(message, true);
+ DownloadableFile outputFile = this.mXmppConnectionService
+ .getFileBackend().getFile(message, false);
+ outputFile.getParentFile().mkdirs();
+ outputFile.createNewFile();
+ final InputStream is = new FileInputStream(inputFile);
+ final OutputStream os = new FileOutputStream(outputFile);
+ api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
- @Override
- public void onReturn(Intent result) {
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
- OpenPgpApi.RESULT_CODE_ERROR)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- try {
- os.flush();
- } catch (IOException ignored) {
- //ignored
- }
- FileBackend.close(os);
- callback.success(message);
- break;
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- callback.userInputRequried(
- (PendingIntent) result
- .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
- message);
- break;
- case OpenPgpApi.RESULT_CODE_ERROR:
- logError(conversation.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- callback.error(R.string.openpgp_error, message);
- break;
- }
- }
- });
- } catch (final IOException e) {
- callback.error(R.string.openpgp_error, message);
- }
- }
- }
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
+ OpenPgpApi.RESULT_CODE_ERROR)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ try {
+ os.flush();
+ } catch (IOException ignored) {
+ //ignored
+ }
+ FileBackend.close(os);
+ callback.success(message);
+ break;
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ callback.userInputRequried(
+ (PendingIntent) result
+ .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+ message);
+ break;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ logError(conversation.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+ callback.error(R.string.openpgp_error, message);
+ break;
+ }
+ }
+ });
+ } catch (final IOException e) {
+ callback.error(R.string.openpgp_error, message);
+ }
+ }
+ }
- public long fetchKeyId(Account account, String status, String signature) {
- if ((signature == null) || (api == null)) {
- return 0;
- }
- if (status == null) {
- status = "";
- }
- final StringBuilder pgpSig = new StringBuilder();
- pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----");
- pgpSig.append('\n');
- pgpSig.append('\n');
- pgpSig.append(status);
- pgpSig.append('\n');
- pgpSig.append("-----BEGIN PGP SIGNATURE-----");
- pgpSig.append('\n');
- pgpSig.append('\n');
- pgpSig.append(signature.replace("\n", "").trim());
- pgpSig.append('\n');
- pgpSig.append("-----END PGP SIGNATURE-----");
- Intent params = new Intent();
- params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
- params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- Intent result = api.executeApi(params, is, os);
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
- OpenPgpApi.RESULT_CODE_ERROR)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- OpenPgpSignatureResult sigResult = result
- .getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
- if (sigResult != null) {
- return sigResult.getKeyId();
- } else {
- return 0;
- }
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- return 0;
- case OpenPgpApi.RESULT_CODE_ERROR:
- logError(account, (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- return 0;
- }
- return 0;
- }
+ public long fetchKeyId(Account account, String status, String signature) {
+ if ((signature == null) || (api == null)) {
+ return 0;
+ }
+ if (status == null) {
+ status = "";
+ }
+ final StringBuilder pgpSig = new StringBuilder();
+ pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----");
+ pgpSig.append('\n');
+ pgpSig.append('\n');
+ pgpSig.append(status);
+ pgpSig.append('\n');
+ pgpSig.append("-----BEGIN PGP SIGNATURE-----");
+ pgpSig.append('\n');
+ pgpSig.append('\n');
+ pgpSig.append(signature.replace("\n", "").trim());
+ pgpSig.append('\n');
+ pgpSig.append("-----END PGP SIGNATURE-----");
+ Intent params = new Intent();
+ params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
+ params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
+ InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ Intent result = api.executeApi(params, is, os);
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
+ OpenPgpApi.RESULT_CODE_ERROR)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ OpenPgpSignatureResult sigResult = result
+ .getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
+ if (sigResult != null) {
+ return sigResult.getKeyId();
+ } else {
+ return 0;
+ }
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ return 0;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ logError(account, (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+ return 0;
+ }
+ return 0;
+ }
- public void chooseKey(final Account account, final UiCallback<Account> callback) {
- Intent p = new Intent();
- p.setAction(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
- api.executeApiAsync(p, null, null, new IOpenPgpCallback() {
+ public void chooseKey(final Account account, final UiCallback<Account> callback) {
+ Intent p = new Intent();
+ p.setAction(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
+ api.executeApiAsync(p, null, null, new IOpenPgpCallback() {
- @Override
- public void onReturn(Intent result) {
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- callback.success(account);
- return;
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- callback.userInputRequried((PendingIntent) result
- .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
- account);
- return;
- case OpenPgpApi.RESULT_CODE_ERROR:
- logError(account, (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- callback.error(R.string.openpgp_error, account);
- }
- }
- });
- }
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ callback.success(account);
+ return;
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ callback.userInputRequried((PendingIntent) result
+ .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+ account);
+ return;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ logError(account, (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+ callback.error(R.string.openpgp_error, account);
+ }
+ }
+ });
+ }
- public void generateSignature(final Account account, String status,
- final UiCallback<Account> callback) {
- if (account.getPgpId() == 0) {
- return;
- }
- Intent params = new Intent();
- params.setAction(OpenPgpApi.ACTION_CLEARTEXT_SIGN);
- params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- params.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, account.getPgpId());
- InputStream is = new ByteArrayInputStream(status.getBytes());
- final OutputStream os = new ByteArrayOutputStream();
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": signing status message \""+status+"\"");
- api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
+ public void generateSignature(final Account account, String status,
+ final UiCallback<Account> callback) {
+ if (account.getPgpId() == 0) {
+ return;
+ }
+ Intent params = new Intent();
+ params.setAction(OpenPgpApi.ACTION_CLEARTEXT_SIGN);
+ params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
+ params.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, account.getPgpId());
+ InputStream is = new ByteArrayInputStream(status.getBytes());
+ final OutputStream os = new ByteArrayOutputStream();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": signing status message \"" + status + "\"");
+ api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
- @Override
- public void onReturn(Intent result) {
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- StringBuilder signatureBuilder = new StringBuilder();
- try {
- os.flush();
- String[] lines = os.toString().split("\n");
- boolean sig = false;
- for (String line : lines) {
- if (sig) {
- if (line.contains("END PGP SIGNATURE")) {
- sig = false;
- } else {
- if (!line.contains("Version")) {
- signatureBuilder.append(line.trim());
- }
- }
- }
- if (line.contains("BEGIN PGP SIGNATURE")) {
- sig = true;
- }
- }
- } catch (IOException e) {
- callback.error(R.string.openpgp_error, account);
- return;
- }
- account.setPgpSignature(signatureBuilder.toString());
- callback.success(account);
- return;
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- callback.userInputRequried((PendingIntent) result
- .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
- account);
- return;
- case OpenPgpApi.RESULT_CODE_ERROR:
- OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
- if (error != null && "signing subkey not found!".equals(error.getMessage())) {
- callback.error(0,account);
- } else {
- logError(account, error);
- callback.error(R.string.unable_to_connect_to_keychain, null);
- }
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ StringBuilder signatureBuilder = new StringBuilder();
+ try {
+ os.flush();
+ String[] lines = os.toString().split("\n");
+ boolean sig = false;
+ for (String line : lines) {
+ if (sig) {
+ if (line.contains("END PGP SIGNATURE")) {
+ sig = false;
+ } else {
+ if (!line.contains("Version")) {
+ signatureBuilder.append(line.trim());
+ }
+ }
+ }
+ if (line.contains("BEGIN PGP SIGNATURE")) {
+ sig = true;
+ }
+ }
+ } catch (IOException e) {
+ callback.error(R.string.openpgp_error, account);
+ return;
+ }
+ account.setPgpSignature(signatureBuilder.toString());
+ callback.success(account);
+ return;
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ callback.userInputRequried((PendingIntent) result
+ .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+ account);
+ return;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
+ if (error != null && "signing subkey not found!".equals(error.getMessage())) {
+ callback.error(0, account);
+ } else {
+ logError(account, error);
+ callback.error(R.string.unable_to_connect_to_keychain, null);
+ }
}
- }
- });
- }
+ }
+ });
+ }
- public void hasKey(final Contact contact, final UiCallback<Contact> callback) {
- Intent params = new Intent();
- params.setAction(OpenPgpApi.ACTION_GET_KEY);
- params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
- api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
+ public void hasKey(final Contact contact, final UiCallback<Contact> callback) {
+ Intent params = new Intent();
+ params.setAction(OpenPgpApi.ACTION_GET_KEY);
+ params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
+ api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
- @Override
- public void onReturn(Intent result) {
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
- case OpenPgpApi.RESULT_CODE_SUCCESS:
- callback.success(contact);
- return;
- case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
- callback.userInputRequried((PendingIntent) result
- .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
- contact);
- return;
- case OpenPgpApi.RESULT_CODE_ERROR:
- logError(contact.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
- callback.error(R.string.openpgp_error, contact);
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS:
+ callback.success(contact);
+ return;
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+ callback.userInputRequried((PendingIntent) result
+ .getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+ contact);
+ return;
+ case OpenPgpApi.RESULT_CODE_ERROR:
+ logError(contact.getAccount(), (OpenPgpError) result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+ callback.error(R.string.openpgp_error, contact);
}
- }
- });
- }
+ }
+ });
+ }
- private static void logError(Account account, OpenPgpError error) {
- if (error != null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": OpenKeychain error '"+error.getMessage()+"' code="+error.getErrorId());
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": OpenKeychain error with no message");
- }
- }
+ private static void logError(Account account, OpenPgpError error) {
+ if (error != null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": OpenKeychain error '" + error.getMessage() + "' code=" + error.getErrorId());
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": OpenKeychain error with no message");
+ }
+ }
- public PendingIntent getIntentForKey(Contact contact) {
- return getIntentForKey(contact.getPgpKeyId());
- }
+ public PendingIntent getIntentForKey(Contact contact) {
+ return getIntentForKey(contact.getPgpKeyId());
+ }
- public PendingIntent getIntentForKey(long pgpKeyId) {
- Intent params = new Intent();
- params.setAction(OpenPgpApi.ACTION_GET_KEY);
- params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId);
- Intent result = api.executeApi(params, null, null);
- return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
- }
+ public PendingIntent getIntentForKey(long pgpKeyId) {
+ Intent params = new Intent();
+ params.setAction(OpenPgpApi.ACTION_GET_KEY);
+ params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId);
+ Intent result = api.executeApi(params, null, null);
+ return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java b/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
index 250516daa..d305a33d5 100644
--- a/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
+++ b/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
@@ -26,102 +26,102 @@ import javax.net.ssl.SSLSession;
public class XmppDomainVerifier implements HostnameVerifier {
- private static final String LOGTAG = "XmppDomainVerifier";
+ private static final String LOGTAG = "XmppDomainVerifier";
- private final String SRVName = "1.3.6.1.5.5.7.8.7";
- private final String xmppAddr = "1.3.6.1.5.5.7.8.5";
+ private final String SRVName = "1.3.6.1.5.5.7.8.7";
+ private final String xmppAddr = "1.3.6.1.5.5.7.8.5";
- @Override
- public boolean verify(String domain, SSLSession sslSession) {
- try {
- Certificate[] chain = sslSession.getPeerCertificates();
- if (chain.length == 0 || !(chain[0] instanceof X509Certificate)) {
- return false;
- }
- X509Certificate certificate = (X509Certificate) chain[0];
- Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
- List<String> xmppAddrs = new ArrayList<>();
- List<String> srvNames = new ArrayList<>();
- List<String> domains = new ArrayList<>();
- if (alternativeNames != null) {
- for (List<?> san : alternativeNames) {
- Integer type = (Integer) san.get(0);
- if (type == 0) {
- Pair<String, String> otherName = parseOtherName((byte[]) san.get(1));
- if (otherName != null) {
- switch (otherName.first) {
- case SRVName:
- srvNames.add(otherName.second);
- break;
- case xmppAddr:
- xmppAddrs.add(otherName.second);
- break;
- default:
- Log.d(LOGTAG, "oid: " + otherName.first + " value: " + otherName.second);
- }
- }
- } else if (type == 2) {
- Object value = san.get(1);
- if (value instanceof String) {
- domains.add((String) value);
- }
- }
- }
- }
- if (srvNames.size() == 0 && xmppAddrs.size() == 0 && domains.size() == 0) {
- X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
- RDN[] rdns = x500name.getRDNs(BCStyle.CN);
- for (int i = 0; i < rdns.length; ++i) {
- domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue()));
- }
- }
- Log.d(LOGTAG, "searching for " + domain + " in srvNames: " + srvNames + " xmppAddrs: " + xmppAddrs + " domains:" + domains);
- return xmppAddrs.contains(domain) || srvNames.contains("_xmpp-client." + domain) || matchDomain(domain, domains);
- } catch (Exception e) {
- return false;
- }
- }
+ @Override
+ public boolean verify(String domain, SSLSession sslSession) {
+ try {
+ Certificate[] chain = sslSession.getPeerCertificates();
+ if (chain.length == 0 || !(chain[0] instanceof X509Certificate)) {
+ return false;
+ }
+ X509Certificate certificate = (X509Certificate) chain[0];
+ Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
+ List<String> xmppAddrs = new ArrayList<>();
+ List<String> srvNames = new ArrayList<>();
+ List<String> domains = new ArrayList<>();
+ if (alternativeNames != null) {
+ for (List<?> san : alternativeNames) {
+ Integer type = (Integer) san.get(0);
+ if (type == 0) {
+ Pair<String, String> otherName = parseOtherName((byte[]) san.get(1));
+ if (otherName != null) {
+ switch (otherName.first) {
+ case SRVName:
+ srvNames.add(otherName.second);
+ break;
+ case xmppAddr:
+ xmppAddrs.add(otherName.second);
+ break;
+ default:
+ Log.d(LOGTAG, "oid: " + otherName.first + " value: " + otherName.second);
+ }
+ }
+ } else if (type == 2) {
+ Object value = san.get(1);
+ if (value instanceof String) {
+ domains.add((String) value);
+ }
+ }
+ }
+ }
+ if (srvNames.size() == 0 && xmppAddrs.size() == 0 && domains.size() == 0) {
+ X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
+ RDN[] rdns = x500name.getRDNs(BCStyle.CN);
+ for (int i = 0; i < rdns.length; ++i) {
+ domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue()));
+ }
+ }
+ Log.d(LOGTAG, "searching for " + domain + " in srvNames: " + srvNames + " xmppAddrs: " + xmppAddrs + " domains:" + domains);
+ return xmppAddrs.contains(domain) || srvNames.contains("_xmpp-client." + domain) || matchDomain(domain, domains);
+ } catch (Exception e) {
+ return false;
+ }
+ }
- private static Pair<String, String> parseOtherName(byte[] otherName) {
- try {
- ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(otherName);
- if (asn1Primitive instanceof DERTaggedObject) {
- ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject();
- if (inner instanceof DLSequence) {
- DLSequence sequence = (DLSequence) inner;
- if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) {
- String oid = sequence.getObjectAt(0).toString();
- ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject();
- if (value instanceof DERUTF8String) {
- return new Pair<>(oid, ((DERUTF8String) value).getString());
- } else if (value instanceof DERIA5String) {
- return new Pair<>(oid, ((DERIA5String) value).getString());
- }
- }
- }
- }
- return null;
- } catch (IOException e) {
- return null;
- }
- }
+ private static Pair<String, String> parseOtherName(byte[] otherName) {
+ try {
+ ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(otherName);
+ if (asn1Primitive instanceof DERTaggedObject) {
+ ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject();
+ if (inner instanceof DLSequence) {
+ DLSequence sequence = (DLSequence) inner;
+ if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) {
+ String oid = sequence.getObjectAt(0).toString();
+ ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject();
+ if (value instanceof DERUTF8String) {
+ return new Pair<>(oid, ((DERUTF8String) value).getString());
+ } else if (value instanceof DERIA5String) {
+ return new Pair<>(oid, ((DERIA5String) value).getString());
+ }
+ }
+ }
+ }
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
- private static boolean matchDomain(String needle, List<String> haystack) {
- for (String entry : haystack) {
- if (entry.startsWith("*.")) {
- int i = needle.indexOf('.');
- Log.d(LOGTAG, "comparing " + needle.substring(i) + " and " + entry.substring(1));
- if (i != -1 && needle.substring(i).equals(entry.substring(1))) {
- Log.d(LOGTAG, "domain " + needle + " matched " + entry);
- return true;
- }
- } else {
- if (entry.equals(needle)) {
- Log.d(LOGTAG, "domain " + needle + " matched " + entry);
- return true;
- }
- }
- }
- return false;
- }
+ private static boolean matchDomain(String needle, List<String> haystack) {
+ for (String entry : haystack) {
+ if (entry.startsWith("*.")) {
+ int i = needle.indexOf('.');
+ Log.d(LOGTAG, "comparing " + needle.substring(i) + " and " + entry.substring(1));
+ if (i != -1 && needle.substring(i).equals(entry.substring(1))) {
+ Log.d(LOGTAG, "domain " + needle + " matched " + entry);
+ return true;
+ }
+ } else {
+ if (entry.equals(needle)) {
+ Log.d(LOGTAG, "domain " + needle + " matched " + entry);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
index 532fbe470..1ed410fdb 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
@@ -51,1049 +51,1049 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
- public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl";
- public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist";
- public static final String PEP_DEVICE_LIST_NOTIFY = PEP_DEVICE_LIST + "+notify";
- public static final String PEP_BUNDLES = PEP_PREFIX + ".bundles";
- public static final String PEP_VERIFICATION = PEP_PREFIX + ".verification";
-
- public static final String LOGPREFIX = "AxolotlService";
-
- public static final int NUM_KEYS_TO_PUBLISH = 100;
- public static final int publishTriesThreshold = 3;
-
- private final Account account;
- private final XmppConnectionService mXmppConnectionService;
- private final SQLiteAxolotlStore axolotlStore;
- private final SessionMap sessions;
- private final Map<Jid, Set<Integer>> deviceIds;
- private final Map<String, XmppAxolotlMessage> messageCache;
- private final FetchStatusMap fetchStatusMap;
- private final SerialSingleThreadExecutor executor;
- private int numPublishTriesOnEmptyPep = 0;
- private boolean pepBroken = false;
-
- @Override
- public void onAdvancedStreamFeaturesAvailable(Account account) {
- if (Config.supportOmemo()
- && account.getXmppConnection() != null
- && account.getXmppConnection().getFeatures().pep()) {
- publishBundlesIfNeeded(true, false);
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping OMEMO initialization");
- }
- }
-
- public boolean fetchMapHasErrors(List<Jid> jids) {
- for(Jid jid : jids) {
- if (deviceIds.get(jid) != null) {
- for (Integer foreignId : this.deviceIds.get(jid)) {
- AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId);
- if (fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- public void preVerifyFingerprint(Contact contact, String fingerprint) {
- axolotlStore.preVerifyFingerprint(contact.getAccount(), contact.getJid().toBareJid().toPreppedString(), fingerprint);
- }
-
- public void preVerifyFingerprint(Account account, String fingerprint) {
- axolotlStore.preVerifyFingerprint(account, account.getJid().toBareJid().toPreppedString(), fingerprint);
- }
-
- private static class AxolotlAddressMap<T> {
- protected Map<String, Map<Integer, T>> map;
- protected final Object MAP_LOCK = new Object();
-
- public AxolotlAddressMap() {
- this.map = new HashMap<>();
- }
-
- public void put(AxolotlAddress address, T value) {
- synchronized (MAP_LOCK) {
- Map<Integer, T> devices = map.get(address.getName());
- if (devices == null) {
- devices = new HashMap<>();
- map.put(address.getName(), devices);
- }
- devices.put(address.getDeviceId(), value);
- }
- }
-
- public T get(AxolotlAddress address) {
- synchronized (MAP_LOCK) {
- Map<Integer, T> devices = map.get(address.getName());
- if (devices == null) {
- return null;
- }
- return devices.get(address.getDeviceId());
- }
- }
-
- public Map<Integer, T> getAll(AxolotlAddress address) {
- synchronized (MAP_LOCK) {
- Map<Integer, T> devices = map.get(address.getName());
- if (devices == null) {
- return new HashMap<>();
- }
- return devices;
- }
- }
-
- public boolean hasAny(AxolotlAddress address) {
- synchronized (MAP_LOCK) {
- Map<Integer, T> devices = map.get(address.getName());
- return devices != null && !devices.isEmpty();
- }
- }
-
- public void clear() {
- map.clear();
- }
-
- }
-
- private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> {
- private final XmppConnectionService xmppConnectionService;
- private final Account account;
-
- public SessionMap(XmppConnectionService service, SQLiteAxolotlStore store, Account account) {
- super();
- this.xmppConnectionService = service;
- this.account = account;
- this.fillMap(store);
- }
-
- private void putDevicesForJid(String bareJid, List<Integer> deviceIds, SQLiteAxolotlStore store) {
- for (Integer deviceId : deviceIds) {
- AxolotlAddress axolotlAddress = new AxolotlAddress(bareJid, deviceId);
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building session for remote address: " + axolotlAddress.toString());
- IdentityKey identityKey = store.loadSession(axolotlAddress).getSessionState().getRemoteIdentityKey();
- if(Config.X509_VERIFICATION) {
- X509Certificate certificate = store.getFingerprintCertificate(identityKey.getFingerprint().replaceAll("\\s", ""));
- if (certificate != null) {
- Bundle information = CryptoHelper.extractCertificateInformation(certificate);
- try {
- final String cn = information.getString("subject_cn");
- final Jid jid = Jid.fromString(bareJid);
- Log.d(Config.LOGTAG,"setting common name for "+jid+" to "+cn);
- account.getRoster().getContact(jid).setCommonName(cn);
- } catch (final InvalidJidException ignored) {
- //ignored
- }
- }
- }
- this.put(axolotlAddress, new XmppAxolotlSession(account, store, axolotlAddress, identityKey));
- }
- }
-
- private void fillMap(SQLiteAxolotlStore store) {
- List<Integer> deviceIds = store.getSubDeviceSessions(account.getJid().toBareJid().toPreppedString());
- putDevicesForJid(account.getJid().toBareJid().toPreppedString(), deviceIds, store);
- for (Contact contact : account.getRoster().getContacts()) {
- Jid bareJid = contact.getJid().toBareJid();
- String address = bareJid.toString();
- deviceIds = store.getSubDeviceSessions(address);
- putDevicesForJid(address, deviceIds, store);
- }
-
- }
-
- @Override
- public void put(AxolotlAddress address, XmppAxolotlSession value) {
- super.put(address, value);
- value.setNotFresh();
- xmppConnectionService.syncRosterToDisk(account); //TODO why?
- }
-
- public void put(XmppAxolotlSession session) {
- this.put(session.getRemoteAddress(), session);
- }
- }
-
- public enum FetchStatus {
- PENDING,
- SUCCESS,
- SUCCESS_VERIFIED,
- TIMEOUT,
- ERROR
- }
-
- private static class FetchStatusMap extends AxolotlAddressMap<FetchStatus> {
-
- public void clearErrorFor(Jid jid) {
- synchronized (MAP_LOCK) {
- Map<Integer, FetchStatus> devices = this.map.get(jid.toBareJid().toPreppedString());
- if (devices == null) {
- return;
- }
- for(Map.Entry<Integer, FetchStatus> entry : devices.entrySet()) {
- if (entry.getValue() == FetchStatus.ERROR) {
- Log.d(Config.LOGTAG,"resetting error for "+jid.toBareJid()+"("+entry.getKey()+")");
- entry.setValue(FetchStatus.TIMEOUT);
- }
- }
- }
- }
- }
-
- public static String getLogprefix(Account account) {
- return LOGPREFIX + " (" + account.getJid().toBareJid().toString() + "): ";
- }
-
- public AxolotlService(Account account, XmppConnectionService connectionService) {
- if (Security.getProvider("BC") == null) {
- Security.addProvider(new BouncyCastleProvider());
- }
- this.mXmppConnectionService = connectionService;
- this.account = account;
- this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
- this.deviceIds = new HashMap<>();
- this.messageCache = new HashMap<>();
- this.sessions = new SessionMap(mXmppConnectionService, axolotlStore, account);
- this.fetchStatusMap = new FetchStatusMap();
- this.executor = new SerialSingleThreadExecutor();
- }
-
- public String getOwnFingerprint() {
- return axolotlStore.getIdentityKeyPair().getPublicKey().getFingerprint().replaceAll("\\s", "");
- }
-
- public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status) {
- return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toPreppedString(), status);
- }
-
- public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status, Jid jid) {
- return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toPreppedString(), status);
- }
-
- public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status, List<Jid> jids) {
- Set<IdentityKey> keys = new HashSet<>();
- for(Jid jid : jids) {
- keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toPreppedString(), status));
- }
- return keys;
- }
-
- public long getNumTrustedKeys(Jid jid) {
- return axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toPreppedString());
- }
-
- public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) {
- for(Jid jid : jids) {
- if (axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toPreppedString()) == 0) {
- return true;
- }
- }
- return false;
- }
-
- private AxolotlAddress getAddressForJid(Jid jid) {
- return new AxolotlAddress(jid.toPreppedString(), 0);
- }
-
- public Set<XmppAxolotlSession> findOwnSessions() {
- AxolotlAddress ownAddress = getAddressForJid(account.getJid().toBareJid());
- return new HashSet<>(this.sessions.getAll(ownAddress).values());
- }
-
- private Set<XmppAxolotlSession> findSessionsForContact(Contact contact) {
- AxolotlAddress contactAddress = getAddressForJid(contact.getJid());
- return new HashSet<>(this.sessions.getAll(contactAddress).values());
- }
-
- private Set<XmppAxolotlSession> findSessionsForConversation(Conversation conversation) {
- HashSet<XmppAxolotlSession> sessions = new HashSet<>();
- for(Jid jid : conversation.getAcceptedCryptoTargets()) {
- sessions.addAll(this.sessions.getAll(getAddressForJid(jid)).values());
- }
- return sessions;
- }
-
- public Set<String> getFingerprintsForOwnSessions() {
- Set<String> fingerprints = new HashSet<>();
- for (XmppAxolotlSession session : findOwnSessions()) {
- fingerprints.add(session.getFingerprint());
- }
- return fingerprints;
- }
-
- public Set<String> getFingerprintsForContact(final Contact contact) {
- Set<String> fingerprints = new HashSet<>();
- for (XmppAxolotlSession session : findSessionsForContact(contact)) {
- fingerprints.add(session.getFingerprint());
- }
- return fingerprints;
- }
-
- private boolean hasAny(Jid jid) {
- return sessions.hasAny(getAddressForJid(jid));
- }
-
- public boolean isPepBroken() {
- return this.pepBroken;
- }
-
- public void resetBrokenness() {
- this.pepBroken = false;
- numPublishTriesOnEmptyPep = 0;
- }
-
- public void clearErrorsInFetchStatusMap(Jid jid) {
- fetchStatusMap.clearErrorFor(jid);
- }
-
- public void regenerateKeys(boolean wipeOther) {
- axolotlStore.regenerate();
- sessions.clear();
- fetchStatusMap.clear();
- publishBundlesIfNeeded(true, wipeOther);
- }
-
- public int getOwnDeviceId() {
- return axolotlStore.getLocalRegistrationId();
- }
-
- public Set<Integer> getOwnDeviceIds() {
- return this.deviceIds.get(account.getJid().toBareJid());
- }
-
- public void registerDevices(final Jid jid, @NonNull final Set<Integer> deviceIds) {
- if (jid.toBareJid().equals(account.getJid().toBareJid())) {
- if (!deviceIds.isEmpty()) {
- Log.d(Config.LOGTAG, getLogprefix(account) + "Received non-empty own device list. Resetting publish attempts and pepBroken status.");
- pepBroken = false;
- numPublishTriesOnEmptyPep = 0;
- }
- if (deviceIds.contains(getOwnDeviceId())) {
- deviceIds.remove(getOwnDeviceId());
- } else {
- publishOwnDeviceId(deviceIds);
- }
- for (Integer deviceId : deviceIds) {
- AxolotlAddress ownDeviceAddress = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
- if (sessions.get(ownDeviceAddress) == null) {
- buildSessionFromPEP(ownDeviceAddress);
- }
- }
- }
- Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toPreppedString()));
- expiredDevices.removeAll(deviceIds);
- for (Integer deviceId : expiredDevices) {
- AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
- XmppAxolotlSession session = sessions.get(address);
- if (session != null && session.getFingerprint() != null) {
- if (session.getTrust().isActive()) {
- session.setTrust(session.getTrust().toInactive());
- }
- }
- }
- Set<Integer> newDevices = new HashSet<>(deviceIds);
- for (Integer deviceId : newDevices) {
- AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
- XmppAxolotlSession session = sessions.get(address);
- if (session != null && session.getFingerprint() != null) {
- if (!session.getTrust().isActive()) {
- session.setTrust(session.getTrust().toActive());
- }
- }
- }
- this.deviceIds.put(jid, deviceIds);
- mXmppConnectionService.keyStatusUpdated(null);
- }
-
- public void wipeOtherPepDevices() {
- if (pepBroken) {
- Log.d(Config.LOGTAG, getLogprefix(account) + "wipeOtherPepDevices called, but PEP is broken. Ignoring... ");
- return;
- }
- Set<Integer> deviceIds = new HashSet<>();
- deviceIds.add(getOwnDeviceId());
- IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIds);
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Wiping all other devices from Pep:" + publish);
- mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- // TODO: implement this!
- }
- });
- }
-
- public void purgeKey(final String fingerprint) {
- axolotlStore.setFingerprintStatus(fingerprint.replaceAll("\\s", ""), FingerprintStatus.createCompromised());
- }
-
- public void publishOwnDeviceIdIfNeeded() {
- if (pepBroken) {
- Log.d(Config.LOGTAG, getLogprefix(account) + "publishOwnDeviceIdIfNeeded called, but PEP is broken. Ignoring... ");
- return;
- }
- IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveDeviceIds(account.getJid().toBareJid());
- mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
- Log.d(Config.LOGTAG, getLogprefix(account) + "Timeout received while retrieving own Device Ids.");
- } else {
- Element item = mXmppConnectionService.getIqParser().getItem(packet);
- Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
- if (!deviceIds.contains(getOwnDeviceId())) {
- publishOwnDeviceId(deviceIds);
- }
- }
- }
- });
- }
-
- public void publishOwnDeviceId(Set<Integer> deviceIds) {
- Set<Integer> deviceIdsCopy = new HashSet<>(deviceIds);
- if (!deviceIdsCopy.contains(getOwnDeviceId())) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Own device " + getOwnDeviceId() + " not in PEP devicelist.");
- if (deviceIdsCopy.isEmpty()) {
- if (numPublishTriesOnEmptyPep >= publishTriesThreshold) {
- Log.w(Config.LOGTAG, getLogprefix(account) + "Own device publish attempt threshold exceeded, aborting...");
- pepBroken = true;
- return;
- } else {
- numPublishTriesOnEmptyPep++;
- Log.w(Config.LOGTAG, getLogprefix(account) + "Own device list empty, attempting to publish (try " + numPublishTriesOnEmptyPep + ")");
- }
- } else {
- numPublishTriesOnEmptyPep = 0;
- }
- deviceIdsCopy.add(getOwnDeviceId());
- IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIdsCopy);
- mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.ERROR) {
- pepBroken = true;
- Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing own device id" + packet.findChild("error"));
- }
- }
- });
- }
- }
-
- public void publishDeviceVerificationAndBundle(final SignedPreKeyRecord signedPreKeyRecord,
- final Set<PreKeyRecord> preKeyRecords,
- final boolean announceAfter,
- final boolean wipe) {
- try {
- IdentityKey axolotlPublicKey = axolotlStore.getIdentityKeyPair().getPublicKey();
- PrivateKey x509PrivateKey = KeyChain.getPrivateKey(mXmppConnectionService, account.getPrivateKeyAlias());
- X509Certificate[] chain = KeyChain.getCertificateChain(mXmppConnectionService, account.getPrivateKeyAlias());
- Signature verifier = Signature.getInstance("sha256WithRSA");
- verifier.initSign(x509PrivateKey,mXmppConnectionService.getRNG());
- verifier.update(axolotlPublicKey.serialize());
- byte[] signature = verifier.sign();
- IqPacket packet = mXmppConnectionService.getIqGenerator().publishVerification(signature, chain, getOwnDeviceId());
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": publish verification for device "+getOwnDeviceId());
- mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announceAfter, wipe);
- }
- });
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public void publishBundlesIfNeeded(final boolean announce, final boolean wipe) {
- if (pepBroken) {
- Log.d(Config.LOGTAG, getLogprefix(account) + "publishBundlesIfNeeded called, but PEP is broken. Ignoring... ");
- return;
- }
- IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice(account.getJid().toBareJid(), getOwnDeviceId());
- mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
-
- if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
- return; //ignore timeout. do nothing
- }
-
- if (packet.getType() == IqPacket.TYPE.ERROR) {
- Element error = packet.findChild("error");
- if (error == null || !error.hasChild("item-not-found")) {
- pepBroken = true;
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "request for device bundles came back with something other than item-not-found" + packet);
- return;
- }
- }
-
- PreKeyBundle bundle = mXmppConnectionService.getIqParser().bundle(packet);
- Map<Integer, ECPublicKey> keys = mXmppConnectionService.getIqParser().preKeyPublics(packet);
- boolean flush = false;
- if (bundle == null) {
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid bundle:" + packet);
- bundle = new PreKeyBundle(-1, -1, -1, null, -1, null, null, null);
- flush = true;
- }
- if (keys == null) {
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid prekeys:" + packet);
- }
- try {
- boolean changed = false;
- // Validate IdentityKey
- IdentityKeyPair identityKeyPair = axolotlStore.getIdentityKeyPair();
- if (flush || !identityKeyPair.getPublicKey().equals(bundle.getIdentityKey())) {
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding own IdentityKey " + identityKeyPair.getPublicKey() + " to PEP.");
- changed = true;
- }
-
- // Validate signedPreKeyRecord + ID
- SignedPreKeyRecord signedPreKeyRecord;
- int numSignedPreKeys = axolotlStore.loadSignedPreKeys().size();
- try {
- signedPreKeyRecord = axolotlStore.loadSignedPreKey(bundle.getSignedPreKeyId());
- if (flush
- || !bundle.getSignedPreKey().equals(signedPreKeyRecord.getKeyPair().getPublicKey())
- || !Arrays.equals(bundle.getSignedPreKeySignature(), signedPreKeyRecord.getSignature())) {
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP.");
- signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1);
- axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord);
- changed = true;
- }
- } catch (InvalidKeyIdException e) {
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP.");
- signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1);
- axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord);
- changed = true;
- }
-
- // Validate PreKeys
- Set<PreKeyRecord> preKeyRecords = new HashSet<>();
- if (keys != null) {
- for (Integer id : keys.keySet()) {
- try {
- PreKeyRecord preKeyRecord = axolotlStore.loadPreKey(id);
- if (preKeyRecord.getKeyPair().getPublicKey().equals(keys.get(id))) {
- preKeyRecords.add(preKeyRecord);
- }
- } catch (InvalidKeyIdException ignored) {
- }
- }
- }
- int newKeys = NUM_KEYS_TO_PUBLISH - preKeyRecords.size();
- if (newKeys > 0) {
- List<PreKeyRecord> newRecords = KeyHelper.generatePreKeys(
- axolotlStore.getCurrentPreKeyId() + 1, newKeys);
- preKeyRecords.addAll(newRecords);
- for (PreKeyRecord record : newRecords) {
- axolotlStore.storePreKey(record.getId(), record);
- }
- changed = true;
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding " + newKeys + " new preKeys to PEP.");
- }
-
-
- if (changed) {
- if (account.getPrivateKeyAlias() != null && Config.X509_VERIFICATION) {
- mXmppConnectionService.publishDisplayName(account);
- publishDeviceVerificationAndBundle(signedPreKeyRecord, preKeyRecords, announce, wipe);
- } else {
- publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announce, wipe);
- }
- } else {
- Log.d(Config.LOGTAG, getLogprefix(account) + "Bundle " + getOwnDeviceId() + " in PEP was current");
- if (wipe) {
- wipeOtherPepDevices();
- } else if (announce) {
- Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId());
- publishOwnDeviceIdIfNeeded();
- }
- }
- } catch (InvalidKeyException e) {
- Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to publish bundle " + getOwnDeviceId() + ", reason: " + e.getMessage());
- }
- }
- });
- }
-
- private void publishDeviceBundle(SignedPreKeyRecord signedPreKeyRecord,
- Set<PreKeyRecord> preKeyRecords,
- final boolean announceAfter,
- final boolean wipe) {
- IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles(
- signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(),
- preKeyRecords, getOwnDeviceId());
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing: " + publish);
- mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Successfully published bundle. ");
- if (wipe) {
- wipeOtherPepDevices();
- } else if (announceAfter) {
- Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId());
- publishOwnDeviceIdIfNeeded();
- }
- } else if (packet.getType() == IqPacket.TYPE.ERROR) {
- pepBroken = true;
- Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing bundle: " + packet.findChild("error"));
- }
- }
- });
- }
-
- public enum AxolotlCapability {
- FULL,
- MISSING_PRESENCE,
- MISSING_KEYS,
- WRONG_CONFIGURATION,
- NO_MEMBERS
- }
-
- public boolean isConversationAxolotlCapable(Conversation conversation) {
- return isConversationAxolotlCapableDetailed(conversation).first == AxolotlCapability.FULL;
- }
-
- public Pair<AxolotlCapability,Jid> isConversationAxolotlCapableDetailed(Conversation conversation) {
- if (conversation.getMode() == Conversation.MODE_SINGLE || (conversation.getMucOptions().membersOnly() && conversation.getMucOptions().nonanonymous())) {
- final List<Jid> jids = getCryptoTargets(conversation);
- for(Jid jid : jids) {
- if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) {
- if (conversation.getAccount().getRoster().getContact(jid).mutualPresenceSubscription()) {
- return new Pair<>(AxolotlCapability.MISSING_KEYS,jid);
- } else {
- return new Pair<>(AxolotlCapability.MISSING_PRESENCE,jid);
- }
- }
- }
- if (jids.size() > 0) {
- return new Pair<>(AxolotlCapability.FULL, null);
- } else {
- return new Pair<>(AxolotlCapability.NO_MEMBERS, null);
- }
- } else {
- return new Pair<>(AxolotlCapability.WRONG_CONFIGURATION, null);
- }
- }
-
- public List<Jid> getCryptoTargets(Conversation conversation) {
- final List<Jid> jids;
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- jids = Arrays.asList(conversation.getJid().toBareJid());
- } else {
- jids = conversation.getMucOptions().getMembers();
- }
- return jids;
- }
-
- public FingerprintStatus getFingerprintTrust(String fingerprint) {
- return axolotlStore.getFingerprintStatus(fingerprint);
- }
-
- public X509Certificate getFingerprintCertificate(String fingerprint) {
- return axolotlStore.getFingerprintCertificate(fingerprint);
- }
-
- public void setFingerprintTrust(String fingerprint, FingerprintStatus status) {
- axolotlStore.setFingerprintStatus(fingerprint, status);
- }
-
- private void verifySessionWithPEP(final XmppAxolotlSession session) {
- Log.d(Config.LOGTAG, "trying to verify fresh session (" + session.getRemoteAddress().getName() + ") with pep");
- final AxolotlAddress address = session.getRemoteAddress();
- final IdentityKey identityKey = session.getIdentityKey();
- try {
- IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(Jid.fromString(address.getName()), address.getDeviceId());
- mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Pair<X509Certificate[],byte[]> verification = mXmppConnectionService.getIqParser().verification(packet);
- if (verification != null) {
- try {
- Signature verifier = Signature.getInstance("sha256WithRSA");
- verifier.initVerify(verification.first[0]);
- verifier.update(identityKey.serialize());
- if (verifier.verify(verification.second)) {
- try {
- mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA");
- String fingerprint = session.getFingerprint();
- Log.d(Config.LOGTAG, "verified session with x.509 signature. fingerprint was: "+fingerprint);
- setFingerprintTrust(fingerprint, FingerprintStatus.createActiveVerified(true));
- axolotlStore.setFingerprintCertificate(fingerprint, verification.first[0]);
- fetchStatusMap.put(address, FetchStatus.SUCCESS_VERIFIED);
- Bundle information = CryptoHelper.extractCertificateInformation(verification.first[0]);
- try {
- final String cn = information.getString("subject_cn");
- final Jid jid = Jid.fromString(address.getName());
- Log.d(Config.LOGTAG,"setting common name for "+jid+" to "+cn);
- account.getRoster().getContact(jid).setCommonName(cn);
- } catch (final InvalidJidException ignored) {
- //ignored
- }
- finishBuildingSessionsFromPEP(address);
- return;
- } catch (Exception e) {
- Log.d(Config.LOGTAG,"could not verify certificate");
- }
- }
- } catch (Exception e) {
- Log.d(Config.LOGTAG, "error during verification " + e.getMessage());
- }
- } else {
- Log.d(Config.LOGTAG,"no verification found");
- }
- fetchStatusMap.put(address, FetchStatus.SUCCESS);
- finishBuildingSessionsFromPEP(address);
- }
- });
- } catch (InvalidJidException e) {
- fetchStatusMap.put(address, FetchStatus.SUCCESS);
- finishBuildingSessionsFromPEP(address);
- }
- }
-
- private void finishBuildingSessionsFromPEP(final AxolotlAddress address) {
- AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), 0);
- Map<Integer, FetchStatus> own = fetchStatusMap.getAll(ownAddress);
- Map<Integer, FetchStatus> remote = fetchStatusMap.getAll(address);
- if (!own.containsValue(FetchStatus.PENDING) && !remote.containsValue(FetchStatus.PENDING)) {
- FetchStatus report = null;
- if (own.containsValue(FetchStatus.SUCCESS) || remote.containsValue(FetchStatus.SUCCESS)) {
- report = FetchStatus.SUCCESS;
- } else if (own.containsValue(FetchStatus.SUCCESS_VERIFIED) || remote.containsValue(FetchStatus.SUCCESS_VERIFIED)) {
- report = FetchStatus.SUCCESS_VERIFIED;
- } else if (own.containsValue(FetchStatus.ERROR) || remote.containsValue(FetchStatus.ERROR)) {
- report = FetchStatus.ERROR;
- }
- mXmppConnectionService.keyStatusUpdated(report);
- }
- }
-
- private void buildSessionFromPEP(final AxolotlAddress address) {
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new sesstion for " + address.toString());
- if (address.getDeviceId() == getOwnDeviceId()) {
- throw new AssertionError("We should NEVER build a session with ourselves. What happened here?!");
- }
-
- try {
- IqPacket bundlesPacket = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice(
- Jid.fromString(address.getName()), address.getDeviceId());
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Retrieving bundle: " + bundlesPacket);
- mXmppConnectionService.sendIqPacket(account, bundlesPacket, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
- fetchStatusMap.put(address, FetchStatus.TIMEOUT);
- } else if (packet.getType() == IqPacket.TYPE.RESULT) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received preKey IQ packet, processing...");
- final IqParser parser = mXmppConnectionService.getIqParser();
- final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet);
- final PreKeyBundle bundle = parser.bundle(packet);
- if (preKeyBundleList.isEmpty() || bundle == null) {
- Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet);
- fetchStatusMap.put(address, FetchStatus.ERROR);
- finishBuildingSessionsFromPEP(address);
- return;
- }
- Random random = new Random();
- final PreKeyBundle preKey = preKeyBundleList.get(random.nextInt(preKeyBundleList.size()));
- if (preKey == null) {
- //should never happen
- fetchStatusMap.put(address, FetchStatus.ERROR);
- finishBuildingSessionsFromPEP(address);
- return;
- }
-
- final PreKeyBundle preKeyBundle = new PreKeyBundle(0, address.getDeviceId(),
- preKey.getPreKeyId(), preKey.getPreKey(),
- bundle.getSignedPreKeyId(), bundle.getSignedPreKey(),
- bundle.getSignedPreKeySignature(), bundle.getIdentityKey());
-
- try {
- SessionBuilder builder = new SessionBuilder(axolotlStore, address);
- builder.process(preKeyBundle);
- XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey());
- sessions.put(address, session);
- if (Config.X509_VERIFICATION) {
- verifySessionWithPEP(session);
- } else {
- FingerprintStatus status = getFingerprintTrust(bundle.getIdentityKey().getFingerprint().replaceAll("\\s",""));
- boolean verified = status != null && status.isVerified();
- fetchStatusMap.put(address, verified ? FetchStatus.SUCCESS_VERIFIED : FetchStatus.SUCCESS);
- finishBuildingSessionsFromPEP(address);
- }
- } catch (UntrustedIdentityException | InvalidKeyException e) {
- Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": "
- + e.getClass().getName() + ", " + e.getMessage());
- fetchStatusMap.put(address, FetchStatus.ERROR);
- finishBuildingSessionsFromPEP(address);
- }
- } else {
- fetchStatusMap.put(address, FetchStatus.ERROR);
- Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while building session:" + packet.findChild("error"));
- finishBuildingSessionsFromPEP(address);
- }
- }
- });
- } catch (InvalidJidException e) {
- Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got address with invalid jid: " + address.getName());
- }
- }
-
- public Set<AxolotlAddress> findDevicesWithoutSession(final Conversation conversation) {
- Set<AxolotlAddress> addresses = new HashSet<>();
- for(Jid jid : getCryptoTargets(conversation)) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Finding devices without session for " + jid);
- if (deviceIds.get(jid) != null) {
- for (Integer foreignId : this.deviceIds.get(jid)) {
- AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId);
- if (sessions.get(address) == null) {
- IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
- if (identityKey != null) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache...");
- XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey);
- sessions.put(address, session);
- } else {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + jid + ":" + foreignId);
- if (fetchStatusMap.get(address) != FetchStatus.ERROR) {
- addresses.add(address);
- } else {
- Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken");
- }
- }
- }
- }
- } else {
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Have no target devices in PEP!");
- }
- }
- if (deviceIds.get(account.getJid().toBareJid()) != null) {
- for (Integer ownId : this.deviceIds.get(account.getJid().toBareJid())) {
- AxolotlAddress address = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), ownId);
- if (sessions.get(address) == null) {
- IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
- if (identityKey != null) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache...");
- XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey);
- sessions.put(address, session);
- } else {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().toBareJid() + ":" + ownId);
- if (fetchStatusMap.get(address) != FetchStatus.ERROR) {
- addresses.add(address);
- } else {
- Log.d(Config.LOGTAG,getLogprefix(account)+"skipping over "+address+" because it's broken");
- }
- }
- }
- }
- }
-
- return addresses;
- }
-
- public boolean createSessionsIfNeeded(final Conversation conversation) {
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Creating axolotl sessions if needed...");
- boolean newSessions = false;
- Set<AxolotlAddress> addresses = findDevicesWithoutSession(conversation);
- for (AxolotlAddress address : addresses) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Processing device: " + address.toString());
- FetchStatus status = fetchStatusMap.get(address);
- if (status == null || status == FetchStatus.TIMEOUT) {
- fetchStatusMap.put(address, FetchStatus.PENDING);
- this.buildSessionFromPEP(address);
- newSessions = true;
- } else if (status == FetchStatus.PENDING) {
- newSessions = true;
- } else {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already fetching bundle for " + address.toString());
- }
- }
-
- return newSessions;
- }
-
- public boolean trustedSessionVerified(final Conversation conversation) {
- Set<XmppAxolotlSession> sessions = findSessionsForConversation(conversation);
- sessions.addAll(findOwnSessions());
- boolean verified = false;
- for(XmppAxolotlSession session : sessions) {
- if (session.getTrust().isTrustedAndActive()) {
- if (session.getTrust().getTrust() == FingerprintStatus.Trust.VERIFIED_X509) {
- verified = true;
- } else {
- return false;
- }
- }
- }
- return verified;
- }
-
- public boolean hasPendingKeyFetches(Account account, List<Jid> jids) {
- AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), 0);
- if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)) {
- return true;
- }
- for(Jid jid : jids) {
- AxolotlAddress foreignAddress = new AxolotlAddress(jid.toBareJid().toPreppedString(), 0);
- if (fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING)) {
- return true;
- }
- }
- return false;
- }
-
- @Nullable
- private XmppAxolotlMessage buildHeader(Conversation conversation) {
- final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(
- account.getJid().toBareJid(), getOwnDeviceId());
-
- Set<XmppAxolotlSession> remoteSessions = findSessionsForConversation(conversation);
- Set<XmppAxolotlSession> ownSessions = findOwnSessions();
- if (remoteSessions.isEmpty()) {
- return null;
- }
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl foreign keyElements...");
- for (XmppAxolotlSession session : remoteSessions) {
- Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
- axolotlMessage.addDevice(session);
- }
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl own keyElements...");
- for (XmppAxolotlSession session : ownSessions) {
- Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
- axolotlMessage.addDevice(session);
- }
-
- return axolotlMessage;
- }
-
- @Nullable
- public XmppAxolotlMessage encrypt(Message message) {
- XmppAxolotlMessage axolotlMessage = buildHeader(message.getConversation());
-
- if (axolotlMessage != null) {
- final String content;
- if (message.hasFileOnRemoteHost()) {
- content = message.getFileParams().url.toString();
- } else {
- content = message.getBody();
- }
- try {
- axolotlMessage.encrypt(content);
- } catch (CryptoFailedException e) {
- Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to encrypt message: " + e.getMessage());
- return null;
- }
- }
-
- return axolotlMessage;
- }
-
- public void preparePayloadMessage(final Message message, final boolean delay) {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- XmppAxolotlMessage axolotlMessage = encrypt(message);
- if (axolotlMessage == null) {
- mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
- //mXmppConnectionService.updateConversationUi();
- } else {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Generated message, caching: " + message.getUuid());
- messageCache.put(message.getUuid(), axolotlMessage);
- mXmppConnectionService.resendMessage(message, delay);
- }
- }
- });
- }
-
- public void prepareKeyTransportMessage(final Conversation conversation, final OnMessageCreatedCallback onMessageCreatedCallback) {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- XmppAxolotlMessage axolotlMessage = buildHeader(conversation);
- onMessageCreatedCallback.run(axolotlMessage);
- }
- });
- }
-
- public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) {
- XmppAxolotlMessage axolotlMessage = messageCache.get(message.getUuid());
- if (axolotlMessage != null) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Cache hit: " + message.getUuid());
- messageCache.remove(message.getUuid());
- } else {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Cache miss: " + message.getUuid());
- }
- return axolotlMessage;
- }
-
- private XmppAxolotlSession recreateUncachedSession(AxolotlAddress address) {
- IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
- return (identityKey != null)
- ? new XmppAxolotlSession(account, axolotlStore, address, identityKey)
- : null;
- }
-
- private XmppAxolotlSession getReceivingSession(XmppAxolotlMessage message) {
- AxolotlAddress senderAddress = new AxolotlAddress(message.getFrom().toString(),
- message.getSenderDeviceId());
- XmppAxolotlSession session = sessions.get(senderAddress);
- if (session == null) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Account: " + account.getJid() + " No axolotl session found while parsing received message " + message);
- session = recreateUncachedSession(senderAddress);
- if (session == null) {
- session = new XmppAxolotlSession(account, axolotlStore, senderAddress);
- }
- }
- return session;
- }
-
- public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message) {
- XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = null;
-
- XmppAxolotlSession session = getReceivingSession(message);
- try {
- plaintextMessage = message.decrypt(session, getOwnDeviceId());
- Integer preKeyId = session.getPreKeyId();
- if (preKeyId != null) {
- publishBundlesIfNeeded(false, false);
- session.resetPreKeyId();
- }
- } catch (CryptoFailedException e) {
- Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to decrypt message: " + e.getMessage());
- }
-
- if (session.isFresh() && plaintextMessage != null) {
- putFreshSession(session);
- }
-
- return plaintextMessage;
- }
-
- public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message) {
- XmppAxolotlMessage.XmppAxolotlKeyTransportMessage keyTransportMessage;
-
- XmppAxolotlSession session = getReceivingSession(message);
- keyTransportMessage = message.getParameters(session, getOwnDeviceId());
-
- if (session.isFresh() && keyTransportMessage != null) {
- putFreshSession(session);
- }
-
- return keyTransportMessage;
- }
-
- private void putFreshSession(XmppAxolotlSession session) {
- Log.d(Config.LOGTAG,"put fresh session");
- sessions.put(session);
- if (Config.X509_VERIFICATION) {
- if (session.getIdentityKey() != null) {
- verifySessionWithPEP(session);
- } else {
- Log.e(Config.LOGTAG,account.getJid().toBareJid()+": identity key was empty after reloading for x509 verification");
- }
- }
- }
+ public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl";
+ public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist";
+ public static final String PEP_DEVICE_LIST_NOTIFY = PEP_DEVICE_LIST + "+notify";
+ public static final String PEP_BUNDLES = PEP_PREFIX + ".bundles";
+ public static final String PEP_VERIFICATION = PEP_PREFIX + ".verification";
+
+ public static final String LOGPREFIX = "AxolotlService";
+
+ public static final int NUM_KEYS_TO_PUBLISH = 100;
+ public static final int publishTriesThreshold = 3;
+
+ private final Account account;
+ private final XmppConnectionService mXmppConnectionService;
+ private final SQLiteAxolotlStore axolotlStore;
+ private final SessionMap sessions;
+ private final Map<Jid, Set<Integer>> deviceIds;
+ private final Map<String, XmppAxolotlMessage> messageCache;
+ private final FetchStatusMap fetchStatusMap;
+ private final SerialSingleThreadExecutor executor;
+ private int numPublishTriesOnEmptyPep = 0;
+ private boolean pepBroken = false;
+
+ @Override
+ public void onAdvancedStreamFeaturesAvailable(Account account) {
+ if (Config.supportOmemo()
+ && account.getXmppConnection() != null
+ && account.getXmppConnection().getFeatures().pep()) {
+ publishBundlesIfNeeded(true, false);
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": skipping OMEMO initialization");
+ }
+ }
+
+ public boolean fetchMapHasErrors(List<Jid> jids) {
+ for (Jid jid : jids) {
+ if (deviceIds.get(jid) != null) {
+ for (Integer foreignId : this.deviceIds.get(jid)) {
+ AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId);
+ if (fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public void preVerifyFingerprint(Contact contact, String fingerprint) {
+ axolotlStore.preVerifyFingerprint(contact.getAccount(), contact.getJid().toBareJid().toPreppedString(), fingerprint);
+ }
+
+ public void preVerifyFingerprint(Account account, String fingerprint) {
+ axolotlStore.preVerifyFingerprint(account, account.getJid().toBareJid().toPreppedString(), fingerprint);
+ }
+
+ private static class AxolotlAddressMap<T> {
+ protected Map<String, Map<Integer, T>> map;
+ protected final Object MAP_LOCK = new Object();
+
+ public AxolotlAddressMap() {
+ this.map = new HashMap<>();
+ }
+
+ public void put(AxolotlAddress address, T value) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, T> devices = map.get(address.getName());
+ if (devices == null) {
+ devices = new HashMap<>();
+ map.put(address.getName(), devices);
+ }
+ devices.put(address.getDeviceId(), value);
+ }
+ }
+
+ public T get(AxolotlAddress address) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, T> devices = map.get(address.getName());
+ if (devices == null) {
+ return null;
+ }
+ return devices.get(address.getDeviceId());
+ }
+ }
+
+ public Map<Integer, T> getAll(AxolotlAddress address) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, T> devices = map.get(address.getName());
+ if (devices == null) {
+ return new HashMap<>();
+ }
+ return devices;
+ }
+ }
+
+ public boolean hasAny(AxolotlAddress address) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, T> devices = map.get(address.getName());
+ return devices != null && !devices.isEmpty();
+ }
+ }
+
+ public void clear() {
+ map.clear();
+ }
+
+ }
+
+ private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> {
+ private final XmppConnectionService xmppConnectionService;
+ private final Account account;
+
+ public SessionMap(XmppConnectionService service, SQLiteAxolotlStore store, Account account) {
+ super();
+ this.xmppConnectionService = service;
+ this.account = account;
+ this.fillMap(store);
+ }
+
+ private void putDevicesForJid(String bareJid, List<Integer> deviceIds, SQLiteAxolotlStore store) {
+ for (Integer deviceId : deviceIds) {
+ AxolotlAddress axolotlAddress = new AxolotlAddress(bareJid, deviceId);
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building session for remote address: " + axolotlAddress.toString());
+ IdentityKey identityKey = store.loadSession(axolotlAddress).getSessionState().getRemoteIdentityKey();
+ if (Config.X509_VERIFICATION) {
+ X509Certificate certificate = store.getFingerprintCertificate(identityKey.getFingerprint().replaceAll("\\s", ""));
+ if (certificate != null) {
+ Bundle information = CryptoHelper.extractCertificateInformation(certificate);
+ try {
+ final String cn = information.getString("subject_cn");
+ final Jid jid = Jid.fromString(bareJid);
+ Log.d(Config.LOGTAG, "setting common name for " + jid + " to " + cn);
+ account.getRoster().getContact(jid).setCommonName(cn);
+ } catch (final InvalidJidException ignored) {
+ //ignored
+ }
+ }
+ }
+ this.put(axolotlAddress, new XmppAxolotlSession(account, store, axolotlAddress, identityKey));
+ }
+ }
+
+ private void fillMap(SQLiteAxolotlStore store) {
+ List<Integer> deviceIds = store.getSubDeviceSessions(account.getJid().toBareJid().toPreppedString());
+ putDevicesForJid(account.getJid().toBareJid().toPreppedString(), deviceIds, store);
+ for (Contact contact : account.getRoster().getContacts()) {
+ Jid bareJid = contact.getJid().toBareJid();
+ String address = bareJid.toString();
+ deviceIds = store.getSubDeviceSessions(address);
+ putDevicesForJid(address, deviceIds, store);
+ }
+
+ }
+
+ @Override
+ public void put(AxolotlAddress address, XmppAxolotlSession value) {
+ super.put(address, value);
+ value.setNotFresh();
+ xmppConnectionService.syncRosterToDisk(account); //TODO why?
+ }
+
+ public void put(XmppAxolotlSession session) {
+ this.put(session.getRemoteAddress(), session);
+ }
+ }
+
+ public enum FetchStatus {
+ PENDING,
+ SUCCESS,
+ SUCCESS_VERIFIED,
+ TIMEOUT,
+ ERROR
+ }
+
+ private static class FetchStatusMap extends AxolotlAddressMap<FetchStatus> {
+
+ public void clearErrorFor(Jid jid) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, FetchStatus> devices = this.map.get(jid.toBareJid().toPreppedString());
+ if (devices == null) {
+ return;
+ }
+ for (Map.Entry<Integer, FetchStatus> entry : devices.entrySet()) {
+ if (entry.getValue() == FetchStatus.ERROR) {
+ Log.d(Config.LOGTAG, "resetting error for " + jid.toBareJid() + "(" + entry.getKey() + ")");
+ entry.setValue(FetchStatus.TIMEOUT);
+ }
+ }
+ }
+ }
+ }
+
+ public static String getLogprefix(Account account) {
+ return LOGPREFIX + " (" + account.getJid().toBareJid().toString() + "): ";
+ }
+
+ public AxolotlService(Account account, XmppConnectionService connectionService) {
+ if (Security.getProvider("BC") == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ this.mXmppConnectionService = connectionService;
+ this.account = account;
+ this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
+ this.deviceIds = new HashMap<>();
+ this.messageCache = new HashMap<>();
+ this.sessions = new SessionMap(mXmppConnectionService, axolotlStore, account);
+ this.fetchStatusMap = new FetchStatusMap();
+ this.executor = new SerialSingleThreadExecutor();
+ }
+
+ public String getOwnFingerprint() {
+ return axolotlStore.getIdentityKeyPair().getPublicKey().getFingerprint().replaceAll("\\s", "");
+ }
+
+ public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status) {
+ return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toPreppedString(), status);
+ }
+
+ public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status, Jid jid) {
+ return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toPreppedString(), status);
+ }
+
+ public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status, List<Jid> jids) {
+ Set<IdentityKey> keys = new HashSet<>();
+ for (Jid jid : jids) {
+ keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toPreppedString(), status));
+ }
+ return keys;
+ }
+
+ public long getNumTrustedKeys(Jid jid) {
+ return axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toPreppedString());
+ }
+
+ public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) {
+ for (Jid jid : jids) {
+ if (axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toPreppedString()) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private AxolotlAddress getAddressForJid(Jid jid) {
+ return new AxolotlAddress(jid.toPreppedString(), 0);
+ }
+
+ public Set<XmppAxolotlSession> findOwnSessions() {
+ AxolotlAddress ownAddress = getAddressForJid(account.getJid().toBareJid());
+ return new HashSet<>(this.sessions.getAll(ownAddress).values());
+ }
+
+ private Set<XmppAxolotlSession> findSessionsForContact(Contact contact) {
+ AxolotlAddress contactAddress = getAddressForJid(contact.getJid());
+ return new HashSet<>(this.sessions.getAll(contactAddress).values());
+ }
+
+ private Set<XmppAxolotlSession> findSessionsForConversation(Conversation conversation) {
+ HashSet<XmppAxolotlSession> sessions = new HashSet<>();
+ for (Jid jid : conversation.getAcceptedCryptoTargets()) {
+ sessions.addAll(this.sessions.getAll(getAddressForJid(jid)).values());
+ }
+ return sessions;
+ }
+
+ public Set<String> getFingerprintsForOwnSessions() {
+ Set<String> fingerprints = new HashSet<>();
+ for (XmppAxolotlSession session : findOwnSessions()) {
+ fingerprints.add(session.getFingerprint());
+ }
+ return fingerprints;
+ }
+
+ public Set<String> getFingerprintsForContact(final Contact contact) {
+ Set<String> fingerprints = new HashSet<>();
+ for (XmppAxolotlSession session : findSessionsForContact(contact)) {
+ fingerprints.add(session.getFingerprint());
+ }
+ return fingerprints;
+ }
+
+ private boolean hasAny(Jid jid) {
+ return sessions.hasAny(getAddressForJid(jid));
+ }
+
+ public boolean isPepBroken() {
+ return this.pepBroken;
+ }
+
+ public void resetBrokenness() {
+ this.pepBroken = false;
+ numPublishTriesOnEmptyPep = 0;
+ }
+
+ public void clearErrorsInFetchStatusMap(Jid jid) {
+ fetchStatusMap.clearErrorFor(jid);
+ }
+
+ public void regenerateKeys(boolean wipeOther) {
+ axolotlStore.regenerate();
+ sessions.clear();
+ fetchStatusMap.clear();
+ publishBundlesIfNeeded(true, wipeOther);
+ }
+
+ public int getOwnDeviceId() {
+ return axolotlStore.getLocalRegistrationId();
+ }
+
+ public Set<Integer> getOwnDeviceIds() {
+ return this.deviceIds.get(account.getJid().toBareJid());
+ }
+
+ public void registerDevices(final Jid jid, @NonNull final Set<Integer> deviceIds) {
+ if (jid.toBareJid().equals(account.getJid().toBareJid())) {
+ if (!deviceIds.isEmpty()) {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "Received non-empty own device list. Resetting publish attempts and pepBroken status.");
+ pepBroken = false;
+ numPublishTriesOnEmptyPep = 0;
+ }
+ if (deviceIds.contains(getOwnDeviceId())) {
+ deviceIds.remove(getOwnDeviceId());
+ } else {
+ publishOwnDeviceId(deviceIds);
+ }
+ for (Integer deviceId : deviceIds) {
+ AxolotlAddress ownDeviceAddress = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
+ if (sessions.get(ownDeviceAddress) == null) {
+ buildSessionFromPEP(ownDeviceAddress);
+ }
+ }
+ }
+ Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toPreppedString()));
+ expiredDevices.removeAll(deviceIds);
+ for (Integer deviceId : expiredDevices) {
+ AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
+ XmppAxolotlSession session = sessions.get(address);
+ if (session != null && session.getFingerprint() != null) {
+ if (session.getTrust().isActive()) {
+ session.setTrust(session.getTrust().toInactive());
+ }
+ }
+ }
+ Set<Integer> newDevices = new HashSet<>(deviceIds);
+ for (Integer deviceId : newDevices) {
+ AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
+ XmppAxolotlSession session = sessions.get(address);
+ if (session != null && session.getFingerprint() != null) {
+ if (!session.getTrust().isActive()) {
+ session.setTrust(session.getTrust().toActive());
+ }
+ }
+ }
+ this.deviceIds.put(jid, deviceIds);
+ mXmppConnectionService.keyStatusUpdated(null);
+ }
+
+ public void wipeOtherPepDevices() {
+ if (pepBroken) {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "wipeOtherPepDevices called, but PEP is broken. Ignoring... ");
+ return;
+ }
+ Set<Integer> deviceIds = new HashSet<>();
+ deviceIds.add(getOwnDeviceId());
+ IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIds);
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Wiping all other devices from Pep:" + publish);
+ mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ // TODO: implement this!
+ }
+ });
+ }
+
+ public void purgeKey(final String fingerprint) {
+ axolotlStore.setFingerprintStatus(fingerprint.replaceAll("\\s", ""), FingerprintStatus.createCompromised());
+ }
+
+ public void publishOwnDeviceIdIfNeeded() {
+ if (pepBroken) {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "publishOwnDeviceIdIfNeeded called, but PEP is broken. Ignoring... ");
+ return;
+ }
+ IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveDeviceIds(account.getJid().toBareJid());
+ mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "Timeout received while retrieving own Device Ids.");
+ } else {
+ Element item = mXmppConnectionService.getIqParser().getItem(packet);
+ Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
+ if (!deviceIds.contains(getOwnDeviceId())) {
+ publishOwnDeviceId(deviceIds);
+ }
+ }
+ }
+ });
+ }
+
+ public void publishOwnDeviceId(Set<Integer> deviceIds) {
+ Set<Integer> deviceIdsCopy = new HashSet<>(deviceIds);
+ if (!deviceIdsCopy.contains(getOwnDeviceId())) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Own device " + getOwnDeviceId() + " not in PEP devicelist.");
+ if (deviceIdsCopy.isEmpty()) {
+ if (numPublishTriesOnEmptyPep >= publishTriesThreshold) {
+ Log.w(Config.LOGTAG, getLogprefix(account) + "Own device publish attempt threshold exceeded, aborting...");
+ pepBroken = true;
+ return;
+ } else {
+ numPublishTriesOnEmptyPep++;
+ Log.w(Config.LOGTAG, getLogprefix(account) + "Own device list empty, attempting to publish (try " + numPublishTriesOnEmptyPep + ")");
+ }
+ } else {
+ numPublishTriesOnEmptyPep = 0;
+ }
+ deviceIdsCopy.add(getOwnDeviceId());
+ IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIdsCopy);
+ mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.ERROR) {
+ pepBroken = true;
+ Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing own device id" + packet.findChild("error"));
+ }
+ }
+ });
+ }
+ }
+
+ public void publishDeviceVerificationAndBundle(final SignedPreKeyRecord signedPreKeyRecord,
+ final Set<PreKeyRecord> preKeyRecords,
+ final boolean announceAfter,
+ final boolean wipe) {
+ try {
+ IdentityKey axolotlPublicKey = axolotlStore.getIdentityKeyPair().getPublicKey();
+ PrivateKey x509PrivateKey = KeyChain.getPrivateKey(mXmppConnectionService, account.getPrivateKeyAlias());
+ X509Certificate[] chain = KeyChain.getCertificateChain(mXmppConnectionService, account.getPrivateKeyAlias());
+ Signature verifier = Signature.getInstance("sha256WithRSA");
+ verifier.initSign(x509PrivateKey, mXmppConnectionService.getRNG());
+ verifier.update(axolotlPublicKey.serialize());
+ byte[] signature = verifier.sign();
+ IqPacket packet = mXmppConnectionService.getIqGenerator().publishVerification(signature, chain, getOwnDeviceId());
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": publish verification for device " + getOwnDeviceId());
+ mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announceAfter, wipe);
+ }
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void publishBundlesIfNeeded(final boolean announce, final boolean wipe) {
+ if (pepBroken) {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "publishBundlesIfNeeded called, but PEP is broken. Ignoring... ");
+ return;
+ }
+ IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice(account.getJid().toBareJid(), getOwnDeviceId());
+ mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+
+ if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
+ return; //ignore timeout. do nothing
+ }
+
+ if (packet.getType() == IqPacket.TYPE.ERROR) {
+ Element error = packet.findChild("error");
+ if (error == null || !error.hasChild("item-not-found")) {
+ pepBroken = true;
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "request for device bundles came back with something other than item-not-found" + packet);
+ return;
+ }
+ }
+
+ PreKeyBundle bundle = mXmppConnectionService.getIqParser().bundle(packet);
+ Map<Integer, ECPublicKey> keys = mXmppConnectionService.getIqParser().preKeyPublics(packet);
+ boolean flush = false;
+ if (bundle == null) {
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid bundle:" + packet);
+ bundle = new PreKeyBundle(-1, -1, -1, null, -1, null, null, null);
+ flush = true;
+ }
+ if (keys == null) {
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid prekeys:" + packet);
+ }
+ try {
+ boolean changed = false;
+ // Validate IdentityKey
+ IdentityKeyPair identityKeyPair = axolotlStore.getIdentityKeyPair();
+ if (flush || !identityKeyPair.getPublicKey().equals(bundle.getIdentityKey())) {
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding own IdentityKey " + identityKeyPair.getPublicKey() + " to PEP.");
+ changed = true;
+ }
+
+ // Validate signedPreKeyRecord + ID
+ SignedPreKeyRecord signedPreKeyRecord;
+ int numSignedPreKeys = axolotlStore.loadSignedPreKeys().size();
+ try {
+ signedPreKeyRecord = axolotlStore.loadSignedPreKey(bundle.getSignedPreKeyId());
+ if (flush
+ || !bundle.getSignedPreKey().equals(signedPreKeyRecord.getKeyPair().getPublicKey())
+ || !Arrays.equals(bundle.getSignedPreKeySignature(), signedPreKeyRecord.getSignature())) {
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP.");
+ signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1);
+ axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord);
+ changed = true;
+ }
+ } catch (InvalidKeyIdException e) {
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP.");
+ signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1);
+ axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord);
+ changed = true;
+ }
+
+ // Validate PreKeys
+ Set<PreKeyRecord> preKeyRecords = new HashSet<>();
+ if (keys != null) {
+ for (Integer id : keys.keySet()) {
+ try {
+ PreKeyRecord preKeyRecord = axolotlStore.loadPreKey(id);
+ if (preKeyRecord.getKeyPair().getPublicKey().equals(keys.get(id))) {
+ preKeyRecords.add(preKeyRecord);
+ }
+ } catch (InvalidKeyIdException ignored) {
+ }
+ }
+ }
+ int newKeys = NUM_KEYS_TO_PUBLISH - preKeyRecords.size();
+ if (newKeys > 0) {
+ List<PreKeyRecord> newRecords = KeyHelper.generatePreKeys(
+ axolotlStore.getCurrentPreKeyId() + 1, newKeys);
+ preKeyRecords.addAll(newRecords);
+ for (PreKeyRecord record : newRecords) {
+ axolotlStore.storePreKey(record.getId(), record);
+ }
+ changed = true;
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding " + newKeys + " new preKeys to PEP.");
+ }
+
+
+ if (changed) {
+ if (account.getPrivateKeyAlias() != null && Config.X509_VERIFICATION) {
+ mXmppConnectionService.publishDisplayName(account);
+ publishDeviceVerificationAndBundle(signedPreKeyRecord, preKeyRecords, announce, wipe);
+ } else {
+ publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announce, wipe);
+ }
+ } else {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "Bundle " + getOwnDeviceId() + " in PEP was current");
+ if (wipe) {
+ wipeOtherPepDevices();
+ } else if (announce) {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId());
+ publishOwnDeviceIdIfNeeded();
+ }
+ }
+ } catch (InvalidKeyException e) {
+ Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to publish bundle " + getOwnDeviceId() + ", reason: " + e.getMessage());
+ }
+ }
+ });
+ }
+
+ private void publishDeviceBundle(SignedPreKeyRecord signedPreKeyRecord,
+ Set<PreKeyRecord> preKeyRecords,
+ final boolean announceAfter,
+ final boolean wipe) {
+ IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles(
+ signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(),
+ preKeyRecords, getOwnDeviceId());
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing: " + publish);
+ mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Successfully published bundle. ");
+ if (wipe) {
+ wipeOtherPepDevices();
+ } else if (announceAfter) {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId());
+ publishOwnDeviceIdIfNeeded();
+ }
+ } else if (packet.getType() == IqPacket.TYPE.ERROR) {
+ pepBroken = true;
+ Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing bundle: " + packet.findChild("error"));
+ }
+ }
+ });
+ }
+
+ public enum AxolotlCapability {
+ FULL,
+ MISSING_PRESENCE,
+ MISSING_KEYS,
+ WRONG_CONFIGURATION,
+ NO_MEMBERS
+ }
+
+ public boolean isConversationAxolotlCapable(Conversation conversation) {
+ return isConversationAxolotlCapableDetailed(conversation).first == AxolotlCapability.FULL;
+ }
+
+ public Pair<AxolotlCapability, Jid> isConversationAxolotlCapableDetailed(Conversation conversation) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE || (conversation.getMucOptions().membersOnly() && conversation.getMucOptions().nonanonymous())) {
+ final List<Jid> jids = getCryptoTargets(conversation);
+ for (Jid jid : jids) {
+ if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) {
+ if (conversation.getAccount().getRoster().getContact(jid).mutualPresenceSubscription()) {
+ return new Pair<>(AxolotlCapability.MISSING_KEYS, jid);
+ } else {
+ return new Pair<>(AxolotlCapability.MISSING_PRESENCE, jid);
+ }
+ }
+ }
+ if (jids.size() > 0) {
+ return new Pair<>(AxolotlCapability.FULL, null);
+ } else {
+ return new Pair<>(AxolotlCapability.NO_MEMBERS, null);
+ }
+ } else {
+ return new Pair<>(AxolotlCapability.WRONG_CONFIGURATION, null);
+ }
+ }
+
+ public List<Jid> getCryptoTargets(Conversation conversation) {
+ final List<Jid> jids;
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ jids = Arrays.asList(conversation.getJid().toBareJid());
+ } else {
+ jids = conversation.getMucOptions().getMembers();
+ }
+ return jids;
+ }
+
+ public FingerprintStatus getFingerprintTrust(String fingerprint) {
+ return axolotlStore.getFingerprintStatus(fingerprint);
+ }
+
+ public X509Certificate getFingerprintCertificate(String fingerprint) {
+ return axolotlStore.getFingerprintCertificate(fingerprint);
+ }
+
+ public void setFingerprintTrust(String fingerprint, FingerprintStatus status) {
+ axolotlStore.setFingerprintStatus(fingerprint, status);
+ }
+
+ private void verifySessionWithPEP(final XmppAxolotlSession session) {
+ Log.d(Config.LOGTAG, "trying to verify fresh session (" + session.getRemoteAddress().getName() + ") with pep");
+ final AxolotlAddress address = session.getRemoteAddress();
+ final IdentityKey identityKey = session.getIdentityKey();
+ try {
+ IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(Jid.fromString(address.getName()), address.getDeviceId());
+ mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Pair<X509Certificate[], byte[]> verification = mXmppConnectionService.getIqParser().verification(packet);
+ if (verification != null) {
+ try {
+ Signature verifier = Signature.getInstance("sha256WithRSA");
+ verifier.initVerify(verification.first[0]);
+ verifier.update(identityKey.serialize());
+ if (verifier.verify(verification.second)) {
+ try {
+ mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA");
+ String fingerprint = session.getFingerprint();
+ Log.d(Config.LOGTAG, "verified session with x.509 signature. fingerprint was: " + fingerprint);
+ setFingerprintTrust(fingerprint, FingerprintStatus.createActiveVerified(true));
+ axolotlStore.setFingerprintCertificate(fingerprint, verification.first[0]);
+ fetchStatusMap.put(address, FetchStatus.SUCCESS_VERIFIED);
+ Bundle information = CryptoHelper.extractCertificateInformation(verification.first[0]);
+ try {
+ final String cn = information.getString("subject_cn");
+ final Jid jid = Jid.fromString(address.getName());
+ Log.d(Config.LOGTAG, "setting common name for " + jid + " to " + cn);
+ account.getRoster().getContact(jid).setCommonName(cn);
+ } catch (final InvalidJidException ignored) {
+ //ignored
+ }
+ finishBuildingSessionsFromPEP(address);
+ return;
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, "could not verify certificate");
+ }
+ }
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, "error during verification " + e.getMessage());
+ }
+ } else {
+ Log.d(Config.LOGTAG, "no verification found");
+ }
+ fetchStatusMap.put(address, FetchStatus.SUCCESS);
+ finishBuildingSessionsFromPEP(address);
+ }
+ });
+ } catch (InvalidJidException e) {
+ fetchStatusMap.put(address, FetchStatus.SUCCESS);
+ finishBuildingSessionsFromPEP(address);
+ }
+ }
+
+ private void finishBuildingSessionsFromPEP(final AxolotlAddress address) {
+ AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), 0);
+ Map<Integer, FetchStatus> own = fetchStatusMap.getAll(ownAddress);
+ Map<Integer, FetchStatus> remote = fetchStatusMap.getAll(address);
+ if (!own.containsValue(FetchStatus.PENDING) && !remote.containsValue(FetchStatus.PENDING)) {
+ FetchStatus report = null;
+ if (own.containsValue(FetchStatus.SUCCESS) || remote.containsValue(FetchStatus.SUCCESS)) {
+ report = FetchStatus.SUCCESS;
+ } else if (own.containsValue(FetchStatus.SUCCESS_VERIFIED) || remote.containsValue(FetchStatus.SUCCESS_VERIFIED)) {
+ report = FetchStatus.SUCCESS_VERIFIED;
+ } else if (own.containsValue(FetchStatus.ERROR) || remote.containsValue(FetchStatus.ERROR)) {
+ report = FetchStatus.ERROR;
+ }
+ mXmppConnectionService.keyStatusUpdated(report);
+ }
+ }
+
+ private void buildSessionFromPEP(final AxolotlAddress address) {
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new sesstion for " + address.toString());
+ if (address.getDeviceId() == getOwnDeviceId()) {
+ throw new AssertionError("We should NEVER build a session with ourselves. What happened here?!");
+ }
+
+ try {
+ IqPacket bundlesPacket = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice(
+ Jid.fromString(address.getName()), address.getDeviceId());
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Retrieving bundle: " + bundlesPacket);
+ mXmppConnectionService.sendIqPacket(account, bundlesPacket, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
+ fetchStatusMap.put(address, FetchStatus.TIMEOUT);
+ } else if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received preKey IQ packet, processing...");
+ final IqParser parser = mXmppConnectionService.getIqParser();
+ final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet);
+ final PreKeyBundle bundle = parser.bundle(packet);
+ if (preKeyBundleList.isEmpty() || bundle == null) {
+ Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet);
+ fetchStatusMap.put(address, FetchStatus.ERROR);
+ finishBuildingSessionsFromPEP(address);
+ return;
+ }
+ Random random = new Random();
+ final PreKeyBundle preKey = preKeyBundleList.get(random.nextInt(preKeyBundleList.size()));
+ if (preKey == null) {
+ //should never happen
+ fetchStatusMap.put(address, FetchStatus.ERROR);
+ finishBuildingSessionsFromPEP(address);
+ return;
+ }
+
+ final PreKeyBundle preKeyBundle = new PreKeyBundle(0, address.getDeviceId(),
+ preKey.getPreKeyId(), preKey.getPreKey(),
+ bundle.getSignedPreKeyId(), bundle.getSignedPreKey(),
+ bundle.getSignedPreKeySignature(), bundle.getIdentityKey());
+
+ try {
+ SessionBuilder builder = new SessionBuilder(axolotlStore, address);
+ builder.process(preKeyBundle);
+ XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey());
+ sessions.put(address, session);
+ if (Config.X509_VERIFICATION) {
+ verifySessionWithPEP(session);
+ } else {
+ FingerprintStatus status = getFingerprintTrust(bundle.getIdentityKey().getFingerprint().replaceAll("\\s", ""));
+ boolean verified = status != null && status.isVerified();
+ fetchStatusMap.put(address, verified ? FetchStatus.SUCCESS_VERIFIED : FetchStatus.SUCCESS);
+ finishBuildingSessionsFromPEP(address);
+ }
+ } catch (UntrustedIdentityException | InvalidKeyException e) {
+ Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": "
+ + e.getClass().getName() + ", " + e.getMessage());
+ fetchStatusMap.put(address, FetchStatus.ERROR);
+ finishBuildingSessionsFromPEP(address);
+ }
+ } else {
+ fetchStatusMap.put(address, FetchStatus.ERROR);
+ Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while building session:" + packet.findChild("error"));
+ finishBuildingSessionsFromPEP(address);
+ }
+ }
+ });
+ } catch (InvalidJidException e) {
+ Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got address with invalid jid: " + address.getName());
+ }
+ }
+
+ public Set<AxolotlAddress> findDevicesWithoutSession(final Conversation conversation) {
+ Set<AxolotlAddress> addresses = new HashSet<>();
+ for (Jid jid : getCryptoTargets(conversation)) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Finding devices without session for " + jid);
+ if (deviceIds.get(jid) != null) {
+ for (Integer foreignId : this.deviceIds.get(jid)) {
+ AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId);
+ if (sessions.get(address) == null) {
+ IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
+ if (identityKey != null) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache...");
+ XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey);
+ sessions.put(address, session);
+ } else {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + jid + ":" + foreignId);
+ if (fetchStatusMap.get(address) != FetchStatus.ERROR) {
+ addresses.add(address);
+ } else {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken");
+ }
+ }
+ }
+ }
+ } else {
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Have no target devices in PEP!");
+ }
+ }
+ if (deviceIds.get(account.getJid().toBareJid()) != null) {
+ for (Integer ownId : this.deviceIds.get(account.getJid().toBareJid())) {
+ AxolotlAddress address = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), ownId);
+ if (sessions.get(address) == null) {
+ IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
+ if (identityKey != null) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache...");
+ XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey);
+ sessions.put(address, session);
+ } else {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().toBareJid() + ":" + ownId);
+ if (fetchStatusMap.get(address) != FetchStatus.ERROR) {
+ addresses.add(address);
+ } else {
+ Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken");
+ }
+ }
+ }
+ }
+ }
+
+ return addresses;
+ }
+
+ public boolean createSessionsIfNeeded(final Conversation conversation) {
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Creating axolotl sessions if needed...");
+ boolean newSessions = false;
+ Set<AxolotlAddress> addresses = findDevicesWithoutSession(conversation);
+ for (AxolotlAddress address : addresses) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Processing device: " + address.toString());
+ FetchStatus status = fetchStatusMap.get(address);
+ if (status == null || status == FetchStatus.TIMEOUT) {
+ fetchStatusMap.put(address, FetchStatus.PENDING);
+ this.buildSessionFromPEP(address);
+ newSessions = true;
+ } else if (status == FetchStatus.PENDING) {
+ newSessions = true;
+ } else {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already fetching bundle for " + address.toString());
+ }
+ }
+
+ return newSessions;
+ }
+
+ public boolean trustedSessionVerified(final Conversation conversation) {
+ Set<XmppAxolotlSession> sessions = findSessionsForConversation(conversation);
+ sessions.addAll(findOwnSessions());
+ boolean verified = false;
+ for (XmppAxolotlSession session : sessions) {
+ if (session.getTrust().isTrustedAndActive()) {
+ if (session.getTrust().getTrust() == FingerprintStatus.Trust.VERIFIED_X509) {
+ verified = true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return verified;
+ }
+
+ public boolean hasPendingKeyFetches(Account account, List<Jid> jids) {
+ AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), 0);
+ if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)) {
+ return true;
+ }
+ for (Jid jid : jids) {
+ AxolotlAddress foreignAddress = new AxolotlAddress(jid.toBareJid().toPreppedString(), 0);
+ if (fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ private XmppAxolotlMessage buildHeader(Conversation conversation) {
+ final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(
+ account.getJid().toBareJid(), getOwnDeviceId());
+
+ Set<XmppAxolotlSession> remoteSessions = findSessionsForConversation(conversation);
+ Set<XmppAxolotlSession> ownSessions = findOwnSessions();
+ if (remoteSessions.isEmpty()) {
+ return null;
+ }
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl foreign keyElements...");
+ for (XmppAxolotlSession session : remoteSessions) {
+ Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
+ axolotlMessage.addDevice(session);
+ }
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl own keyElements...");
+ for (XmppAxolotlSession session : ownSessions) {
+ Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
+ axolotlMessage.addDevice(session);
+ }
+
+ return axolotlMessage;
+ }
+
+ @Nullable
+ public XmppAxolotlMessage encrypt(Message message) {
+ XmppAxolotlMessage axolotlMessage = buildHeader(message.getConversation());
+
+ if (axolotlMessage != null) {
+ final String content;
+ if (message.hasFileOnRemoteHost()) {
+ content = message.getFileParams().url.toString();
+ } else {
+ content = message.getBody();
+ }
+ try {
+ axolotlMessage.encrypt(content);
+ } catch (CryptoFailedException e) {
+ Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to encrypt message: " + e.getMessage());
+ return null;
+ }
+ }
+
+ return axolotlMessage;
+ }
+
+ public void preparePayloadMessage(final Message message, final boolean delay) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ XmppAxolotlMessage axolotlMessage = encrypt(message);
+ if (axolotlMessage == null) {
+ mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
+ //mXmppConnectionService.updateConversationUi();
+ } else {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Generated message, caching: " + message.getUuid());
+ messageCache.put(message.getUuid(), axolotlMessage);
+ mXmppConnectionService.resendMessage(message, delay);
+ }
+ }
+ });
+ }
+
+ public void prepareKeyTransportMessage(final Conversation conversation, final OnMessageCreatedCallback onMessageCreatedCallback) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ XmppAxolotlMessage axolotlMessage = buildHeader(conversation);
+ onMessageCreatedCallback.run(axolotlMessage);
+ }
+ });
+ }
+
+ public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) {
+ XmppAxolotlMessage axolotlMessage = messageCache.get(message.getUuid());
+ if (axolotlMessage != null) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Cache hit: " + message.getUuid());
+ messageCache.remove(message.getUuid());
+ } else {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Cache miss: " + message.getUuid());
+ }
+ return axolotlMessage;
+ }
+
+ private XmppAxolotlSession recreateUncachedSession(AxolotlAddress address) {
+ IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
+ return (identityKey != null)
+ ? new XmppAxolotlSession(account, axolotlStore, address, identityKey)
+ : null;
+ }
+
+ private XmppAxolotlSession getReceivingSession(XmppAxolotlMessage message) {
+ AxolotlAddress senderAddress = new AxolotlAddress(message.getFrom().toString(),
+ message.getSenderDeviceId());
+ XmppAxolotlSession session = sessions.get(senderAddress);
+ if (session == null) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Account: " + account.getJid() + " No axolotl session found while parsing received message " + message);
+ session = recreateUncachedSession(senderAddress);
+ if (session == null) {
+ session = new XmppAxolotlSession(account, axolotlStore, senderAddress);
+ }
+ }
+ return session;
+ }
+
+ public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message) {
+ XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = null;
+
+ XmppAxolotlSession session = getReceivingSession(message);
+ try {
+ plaintextMessage = message.decrypt(session, getOwnDeviceId());
+ Integer preKeyId = session.getPreKeyId();
+ if (preKeyId != null) {
+ publishBundlesIfNeeded(false, false);
+ session.resetPreKeyId();
+ }
+ } catch (CryptoFailedException e) {
+ Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to decrypt message: " + e.getMessage());
+ }
+
+ if (session.isFresh() && plaintextMessage != null) {
+ putFreshSession(session);
+ }
+
+ return plaintextMessage;
+ }
+
+ public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message) {
+ XmppAxolotlMessage.XmppAxolotlKeyTransportMessage keyTransportMessage;
+
+ XmppAxolotlSession session = getReceivingSession(message);
+ keyTransportMessage = message.getParameters(session, getOwnDeviceId());
+
+ if (session.isFresh() && keyTransportMessage != null) {
+ putFreshSession(session);
+ }
+
+ return keyTransportMessage;
+ }
+
+ private void putFreshSession(XmppAxolotlSession session) {
+ Log.d(Config.LOGTAG, "put fresh session");
+ sessions.put(session);
+ if (Config.X509_VERIFICATION) {
+ if (session.getIdentityKey() != null) {
+ verifySessionWithPEP(session);
+ } else {
+ Log.e(Config.LOGTAG, account.getJid().toBareJid() + ": identity key was empty after reloading for x509 verification");
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/CryptoFailedException.java b/src/main/java/de/pixart/messenger/crypto/axolotl/CryptoFailedException.java
index b29d7cb30..da847393b 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/CryptoFailedException.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/CryptoFailedException.java
@@ -1,7 +1,7 @@
package de.pixart.messenger.crypto.axolotl;
public class CryptoFailedException extends Exception {
- public CryptoFailedException(Exception e){
- super(e);
- }
+ public CryptoFailedException(Exception e) {
+ super(e);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java b/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
index db67617b9..3be469b5a 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
@@ -32,8 +32,8 @@ public class FingerprintStatus {
public ContentValues toContentValues() {
final ContentValues contentValues = new ContentValues();
- contentValues.put(SQLiteAxolotlStore.TRUST,trust.toString());
- contentValues.put(SQLiteAxolotlStore.ACTIVE,active ? 1 : 0);
+ contentValues.put(SQLiteAxolotlStore.TRUST, trust.toString());
+ contentValues.put(SQLiteAxolotlStore.ACTIVE, active ? 1 : 0);
return contentValues;
}
@@ -41,7 +41,7 @@ public class FingerprintStatus {
final FingerprintStatus status = new FingerprintStatus();
try {
status.trust = Trust.valueOf(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.TRUST)));
- } catch(IllegalArgumentException e) {
+ } catch (IllegalArgumentException e) {
status.trust = Trust.UNTRUSTED;
}
status.active = cursor.getInt(cursor.getColumnIndex(SQLiteAxolotlStore.ACTIVE)) > 0;
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/NoSessionsCreatedException.java b/src/main/java/de/pixart/messenger/crypto/axolotl/NoSessionsCreatedException.java
index 5d0a7547a..92635a364 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/NoSessionsCreatedException.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/NoSessionsCreatedException.java
@@ -1,4 +1,4 @@
package de.pixart.messenger.crypto.axolotl;
-public class NoSessionsCreatedException extends Throwable{
+public class NoSessionsCreatedException extends Throwable {
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/OnMessageCreatedCallback.java b/src/main/java/de/pixart/messenger/crypto/axolotl/OnMessageCreatedCallback.java
index a6daeb196..088831661 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/OnMessageCreatedCallback.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/OnMessageCreatedCallback.java
@@ -1,5 +1,5 @@
package de.pixart.messenger.crypto.axolotl;
public interface OnMessageCreatedCallback {
- void run(XmppAxolotlMessage message);
+ void run(XmppAxolotlMessage message);
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/SQLiteAxolotlStore.java b/src/main/java/de/pixart/messenger/crypto/axolotl/SQLiteAxolotlStore.java
index 2980bf7cc..c45e5045e 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/SQLiteAxolotlStore.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/SQLiteAxolotlStore.java
@@ -25,421 +25,421 @@ import de.pixart.messenger.services.XmppConnectionService;
public class SQLiteAxolotlStore implements AxolotlStore {
- public static final String PREKEY_TABLENAME = "prekeys";
- public static final String SIGNED_PREKEY_TABLENAME = "signed_prekeys";
- public static final String SESSION_TABLENAME = "sessions";
- public static final String IDENTITIES_TABLENAME = "identities";
- public static final String ACCOUNT = "account";
- public static final String DEVICE_ID = "device_id";
- public static final String ID = "id";
- public static final String KEY = "key";
- public static final String FINGERPRINT = "fingerprint";
- public static final String NAME = "name";
- public static final String TRUSTED = "trusted"; //no longer used
- public static final String TRUST = "trust";
- public static final String ACTIVE = "active";
- public static final String OWN = "ownkey";
- public static final String CERTIFICATE = "certificate";
-
- public static final String JSONKEY_REGISTRATION_ID = "axolotl_reg_id";
- public static final String JSONKEY_CURRENT_PREKEY_ID = "axolotl_cur_prekey_id";
-
- private static final int NUM_TRUSTS_TO_CACHE = 100;
-
- private final Account account;
- private final XmppConnectionService mXmppConnectionService;
-
- private IdentityKeyPair identityKeyPair;
- private int localRegistrationId;
- private int currentPreKeyId = 0;
-
- private final LruCache<String, FingerprintStatus> trustCache =
- new LruCache<String, FingerprintStatus>(NUM_TRUSTS_TO_CACHE) {
- @Override
- protected FingerprintStatus create(String fingerprint) {
- return mXmppConnectionService.databaseBackend.getFingerprintStatus(account, fingerprint);
- }
- };
-
- private static IdentityKeyPair generateIdentityKeyPair() {
- Log.i(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Generating axolotl IdentityKeyPair...");
- ECKeyPair identityKeyPairKeys = Curve.generateKeyPair();
- return new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()),
- identityKeyPairKeys.getPrivateKey());
- }
-
- private static int generateRegistrationId() {
- Log.i(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Generating axolotl registration ID...");
- return KeyHelper.generateRegistrationId(true);
- }
-
- public SQLiteAxolotlStore(Account account, XmppConnectionService service) {
- this.account = account;
- this.mXmppConnectionService = service;
- this.localRegistrationId = loadRegistrationId();
- this.currentPreKeyId = loadCurrentPreKeyId();
- for (SignedPreKeyRecord record : loadSignedPreKeys()) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got Axolotl signed prekey record:" + record.getId());
- }
- }
-
- public int getCurrentPreKeyId() {
- return currentPreKeyId;
- }
-
- // --------------------------------------
- // IdentityKeyStore
- // --------------------------------------
-
- private IdentityKeyPair loadIdentityKeyPair() {
- synchronized (mXmppConnectionService) {
- IdentityKeyPair ownKey = mXmppConnectionService.databaseBackend.loadOwnIdentityKeyPair(account);
-
- if (ownKey != null) {
- return ownKey;
- } else {
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve own IdentityKeyPair");
- ownKey = generateIdentityKeyPair();
- mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
- }
- return ownKey;
- }
- }
-
- private int loadRegistrationId() {
- return loadRegistrationId(false);
- }
-
- private int loadRegistrationId(boolean regenerate) {
- String regIdString = this.account.getKey(JSONKEY_REGISTRATION_ID);
- int reg_id;
- if (!regenerate && regIdString != null) {
- reg_id = Integer.valueOf(regIdString);
- } else {
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve axolotl registration id for account " + account.getJid());
- reg_id = generateRegistrationId();
- boolean success = this.account.setKey(JSONKEY_REGISTRATION_ID, Integer.toString(reg_id));
- if (success) {
- mXmppConnectionService.databaseBackend.updateAccount(account);
- } else {
- Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to write new key to the database!");
- }
- }
- return reg_id;
- }
-
- private int loadCurrentPreKeyId() {
- String prekeyIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
- int prekey_id;
- if (prekeyIdString != null) {
- prekey_id = Integer.valueOf(prekeyIdString);
- } else {
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve current prekey id for account " + account.getJid());
- prekey_id = 0;
- }
- return prekey_id;
- }
-
- public void regenerate() {
- mXmppConnectionService.databaseBackend.wipeAxolotlDb(account);
- trustCache.evictAll();
- account.setKey(JSONKEY_CURRENT_PREKEY_ID, Integer.toString(0));
- identityKeyPair = loadIdentityKeyPair();
- localRegistrationId = loadRegistrationId(true);
- currentPreKeyId = 0;
- mXmppConnectionService.updateAccountUi();
- }
-
- /**
- * Get the local client's identity key pair.
- *
- * @return The local client's persistent identity key pair.
- */
- @Override
- public IdentityKeyPair getIdentityKeyPair() {
- if (identityKeyPair == null) {
- identityKeyPair = loadIdentityKeyPair();
- }
- return identityKeyPair;
- }
-
- /**
- * Return the local client's registration ID.
- * <p/>
- * Clients should maintain a registration ID, a random number
- * between 1 and 16380 that's generated once at install time.
- *
- * @return the local client's registration ID.
- */
- @Override
- public int getLocalRegistrationId() {
- return localRegistrationId;
- }
-
- /**
- * Save a remote client's identity key
- * <p/>
- * Store a remote client's identity key as trusted.
- *
- * @param name The name of the remote client.
- * @param identityKey The remote client's identity key.
- */
- @Override
- public void saveIdentity(String name, IdentityKey identityKey) {
- if (!mXmppConnectionService.databaseBackend.loadIdentityKeys(account, name).contains(identityKey)) {
- String fingerprint = identityKey.getFingerprint().replaceAll("\\s", "");
- FingerprintStatus status = getFingerprintStatus(fingerprint);
- if (status == null) {
- status = FingerprintStatus.createActiveUndecided(); //default for new keys
- } else {
- status = status.toActive();
- }
- mXmppConnectionService.databaseBackend.storeIdentityKey(account, name, identityKey, status);
- trustCache.remove(fingerprint);
- }
- }
-
- /**
- * Verify a remote client's identity key.
- * <p/>
- * Determine whether a remote client's identity is trusted. Convention is
- * that the TextSecure protocol is 'trust on first use.' This means that
- * an identity key is considered 'trusted' if there is no entry for the recipient
- * in the local store, or if it matches the saved key for a recipient in the local
- * store. Only if it mismatches an entry in the local store is it considered
- * 'untrusted.'
- *
- * @param name The name of the remote client.
- * @param identityKey The identity key to verify.
- * @return true if trusted, false if untrusted.
- */
- @Override
- public boolean isTrustedIdentity(String name, IdentityKey identityKey) {
- return true;
- }
-
- public FingerprintStatus getFingerprintStatus(String fingerprint) {
- return (fingerprint == null)? null : trustCache.get(fingerprint);
- }
-
- public void setFingerprintStatus(String fingerprint, FingerprintStatus status) {
- mXmppConnectionService.databaseBackend.setIdentityKeyTrust(account, fingerprint, status);
- trustCache.remove(fingerprint);
- }
-
- public void setFingerprintCertificate(String fingerprint, X509Certificate x509Certificate) {
- mXmppConnectionService.databaseBackend.setIdentityKeyCertificate(account, fingerprint, x509Certificate);
- }
-
- public X509Certificate getFingerprintCertificate(String fingerprint) {
- return mXmppConnectionService.databaseBackend.getIdentityKeyCertifcate(account, fingerprint);
- }
-
- public Set<IdentityKey> getContactKeysWithTrust(String bareJid, FingerprintStatus status) {
- return mXmppConnectionService.databaseBackend.loadIdentityKeys(account, bareJid, status);
- }
-
- public long getContactNumTrustedKeys(String bareJid) {
- return mXmppConnectionService.databaseBackend.numTrustedKeys(account, bareJid);
- }
-
- // --------------------------------------
- // SessionStore
- // --------------------------------------
-
- /**
- * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple,
- * or a new SessionRecord if one does not currently exist.
- * <p/>
- * It is important that implementations return a copy of the current durable information. The
- * returned SessionRecord may be modified, but those changes should not have an effect on the
- * durable session state (what is returned by subsequent calls to this method) without the
- * store method being called here first.
- *
- * @param address The name and device ID of the remote client.
- * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or
- * a new SessionRecord if one does not currently exist.
- */
- @Override
- public SessionRecord loadSession(AxolotlAddress address) {
- SessionRecord session = mXmppConnectionService.databaseBackend.loadSession(this.account, address);
- return (session != null) ? session : new SessionRecord();
- }
-
- /**
- * Returns all known devices with active sessions for a recipient
- *
- * @param name the name of the client.
- * @return all known sub-devices with active sessions.
- */
- @Override
- public List<Integer> getSubDeviceSessions(String name) {
- return mXmppConnectionService.databaseBackend.getSubDeviceSessions(account,
- new AxolotlAddress(name, 0));
- }
-
- /**
- * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple.
- *
- * @param address the address of the remote client.
- * @param record the current SessionRecord for the remote client.
- */
- @Override
- public void storeSession(AxolotlAddress address, SessionRecord record) {
- mXmppConnectionService.databaseBackend.storeSession(account, address, record);
- }
-
- /**
- * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple.
- *
- * @param address the address of the remote client.
- * @return true if a {@link SessionRecord} exists, false otherwise.
- */
- @Override
- public boolean containsSession(AxolotlAddress address) {
- return mXmppConnectionService.databaseBackend.containsSession(account, address);
- }
-
- /**
- * Remove a {@link SessionRecord} for a recipientId + deviceId tuple.
- *
- * @param address the address of the remote client.
- */
- @Override
- public void deleteSession(AxolotlAddress address) {
- mXmppConnectionService.databaseBackend.deleteSession(account, address);
- }
-
- /**
- * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId.
- *
- * @param name the name of the remote client.
- */
- @Override
- public void deleteAllSessions(String name) {
- AxolotlAddress address = new AxolotlAddress(name, 0);
- mXmppConnectionService.databaseBackend.deleteAllSessions(account,
- address);
- }
-
- // --------------------------------------
- // PreKeyStore
- // --------------------------------------
-
- /**
- * Load a local PreKeyRecord.
- *
- * @param preKeyId the ID of the local PreKeyRecord.
- * @return the corresponding PreKeyRecord.
- * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord.
- */
- @Override
- public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
- PreKeyRecord record = mXmppConnectionService.databaseBackend.loadPreKey(account, preKeyId);
- if (record == null) {
- throw new InvalidKeyIdException("No such PreKeyRecord: " + preKeyId);
- }
- return record;
- }
-
- /**
- * Store a local PreKeyRecord.
- *
- * @param preKeyId the ID of the PreKeyRecord to store.
- * @param record the PreKeyRecord.
- */
- @Override
- public void storePreKey(int preKeyId, PreKeyRecord record) {
- mXmppConnectionService.databaseBackend.storePreKey(account, record);
- currentPreKeyId = preKeyId;
- boolean success = this.account.setKey(JSONKEY_CURRENT_PREKEY_ID, Integer.toString(preKeyId));
- if (success) {
- mXmppConnectionService.databaseBackend.updateAccount(account);
- } else {
- Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to write new prekey id to the database!");
- }
- }
-
- /**
- * @param preKeyId A PreKeyRecord ID.
- * @return true if the store has a record for the preKeyId, otherwise false.
- */
- @Override
- public boolean containsPreKey(int preKeyId) {
- return mXmppConnectionService.databaseBackend.containsPreKey(account, preKeyId);
- }
-
- /**
- * Delete a PreKeyRecord from local storage.
- *
- * @param preKeyId The ID of the PreKeyRecord to remove.
- */
- @Override
- public void removePreKey(int preKeyId) {
- mXmppConnectionService.databaseBackend.deletePreKey(account, preKeyId);
- }
-
- // --------------------------------------
- // SignedPreKeyStore
- // --------------------------------------
-
- /**
- * Load a local SignedPreKeyRecord.
- *
- * @param signedPreKeyId the ID of the local SignedPreKeyRecord.
- * @return the corresponding SignedPreKeyRecord.
- * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord.
- */
- @Override
- public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
- SignedPreKeyRecord record = mXmppConnectionService.databaseBackend.loadSignedPreKey(account, signedPreKeyId);
- if (record == null) {
- throw new InvalidKeyIdException("No such SignedPreKeyRecord: " + signedPreKeyId);
- }
- return record;
- }
-
- /**
- * Load all local SignedPreKeyRecords.
- *
- * @return All stored SignedPreKeyRecords.
- */
- @Override
- public List<SignedPreKeyRecord> loadSignedPreKeys() {
- return mXmppConnectionService.databaseBackend.loadSignedPreKeys(account);
- }
-
- /**
- * Store a local SignedPreKeyRecord.
- *
- * @param signedPreKeyId the ID of the SignedPreKeyRecord to store.
- * @param record the SignedPreKeyRecord.
- */
- @Override
- public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
- mXmppConnectionService.databaseBackend.storeSignedPreKey(account, record);
- }
-
- /**
- * @param signedPreKeyId A SignedPreKeyRecord ID.
- * @return true if the store has a record for the signedPreKeyId, otherwise false.
- */
- @Override
- public boolean containsSignedPreKey(int signedPreKeyId) {
- return mXmppConnectionService.databaseBackend.containsSignedPreKey(account, signedPreKeyId);
- }
-
- /**
- * Delete a SignedPreKeyRecord from local storage.
- *
- * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove.
- */
- @Override
- public void removeSignedPreKey(int signedPreKeyId) {
- mXmppConnectionService.databaseBackend.deleteSignedPreKey(account, signedPreKeyId);
- }
-
- public void preVerifyFingerprint(Account account, String name, String fingerprint) {
- mXmppConnectionService.databaseBackend.storePreVerification(account,name,fingerprint,FingerprintStatus.createInactiveVerified());
- }
+ public static final String PREKEY_TABLENAME = "prekeys";
+ public static final String SIGNED_PREKEY_TABLENAME = "signed_prekeys";
+ public static final String SESSION_TABLENAME = "sessions";
+ public static final String IDENTITIES_TABLENAME = "identities";
+ public static final String ACCOUNT = "account";
+ public static final String DEVICE_ID = "device_id";
+ public static final String ID = "id";
+ public static final String KEY = "key";
+ public static final String FINGERPRINT = "fingerprint";
+ public static final String NAME = "name";
+ public static final String TRUSTED = "trusted"; //no longer used
+ public static final String TRUST = "trust";
+ public static final String ACTIVE = "active";
+ public static final String OWN = "ownkey";
+ public static final String CERTIFICATE = "certificate";
+
+ public static final String JSONKEY_REGISTRATION_ID = "axolotl_reg_id";
+ public static final String JSONKEY_CURRENT_PREKEY_ID = "axolotl_cur_prekey_id";
+
+ private static final int NUM_TRUSTS_TO_CACHE = 100;
+
+ private final Account account;
+ private final XmppConnectionService mXmppConnectionService;
+
+ private IdentityKeyPair identityKeyPair;
+ private int localRegistrationId;
+ private int currentPreKeyId = 0;
+
+ private final LruCache<String, FingerprintStatus> trustCache =
+ new LruCache<String, FingerprintStatus>(NUM_TRUSTS_TO_CACHE) {
+ @Override
+ protected FingerprintStatus create(String fingerprint) {
+ return mXmppConnectionService.databaseBackend.getFingerprintStatus(account, fingerprint);
+ }
+ };
+
+ private static IdentityKeyPair generateIdentityKeyPair() {
+ Log.i(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Generating axolotl IdentityKeyPair...");
+ ECKeyPair identityKeyPairKeys = Curve.generateKeyPair();
+ return new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()),
+ identityKeyPairKeys.getPrivateKey());
+ }
+
+ private static int generateRegistrationId() {
+ Log.i(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Generating axolotl registration ID...");
+ return KeyHelper.generateRegistrationId(true);
+ }
+
+ public SQLiteAxolotlStore(Account account, XmppConnectionService service) {
+ this.account = account;
+ this.mXmppConnectionService = service;
+ this.localRegistrationId = loadRegistrationId();
+ this.currentPreKeyId = loadCurrentPreKeyId();
+ for (SignedPreKeyRecord record : loadSignedPreKeys()) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got Axolotl signed prekey record:" + record.getId());
+ }
+ }
+
+ public int getCurrentPreKeyId() {
+ return currentPreKeyId;
+ }
+
+ // --------------------------------------
+ // IdentityKeyStore
+ // --------------------------------------
+
+ private IdentityKeyPair loadIdentityKeyPair() {
+ synchronized (mXmppConnectionService) {
+ IdentityKeyPair ownKey = mXmppConnectionService.databaseBackend.loadOwnIdentityKeyPair(account);
+
+ if (ownKey != null) {
+ return ownKey;
+ } else {
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve own IdentityKeyPair");
+ ownKey = generateIdentityKeyPair();
+ mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
+ }
+ return ownKey;
+ }
+ }
+
+ private int loadRegistrationId() {
+ return loadRegistrationId(false);
+ }
+
+ private int loadRegistrationId(boolean regenerate) {
+ String regIdString = this.account.getKey(JSONKEY_REGISTRATION_ID);
+ int reg_id;
+ if (!regenerate && regIdString != null) {
+ reg_id = Integer.valueOf(regIdString);
+ } else {
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve axolotl registration id for account " + account.getJid());
+ reg_id = generateRegistrationId();
+ boolean success = this.account.setKey(JSONKEY_REGISTRATION_ID, Integer.toString(reg_id));
+ if (success) {
+ mXmppConnectionService.databaseBackend.updateAccount(account);
+ } else {
+ Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to write new key to the database!");
+ }
+ }
+ return reg_id;
+ }
+
+ private int loadCurrentPreKeyId() {
+ String prekeyIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
+ int prekey_id;
+ if (prekeyIdString != null) {
+ prekey_id = Integer.valueOf(prekeyIdString);
+ } else {
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve current prekey id for account " + account.getJid());
+ prekey_id = 0;
+ }
+ return prekey_id;
+ }
+
+ public void regenerate() {
+ mXmppConnectionService.databaseBackend.wipeAxolotlDb(account);
+ trustCache.evictAll();
+ account.setKey(JSONKEY_CURRENT_PREKEY_ID, Integer.toString(0));
+ identityKeyPair = loadIdentityKeyPair();
+ localRegistrationId = loadRegistrationId(true);
+ currentPreKeyId = 0;
+ mXmppConnectionService.updateAccountUi();
+ }
+
+ /**
+ * Get the local client's identity key pair.
+ *
+ * @return The local client's persistent identity key pair.
+ */
+ @Override
+ public IdentityKeyPair getIdentityKeyPair() {
+ if (identityKeyPair == null) {
+ identityKeyPair = loadIdentityKeyPair();
+ }
+ return identityKeyPair;
+ }
+
+ /**
+ * Return the local client's registration ID.
+ * <p/>
+ * Clients should maintain a registration ID, a random number
+ * between 1 and 16380 that's generated once at install time.
+ *
+ * @return the local client's registration ID.
+ */
+ @Override
+ public int getLocalRegistrationId() {
+ return localRegistrationId;
+ }
+
+ /**
+ * Save a remote client's identity key
+ * <p/>
+ * Store a remote client's identity key as trusted.
+ *
+ * @param name The name of the remote client.
+ * @param identityKey The remote client's identity key.
+ */
+ @Override
+ public void saveIdentity(String name, IdentityKey identityKey) {
+ if (!mXmppConnectionService.databaseBackend.loadIdentityKeys(account, name).contains(identityKey)) {
+ String fingerprint = identityKey.getFingerprint().replaceAll("\\s", "");
+ FingerprintStatus status = getFingerprintStatus(fingerprint);
+ if (status == null) {
+ status = FingerprintStatus.createActiveUndecided(); //default for new keys
+ } else {
+ status = status.toActive();
+ }
+ mXmppConnectionService.databaseBackend.storeIdentityKey(account, name, identityKey, status);
+ trustCache.remove(fingerprint);
+ }
+ }
+
+ /**
+ * Verify a remote client's identity key.
+ * <p/>
+ * Determine whether a remote client's identity is trusted. Convention is
+ * that the TextSecure protocol is 'trust on first use.' This means that
+ * an identity key is considered 'trusted' if there is no entry for the recipient
+ * in the local store, or if it matches the saved key for a recipient in the local
+ * store. Only if it mismatches an entry in the local store is it considered
+ * 'untrusted.'
+ *
+ * @param name The name of the remote client.
+ * @param identityKey The identity key to verify.
+ * @return true if trusted, false if untrusted.
+ */
+ @Override
+ public boolean isTrustedIdentity(String name, IdentityKey identityKey) {
+ return true;
+ }
+
+ public FingerprintStatus getFingerprintStatus(String fingerprint) {
+ return (fingerprint == null) ? null : trustCache.get(fingerprint);
+ }
+
+ public void setFingerprintStatus(String fingerprint, FingerprintStatus status) {
+ mXmppConnectionService.databaseBackend.setIdentityKeyTrust(account, fingerprint, status);
+ trustCache.remove(fingerprint);
+ }
+
+ public void setFingerprintCertificate(String fingerprint, X509Certificate x509Certificate) {
+ mXmppConnectionService.databaseBackend.setIdentityKeyCertificate(account, fingerprint, x509Certificate);
+ }
+
+ public X509Certificate getFingerprintCertificate(String fingerprint) {
+ return mXmppConnectionService.databaseBackend.getIdentityKeyCertifcate(account, fingerprint);
+ }
+
+ public Set<IdentityKey> getContactKeysWithTrust(String bareJid, FingerprintStatus status) {
+ return mXmppConnectionService.databaseBackend.loadIdentityKeys(account, bareJid, status);
+ }
+
+ public long getContactNumTrustedKeys(String bareJid) {
+ return mXmppConnectionService.databaseBackend.numTrustedKeys(account, bareJid);
+ }
+
+ // --------------------------------------
+ // SessionStore
+ // --------------------------------------
+
+ /**
+ * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple,
+ * or a new SessionRecord if one does not currently exist.
+ * <p/>
+ * It is important that implementations return a copy of the current durable information. The
+ * returned SessionRecord may be modified, but those changes should not have an effect on the
+ * durable session state (what is returned by subsequent calls to this method) without the
+ * store method being called here first.
+ *
+ * @param address The name and device ID of the remote client.
+ * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or
+ * a new SessionRecord if one does not currently exist.
+ */
+ @Override
+ public SessionRecord loadSession(AxolotlAddress address) {
+ SessionRecord session = mXmppConnectionService.databaseBackend.loadSession(this.account, address);
+ return (session != null) ? session : new SessionRecord();
+ }
+
+ /**
+ * Returns all known devices with active sessions for a recipient
+ *
+ * @param name the name of the client.
+ * @return all known sub-devices with active sessions.
+ */
+ @Override
+ public List<Integer> getSubDeviceSessions(String name) {
+ return mXmppConnectionService.databaseBackend.getSubDeviceSessions(account,
+ new AxolotlAddress(name, 0));
+ }
+
+ /**
+ * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple.
+ *
+ * @param address the address of the remote client.
+ * @param record the current SessionRecord for the remote client.
+ */
+ @Override
+ public void storeSession(AxolotlAddress address, SessionRecord record) {
+ mXmppConnectionService.databaseBackend.storeSession(account, address, record);
+ }
+
+ /**
+ * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple.
+ *
+ * @param address the address of the remote client.
+ * @return true if a {@link SessionRecord} exists, false otherwise.
+ */
+ @Override
+ public boolean containsSession(AxolotlAddress address) {
+ return mXmppConnectionService.databaseBackend.containsSession(account, address);
+ }
+
+ /**
+ * Remove a {@link SessionRecord} for a recipientId + deviceId tuple.
+ *
+ * @param address the address of the remote client.
+ */
+ @Override
+ public void deleteSession(AxolotlAddress address) {
+ mXmppConnectionService.databaseBackend.deleteSession(account, address);
+ }
+
+ /**
+ * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId.
+ *
+ * @param name the name of the remote client.
+ */
+ @Override
+ public void deleteAllSessions(String name) {
+ AxolotlAddress address = new AxolotlAddress(name, 0);
+ mXmppConnectionService.databaseBackend.deleteAllSessions(account,
+ address);
+ }
+
+ // --------------------------------------
+ // PreKeyStore
+ // --------------------------------------
+
+ /**
+ * Load a local PreKeyRecord.
+ *
+ * @param preKeyId the ID of the local PreKeyRecord.
+ * @return the corresponding PreKeyRecord.
+ * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord.
+ */
+ @Override
+ public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
+ PreKeyRecord record = mXmppConnectionService.databaseBackend.loadPreKey(account, preKeyId);
+ if (record == null) {
+ throw new InvalidKeyIdException("No such PreKeyRecord: " + preKeyId);
+ }
+ return record;
+ }
+
+ /**
+ * Store a local PreKeyRecord.
+ *
+ * @param preKeyId the ID of the PreKeyRecord to store.
+ * @param record the PreKeyRecord.
+ */
+ @Override
+ public void storePreKey(int preKeyId, PreKeyRecord record) {
+ mXmppConnectionService.databaseBackend.storePreKey(account, record);
+ currentPreKeyId = preKeyId;
+ boolean success = this.account.setKey(JSONKEY_CURRENT_PREKEY_ID, Integer.toString(preKeyId));
+ if (success) {
+ mXmppConnectionService.databaseBackend.updateAccount(account);
+ } else {
+ Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to write new prekey id to the database!");
+ }
+ }
+
+ /**
+ * @param preKeyId A PreKeyRecord ID.
+ * @return true if the store has a record for the preKeyId, otherwise false.
+ */
+ @Override
+ public boolean containsPreKey(int preKeyId) {
+ return mXmppConnectionService.databaseBackend.containsPreKey(account, preKeyId);
+ }
+
+ /**
+ * Delete a PreKeyRecord from local storage.
+ *
+ * @param preKeyId The ID of the PreKeyRecord to remove.
+ */
+ @Override
+ public void removePreKey(int preKeyId) {
+ mXmppConnectionService.databaseBackend.deletePreKey(account, preKeyId);
+ }
+
+ // --------------------------------------
+ // SignedPreKeyStore
+ // --------------------------------------
+
+ /**
+ * Load a local SignedPreKeyRecord.
+ *
+ * @param signedPreKeyId the ID of the local SignedPreKeyRecord.
+ * @return the corresponding SignedPreKeyRecord.
+ * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord.
+ */
+ @Override
+ public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
+ SignedPreKeyRecord record = mXmppConnectionService.databaseBackend.loadSignedPreKey(account, signedPreKeyId);
+ if (record == null) {
+ throw new InvalidKeyIdException("No such SignedPreKeyRecord: " + signedPreKeyId);
+ }
+ return record;
+ }
+
+ /**
+ * Load all local SignedPreKeyRecords.
+ *
+ * @return All stored SignedPreKeyRecords.
+ */
+ @Override
+ public List<SignedPreKeyRecord> loadSignedPreKeys() {
+ return mXmppConnectionService.databaseBackend.loadSignedPreKeys(account);
+ }
+
+ /**
+ * Store a local SignedPreKeyRecord.
+ *
+ * @param signedPreKeyId the ID of the SignedPreKeyRecord to store.
+ * @param record the SignedPreKeyRecord.
+ */
+ @Override
+ public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
+ mXmppConnectionService.databaseBackend.storeSignedPreKey(account, record);
+ }
+
+ /**
+ * @param signedPreKeyId A SignedPreKeyRecord ID.
+ * @return true if the store has a record for the signedPreKeyId, otherwise false.
+ */
+ @Override
+ public boolean containsSignedPreKey(int signedPreKeyId) {
+ return mXmppConnectionService.databaseBackend.containsSignedPreKey(account, signedPreKeyId);
+ }
+
+ /**
+ * Delete a SignedPreKeyRecord from local storage.
+ *
+ * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove.
+ */
+ @Override
+ public void removeSignedPreKey(int signedPreKeyId) {
+ mXmppConnectionService.databaseBackend.deleteSignedPreKey(account, signedPreKeyId);
+ }
+
+ public void preVerifyFingerprint(Account account, String name, String fingerprint) {
+ mXmppConnectionService.databaseBackend.storePreVerification(account, name, fingerprint, FingerprintStatus.createInactiveVerified());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
index 431f81d9b..f49eefb5a 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
@@ -26,227 +26,227 @@ import de.pixart.messenger.xml.Element;
import de.pixart.messenger.xmpp.jid.Jid;
public class XmppAxolotlMessage {
- public static final String CONTAINERTAG = "encrypted";
- public static final String HEADER = "header";
- public static final String SOURCEID = "sid";
- public static final String KEYTAG = "key";
- public static final String REMOTEID = "rid";
- public static final String IVTAG = "iv";
- public static final String PAYLOAD = "payload";
-
- private static final String KEYTYPE = "AES";
- private static final String CIPHERMODE = "AES/GCM/NoPadding";
- private static final String PROVIDER = "BC";
-
- private byte[] innerKey;
- private byte[] ciphertext = null;
- private byte[] iv = null;
- private final Map<Integer, byte[]> keys;
- private final Jid from;
- private final int sourceDeviceId;
-
- public static class XmppAxolotlPlaintextMessage {
- private final String plaintext;
- private final String fingerprint;
-
- public XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
- this.plaintext = plaintext;
- this.fingerprint = fingerprint;
- }
-
- public String getPlaintext() {
- return plaintext;
- }
-
-
- public String getFingerprint() {
- return fingerprint;
- }
- }
-
- public static class XmppAxolotlKeyTransportMessage {
- private final String fingerprint;
- private final byte[] key;
- private final byte[] iv;
-
- public XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) {
- this.fingerprint = fingerprint;
- this.key = key;
- this.iv = iv;
- }
-
- public String getFingerprint() {
- return fingerprint;
- }
-
- public byte[] getKey() {
- return key;
- }
-
- public byte[] getIv() {
- return iv;
- }
- }
-
- private XmppAxolotlMessage(final Element axolotlMessage, final Jid from) throws IllegalArgumentException {
- this.from = from;
- Element header = axolotlMessage.findChild(HEADER);
- try {
- this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID));
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("invalid source id");
- }
- List<Element> keyElements = header.getChildren();
- this.keys = new HashMap<>(keyElements.size());
- for (Element keyElement : keyElements) {
- switch (keyElement.getName()) {
- case KEYTAG:
- try {
- Integer recipientId = Integer.parseInt(keyElement.getAttribute(REMOTEID));
- byte[] key = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
- this.keys.put(recipientId, key);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("invalid remote id");
- }
- break;
- case IVTAG:
- if (this.iv != null) {
- throw new IllegalArgumentException("Duplicate iv entry");
- }
- iv = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
- break;
- default:
- Log.w(Config.LOGTAG, "Unexpected element in header: " + keyElement.toString());
- break;
- }
- }
- Element payloadElement = axolotlMessage.findChild(PAYLOAD);
- if (payloadElement != null) {
- ciphertext = Base64.decode(payloadElement.getContent().trim(), Base64.DEFAULT);
- }
- }
-
- public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
- this.from = from;
- this.sourceDeviceId = sourceDeviceId;
- this.keys = new HashMap<>();
- this.iv = generateIv();
- this.innerKey = generateKey();
- }
-
- public static XmppAxolotlMessage fromElement(Element element, Jid from) {
- return new XmppAxolotlMessage(element, from);
- }
-
- private static byte[] generateKey() {
- try {
- KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE);
- generator.init(128);
- return generator.generateKey().getEncoded();
- } catch (NoSuchAlgorithmException e) {
- Log.e(Config.LOGTAG, e.getMessage());
- return null;
- }
- }
-
- private static byte[] generateIv() {
- SecureRandom random = new SecureRandom();
- byte[] iv = new byte[16];
- random.nextBytes(iv);
- return iv;
- }
-
- public void encrypt(String plaintext) throws CryptoFailedException {
- try {
- SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
- IvParameterSpec ivSpec = new IvParameterSpec(iv);
- Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
- cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
- this.ciphertext = cipher.doFinal(plaintext.getBytes());
- } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
- | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException
- | InvalidAlgorithmParameterException e) {
- throw new CryptoFailedException(e);
- }
- }
-
- public Jid getFrom() {
- return this.from;
- }
-
- public int getSenderDeviceId() {
- return sourceDeviceId;
- }
-
- public byte[] getCiphertext() {
- return ciphertext;
- }
-
- public void addDevice(XmppAxolotlSession session) {
- byte[] key = session.processSending(innerKey);
- if (key != null) {
- keys.put(session.getRemoteAddress().getDeviceId(), key);
- }
- }
-
- public byte[] getInnerKey() {
- return innerKey;
- }
-
- public byte[] getIV() {
- return this.iv;
- }
-
- public Element toElement() {
- Element encryptionElement = new Element(CONTAINERTAG, AxolotlService.PEP_PREFIX);
- Element headerElement = encryptionElement.addChild(HEADER);
- headerElement.setAttribute(SOURCEID, sourceDeviceId);
- for (Map.Entry<Integer, byte[]> keyEntry : keys.entrySet()) {
- Element keyElement = new Element(KEYTAG);
- keyElement.setAttribute(REMOTEID, keyEntry.getKey());
- keyElement.setContent(Base64.encodeToString(keyEntry.getValue(), Base64.NO_WRAP));
- headerElement.addChild(keyElement);
- }
- headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.NO_WRAP));
- if (ciphertext != null) {
- Element payload = encryptionElement.addChild(PAYLOAD);
- payload.setContent(Base64.encodeToString(ciphertext, Base64.NO_WRAP));
- }
- return encryptionElement;
- }
-
- private byte[] unpackKey(XmppAxolotlSession session, Integer sourceDeviceId) {
- byte[] encryptedKey = keys.get(sourceDeviceId);
- return (encryptedKey != null) ? session.processReceiving(encryptedKey) : null;
- }
-
- public XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) {
- byte[] key = unpackKey(session, sourceDeviceId);
- return (key != null)
- ? new XmppAxolotlKeyTransportMessage(session.getFingerprint(), key, getIV())
- : null;
- }
-
- public XmppAxolotlPlaintextMessage decrypt(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
- XmppAxolotlPlaintextMessage plaintextMessage = null;
- byte[] key = unpackKey(session, sourceDeviceId);
- if (key != null) {
- try {
- Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
- SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
- IvParameterSpec ivSpec = new IvParameterSpec(iv);
-
- cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
-
- String plaintext = new String(cipher.doFinal(ciphertext));
- plaintextMessage = new XmppAxolotlPlaintextMessage(plaintext, session.getFingerprint());
-
- } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
- | InvalidAlgorithmParameterException | IllegalBlockSizeException
- | BadPaddingException | NoSuchProviderException e) {
- throw new CryptoFailedException(e);
- }
- }
- return plaintextMessage;
- }
+ public static final String CONTAINERTAG = "encrypted";
+ public static final String HEADER = "header";
+ public static final String SOURCEID = "sid";
+ public static final String KEYTAG = "key";
+ public static final String REMOTEID = "rid";
+ public static final String IVTAG = "iv";
+ public static final String PAYLOAD = "payload";
+
+ private static final String KEYTYPE = "AES";
+ private static final String CIPHERMODE = "AES/GCM/NoPadding";
+ private static final String PROVIDER = "BC";
+
+ private byte[] innerKey;
+ private byte[] ciphertext = null;
+ private byte[] iv = null;
+ private final Map<Integer, byte[]> keys;
+ private final Jid from;
+ private final int sourceDeviceId;
+
+ public static class XmppAxolotlPlaintextMessage {
+ private final String plaintext;
+ private final String fingerprint;
+
+ public XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
+ this.plaintext = plaintext;
+ this.fingerprint = fingerprint;
+ }
+
+ public String getPlaintext() {
+ return plaintext;
+ }
+
+
+ public String getFingerprint() {
+ return fingerprint;
+ }
+ }
+
+ public static class XmppAxolotlKeyTransportMessage {
+ private final String fingerprint;
+ private final byte[] key;
+ private final byte[] iv;
+
+ public XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) {
+ this.fingerprint = fingerprint;
+ this.key = key;
+ this.iv = iv;
+ }
+
+ public String getFingerprint() {
+ return fingerprint;
+ }
+
+ public byte[] getKey() {
+ return key;
+ }
+
+ public byte[] getIv() {
+ return iv;
+ }
+ }
+
+ private XmppAxolotlMessage(final Element axolotlMessage, final Jid from) throws IllegalArgumentException {
+ this.from = from;
+ Element header = axolotlMessage.findChild(HEADER);
+ try {
+ this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("invalid source id");
+ }
+ List<Element> keyElements = header.getChildren();
+ this.keys = new HashMap<>(keyElements.size());
+ for (Element keyElement : keyElements) {
+ switch (keyElement.getName()) {
+ case KEYTAG:
+ try {
+ Integer recipientId = Integer.parseInt(keyElement.getAttribute(REMOTEID));
+ byte[] key = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
+ this.keys.put(recipientId, key);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("invalid remote id");
+ }
+ break;
+ case IVTAG:
+ if (this.iv != null) {
+ throw new IllegalArgumentException("Duplicate iv entry");
+ }
+ iv = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
+ break;
+ default:
+ Log.w(Config.LOGTAG, "Unexpected element in header: " + keyElement.toString());
+ break;
+ }
+ }
+ Element payloadElement = axolotlMessage.findChild(PAYLOAD);
+ if (payloadElement != null) {
+ ciphertext = Base64.decode(payloadElement.getContent().trim(), Base64.DEFAULT);
+ }
+ }
+
+ public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
+ this.from = from;
+ this.sourceDeviceId = sourceDeviceId;
+ this.keys = new HashMap<>();
+ this.iv = generateIv();
+ this.innerKey = generateKey();
+ }
+
+ public static XmppAxolotlMessage fromElement(Element element, Jid from) {
+ return new XmppAxolotlMessage(element, from);
+ }
+
+ private static byte[] generateKey() {
+ try {
+ KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE);
+ generator.init(128);
+ return generator.generateKey().getEncoded();
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(Config.LOGTAG, e.getMessage());
+ return null;
+ }
+ }
+
+ private static byte[] generateIv() {
+ SecureRandom random = new SecureRandom();
+ byte[] iv = new byte[16];
+ random.nextBytes(iv);
+ return iv;
+ }
+
+ public void encrypt(String plaintext) throws CryptoFailedException {
+ try {
+ SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
+ this.ciphertext = cipher.doFinal(plaintext.getBytes());
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+ | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException
+ | InvalidAlgorithmParameterException e) {
+ throw new CryptoFailedException(e);
+ }
+ }
+
+ public Jid getFrom() {
+ return this.from;
+ }
+
+ public int getSenderDeviceId() {
+ return sourceDeviceId;
+ }
+
+ public byte[] getCiphertext() {
+ return ciphertext;
+ }
+
+ public void addDevice(XmppAxolotlSession session) {
+ byte[] key = session.processSending(innerKey);
+ if (key != null) {
+ keys.put(session.getRemoteAddress().getDeviceId(), key);
+ }
+ }
+
+ public byte[] getInnerKey() {
+ return innerKey;
+ }
+
+ public byte[] getIV() {
+ return this.iv;
+ }
+
+ public Element toElement() {
+ Element encryptionElement = new Element(CONTAINERTAG, AxolotlService.PEP_PREFIX);
+ Element headerElement = encryptionElement.addChild(HEADER);
+ headerElement.setAttribute(SOURCEID, sourceDeviceId);
+ for (Map.Entry<Integer, byte[]> keyEntry : keys.entrySet()) {
+ Element keyElement = new Element(KEYTAG);
+ keyElement.setAttribute(REMOTEID, keyEntry.getKey());
+ keyElement.setContent(Base64.encodeToString(keyEntry.getValue(), Base64.NO_WRAP));
+ headerElement.addChild(keyElement);
+ }
+ headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.NO_WRAP));
+ if (ciphertext != null) {
+ Element payload = encryptionElement.addChild(PAYLOAD);
+ payload.setContent(Base64.encodeToString(ciphertext, Base64.NO_WRAP));
+ }
+ return encryptionElement;
+ }
+
+ private byte[] unpackKey(XmppAxolotlSession session, Integer sourceDeviceId) {
+ byte[] encryptedKey = keys.get(sourceDeviceId);
+ return (encryptedKey != null) ? session.processReceiving(encryptedKey) : null;
+ }
+
+ public XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) {
+ byte[] key = unpackKey(session, sourceDeviceId);
+ return (key != null)
+ ? new XmppAxolotlKeyTransportMessage(session.getFingerprint(), key, getIV())
+ : null;
+ }
+
+ public XmppAxolotlPlaintextMessage decrypt(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
+ XmppAxolotlPlaintextMessage plaintextMessage = null;
+ byte[] key = unpackKey(session, sourceDeviceId);
+ if (key != null) {
+ try {
+ Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
+ SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+
+ String plaintext = new String(cipher.doFinal(ciphertext));
+ plaintextMessage = new XmppAxolotlPlaintextMessage(plaintext, session.getFingerprint());
+
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+ | InvalidAlgorithmParameterException | IllegalBlockSizeException
+ | BadPaddingException | NoSuchProviderException e) {
+ throw new CryptoFailedException(e);
+ }
+ }
+ return plaintextMessage;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
index 566afef9a..27647fb66 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
@@ -23,113 +23,113 @@ import de.pixart.messenger.Config;
import de.pixart.messenger.entities.Account;
public class XmppAxolotlSession {
- private final SessionCipher cipher;
- private final SQLiteAxolotlStore sqLiteAxolotlStore;
- private final AxolotlAddress remoteAddress;
- private final Account account;
- private IdentityKey identityKey;
- private Integer preKeyId = null;
- private boolean fresh = true;
-
- public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, IdentityKey identityKey) {
- this(account, store, remoteAddress);
- this.identityKey = identityKey;
- }
-
- public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress) {
- this.cipher = new SessionCipher(store, remoteAddress);
- this.remoteAddress = remoteAddress;
- this.sqLiteAxolotlStore = store;
- this.account = account;
- }
-
- public Integer getPreKeyId() {
- return preKeyId;
- }
-
- public void resetPreKeyId() {
-
- preKeyId = null;
- }
-
- public String getFingerprint() {
- return identityKey == null ? null : identityKey.getFingerprint().replaceAll("\\s", "");
- }
-
- public IdentityKey getIdentityKey() {
- return identityKey;
- }
-
- public AxolotlAddress getRemoteAddress() {
- return remoteAddress;
- }
-
- public boolean isFresh() {
- return fresh;
- }
-
- public void setNotFresh() {
- this.fresh = false;
- }
-
- protected void setTrust(FingerprintStatus status) {
- sqLiteAxolotlStore.setFingerprintStatus(getFingerprint(), status);
- }
-
- public FingerprintStatus getTrust() {
- FingerprintStatus status = sqLiteAxolotlStore.getFingerprintStatus(getFingerprint());
- return (status == null) ? FingerprintStatus.createActiveUndecided() : status;
- }
-
- @Nullable
- public byte[] processReceiving(byte[] encryptedKey) {
- byte[] plaintext = null;
- FingerprintStatus status = getTrust();
- if (!status.isCompromised()) {
- try {
- try {
- PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
- if (!message.getPreKeyId().isPresent()) {
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId");
- return null;
- }
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
- IdentityKey msgIdentityKey = message.getIdentityKey();
- if (this.identityKey != null && !this.identityKey.equals(msgIdentityKey)) {
- Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.getFingerprint() + ", received message with fingerprint " + msgIdentityKey.getFingerprint());
- } else {
- this.identityKey = msgIdentityKey;
- plaintext = cipher.decrypt(message);
- preKeyId = message.getPreKeyId().get();
- }
- } catch (InvalidMessageException | InvalidVersionException e) {
- Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
- WhisperMessage message = new WhisperMessage(encryptedKey);
- plaintext = cipher.decrypt(message);
- } catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
- }
- } catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) {
- Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
- }
-
- if (plaintext != null) {
- if (!status.isActive()) {
- setTrust(status.toActive());
- }
- }
- }
- return plaintext;
- }
-
- @Nullable
- public byte[] processSending(@NonNull byte[] outgoingMessage) {
- FingerprintStatus status = getTrust();
- if (status.isTrustedAndActive()) {
- CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
- return ciphertextMessage.serialize();
- } else {
- return null;
- }
- }
+ private final SessionCipher cipher;
+ private final SQLiteAxolotlStore sqLiteAxolotlStore;
+ private final AxolotlAddress remoteAddress;
+ private final Account account;
+ private IdentityKey identityKey;
+ private Integer preKeyId = null;
+ private boolean fresh = true;
+
+ public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, IdentityKey identityKey) {
+ this(account, store, remoteAddress);
+ this.identityKey = identityKey;
+ }
+
+ public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress) {
+ this.cipher = new SessionCipher(store, remoteAddress);
+ this.remoteAddress = remoteAddress;
+ this.sqLiteAxolotlStore = store;
+ this.account = account;
+ }
+
+ public Integer getPreKeyId() {
+ return preKeyId;
+ }
+
+ public void resetPreKeyId() {
+
+ preKeyId = null;
+ }
+
+ public String getFingerprint() {
+ return identityKey == null ? null : identityKey.getFingerprint().replaceAll("\\s", "");
+ }
+
+ public IdentityKey getIdentityKey() {
+ return identityKey;
+ }
+
+ public AxolotlAddress getRemoteAddress() {
+ return remoteAddress;
+ }
+
+ public boolean isFresh() {
+ return fresh;
+ }
+
+ public void setNotFresh() {
+ this.fresh = false;
+ }
+
+ protected void setTrust(FingerprintStatus status) {
+ sqLiteAxolotlStore.setFingerprintStatus(getFingerprint(), status);
+ }
+
+ public FingerprintStatus getTrust() {
+ FingerprintStatus status = sqLiteAxolotlStore.getFingerprintStatus(getFingerprint());
+ return (status == null) ? FingerprintStatus.createActiveUndecided() : status;
+ }
+
+ @Nullable
+ public byte[] processReceiving(byte[] encryptedKey) {
+ byte[] plaintext = null;
+ FingerprintStatus status = getTrust();
+ if (!status.isCompromised()) {
+ try {
+ try {
+ PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
+ if (!message.getPreKeyId().isPresent()) {
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId");
+ return null;
+ }
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
+ IdentityKey msgIdentityKey = message.getIdentityKey();
+ if (this.identityKey != null && !this.identityKey.equals(msgIdentityKey)) {
+ Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.getFingerprint() + ", received message with fingerprint " + msgIdentityKey.getFingerprint());
+ } else {
+ this.identityKey = msgIdentityKey;
+ plaintext = cipher.decrypt(message);
+ preKeyId = message.getPreKeyId().get();
+ }
+ } catch (InvalidMessageException | InvalidVersionException e) {
+ Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
+ WhisperMessage message = new WhisperMessage(encryptedKey);
+ plaintext = cipher.decrypt(message);
+ } catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
+ }
+ } catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) {
+ Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
+ }
+
+ if (plaintext != null) {
+ if (!status.isActive()) {
+ setTrust(status.toActive());
+ }
+ }
+ }
+ return plaintext;
+ }
+
+ @Nullable
+ public byte[] processSending(@NonNull byte[] outgoingMessage) {
+ FingerprintStatus status = getTrust();
+ if (status.isTrustedAndActive()) {
+ CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
+ return ciphertextMessage.serialize();
+ } else {
+ return null;
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/Anonymous.java b/src/main/java/de/pixart/messenger/crypto/sasl/Anonymous.java
index 7e4e98bc6..93c3de64b 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/Anonymous.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/Anonymous.java
@@ -7,22 +7,22 @@ import de.pixart.messenger.xml.TagWriter;
public class Anonymous extends SaslMechanism {
- public Anonymous(TagWriter tagWriter, Account account, SecureRandom rng) {
- super(tagWriter, account, rng);
- }
+ public Anonymous(TagWriter tagWriter, Account account, SecureRandom rng) {
+ super(tagWriter, account, rng);
+ }
- @Override
- public int getPriority() {
- return 0;
- }
+ @Override
+ public int getPriority() {
+ return 0;
+ }
- @Override
- public String getMechanism() {
- return "ANONYMOUS";
- }
+ @Override
+ public String getMechanism() {
+ return "ANONYMOUS";
+ }
- @Override
- public String getClientFirstMessage() {
- return "";
- }
+ @Override
+ public String getClientFirstMessage() {
+ return "";
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java b/src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java
index 09ac4865a..e974d8cd4 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java
@@ -13,79 +13,79 @@ import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.xml.TagWriter;
public class DigestMd5 extends SaslMechanism {
- public DigestMd5(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
- super(tagWriter, account, rng);
- }
+ public DigestMd5(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
+ super(tagWriter, account, rng);
+ }
- @Override
- public int getPriority() {
- return 10;
- }
+ @Override
+ public int getPriority() {
+ return 10;
+ }
- @Override
- public String getMechanism() {
- return "DIGEST-MD5";
- }
+ @Override
+ public String getMechanism() {
+ return "DIGEST-MD5";
+ }
- private State state = State.INITIAL;
+ private State state = State.INITIAL;
- @Override
- public String getResponse(final String challenge) throws AuthenticationException {
- switch (state) {
- case INITIAL:
- state = State.RESPONSE_SENT;
- final String encodedResponse;
- try {
- final Tokenizer tokenizer = new Tokenizer(Base64.decode(challenge, Base64.DEFAULT));
- String nonce = "";
- for (final String token : tokenizer) {
- final String[] parts = token.split("=", 2);
- if (parts[0].equals("nonce")) {
- nonce = parts[1].replace("\"", "");
- } else if (parts[0].equals("rspauth")) {
- return "";
- }
- }
- final String digestUri = "xmpp/" + account.getServer();
- final String nonceCount = "00000001";
- final String x = account.getUsername() + ":" + account.getServer() + ":"
- + account.getPassword();
- final MessageDigest md = MessageDigest.getInstance("MD5");
- final byte[] y = md.digest(x.getBytes(Charset.defaultCharset()));
- final String cNonce = new BigInteger(100, rng).toString(32);
- final byte[] a1 = CryptoHelper.concatenateByteArrays(y,
- (":" + nonce + ":" + cNonce).getBytes(Charset.defaultCharset()));
- final String a2 = "AUTHENTICATE:" + digestUri;
- final String ha1 = CryptoHelper.bytesToHex(md.digest(a1));
- final String ha2 = CryptoHelper.bytesToHex(md.digest(a2.getBytes(Charset
- .defaultCharset())));
- final String kd = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce
- + ":auth:" + ha2;
- final String response = CryptoHelper.bytesToHex(md.digest(kd.getBytes(Charset
- .defaultCharset())));
- final String saslString = "username=\"" + account.getUsername()
- + "\",realm=\"" + account.getServer() + "\",nonce=\""
- + nonce + "\",cnonce=\"" + cNonce + "\",nc=" + nonceCount
- + ",qop=auth,digest-uri=\"" + digestUri + "\",response="
- + response + ",charset=utf-8";
- encodedResponse = Base64.encodeToString(
- saslString.getBytes(Charset.defaultCharset()),
- Base64.NO_WRAP);
- } catch (final NoSuchAlgorithmException e) {
- throw new AuthenticationException(e);
- }
+ @Override
+ public String getResponse(final String challenge) throws AuthenticationException {
+ switch (state) {
+ case INITIAL:
+ state = State.RESPONSE_SENT;
+ final String encodedResponse;
+ try {
+ final Tokenizer tokenizer = new Tokenizer(Base64.decode(challenge, Base64.DEFAULT));
+ String nonce = "";
+ for (final String token : tokenizer) {
+ final String[] parts = token.split("=", 2);
+ if (parts[0].equals("nonce")) {
+ nonce = parts[1].replace("\"", "");
+ } else if (parts[0].equals("rspauth")) {
+ return "";
+ }
+ }
+ final String digestUri = "xmpp/" + account.getServer();
+ final String nonceCount = "00000001";
+ final String x = account.getUsername() + ":" + account.getServer() + ":"
+ + account.getPassword();
+ final MessageDigest md = MessageDigest.getInstance("MD5");
+ final byte[] y = md.digest(x.getBytes(Charset.defaultCharset()));
+ final String cNonce = new BigInteger(100, rng).toString(32);
+ final byte[] a1 = CryptoHelper.concatenateByteArrays(y,
+ (":" + nonce + ":" + cNonce).getBytes(Charset.defaultCharset()));
+ final String a2 = "AUTHENTICATE:" + digestUri;
+ final String ha1 = CryptoHelper.bytesToHex(md.digest(a1));
+ final String ha2 = CryptoHelper.bytesToHex(md.digest(a2.getBytes(Charset
+ .defaultCharset())));
+ final String kd = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce
+ + ":auth:" + ha2;
+ final String response = CryptoHelper.bytesToHex(md.digest(kd.getBytes(Charset
+ .defaultCharset())));
+ final String saslString = "username=\"" + account.getUsername()
+ + "\",realm=\"" + account.getServer() + "\",nonce=\""
+ + nonce + "\",cnonce=\"" + cNonce + "\",nc=" + nonceCount
+ + ",qop=auth,digest-uri=\"" + digestUri + "\",response="
+ + response + ",charset=utf-8";
+ encodedResponse = Base64.encodeToString(
+ saslString.getBytes(Charset.defaultCharset()),
+ Base64.NO_WRAP);
+ } catch (final NoSuchAlgorithmException e) {
+ throw new AuthenticationException(e);
+ }
- return encodedResponse;
- case RESPONSE_SENT:
- state = State.VALID_SERVER_RESPONSE;
- break;
- case VALID_SERVER_RESPONSE:
- if (challenge==null) {
- return null; //everything is fine
- }
- default:
- throw new InvalidStateException(state);
- }
- return null;
- }
+ return encodedResponse;
+ case RESPONSE_SENT:
+ state = State.VALID_SERVER_RESPONSE;
+ break;
+ case VALID_SERVER_RESPONSE:
+ if (challenge == null) {
+ return null; //everything is fine
+ }
+ default:
+ throw new InvalidStateException(state);
+ }
+ return null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/External.java b/src/main/java/de/pixart/messenger/crypto/sasl/External.java
index a1a79a0a8..b80d174f0 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/External.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/External.java
@@ -9,22 +9,22 @@ import de.pixart.messenger.xml.TagWriter;
public class External extends SaslMechanism {
- public External(TagWriter tagWriter, Account account, SecureRandom rng) {
- super(tagWriter, account, rng);
- }
-
- @Override
- public int getPriority() {
- return 25;
- }
-
- @Override
- public String getMechanism() {
- return "EXTERNAL";
- }
-
- @Override
- public String getClientFirstMessage() {
- return Base64.encodeToString(account.getJid().toBareJid().toString().getBytes(),Base64.NO_WRAP);
- }
+ public External(TagWriter tagWriter, Account account, SecureRandom rng) {
+ super(tagWriter, account, rng);
+ }
+
+ @Override
+ public int getPriority() {
+ return 25;
+ }
+
+ @Override
+ public String getMechanism() {
+ return "EXTERNAL";
+ }
+
+ @Override
+ public String getClientFirstMessage() {
+ return Base64.encodeToString(account.getJid().toBareJid().toString().getBytes(), Base64.NO_WRAP);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/Plain.java b/src/main/java/de/pixart/messenger/crypto/sasl/Plain.java
index d0aa90b49..27de45247 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/Plain.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/Plain.java
@@ -8,23 +8,23 @@ import de.pixart.messenger.entities.Account;
import de.pixart.messenger.xml.TagWriter;
public class Plain extends SaslMechanism {
- public Plain(final TagWriter tagWriter, final Account account) {
- super(tagWriter, account, null);
- }
+ public Plain(final TagWriter tagWriter, final Account account) {
+ super(tagWriter, account, null);
+ }
- @Override
- public int getPriority() {
- return 10;
- }
+ @Override
+ public int getPriority() {
+ return 10;
+ }
- @Override
- public String getMechanism() {
- return "PLAIN";
- }
+ @Override
+ public String getMechanism() {
+ return "PLAIN";
+ }
- @Override
- public String getClientFirstMessage() {
- final String sasl = '\u0000' + account.getUsername() + '\u0000' + account.getPassword();
- return Base64.encodeToString(sasl.getBytes(Charset.defaultCharset()), Base64.NO_WRAP);
- }
+ @Override
+ public String getClientFirstMessage() {
+ final String sasl = '\u0000' + account.getUsername() + '\u0000' + account.getPassword();
+ return Base64.encodeToString(sasl.getBytes(Charset.defaultCharset()), Base64.NO_WRAP);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/SaslMechanism.java b/src/main/java/de/pixart/messenger/crypto/sasl/SaslMechanism.java
index 19e8f3591..8b8883b9f 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/SaslMechanism.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/SaslMechanism.java
@@ -7,56 +7,59 @@ import de.pixart.messenger.xml.TagWriter;
public abstract class SaslMechanism {
- final protected TagWriter tagWriter;
- final protected Account account;
- final protected SecureRandom rng;
-
- protected enum State {
- INITIAL,
- AUTH_TEXT_SENT,
- RESPONSE_SENT,
- VALID_SERVER_RESPONSE,
- }
-
- public static class AuthenticationException extends Exception {
- public AuthenticationException(final String message) {
- super(message);
- }
-
- public AuthenticationException(final Exception inner) {
- super(inner);
- }
- }
-
- public static class InvalidStateException extends AuthenticationException {
- public InvalidStateException(final String message) {
- super(message);
- }
-
- public InvalidStateException(final State state) {
- this("Invalid state: " + state.toString());
- }
- }
-
- public SaslMechanism(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
- this.tagWriter = tagWriter;
- this.account = account;
- this.rng = rng;
- }
-
- /**
- * The priority is used to pin the authentication mechanism. If authentication fails, it MAY be retried with another
- * mechanism of the same priority, but MUST NOT be tried with a mechanism of lower priority (to prevent downgrade
- * attacks).
- * @return An arbitrary int representing the priority
- */
- public abstract int getPriority();
-
- public abstract String getMechanism();
- public String getClientFirstMessage() {
- return "";
- }
- public String getResponse(final String challenge) throws AuthenticationException {
- return "";
- }
+ final protected TagWriter tagWriter;
+ final protected Account account;
+ final protected SecureRandom rng;
+
+ protected enum State {
+ INITIAL,
+ AUTH_TEXT_SENT,
+ RESPONSE_SENT,
+ VALID_SERVER_RESPONSE,
+ }
+
+ public static class AuthenticationException extends Exception {
+ public AuthenticationException(final String message) {
+ super(message);
+ }
+
+ public AuthenticationException(final Exception inner) {
+ super(inner);
+ }
+ }
+
+ public static class InvalidStateException extends AuthenticationException {
+ public InvalidStateException(final String message) {
+ super(message);
+ }
+
+ public InvalidStateException(final State state) {
+ this("Invalid state: " + state.toString());
+ }
+ }
+
+ public SaslMechanism(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
+ this.tagWriter = tagWriter;
+ this.account = account;
+ this.rng = rng;
+ }
+
+ /**
+ * The priority is used to pin the authentication mechanism. If authentication fails, it MAY be retried with another
+ * mechanism of the same priority, but MUST NOT be tried with a mechanism of lower priority (to prevent downgrade
+ * attacks).
+ *
+ * @return An arbitrary int representing the priority
+ */
+ public abstract int getPriority();
+
+ public abstract String getMechanism();
+
+ public String getClientFirstMessage() {
+ return "";
+ }
+
+ public String getResponse(final String challenge) throws AuthenticationException {
+ return "";
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/ScramSha1.java b/src/main/java/de/pixart/messenger/crypto/sasl/ScramSha1.java
index abff542d4..d21cad37d 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/ScramSha1.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/ScramSha1.java
@@ -18,221 +18,222 @@ import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.xml.TagWriter;
public class ScramSha1 extends SaslMechanism {
- // TODO: When channel binding (SCRAM-SHA1-PLUS) is supported in future, generalize this to indicate support and/or usage.
- final private static String GS2_HEADER = "n,,";
- private String clientFirstMessageBare;
- final private String clientNonce;
- private byte[] serverSignature = null;
- private static HMac HMAC;
- private static Digest DIGEST;
- private static final byte[] CLIENT_KEY_BYTES = "Client Key".getBytes();
- private static final byte[] SERVER_KEY_BYTES = "Server Key".getBytes();
-
- public static class KeyPair {
- final public byte[] clientKey;
- final public byte[] serverKey;
-
- public KeyPair(final byte[] clientKey, final byte[] serverKey) {
- this.clientKey = clientKey;
- this.serverKey = serverKey;
- }
- }
-
- private static final LruCache<String, KeyPair> CACHE;
-
- static {
- DIGEST = new SHA1Digest();
- HMAC = new HMac(new SHA1Digest());
- CACHE = new LruCache<String, KeyPair>(10) {
- protected KeyPair create(final String k) {
- // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
- // Changing any of these values forces a cache miss. `CryptoHelper.bytesToHex()'
- // is applied to prevent commas in the strings breaking things.
- final String[] kparts = k.split(",", 4);
- try {
- final byte[] saltedPassword, serverKey, clientKey;
- saltedPassword = hi(CryptoHelper.hexToString(kparts[1]).getBytes(),
- Base64.decode(CryptoHelper.hexToString(kparts[2]), Base64.DEFAULT), Integer.valueOf(kparts[3]));
- serverKey = hmac(saltedPassword, SERVER_KEY_BYTES);
- clientKey = hmac(saltedPassword, CLIENT_KEY_BYTES);
-
- return new KeyPair(clientKey, serverKey);
- } catch (final InvalidKeyException | NumberFormatException e) {
- return null;
- }
- }
- };
- }
-
- private State state = State.INITIAL;
-
- public ScramSha1(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
- super(tagWriter, account, rng);
-
- // This nonce should be different for each authentication attempt.
- clientNonce = new BigInteger(100, this.rng).toString(32);
- clientFirstMessageBare = "";
- }
-
- @Override
- public int getPriority() {
- return 20;
- }
-
- @Override
- public String getMechanism() {
- return "SCRAM-SHA-1";
- }
-
- @Override
- public String getClientFirstMessage() {
- if (clientFirstMessageBare.isEmpty() && state == State.INITIAL) {
- clientFirstMessageBare = "n=" + CryptoHelper.saslEscape(CryptoHelper.saslPrep(account.getUsername())) +
- ",r=" + this.clientNonce;
- state = State.AUTH_TEXT_SENT;
- }
- return Base64.encodeToString(
- (GS2_HEADER + clientFirstMessageBare).getBytes(Charset.defaultCharset()),
- Base64.NO_WRAP);
- }
-
- @Override
- public String getResponse(final String challenge) throws AuthenticationException {
- switch (state) {
- case AUTH_TEXT_SENT:
- if (challenge == null) {
- throw new AuthenticationException("challenge can not be null");
- }
- byte[] serverFirstMessage = Base64.decode(challenge, Base64.DEFAULT);
- final Tokenizer tokenizer = new Tokenizer(serverFirstMessage);
- String nonce = "";
- int iterationCount = -1;
- String salt = "";
- for (final String token : tokenizer) {
- if (token.charAt(1) == '=') {
- switch (token.charAt(0)) {
- case 'i':
- try {
- iterationCount = Integer.parseInt(token.substring(2));
- } catch (final NumberFormatException e) {
- throw new AuthenticationException(e);
- }
- break;
- case 's':
- salt = token.substring(2);
- break;
- case 'r':
- nonce = token.substring(2);
- break;
- case 'm':
- /*
+ // TODO: When channel binding (SCRAM-SHA1-PLUS) is supported in future, generalize this to indicate support and/or usage.
+ final private static String GS2_HEADER = "n,,";
+ private String clientFirstMessageBare;
+ final private String clientNonce;
+ private byte[] serverSignature = null;
+ private static HMac HMAC;
+ private static Digest DIGEST;
+ private static final byte[] CLIENT_KEY_BYTES = "Client Key".getBytes();
+ private static final byte[] SERVER_KEY_BYTES = "Server Key".getBytes();
+
+ public static class KeyPair {
+ final public byte[] clientKey;
+ final public byte[] serverKey;
+
+ public KeyPair(final byte[] clientKey, final byte[] serverKey) {
+ this.clientKey = clientKey;
+ this.serverKey = serverKey;
+ }
+ }
+
+ private static final LruCache<String, KeyPair> CACHE;
+
+ static {
+ DIGEST = new SHA1Digest();
+ HMAC = new HMac(new SHA1Digest());
+ CACHE = new LruCache<String, KeyPair>(10) {
+ protected KeyPair create(final String k) {
+ // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
+ // Changing any of these values forces a cache miss. `CryptoHelper.bytesToHex()'
+ // is applied to prevent commas in the strings breaking things.
+ final String[] kparts = k.split(",", 4);
+ try {
+ final byte[] saltedPassword, serverKey, clientKey;
+ saltedPassword = hi(CryptoHelper.hexToString(kparts[1]).getBytes(),
+ Base64.decode(CryptoHelper.hexToString(kparts[2]), Base64.DEFAULT), Integer.valueOf(kparts[3]));
+ serverKey = hmac(saltedPassword, SERVER_KEY_BYTES);
+ clientKey = hmac(saltedPassword, CLIENT_KEY_BYTES);
+
+ return new KeyPair(clientKey, serverKey);
+ } catch (final InvalidKeyException | NumberFormatException e) {
+ return null;
+ }
+ }
+ };
+ }
+
+ private State state = State.INITIAL;
+
+ public ScramSha1(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
+ super(tagWriter, account, rng);
+
+ // This nonce should be different for each authentication attempt.
+ clientNonce = new BigInteger(100, this.rng).toString(32);
+ clientFirstMessageBare = "";
+ }
+
+ @Override
+ public int getPriority() {
+ return 20;
+ }
+
+ @Override
+ public String getMechanism() {
+ return "SCRAM-SHA-1";
+ }
+
+ @Override
+ public String getClientFirstMessage() {
+ if (clientFirstMessageBare.isEmpty() && state == State.INITIAL) {
+ clientFirstMessageBare = "n=" + CryptoHelper.saslEscape(CryptoHelper.saslPrep(account.getUsername())) +
+ ",r=" + this.clientNonce;
+ state = State.AUTH_TEXT_SENT;
+ }
+ return Base64.encodeToString(
+ (GS2_HEADER + clientFirstMessageBare).getBytes(Charset.defaultCharset()),
+ Base64.NO_WRAP);
+ }
+
+ @Override
+ public String getResponse(final String challenge) throws AuthenticationException {
+ switch (state) {
+ case AUTH_TEXT_SENT:
+ if (challenge == null) {
+ throw new AuthenticationException("challenge can not be null");
+ }
+ byte[] serverFirstMessage = Base64.decode(challenge, Base64.DEFAULT);
+ final Tokenizer tokenizer = new Tokenizer(serverFirstMessage);
+ String nonce = "";
+ int iterationCount = -1;
+ String salt = "";
+ for (final String token : tokenizer) {
+ if (token.charAt(1) == '=') {
+ switch (token.charAt(0)) {
+ case 'i':
+ try {
+ iterationCount = Integer.parseInt(token.substring(2));
+ } catch (final NumberFormatException e) {
+ throw new AuthenticationException(e);
+ }
+ break;
+ case 's':
+ salt = token.substring(2);
+ break;
+ case 'r':
+ nonce = token.substring(2);
+ break;
+ case 'm':
+ /*
* RFC 5802:
* m: This attribute is reserved for future extensibility. In this
* version of SCRAM, its presence in a client or a server message
* MUST cause authentication failure when the attribute is parsed by
* the other end.
*/
- throw new AuthenticationException("Server sent reserved token: `m'");
- }
- }
- }
-
- if (iterationCount < 0) {
- throw new AuthenticationException("Server did not send iteration count");
- }
- if (nonce.isEmpty() || !nonce.startsWith(clientNonce)) {
- throw new AuthenticationException("Server nonce does not contain client nonce: " + nonce);
- }
- if (salt.isEmpty()) {
- throw new AuthenticationException("Server sent empty salt");
- }
-
- final String clientFinalMessageWithoutProof = "c=" + Base64.encodeToString(
- GS2_HEADER.getBytes(), Base64.NO_WRAP) + ",r=" + nonce;
- final byte[] authMessage = (clientFirstMessageBare + ',' + new String(serverFirstMessage) + ','
- + clientFinalMessageWithoutProof).getBytes();
-
- // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
- final KeyPair keys = CACHE.get(
- CryptoHelper.bytesToHex(account.getJid().toBareJid().toString().getBytes()) + ","
- + CryptoHelper.bytesToHex(account.getPassword().getBytes()) + ","
- + CryptoHelper.bytesToHex(salt.getBytes()) + ","
- + String.valueOf(iterationCount)
- );
- if (keys == null) {
- throw new AuthenticationException("Invalid keys generated");
- }
- final byte[] clientSignature;
- try {
- serverSignature = hmac(keys.serverKey, authMessage);
- final byte[] storedKey = digest(keys.clientKey);
-
- clientSignature = hmac(storedKey, authMessage);
-
- } catch (final InvalidKeyException e) {
- throw new AuthenticationException(e);
- }
-
- final byte[] clientProof = new byte[keys.clientKey.length];
-
- for (int i = 0; i < clientProof.length; i++) {
- clientProof[i] = (byte) (keys.clientKey[i] ^ clientSignature[i]);
- }
-
-
- final String clientFinalMessage = clientFinalMessageWithoutProof + ",p=" +
- Base64.encodeToString(clientProof, Base64.NO_WRAP);
- state = State.RESPONSE_SENT;
- return Base64.encodeToString(clientFinalMessage.getBytes(), Base64.NO_WRAP);
- case RESPONSE_SENT:
- try {
- final String clientCalculatedServerFinalMessage = "v=" +
- Base64.encodeToString(serverSignature, Base64.NO_WRAP);
- if (!clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) {
- throw new Exception();
- };
- state = State.VALID_SERVER_RESPONSE;
- return "";
- } catch(Exception e) {
- throw new AuthenticationException("Server final message does not match calculated final message");
- }
- default:
- throw new InvalidStateException(state);
- }
- }
-
- public static synchronized byte[] hmac(final byte[] key, final byte[] input)
- throws InvalidKeyException {
- HMAC.init(new KeyParameter(key));
- HMAC.update(input, 0, input.length);
- final byte[] out = new byte[HMAC.getMacSize()];
- HMAC.doFinal(out, 0);
- return out;
- }
-
- public static synchronized byte[] digest(byte[] bytes) {
- DIGEST.reset();
- DIGEST.update(bytes, 0, bytes.length);
- final byte[] out = new byte[DIGEST.getDigestSize()];
- DIGEST.doFinal(out, 0);
- return out;
- }
-
- /*
- * Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the
- * pseudorandom function (PRF) and with dkLen == output length of
- * HMAC() == output length of H().
- */
- private static synchronized byte[] hi(final byte[] key, final byte[] salt, final int iterations)
- throws InvalidKeyException {
- byte[] u = hmac(key, CryptoHelper.concatenateByteArrays(salt, CryptoHelper.ONE));
- byte[] out = u.clone();
- for (int i = 1; i < iterations; i++) {
- u = hmac(key, u);
- for (int j = 0; j < u.length; j++) {
- out[j] ^= u[j];
- }
- }
- return out;
- }
+ throw new AuthenticationException("Server sent reserved token: `m'");
+ }
+ }
+ }
+
+ if (iterationCount < 0) {
+ throw new AuthenticationException("Server did not send iteration count");
+ }
+ if (nonce.isEmpty() || !nonce.startsWith(clientNonce)) {
+ throw new AuthenticationException("Server nonce does not contain client nonce: " + nonce);
+ }
+ if (salt.isEmpty()) {
+ throw new AuthenticationException("Server sent empty salt");
+ }
+
+ final String clientFinalMessageWithoutProof = "c=" + Base64.encodeToString(
+ GS2_HEADER.getBytes(), Base64.NO_WRAP) + ",r=" + nonce;
+ final byte[] authMessage = (clientFirstMessageBare + ',' + new String(serverFirstMessage) + ','
+ + clientFinalMessageWithoutProof).getBytes();
+
+ // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
+ final KeyPair keys = CACHE.get(
+ CryptoHelper.bytesToHex(account.getJid().toBareJid().toString().getBytes()) + ","
+ + CryptoHelper.bytesToHex(account.getPassword().getBytes()) + ","
+ + CryptoHelper.bytesToHex(salt.getBytes()) + ","
+ + String.valueOf(iterationCount)
+ );
+ if (keys == null) {
+ throw new AuthenticationException("Invalid keys generated");
+ }
+ final byte[] clientSignature;
+ try {
+ serverSignature = hmac(keys.serverKey, authMessage);
+ final byte[] storedKey = digest(keys.clientKey);
+
+ clientSignature = hmac(storedKey, authMessage);
+
+ } catch (final InvalidKeyException e) {
+ throw new AuthenticationException(e);
+ }
+
+ final byte[] clientProof = new byte[keys.clientKey.length];
+
+ for (int i = 0; i < clientProof.length; i++) {
+ clientProof[i] = (byte) (keys.clientKey[i] ^ clientSignature[i]);
+ }
+
+
+ final String clientFinalMessage = clientFinalMessageWithoutProof + ",p=" +
+ Base64.encodeToString(clientProof, Base64.NO_WRAP);
+ state = State.RESPONSE_SENT;
+ return Base64.encodeToString(clientFinalMessage.getBytes(), Base64.NO_WRAP);
+ case RESPONSE_SENT:
+ try {
+ final String clientCalculatedServerFinalMessage = "v=" +
+ Base64.encodeToString(serverSignature, Base64.NO_WRAP);
+ if (!clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) {
+ throw new Exception();
+ }
+ ;
+ state = State.VALID_SERVER_RESPONSE;
+ return "";
+ } catch (Exception e) {
+ throw new AuthenticationException("Server final message does not match calculated final message");
+ }
+ default:
+ throw new InvalidStateException(state);
+ }
+ }
+
+ public static synchronized byte[] hmac(final byte[] key, final byte[] input)
+ throws InvalidKeyException {
+ HMAC.init(new KeyParameter(key));
+ HMAC.update(input, 0, input.length);
+ final byte[] out = new byte[HMAC.getMacSize()];
+ HMAC.doFinal(out, 0);
+ return out;
+ }
+
+ public static synchronized byte[] digest(byte[] bytes) {
+ DIGEST.reset();
+ DIGEST.update(bytes, 0, bytes.length);
+ final byte[] out = new byte[DIGEST.getDigestSize()];
+ DIGEST.doFinal(out, 0);
+ return out;
+ }
+
+ /*
+ * Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the
+ * pseudorandom function (PRF) and with dkLen == output length of
+ * HMAC() == output length of H().
+ */
+ private static synchronized byte[] hi(final byte[] key, final byte[] salt, final int iterations)
+ throws InvalidKeyException {
+ byte[] u = hmac(key, CryptoHelper.concatenateByteArrays(salt, CryptoHelper.ONE));
+ byte[] out = u.clone();
+ for (int i = 1; i < iterations; i++) {
+ u = hmac(key, u);
+ for (int j = 0; j < u.length; j++) {
+ out[j] ^= u[j];
+ }
+ }
+ return out;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/Tokenizer.java b/src/main/java/de/pixart/messenger/crypto/sasl/Tokenizer.java
index 01cd07929..67e4418dd 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/Tokenizer.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/Tokenizer.java
@@ -10,69 +10,69 @@ import java.util.NoSuchElementException;
* A tokenizer for GS2 header strings
*/
public final class Tokenizer implements Iterator<String>, Iterable<String> {
- private final List<String> parts;
- private int index;
+ private final List<String> parts;
+ private int index;
- public Tokenizer(final byte[] challenge) {
- final String challengeString = new String(challenge);
- parts = new ArrayList<>(Arrays.asList(challengeString.split(",")));
- // Trim parts.
- for (int i = 0; i < parts.size(); i++) {
- parts.set(i, parts.get(i).trim());
- }
- index = 0;
- }
+ public Tokenizer(final byte[] challenge) {
+ final String challengeString = new String(challenge);
+ parts = new ArrayList<>(Arrays.asList(challengeString.split(",")));
+ // Trim parts.
+ for (int i = 0; i < parts.size(); i++) {
+ parts.set(i, parts.get(i).trim());
+ }
+ index = 0;
+ }
- /**
- * Returns true if there is at least one more element, false otherwise.
- *
- * @see #next
- */
- @Override
- public boolean hasNext() {
- return parts.size() != index + 1;
- }
+ /**
+ * Returns true if there is at least one more element, false otherwise.
+ *
+ * @see #next
+ */
+ @Override
+ public boolean hasNext() {
+ return parts.size() != index + 1;
+ }
- /**
- * Returns the next object and advances the iterator.
- *
- * @return the next object.
- * @throws java.util.NoSuchElementException if there are no more elements.
- * @see #hasNext
- */
- @Override
- public String next() {
- if (hasNext()) {
- return parts.get(index++);
- } else {
- throw new NoSuchElementException("No such element. Size is: " + parts.size());
- }
- }
+ /**
+ * Returns the next object and advances the iterator.
+ *
+ * @return the next object.
+ * @throws java.util.NoSuchElementException if there are no more elements.
+ * @see #hasNext
+ */
+ @Override
+ public String next() {
+ if (hasNext()) {
+ return parts.get(index++);
+ } else {
+ throw new NoSuchElementException("No such element. Size is: " + parts.size());
+ }
+ }
- /**
- * Removes the last object returned by {@code next} from the collection.
- * This method can only be called once between each call to {@code next}.
- *
- * @throws UnsupportedOperationException if removing is not supported by the collection being
- * iterated.
- * @throws IllegalStateException if {@code next} has not been called, or {@code remove} has
- * already been called after the last call to {@code next}.
- */
- @Override
- public void remove() {
- if(index <= 0) {
- throw new IllegalStateException("You can't delete an element before first next() method call");
- }
- parts.remove(--index);
- }
+ /**
+ * Removes the last object returned by {@code next} from the collection.
+ * This method can only be called once between each call to {@code next}.
+ *
+ * @throws UnsupportedOperationException if removing is not supported by the collection being
+ * iterated.
+ * @throws IllegalStateException if {@code next} has not been called, or {@code remove} has
+ * already been called after the last call to {@code next}.
+ */
+ @Override
+ public void remove() {
+ if (index <= 0) {
+ throw new IllegalStateException("You can't delete an element before first next() method call");
+ }
+ parts.remove(--index);
+ }
- /**
- * Returns an {@link java.util.Iterator} for the elements in this object.
- *
- * @return An {@code Iterator} instance.
- */
- @Override
- public Iterator<String> iterator() {
- return parts.iterator();
- }
+ /**
+ * Returns an {@link java.util.Iterator} for the elements in this object.
+ *
+ * @return An {@code Iterator} instance.
+ */
+ @Override
+ public Iterator<String> iterator() {
+ return parts.iterator();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/AbstractEntity.java b/src/main/java/de/pixart/messenger/entities/AbstractEntity.java
index 1194cb819..2cac3405e 100644
--- a/src/main/java/de/pixart/messenger/entities/AbstractEntity.java
+++ b/src/main/java/de/pixart/messenger/entities/AbstractEntity.java
@@ -4,17 +4,17 @@ import android.content.ContentValues;
public abstract class AbstractEntity {
- public static final String UUID = "uuid";
+ public static final String UUID = "uuid";
- protected String uuid;
+ protected String uuid;
- public String getUuid() {
- return this.uuid;
- }
+ public String getUuid() {
+ return this.uuid;
+ }
- public abstract ContentValues getContentValues();
+ public abstract ContentValues getContentValues();
- public boolean equals(AbstractEntity entity) {
- return this.getUuid().equals(entity.getUuid());
- }
+ public boolean equals(AbstractEntity entity) {
+ return this.getUuid().equals(entity.getUuid());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Account.java b/src/main/java/de/pixart/messenger/entities/Account.java
index 7a94750ab..1b349e526 100644
--- a/src/main/java/de/pixart/messenger/entities/Account.java
+++ b/src/main/java/de/pixart/messenger/entities/Account.java
@@ -33,633 +33,633 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class Account extends AbstractEntity {
- public static final String TABLENAME = "accounts";
-
- public static final String USERNAME = "username";
- public static final String SERVER = "server";
- public static final String PASSWORD = "password";
- public static final String OPTIONS = "options";
- public static final String ROSTERVERSION = "rosterversion";
- public static final String KEYS = "keys";
- public static final String AVATAR = "avatar";
- public static final String DISPLAY_NAME = "display_name";
- public static final String HOSTNAME = "hostname";
- public static final String PORT = "port";
- public static final String STATUS = "status";
- public static final String STATUS_MESSAGE = "status_message";
-
- public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
-
- public static final int OPTION_USETLS = 0;
- public static final int OPTION_DISABLED = 1;
- public static final int OPTION_REGISTER = 2;
- public static final int OPTION_USECOMPRESSION = 3;
- public static final int OPTION_MAGIC_CREATE = 4;
- public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>();
-
- public boolean httpUploadAvailable(long filesize) {
- return xmppConnection != null && xmppConnection.getFeatures().httpUpload(filesize);
- }
-
- public boolean httpUploadAvailable() {
- return httpUploadAvailable(0);
- }
-
- public void setDisplayName(String displayName) {
- this.displayName = displayName;
- }
-
- public String getDisplayName() {
- return displayName;
- }
-
- public XmppConnection.Identity getServerIdentity() {
- if (xmppConnection == null) {
- return XmppConnection.Identity.UNKNOWN;
- } else {
- return xmppConnection.getServerIdentity();
- }
- }
-
- public Contact getSelfContact() {
- return getRoster().getContact(jid);
- }
-
- public boolean hasPendingPgpIntent(Conversation conversation) {
- return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
- }
-
- public boolean isPgpDecryptionServiceConnected() {
- return pgpDecryptionService != null && pgpDecryptionService.isConnected();
- }
-
- public boolean setShowErrorNotification(boolean newValue) {
- boolean oldValue = showErrorNotification();
- setKey("show_error",Boolean.toString(newValue));
- return newValue != oldValue;
- }
-
- public boolean showErrorNotification() {
- String key = getKey("show_error");
- return key == null || Boolean.parseBoolean(key);
- }
-
- public enum State {
- DISABLED,
- OFFLINE,
- CONNECTING,
- ONLINE,
- NO_INTERNET,
- UNAUTHORIZED(true),
- SERVER_NOT_FOUND(true),
- REGISTRATION_FAILED(true),
- REGISTRATION_CONFLICT(true),
- REGISTRATION_SUCCESSFUL,
- REGISTRATION_NOT_SUPPORTED(true),
- SECURITY_ERROR(true),
- INCOMPATIBLE_SERVER(true),
- TOR_NOT_AVAILABLE(true),
- BIND_FAILURE(true),
- HOST_UNKNOWN(true),
- REGISTRATION_PLEASE_WAIT(true),
- STREAM_ERROR(true),
- POLICY_VIOLATION(true),
- REGISTRATION_PASSWORD_TOO_WEAK(true),
- PAYMENT_REQUIRED(true),
- MISSING_INTERNET_PERMISSION(true);
-
- private final boolean isError;
-
- public boolean isError() {
- return this.isError;
- }
-
- State(final boolean isError) {
- this.isError = isError;
- }
-
- State() {
- this(false);
- }
-
- public int getReadableId() {
- switch (this) {
- case DISABLED:
- return R.string.account_status_disabled;
- case ONLINE:
- return R.string.account_status_online;
- case CONNECTING:
- return R.string.account_status_connecting;
- case OFFLINE:
- return R.string.account_status_offline;
- case UNAUTHORIZED:
- return R.string.account_status_unauthorized;
- case SERVER_NOT_FOUND:
- return R.string.account_status_not_found;
- case NO_INTERNET:
- return R.string.account_status_no_internet;
- case REGISTRATION_FAILED:
- return R.string.account_status_regis_fail;
- case REGISTRATION_CONFLICT:
- return R.string.account_status_regis_conflict;
- case REGISTRATION_SUCCESSFUL:
- return R.string.account_status_regis_success;
- case REGISTRATION_NOT_SUPPORTED:
- return R.string.account_status_regis_not_sup;
- case SECURITY_ERROR:
- return R.string.account_status_security_error;
- case INCOMPATIBLE_SERVER:
- return R.string.account_status_incompatible_server;
- case TOR_NOT_AVAILABLE:
- return R.string.account_status_tor_unavailable;
- case BIND_FAILURE:
- return R.string.account_status_bind_failure;
- case HOST_UNKNOWN:
- return R.string.account_status_host_unknown;
- case POLICY_VIOLATION:
- return R.string.account_status_policy_violation;
- case REGISTRATION_PLEASE_WAIT:
- return R.string.registration_please_wait;
- case REGISTRATION_PASSWORD_TOO_WEAK:
- return R.string.registration_password_too_weak;
- case STREAM_ERROR:
- return R.string.account_status_stream_error;
- case PAYMENT_REQUIRED:
- return R.string.payment_required;
- case MISSING_INTERNET_PERMISSION:
- return R.string.missing_internet_permission;
- default:
- return R.string.account_status_unknown;
- }
- }
- }
-
- public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<>();
- public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<>();
-
- private static final String KEY_PGP_SIGNATURE = "pgp_signature";
- private static final String KEY_PGP_ID = "pgp_id";
-
- protected Jid jid;
- protected String password;
- protected int options = 0;
- protected String rosterVersion;
- protected State status = State.OFFLINE;
- protected final JSONObject keys;
- protected String avatar;
- protected String displayName = null;
- protected String hostname = null;
- protected int port = 5222;
- protected boolean online = false;
- private OtrService mOtrService = null;
- private AxolotlService axolotlService = null;
- private PgpDecryptionService pgpDecryptionService = null;
- private XmppConnection xmppConnection = null;
- private long mEndGracePeriod = 0L;
- private String otrFingerprint;
- private final Roster roster = new Roster(this);
- private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
- private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
- private Presence.Status presenceStatus = Presence.Status.ONLINE;
- private String presenceStatusMessage = null;
-
- public Account(final Jid jid, final String password) {
- this(java.util.UUID.randomUUID().toString(), jid,
- password, 0, null, "", null, null, null, 5222, Presence.Status.ONLINE, null);
- }
-
- private Account(final String uuid, final Jid jid,
- final String password, final int options, final String rosterVersion, final String keys,
- final String avatar, String displayName, String hostname, int port,
- final Presence.Status status, String statusMessage) {
- this.uuid = uuid;
- this.jid = jid;
- if (jid.isBareJid()) {
- this.setResource("mobile");
- }
- this.password = password;
- this.options = options;
- this.rosterVersion = rosterVersion;
- JSONObject tmp;
- try {
- tmp = new JSONObject(keys);
- } catch (JSONException e) {
- tmp = new JSONObject();
- }
- this.keys = tmp;
- this.avatar = avatar;
- this.displayName = displayName;
- this.hostname = hostname;
- this.port = port;
- this.presenceStatus = status;
- this.presenceStatusMessage = statusMessage;
- }
-
- public static Account fromCursor(final Cursor cursor) {
- Jid jid = null;
- try {
- jid = Jid.fromParts(cursor.getString(cursor.getColumnIndex(USERNAME)),
- cursor.getString(cursor.getColumnIndex(SERVER)), "mobile");
- } catch (final InvalidJidException ignored) {
- }
- return new Account(cursor.getString(cursor.getColumnIndex(UUID)),
- jid,
- cursor.getString(cursor.getColumnIndex(PASSWORD)),
- cursor.getInt(cursor.getColumnIndex(OPTIONS)),
- cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
- cursor.getString(cursor.getColumnIndex(KEYS)),
- cursor.getString(cursor.getColumnIndex(AVATAR)),
- cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)),
- cursor.getString(cursor.getColumnIndex(HOSTNAME)),
- cursor.getInt(cursor.getColumnIndex(PORT)),
- Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS))),
- cursor.getString(cursor.getColumnIndex(STATUS_MESSAGE)));
- }
-
- public boolean isOptionSet(final int option) {
- return ((options & (1 << option)) != 0);
- }
-
- public void setOption(final int option, final boolean value) {
- if (value) {
- this.options |= 1 << option;
- } else {
- this.options &= ~(1 << option);
- }
- }
-
- public String getUsername() {
- return jid.getLocalpart();
- }
-
- public boolean setJid(final Jid next) {
- final Jid prev = this.jid != null ? this.jid.toBareJid() : null;
- this.jid = next;
- return prev == null || (next != null && !prev.equals(next.toBareJid())
- );
- }
-
- public Jid getServer() {
- return jid.toDomainJid();
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(final String password) {
- this.password = password;
- }
-
- public void setHostname(String hostname) {
- this.hostname = hostname;
- }
-
- public String getHostname() {
- return this.hostname == null ? "" : this.hostname;
- }
-
- public boolean isOnion() {
- final Jid server = getServer();
- return server != null && server.toString().toLowerCase().endsWith(".onion");
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public int getPort() {
- return this.port;
- }
-
- public State getStatus() {
- if (isOptionSet(OPTION_DISABLED)) {
- return State.DISABLED;
- } else {
- return this.status;
- }
- }
-
- public void setStatus(final State status) {
- this.status = status;
- }
-
- public boolean errorStatus() {
- return getStatus().isError();
- }
-
- public boolean hasErrorStatus() {
- return getXmppConnection() != null
- && (getStatus().isError() || getStatus() == State.CONNECTING)
- && getXmppConnection().getAttempt() >= 3;
- }
-
- public void setPresenceStatus(Presence.Status status) {
- this.presenceStatus = status;
- }
-
- public Presence.Status getPresenceStatus() {
- return this.presenceStatus;
- }
-
- public void setPresenceStatusMessage(String message) {
- this.presenceStatusMessage = message;
- }
-
- public String getPresenceStatusMessage() {
- return this.presenceStatusMessage;
- }
-
- public String getResource() {
- return jid.getResourcepart();
- }
-
- public boolean setResource(final String resource) {
- final String oldResource = jid.getResourcepart();
- if (oldResource == null || !oldResource.equals(resource)) {
- try {
- jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource);
- return true;
- } catch (final InvalidJidException ignored) {
- return true;
- }
- }
- return false;
- }
-
- public Jid getJid() {
- return jid;
- }
-
- public JSONObject getKeys() {
- return keys;
- }
-
- public String getKey(final String name) {
- synchronized (this.keys) {
- return this.keys.optString(name, null);
- }
- }
-
- public int getKeyAsInt(final String name, int defaultValue) {
- String key = getKey(name);
- try {
- return key == null ? defaultValue : Integer.parseInt(key);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
-
- public boolean setKey(final String keyName, final String keyValue) {
- synchronized (this.keys) {
- try {
- this.keys.put(keyName, keyValue);
- return true;
- } catch (final JSONException e) {
- return false;
- }
- }
- }
-
- public boolean setPrivateKeyAlias(String alias) {
- return setKey("private_key_alias", alias);
- }
-
- public String getPrivateKeyAlias() {
- return getKey("private_key_alias");
- }
-
- @Override
- public ContentValues getContentValues() {
- final ContentValues values = new ContentValues();
- values.put(UUID, uuid);
- values.put(USERNAME, jid.getLocalpart());
- values.put(SERVER, jid.getDomainpart());
- values.put(PASSWORD, password);
- values.put(OPTIONS, options);
- synchronized (this.keys) {
- values.put(KEYS, this.keys.toString());
- }
- values.put(ROSTERVERSION, rosterVersion);
- values.put(AVATAR, avatar);
- values.put(DISPLAY_NAME, displayName);
- values.put(HOSTNAME, hostname);
- values.put(PORT, port);
- values.put(STATUS, presenceStatus.toShowString());
- values.put(STATUS_MESSAGE, presenceStatusMessage);
- return values;
- }
-
- public AxolotlService getAxolotlService() {
- return axolotlService;
- }
-
- public void initAccountServices(final XmppConnectionService context) {
- this.mOtrService = new OtrService(context, this);
- this.axolotlService = new AxolotlService(this, context);
- this.pgpDecryptionService = new PgpDecryptionService(context);
- if (xmppConnection != null) {
- xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
- }
- }
-
- public OtrService getOtrService() {
- return this.mOtrService;
- }
-
- public PgpDecryptionService getPgpDecryptionService() {
- return this.pgpDecryptionService;
- }
-
- public XmppConnection getXmppConnection() {
- return this.xmppConnection;
- }
-
- public void setXmppConnection(final XmppConnection connection) {
- this.xmppConnection = connection;
- }
-
- public String getOtrFingerprint() {
- if (this.otrFingerprint == null) {
- try {
- if (this.mOtrService == null) {
- return null;
- }
- final PublicKey publicKey = this.mOtrService.getPublicKey();
- if (publicKey == null || !(publicKey instanceof DSAPublicKey)) {
- return null;
- }
- this.otrFingerprint = new OtrCryptoEngineImpl().getFingerprint(publicKey);
- return this.otrFingerprint;
- } catch (final OtrCryptoException ignored) {
- return null;
- }
- } else {
- return this.otrFingerprint;
- }
- }
-
- public String getRosterVersion() {
- if (this.rosterVersion == null) {
- return "";
- } else {
- return this.rosterVersion;
- }
- }
-
- public void setRosterVersion(final String version) {
- this.rosterVersion = version;
- }
-
- public int countPresences() {
- return this.getSelfContact().getPresences().size();
- }
-
- public String getPgpSignature() {
- return getKey(KEY_PGP_SIGNATURE);
- }
-
- public boolean setPgpSignature(String signature) {
- return setKey(KEY_PGP_SIGNATURE, signature);
- }
-
- public boolean unsetPgpSignature() {
- synchronized (this.keys) {
- return keys.remove(KEY_PGP_SIGNATURE) != null;
- }
- }
-
- public long getPgpId() {
- synchronized (this.keys) {
- if (keys.has(KEY_PGP_ID)) {
- try {
- return keys.getLong(KEY_PGP_ID);
- } catch (JSONException e) {
- return 0;
- }
- } else {
- return 0;
- }
- }
- }
-
- public boolean setPgpSignId(long pgpID) {
- synchronized (this.keys) {
- try {
- keys.put(KEY_PGP_ID, pgpID);
- } catch (JSONException e) {
- return false;
- }
- return true;
- }
- }
-
- public Roster getRoster() {
- return this.roster;
- }
-
- public List<Bookmark> getBookmarks() {
- return this.bookmarks;
- }
-
- public void setBookmarks(final List<Bookmark> bookmarks) {
- this.bookmarks = bookmarks;
- }
-
- public boolean hasBookmarkFor(final Jid conferenceJid) {
- for (final Bookmark bookmark : this.bookmarks) {
- final Jid jid = bookmark.getJid();
- if (jid != null && jid.equals(conferenceJid.toBareJid())) {
- return true;
- }
- }
- return false;
- }
-
- public boolean setAvatar(final String filename) {
- if (this.avatar != null && this.avatar.equals(filename)) {
- return false;
- } else {
- this.avatar = filename;
- return true;
- }
- }
-
- public String getAvatar() {
- return this.avatar;
- }
-
- public void activateGracePeriod(long duration) {
- this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
- }
-
- public void deactivateGracePeriod() {
- this.mEndGracePeriod = 0L;
- }
-
- public boolean inGracePeriod() {
- return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
- }
-
- public String getShareableUri() {
- List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
- String uri = "xmpp:"+this.getJid().toBareJid().toString();
- if (fingerprints.size() > 0) {
- StringBuilder builder = new StringBuilder(uri);
- builder.append('?');
- for(int i = 0; i < fingerprints.size(); ++i) {
- XmppUri.FingerprintType type = fingerprints.get(i).type;
- if (type == XmppUri.FingerprintType.OMEMO) {
- builder.append(XmppUri.OMEMO_URI_PARAM);
- builder.append(fingerprints.get(i).deviceId);
- } else if (type == XmppUri.FingerprintType.OTR) {
- builder.append(XmppUri.OTR_URI_PARAM);
- }
- builder.append('=');
- builder.append(fingerprints.get(i).fingerprint);
- if (i != fingerprints.size() -1) {
- builder.append(';');
- }
- }
- return builder.toString();
- } else {
- return uri;
- }
- }
-
- private List<XmppUri.Fingerprint> getFingerprints() {
- ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
- final String otr = this.getOtrFingerprint();
- if (otr != null) {
- fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OTR,otr));
- }
- fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO,axolotlService.getOwnFingerprint().substring(2),axolotlService.getOwnDeviceId()));
- for(XmppAxolotlSession session : axolotlService.findOwnSessions()) {
- if (session.getTrust().isVerified() && session.getTrust().isActive()) {
- fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO,session.getFingerprint().substring(2).replaceAll("\\s",""),session.getRemoteAddress().getDeviceId()));
- }
- }
- return fingerprints;
- }
-
- public boolean isBlocked(final ListItem contact) {
- final Jid jid = contact.getJid();
- return jid != null && (blocklist.contains(jid.toBareJid()) || blocklist.contains(jid.toDomainJid()));
- }
-
- public boolean isBlocked(final Jid jid) {
- return jid != null && blocklist.contains(jid.toBareJid());
- }
-
- public Collection<Jid> getBlocklist() {
- return this.blocklist;
- }
-
- public void clearBlocklist() {
- getBlocklist().clear();
- }
-
- public boolean isOnlineAndConnected() {
- return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
- }
+ public static final String TABLENAME = "accounts";
+
+ public static final String USERNAME = "username";
+ public static final String SERVER = "server";
+ public static final String PASSWORD = "password";
+ public static final String OPTIONS = "options";
+ public static final String ROSTERVERSION = "rosterversion";
+ public static final String KEYS = "keys";
+ public static final String AVATAR = "avatar";
+ public static final String DISPLAY_NAME = "display_name";
+ public static final String HOSTNAME = "hostname";
+ public static final String PORT = "port";
+ public static final String STATUS = "status";
+ public static final String STATUS_MESSAGE = "status_message";
+
+ public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
+
+ public static final int OPTION_USETLS = 0;
+ public static final int OPTION_DISABLED = 1;
+ public static final int OPTION_REGISTER = 2;
+ public static final int OPTION_USECOMPRESSION = 3;
+ public static final int OPTION_MAGIC_CREATE = 4;
+ public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>();
+
+ public boolean httpUploadAvailable(long filesize) {
+ return xmppConnection != null && xmppConnection.getFeatures().httpUpload(filesize);
+ }
+
+ public boolean httpUploadAvailable() {
+ return httpUploadAvailable(0);
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public XmppConnection.Identity getServerIdentity() {
+ if (xmppConnection == null) {
+ return XmppConnection.Identity.UNKNOWN;
+ } else {
+ return xmppConnection.getServerIdentity();
+ }
+ }
+
+ public Contact getSelfContact() {
+ return getRoster().getContact(jid);
+ }
+
+ public boolean hasPendingPgpIntent(Conversation conversation) {
+ return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
+ }
+
+ public boolean isPgpDecryptionServiceConnected() {
+ return pgpDecryptionService != null && pgpDecryptionService.isConnected();
+ }
+
+ public boolean setShowErrorNotification(boolean newValue) {
+ boolean oldValue = showErrorNotification();
+ setKey("show_error", Boolean.toString(newValue));
+ return newValue != oldValue;
+ }
+
+ public boolean showErrorNotification() {
+ String key = getKey("show_error");
+ return key == null || Boolean.parseBoolean(key);
+ }
+
+ public enum State {
+ DISABLED,
+ OFFLINE,
+ CONNECTING,
+ ONLINE,
+ NO_INTERNET,
+ UNAUTHORIZED(true),
+ SERVER_NOT_FOUND(true),
+ REGISTRATION_FAILED(true),
+ REGISTRATION_CONFLICT(true),
+ REGISTRATION_SUCCESSFUL,
+ REGISTRATION_NOT_SUPPORTED(true),
+ SECURITY_ERROR(true),
+ INCOMPATIBLE_SERVER(true),
+ TOR_NOT_AVAILABLE(true),
+ BIND_FAILURE(true),
+ HOST_UNKNOWN(true),
+ REGISTRATION_PLEASE_WAIT(true),
+ STREAM_ERROR(true),
+ POLICY_VIOLATION(true),
+ REGISTRATION_PASSWORD_TOO_WEAK(true),
+ PAYMENT_REQUIRED(true),
+ MISSING_INTERNET_PERMISSION(true);
+
+ private final boolean isError;
+
+ public boolean isError() {
+ return this.isError;
+ }
+
+ State(final boolean isError) {
+ this.isError = isError;
+ }
+
+ State() {
+ this(false);
+ }
+
+ public int getReadableId() {
+ switch (this) {
+ case DISABLED:
+ return R.string.account_status_disabled;
+ case ONLINE:
+ return R.string.account_status_online;
+ case CONNECTING:
+ return R.string.account_status_connecting;
+ case OFFLINE:
+ return R.string.account_status_offline;
+ case UNAUTHORIZED:
+ return R.string.account_status_unauthorized;
+ case SERVER_NOT_FOUND:
+ return R.string.account_status_not_found;
+ case NO_INTERNET:
+ return R.string.account_status_no_internet;
+ case REGISTRATION_FAILED:
+ return R.string.account_status_regis_fail;
+ case REGISTRATION_CONFLICT:
+ return R.string.account_status_regis_conflict;
+ case REGISTRATION_SUCCESSFUL:
+ return R.string.account_status_regis_success;
+ case REGISTRATION_NOT_SUPPORTED:
+ return R.string.account_status_regis_not_sup;
+ case SECURITY_ERROR:
+ return R.string.account_status_security_error;
+ case INCOMPATIBLE_SERVER:
+ return R.string.account_status_incompatible_server;
+ case TOR_NOT_AVAILABLE:
+ return R.string.account_status_tor_unavailable;
+ case BIND_FAILURE:
+ return R.string.account_status_bind_failure;
+ case HOST_UNKNOWN:
+ return R.string.account_status_host_unknown;
+ case POLICY_VIOLATION:
+ return R.string.account_status_policy_violation;
+ case REGISTRATION_PLEASE_WAIT:
+ return R.string.registration_please_wait;
+ case REGISTRATION_PASSWORD_TOO_WEAK:
+ return R.string.registration_password_too_weak;
+ case STREAM_ERROR:
+ return R.string.account_status_stream_error;
+ case PAYMENT_REQUIRED:
+ return R.string.payment_required;
+ case MISSING_INTERNET_PERMISSION:
+ return R.string.missing_internet_permission;
+ default:
+ return R.string.account_status_unknown;
+ }
+ }
+ }
+
+ public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<>();
+ public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<>();
+
+ private static final String KEY_PGP_SIGNATURE = "pgp_signature";
+ private static final String KEY_PGP_ID = "pgp_id";
+
+ protected Jid jid;
+ protected String password;
+ protected int options = 0;
+ protected String rosterVersion;
+ protected State status = State.OFFLINE;
+ protected final JSONObject keys;
+ protected String avatar;
+ protected String displayName = null;
+ protected String hostname = null;
+ protected int port = 5222;
+ protected boolean online = false;
+ private OtrService mOtrService = null;
+ private AxolotlService axolotlService = null;
+ private PgpDecryptionService pgpDecryptionService = null;
+ private XmppConnection xmppConnection = null;
+ private long mEndGracePeriod = 0L;
+ private String otrFingerprint;
+ private final Roster roster = new Roster(this);
+ private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
+ private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
+ private Presence.Status presenceStatus = Presence.Status.ONLINE;
+ private String presenceStatusMessage = null;
+
+ public Account(final Jid jid, final String password) {
+ this(java.util.UUID.randomUUID().toString(), jid,
+ password, 0, null, "", null, null, null, 5222, Presence.Status.ONLINE, null);
+ }
+
+ private Account(final String uuid, final Jid jid,
+ final String password, final int options, final String rosterVersion, final String keys,
+ final String avatar, String displayName, String hostname, int port,
+ final Presence.Status status, String statusMessage) {
+ this.uuid = uuid;
+ this.jid = jid;
+ if (jid.isBareJid()) {
+ this.setResource("mobile");
+ }
+ this.password = password;
+ this.options = options;
+ this.rosterVersion = rosterVersion;
+ JSONObject tmp;
+ try {
+ tmp = new JSONObject(keys);
+ } catch (JSONException e) {
+ tmp = new JSONObject();
+ }
+ this.keys = tmp;
+ this.avatar = avatar;
+ this.displayName = displayName;
+ this.hostname = hostname;
+ this.port = port;
+ this.presenceStatus = status;
+ this.presenceStatusMessage = statusMessage;
+ }
+
+ public static Account fromCursor(final Cursor cursor) {
+ Jid jid = null;
+ try {
+ jid = Jid.fromParts(cursor.getString(cursor.getColumnIndex(USERNAME)),
+ cursor.getString(cursor.getColumnIndex(SERVER)), "mobile");
+ } catch (final InvalidJidException ignored) {
+ }
+ return new Account(cursor.getString(cursor.getColumnIndex(UUID)),
+ jid,
+ cursor.getString(cursor.getColumnIndex(PASSWORD)),
+ cursor.getInt(cursor.getColumnIndex(OPTIONS)),
+ cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
+ cursor.getString(cursor.getColumnIndex(KEYS)),
+ cursor.getString(cursor.getColumnIndex(AVATAR)),
+ cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)),
+ cursor.getString(cursor.getColumnIndex(HOSTNAME)),
+ cursor.getInt(cursor.getColumnIndex(PORT)),
+ Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS))),
+ cursor.getString(cursor.getColumnIndex(STATUS_MESSAGE)));
+ }
+
+ public boolean isOptionSet(final int option) {
+ return ((options & (1 << option)) != 0);
+ }
+
+ public void setOption(final int option, final boolean value) {
+ if (value) {
+ this.options |= 1 << option;
+ } else {
+ this.options &= ~(1 << option);
+ }
+ }
+
+ public String getUsername() {
+ return jid.getLocalpart();
+ }
+
+ public boolean setJid(final Jid next) {
+ final Jid prev = this.jid != null ? this.jid.toBareJid() : null;
+ this.jid = next;
+ return prev == null || (next != null && !prev.equals(next.toBareJid())
+ );
+ }
+
+ public Jid getServer() {
+ return jid.toDomainJid();
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(final String password) {
+ this.password = password;
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
+ public String getHostname() {
+ return this.hostname == null ? "" : this.hostname;
+ }
+
+ public boolean isOnion() {
+ final Jid server = getServer();
+ return server != null && server.toString().toLowerCase().endsWith(".onion");
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public int getPort() {
+ return this.port;
+ }
+
+ public State getStatus() {
+ if (isOptionSet(OPTION_DISABLED)) {
+ return State.DISABLED;
+ } else {
+ return this.status;
+ }
+ }
+
+ public void setStatus(final State status) {
+ this.status = status;
+ }
+
+ public boolean errorStatus() {
+ return getStatus().isError();
+ }
+
+ public boolean hasErrorStatus() {
+ return getXmppConnection() != null
+ && (getStatus().isError() || getStatus() == State.CONNECTING)
+ && getXmppConnection().getAttempt() >= 3;
+ }
+
+ public void setPresenceStatus(Presence.Status status) {
+ this.presenceStatus = status;
+ }
+
+ public Presence.Status getPresenceStatus() {
+ return this.presenceStatus;
+ }
+
+ public void setPresenceStatusMessage(String message) {
+ this.presenceStatusMessage = message;
+ }
+
+ public String getPresenceStatusMessage() {
+ return this.presenceStatusMessage;
+ }
+
+ public String getResource() {
+ return jid.getResourcepart();
+ }
+
+ public boolean setResource(final String resource) {
+ final String oldResource = jid.getResourcepart();
+ if (oldResource == null || !oldResource.equals(resource)) {
+ try {
+ jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource);
+ return true;
+ } catch (final InvalidJidException ignored) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Jid getJid() {
+ return jid;
+ }
+
+ public JSONObject getKeys() {
+ return keys;
+ }
+
+ public String getKey(final String name) {
+ synchronized (this.keys) {
+ return this.keys.optString(name, null);
+ }
+ }
+
+ public int getKeyAsInt(final String name, int defaultValue) {
+ String key = getKey(name);
+ try {
+ return key == null ? defaultValue : Integer.parseInt(key);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ public boolean setKey(final String keyName, final String keyValue) {
+ synchronized (this.keys) {
+ try {
+ this.keys.put(keyName, keyValue);
+ return true;
+ } catch (final JSONException e) {
+ return false;
+ }
+ }
+ }
+
+ public boolean setPrivateKeyAlias(String alias) {
+ return setKey("private_key_alias", alias);
+ }
+
+ public String getPrivateKeyAlias() {
+ return getKey("private_key_alias");
+ }
+
+ @Override
+ public ContentValues getContentValues() {
+ final ContentValues values = new ContentValues();
+ values.put(UUID, uuid);
+ values.put(USERNAME, jid.getLocalpart());
+ values.put(SERVER, jid.getDomainpart());
+ values.put(PASSWORD, password);
+ values.put(OPTIONS, options);
+ synchronized (this.keys) {
+ values.put(KEYS, this.keys.toString());
+ }
+ values.put(ROSTERVERSION, rosterVersion);
+ values.put(AVATAR, avatar);
+ values.put(DISPLAY_NAME, displayName);
+ values.put(HOSTNAME, hostname);
+ values.put(PORT, port);
+ values.put(STATUS, presenceStatus.toShowString());
+ values.put(STATUS_MESSAGE, presenceStatusMessage);
+ return values;
+ }
+
+ public AxolotlService getAxolotlService() {
+ return axolotlService;
+ }
+
+ public void initAccountServices(final XmppConnectionService context) {
+ this.mOtrService = new OtrService(context, this);
+ this.axolotlService = new AxolotlService(this, context);
+ this.pgpDecryptionService = new PgpDecryptionService(context);
+ if (xmppConnection != null) {
+ xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
+ }
+ }
+
+ public OtrService getOtrService() {
+ return this.mOtrService;
+ }
+
+ public PgpDecryptionService getPgpDecryptionService() {
+ return this.pgpDecryptionService;
+ }
+
+ public XmppConnection getXmppConnection() {
+ return this.xmppConnection;
+ }
+
+ public void setXmppConnection(final XmppConnection connection) {
+ this.xmppConnection = connection;
+ }
+
+ public String getOtrFingerprint() {
+ if (this.otrFingerprint == null) {
+ try {
+ if (this.mOtrService == null) {
+ return null;
+ }
+ final PublicKey publicKey = this.mOtrService.getPublicKey();
+ if (publicKey == null || !(publicKey instanceof DSAPublicKey)) {
+ return null;
+ }
+ this.otrFingerprint = new OtrCryptoEngineImpl().getFingerprint(publicKey);
+ return this.otrFingerprint;
+ } catch (final OtrCryptoException ignored) {
+ return null;
+ }
+ } else {
+ return this.otrFingerprint;
+ }
+ }
+
+ public String getRosterVersion() {
+ if (this.rosterVersion == null) {
+ return "";
+ } else {
+ return this.rosterVersion;
+ }
+ }
+
+ public void setRosterVersion(final String version) {
+ this.rosterVersion = version;
+ }
+
+ public int countPresences() {
+ return this.getSelfContact().getPresences().size();
+ }
+
+ public String getPgpSignature() {
+ return getKey(KEY_PGP_SIGNATURE);
+ }
+
+ public boolean setPgpSignature(String signature) {
+ return setKey(KEY_PGP_SIGNATURE, signature);
+ }
+
+ public boolean unsetPgpSignature() {
+ synchronized (this.keys) {
+ return keys.remove(KEY_PGP_SIGNATURE) != null;
+ }
+ }
+
+ public long getPgpId() {
+ synchronized (this.keys) {
+ if (keys.has(KEY_PGP_ID)) {
+ try {
+ return keys.getLong(KEY_PGP_ID);
+ } catch (JSONException e) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ public boolean setPgpSignId(long pgpID) {
+ synchronized (this.keys) {
+ try {
+ keys.put(KEY_PGP_ID, pgpID);
+ } catch (JSONException e) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public Roster getRoster() {
+ return this.roster;
+ }
+
+ public List<Bookmark> getBookmarks() {
+ return this.bookmarks;
+ }
+
+ public void setBookmarks(final List<Bookmark> bookmarks) {
+ this.bookmarks = bookmarks;
+ }
+
+ public boolean hasBookmarkFor(final Jid conferenceJid) {
+ for (final Bookmark bookmark : this.bookmarks) {
+ final Jid jid = bookmark.getJid();
+ if (jid != null && jid.equals(conferenceJid.toBareJid())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean setAvatar(final String filename) {
+ if (this.avatar != null && this.avatar.equals(filename)) {
+ return false;
+ } else {
+ this.avatar = filename;
+ return true;
+ }
+ }
+
+ public String getAvatar() {
+ return this.avatar;
+ }
+
+ public void activateGracePeriod(long duration) {
+ this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
+ }
+
+ public void deactivateGracePeriod() {
+ this.mEndGracePeriod = 0L;
+ }
+
+ public boolean inGracePeriod() {
+ return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
+ }
+
+ public String getShareableUri() {
+ List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
+ String uri = "xmpp:" + this.getJid().toBareJid().toString();
+ if (fingerprints.size() > 0) {
+ StringBuilder builder = new StringBuilder(uri);
+ builder.append('?');
+ for (int i = 0; i < fingerprints.size(); ++i) {
+ XmppUri.FingerprintType type = fingerprints.get(i).type;
+ if (type == XmppUri.FingerprintType.OMEMO) {
+ builder.append(XmppUri.OMEMO_URI_PARAM);
+ builder.append(fingerprints.get(i).deviceId);
+ } else if (type == XmppUri.FingerprintType.OTR) {
+ builder.append(XmppUri.OTR_URI_PARAM);
+ }
+ builder.append('=');
+ builder.append(fingerprints.get(i).fingerprint);
+ if (i != fingerprints.size() - 1) {
+ builder.append(';');
+ }
+ }
+ return builder.toString();
+ } else {
+ return uri;
+ }
+ }
+
+ private List<XmppUri.Fingerprint> getFingerprints() {
+ ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
+ final String otr = this.getOtrFingerprint();
+ if (otr != null) {
+ fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OTR, otr));
+ }
+ fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO, axolotlService.getOwnFingerprint().substring(2), axolotlService.getOwnDeviceId()));
+ for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
+ if (session.getTrust().isVerified() && session.getTrust().isActive()) {
+ fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO, session.getFingerprint().substring(2).replaceAll("\\s", ""), session.getRemoteAddress().getDeviceId()));
+ }
+ }
+ return fingerprints;
+ }
+
+ public boolean isBlocked(final ListItem contact) {
+ final Jid jid = contact.getJid();
+ return jid != null && (blocklist.contains(jid.toBareJid()) || blocklist.contains(jid.toDomainJid()));
+ }
+
+ public boolean isBlocked(final Jid jid) {
+ return jid != null && blocklist.contains(jid.toBareJid());
+ }
+
+ public Collection<Jid> getBlocklist() {
+ return this.blocklist;
+ }
+
+ public void clearBlocklist() {
+ getBlocklist().clear();
+ }
+
+ public boolean isOnlineAndConnected() {
+ return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Blockable.java b/src/main/java/de/pixart/messenger/entities/Blockable.java
index 07860bcfe..9357f1cdb 100644
--- a/src/main/java/de/pixart/messenger/entities/Blockable.java
+++ b/src/main/java/de/pixart/messenger/entities/Blockable.java
@@ -3,9 +3,13 @@ package de.pixart.messenger.entities;
import de.pixart.messenger.xmpp.jid.Jid;
public interface Blockable {
- public boolean isBlocked();
- public boolean isDomainBlocked();
- public Jid getBlockedJid();
- public Jid getJid();
- public Account getAccount();
+ public boolean isBlocked();
+
+ public boolean isDomainBlocked();
+
+ public Jid getBlockedJid();
+
+ public Jid getJid();
+
+ public Account getAccount();
}
diff --git a/src/main/java/de/pixart/messenger/entities/Bookmark.java b/src/main/java/de/pixart/messenger/entities/Bookmark.java
index 9c99a2baf..c2e31039b 100644
--- a/src/main/java/de/pixart/messenger/entities/Bookmark.java
+++ b/src/main/java/de/pixart/messenger/entities/Bookmark.java
@@ -12,160 +12,160 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class Bookmark extends Element implements ListItem {
- private Account account;
- private Conversation mJoinedConversation;
-
- public Bookmark(final Account account, final Jid jid) {
- super("conference");
- this.setAttribute("jid", jid.toString());
- this.account = account;
- }
-
- private Bookmark(Account account) {
- super("conference");
- this.account = account;
- }
-
- public static Bookmark parse(Element element, Account account) {
- Bookmark bookmark = new Bookmark(account);
- bookmark.setAttributes(element.getAttributes());
- bookmark.setChildren(element.getChildren());
- return bookmark;
- }
-
- public void setAutojoin(boolean autojoin) {
- if (autojoin) {
- this.setAttribute("autojoin", "true");
- } else {
- this.setAttribute("autojoin", "false");
- }
- }
-
- @Override
- public int compareTo(final ListItem another) {
- return this.getDisplayName().compareToIgnoreCase(
- another.getDisplayName());
- }
-
- @Override
- public String getDisplayName() {
- if (this.mJoinedConversation != null) {
- return this.mJoinedConversation.getName();
- } else if (getBookmarkName() != null
- && !getBookmarkName().trim().isEmpty()) {
- return getBookmarkName().trim();
- } else {
- Jid jid = this.getJid();
- String name = jid != null ? jid.getLocalpart() : getAttribute("jid");
- return name != null ? name : "";
- }
- }
-
- @Override
- public String getDisplayJid() {
- Jid jid = getJid();
- if (jid != null) {
- return jid.toString();
- } else {
- return getAttribute("jid"); //fallback if jid wasn't parsable
- }
- }
-
- @Override
- public Jid getJid() {
- return this.getAttributeAsJid("jid");
- }
-
- @Override
- public List<Tag> getTags(Context context) {
- ArrayList<Tag> tags = new ArrayList<>();
- for (Element element : getChildren()) {
- if (element.getName().equals("group") && element.getContent() != null) {
- String group = element.getContent();
- tags.add(new Tag(group, UIHelper.getColorForName(group)));
- }
- }
- return tags;
- }
-
- public String getNick() {
- return this.findChildContent("nick");
- }
-
- public void setNick(String nick) {
- Element element = this.findChild("nick");
- if (element == null) {
- element = this.addChild("nick");
- }
- element.setContent(nick);
- }
-
- public boolean autojoin() {
- return this.getAttributeAsBoolean("autojoin");
- }
-
- public String getPassword() {
- return this.findChildContent("password");
- }
-
- public void setPassword(String password) {
- Element element = this.findChild("password");
- if (element != null) {
- element.setContent(password);
- }
- }
-
- @Override
- public boolean match(Context context, String needle) {
- if (needle == null) {
- return true;
- }
- needle = needle.toLowerCase(Locale.US);
- final Jid jid = getJid();
- return (jid != null && jid.toString().contains(needle)) ||
- getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(context, needle);
- }
-
- private boolean matchInTag(Context context, String needle) {
- needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags(context)) {
- if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
- return true;
- }
- }
- return false;
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public Conversation getConversation() {
- return this.mJoinedConversation;
- }
-
- public void setConversation(Conversation conversation) {
- this.mJoinedConversation = conversation;
- }
-
- public String getBookmarkName() {
- return this.getAttribute("name");
- }
-
- public boolean setBookmarkName(String name) {
- String before = getBookmarkName();
- if (name != null && !name.equals(before)) {
- this.setAttribute("name", name);
- return true;
- } else {
- return false;
- }
- }
-
- public void unregisterConversation() {
- if (this.mJoinedConversation != null) {
- this.mJoinedConversation.deregisterWithBookmark();
- }
- }
+ private Account account;
+ private Conversation mJoinedConversation;
+
+ public Bookmark(final Account account, final Jid jid) {
+ super("conference");
+ this.setAttribute("jid", jid.toString());
+ this.account = account;
+ }
+
+ private Bookmark(Account account) {
+ super("conference");
+ this.account = account;
+ }
+
+ public static Bookmark parse(Element element, Account account) {
+ Bookmark bookmark = new Bookmark(account);
+ bookmark.setAttributes(element.getAttributes());
+ bookmark.setChildren(element.getChildren());
+ return bookmark;
+ }
+
+ public void setAutojoin(boolean autojoin) {
+ if (autojoin) {
+ this.setAttribute("autojoin", "true");
+ } else {
+ this.setAttribute("autojoin", "false");
+ }
+ }
+
+ @Override
+ public int compareTo(final ListItem another) {
+ return this.getDisplayName().compareToIgnoreCase(
+ another.getDisplayName());
+ }
+
+ @Override
+ public String getDisplayName() {
+ if (this.mJoinedConversation != null) {
+ return this.mJoinedConversation.getName();
+ } else if (getBookmarkName() != null
+ && !getBookmarkName().trim().isEmpty()) {
+ return getBookmarkName().trim();
+ } else {
+ Jid jid = this.getJid();
+ String name = jid != null ? jid.getLocalpart() : getAttribute("jid");
+ return name != null ? name : "";
+ }
+ }
+
+ @Override
+ public String getDisplayJid() {
+ Jid jid = getJid();
+ if (jid != null) {
+ return jid.toString();
+ } else {
+ return getAttribute("jid"); //fallback if jid wasn't parsable
+ }
+ }
+
+ @Override
+ public Jid getJid() {
+ return this.getAttributeAsJid("jid");
+ }
+
+ @Override
+ public List<Tag> getTags(Context context) {
+ ArrayList<Tag> tags = new ArrayList<>();
+ for (Element element : getChildren()) {
+ if (element.getName().equals("group") && element.getContent() != null) {
+ String group = element.getContent();
+ tags.add(new Tag(group, UIHelper.getColorForName(group)));
+ }
+ }
+ return tags;
+ }
+
+ public String getNick() {
+ return this.findChildContent("nick");
+ }
+
+ public void setNick(String nick) {
+ Element element = this.findChild("nick");
+ if (element == null) {
+ element = this.addChild("nick");
+ }
+ element.setContent(nick);
+ }
+
+ public boolean autojoin() {
+ return this.getAttributeAsBoolean("autojoin");
+ }
+
+ public String getPassword() {
+ return this.findChildContent("password");
+ }
+
+ public void setPassword(String password) {
+ Element element = this.findChild("password");
+ if (element != null) {
+ element.setContent(password);
+ }
+ }
+
+ @Override
+ public boolean match(Context context, String needle) {
+ if (needle == null) {
+ return true;
+ }
+ needle = needle.toLowerCase(Locale.US);
+ final Jid jid = getJid();
+ return (jid != null && jid.toString().contains(needle)) ||
+ getDisplayName().toLowerCase(Locale.US).contains(needle) ||
+ matchInTag(context, needle);
+ }
+
+ private boolean matchInTag(Context context, String needle) {
+ needle = needle.toLowerCase(Locale.US);
+ for (Tag tag : getTags(context)) {
+ if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public Conversation getConversation() {
+ return this.mJoinedConversation;
+ }
+
+ public void setConversation(Conversation conversation) {
+ this.mJoinedConversation = conversation;
+ }
+
+ public String getBookmarkName() {
+ return this.getAttribute("name");
+ }
+
+ public boolean setBookmarkName(String name) {
+ String before = getBookmarkName();
+ if (name != null && !name.equals(before)) {
+ this.setAttribute("name", name);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void unregisterConversation() {
+ if (this.mJoinedConversation != null) {
+ this.mJoinedConversation.deregisterWithBookmark();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Contact.java b/src/main/java/de/pixart/messenger/entities/Contact.java
index 32af78de0..06f7ea4d0 100644
--- a/src/main/java/de/pixart/messenger/entities/Contact.java
+++ b/src/main/java/de/pixart/messenger/entities/Contact.java
@@ -22,534 +22,535 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.pixart.messenger.xmpp.pep.Avatar;
public class Contact implements ListItem, Blockable {
- public static final String TABLENAME = "contacts";
-
- public static final String SYSTEMNAME = "systemname";
- public static final String SERVERNAME = "servername";
- public static final String JID = "jid";
- public static final String OPTIONS = "options";
- public static final String SYSTEMACCOUNT = "systemaccount";
- public static final String PHOTOURI = "photouri";
- public static final String KEYS = "pgpkey";
- public static final String ACCOUNT = "accountUuid";
- public static final String AVATAR = "avatar";
- public static final String LAST_PRESENCE = "last_presence";
- public static final String LAST_TIME = "last_time";
- public static final String GROUPS = "groups";
- protected String accountUuid;
- protected String systemName;
- protected String serverName;
- protected String presenceName;
- protected String commonName;
- protected Jid jid;
- protected int subscription = 0;
- protected String systemAccount;
- protected String photoUri;
- protected JSONObject keys = new JSONObject();
- protected JSONArray groups = new JSONArray();
- protected final Presences presences = new Presences();
- protected Account account;
- protected Avatar avatar;
-
- private boolean mActive = false;
- private long mLastseen = 0;
- private String mLastPresence = null;
-
- public Contact(final String account, final String systemName, final String serverName,
- final Jid jid, final int subscription, final String photoUri,
- final String systemAccount, final String keys, final String avatar, final long lastseen,
- final String presence, final String groups) {
- this.accountUuid = account;
- this.systemName = systemName;
- this.serverName = serverName;
- this.jid = jid;
- this.subscription = subscription;
- this.photoUri = photoUri;
- this.systemAccount = systemAccount;
- try {
- this.keys = (keys == null ? new JSONObject("") : new JSONObject(keys));
- } catch (JSONException e) {
- this.keys = new JSONObject();
- }
- if (avatar != null) {
- this.avatar = new Avatar();
- this.avatar.sha1sum = avatar;
- this.avatar.origin = Avatar.Origin.VCARD; //always assume worst
- }
- try {
- this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
- } catch (JSONException e) {
- this.groups = new JSONArray();
- }
- this.mLastseen = lastseen;
- this.mLastPresence = presence;
- }
-
- public Contact(final Jid jid) {
- this.jid = jid;
- }
-
- public static Contact fromCursor(final Cursor cursor) {
- final Jid jid;
- try {
- jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(JID)), true);
- } catch (final InvalidJidException e) {
- // TODO: Borked DB... handle this somehow?
- return null;
- }
- return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)),
- cursor.getString(cursor.getColumnIndex(SYSTEMNAME)),
- cursor.getString(cursor.getColumnIndex(SERVERNAME)),
- jid,
- cursor.getInt(cursor.getColumnIndex(OPTIONS)),
- cursor.getString(cursor.getColumnIndex(PHOTOURI)),
- cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
- cursor.getString(cursor.getColumnIndex(KEYS)),
- cursor.getString(cursor.getColumnIndex(AVATAR)),
- cursor.getLong(cursor.getColumnIndex(LAST_TIME)),
- cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)),
- cursor.getString(cursor.getColumnIndex(GROUPS)));
- }
-
- public String getDisplayName() {
- if (this.commonName != null && Config.X509_VERIFICATION) {
- return this.commonName;
- } else if (this.systemName != null) {
- return this.systemName;
- } else if (this.serverName != null) {
- return this.serverName;
+ public static final String TABLENAME = "contacts";
+
+ public static final String SYSTEMNAME = "systemname";
+ public static final String SERVERNAME = "servername";
+ public static final String JID = "jid";
+ public static final String OPTIONS = "options";
+ public static final String SYSTEMACCOUNT = "systemaccount";
+ public static final String PHOTOURI = "photouri";
+ public static final String KEYS = "pgpkey";
+ public static final String ACCOUNT = "accountUuid";
+ public static final String AVATAR = "avatar";
+ public static final String LAST_PRESENCE = "last_presence";
+ public static final String LAST_TIME = "last_time";
+ public static final String GROUPS = "groups";
+ protected String accountUuid;
+ protected String systemName;
+ protected String serverName;
+ protected String presenceName;
+ protected String commonName;
+ protected Jid jid;
+ protected int subscription = 0;
+ protected String systemAccount;
+ protected String photoUri;
+ protected JSONObject keys = new JSONObject();
+ protected JSONArray groups = new JSONArray();
+ protected final Presences presences = new Presences();
+ protected Account account;
+ protected Avatar avatar;
+
+ private boolean mActive = false;
+ private long mLastseen = 0;
+ private String mLastPresence = null;
+
+ public Contact(final String account, final String systemName, final String serverName,
+ final Jid jid, final int subscription, final String photoUri,
+ final String systemAccount, final String keys, final String avatar, final long lastseen,
+ final String presence, final String groups) {
+ this.accountUuid = account;
+ this.systemName = systemName;
+ this.serverName = serverName;
+ this.jid = jid;
+ this.subscription = subscription;
+ this.photoUri = photoUri;
+ this.systemAccount = systemAccount;
+ try {
+ this.keys = (keys == null ? new JSONObject("") : new JSONObject(keys));
+ } catch (JSONException e) {
+ this.keys = new JSONObject();
+ }
+ if (avatar != null) {
+ this.avatar = new Avatar();
+ this.avatar.sha1sum = avatar;
+ this.avatar.origin = Avatar.Origin.VCARD; //always assume worst
+ }
+ try {
+ this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
+ } catch (JSONException e) {
+ this.groups = new JSONArray();
+ }
+ this.mLastseen = lastseen;
+ this.mLastPresence = presence;
+ }
+
+ public Contact(final Jid jid) {
+ this.jid = jid;
+ }
+
+ public static Contact fromCursor(final Cursor cursor) {
+ final Jid jid;
+ try {
+ jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(JID)), true);
+ } catch (final InvalidJidException e) {
+ // TODO: Borked DB... handle this somehow?
+ return null;
+ }
+ return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)),
+ cursor.getString(cursor.getColumnIndex(SYSTEMNAME)),
+ cursor.getString(cursor.getColumnIndex(SERVERNAME)),
+ jid,
+ cursor.getInt(cursor.getColumnIndex(OPTIONS)),
+ cursor.getString(cursor.getColumnIndex(PHOTOURI)),
+ cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
+ cursor.getString(cursor.getColumnIndex(KEYS)),
+ cursor.getString(cursor.getColumnIndex(AVATAR)),
+ cursor.getLong(cursor.getColumnIndex(LAST_TIME)),
+ cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)),
+ cursor.getString(cursor.getColumnIndex(GROUPS)));
+ }
+
+ public String getDisplayName() {
+ if (this.commonName != null && Config.X509_VERIFICATION) {
+ return this.commonName;
+ } else if (this.systemName != null) {
+ return this.systemName;
+ } else if (this.serverName != null) {
+ return this.serverName;
} else if (this.presenceName != null && mutualPresenceSubscription()) {
- return this.presenceName;
- } else if (jid.hasLocalpart()) {
- return jid.getLocalpart();
- } else {
- return jid.getDomainpart();
- }
- }
-
- @Override
- public String getDisplayJid() {
- if (jid != null) {
- return jid.toString();
- } else {
- return null;
- }
- }
-
- public String getProfilePhoto() {
- return this.photoUri;
- }
-
- public Jid getJid() {
- return jid;
- }
-
- @Override
- public List<Tag> getTags(Context context) {
- final ArrayList<Tag> tags = new ArrayList<>();
- for (final String group : getGroups()) {
- tags.add(new Tag(group, UIHelper.getColorForName(group)));
- }
- Presence.Status status = getShownStatus();
- if (status != Presence.Status.OFFLINE) {
- tags.add(UIHelper.getTagForStatus(context, status));
- }
- if (isBlocked()) {
- tags.add(new Tag("blocked", 0xff2e2f3b));
- }
- return tags;
- }
-
- public boolean match(Context context, String needle) {
- if (needle == null || needle.isEmpty()) {
- return true;
- }
- needle = needle.toLowerCase(Locale.US).trim();
- String[] parts = needle.split("\\s+");
- if (parts.length > 1) {
- for(int i = 0; i < parts.length; ++i) {
- if (!match(context, parts[i])) {
- return false;
- }
- }
- return true;
- } else {
- return jid.toString().contains(needle) ||
- getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(context, needle);
- }
- }
-
- private boolean matchInTag(Context context, String needle) {
- needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags(context)) {
- if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
- return true;
- }
- }
- return false;
- }
-
- public ContentValues getContentValues() {
- synchronized (this.keys) {
- final ContentValues values = new ContentValues();
- values.put(ACCOUNT, accountUuid);
- values.put(SYSTEMNAME, systemName);
- values.put(SERVERNAME, serverName);
- values.put(JID, jid.toPreppedString());
- values.put(OPTIONS, subscription);
- values.put(SYSTEMACCOUNT, systemAccount);
- values.put(PHOTOURI, photoUri);
- values.put(KEYS, keys.toString());
- values.put(AVATAR, avatar == null ? null : avatar.getFilename());
- values.put(LAST_PRESENCE, mLastPresence);
- values.put(LAST_TIME, mLastseen);
- values.put(GROUPS, groups.toString());
- return values;
- }
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public void setAccount(Account account) {
- this.account = account;
- this.accountUuid = account.getUuid();
- }
-
- public Presences getPresences() {
- return this.presences;
- }
-
- public void updatePresence(final String resource, final Presence presence) {
- this.presences.updatePresence(resource, presence);
- }
-
- public void removePresence(final String resource) {
- this.presences.removePresence(resource);
- }
-
- public void clearPresences() {
- this.presences.clearPresences();
- this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
- }
-
- public Presence.Status getShownStatus() {
- return this.presences.getShownStatus();
- }
-
- public boolean setPhotoUri(String uri) {
- if (uri != null && !uri.equals(this.photoUri)) {
- this.photoUri = uri;
- return true;
- } else if (this.photoUri != null && uri == null) {
- this.photoUri = null;
- return true;
- } else {
- return false;
- }
- }
-
- public void setServerName(String serverName) {
- this.serverName = serverName;
- }
-
- public void setSystemName(String systemName) {
- this.systemName = systemName;
- }
-
- public void setPresenceName(String presenceName) {
- this.presenceName = presenceName;
- }
-
- public Uri getSystemAccount() {
- if (systemAccount == null) {
- return null;
- } else {
- String[] parts = systemAccount.split("#");
- if (parts.length != 2) {
- return null;
- } else {
- long id = Long.parseLong(parts[0]);
- return ContactsContract.Contacts.getLookupUri(id, parts[1]);
- }
- }
- }
-
- public void setSystemAccount(String account) {
- this.systemAccount = account;
- }
-
- public List<String> getGroups() {
- ArrayList<String> groups = new ArrayList<String>();
- for (int i = 0; i < this.groups.length(); ++i) {
- try {
- groups.add(this.groups.getString(i));
- } catch (final JSONException ignored) {
- }
- }
- return groups;
- }
-
- public ArrayList<String> getOtrFingerprints() {
- synchronized (this.keys) {
- final ArrayList<String> fingerprints = new ArrayList<String>();
- try {
- if (this.keys.has("otr_fingerprints")) {
- final JSONArray prints = this.keys.getJSONArray("otr_fingerprints");
- for (int i = 0; i < prints.length(); ++i) {
- final String print = prints.isNull(i) ? null : prints.getString(i);
- if (print != null && !print.isEmpty()) {
- fingerprints.add(prints.getString(i));
- }
- }
- }
- } catch (final JSONException ignored) {
-
- }
- return fingerprints;
- }
- }
- public boolean addOtrFingerprint(String print) {
- synchronized (this.keys) {
- if (getOtrFingerprints().contains(print)) {
- return false;
- }
- try {
- JSONArray fingerprints;
- if (!this.keys.has("otr_fingerprints")) {
- fingerprints = new JSONArray();
- } else {
- fingerprints = this.keys.getJSONArray("otr_fingerprints");
- }
- fingerprints.put(print);
- this.keys.put("otr_fingerprints", fingerprints);
- return true;
- } catch (final JSONException ignored) {
- return false;
- }
- }
- }
-
- public long getPgpKeyId() {
- synchronized (this.keys) {
- if (this.keys.has("pgp_keyid")) {
- try {
- return this.keys.getLong("pgp_keyid");
- } catch (JSONException e) {
- return 0;
- }
- } else {
- return 0;
- }
- }
- }
-
- public void setPgpKeyId(long keyId) {
- synchronized (this.keys) {
- try {
- this.keys.put("pgp_keyid", keyId);
- } catch (final JSONException ignored) {
- }
- }
- }
-
- public void setOption(int option) {
- this.subscription |= 1 << option;
- }
-
- public void resetOption(int option) {
- this.subscription &= ~(1 << option);
- }
-
- public boolean getOption(int option) {
- return ((this.subscription & (1 << option)) != 0);
- }
-
- public boolean showInRoster() {
- return (this.getOption(Contact.Options.IN_ROSTER) && (!this
- .getOption(Contact.Options.DIRTY_DELETE)))
- || (this.getOption(Contact.Options.DIRTY_PUSH));
- }
-
- public void parseSubscriptionFromElement(Element item) {
- String ask = item.getAttribute("ask");
- String subscription = item.getAttribute("subscription");
-
- if (subscription != null) {
- switch (subscription) {
- case "to":
- this.resetOption(Options.FROM);
- this.setOption(Options.TO);
- break;
- case "from":
- this.resetOption(Options.TO);
- this.setOption(Options.FROM);
- this.resetOption(Options.PREEMPTIVE_GRANT);
- this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
- break;
- case "both":
- this.setOption(Options.TO);
- this.setOption(Options.FROM);
- this.resetOption(Options.PREEMPTIVE_GRANT);
- this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
- break;
- case "none":
- this.resetOption(Options.FROM);
- this.resetOption(Options.TO);
- break;
- }
- }
-
- // do NOT override asking if pending push request
- if (!this.getOption(Contact.Options.DIRTY_PUSH)) {
- if ((ask != null) && (ask.equals("subscribe"))) {
- this.setOption(Contact.Options.ASKING);
- } else {
- this.resetOption(Contact.Options.ASKING);
- }
- }
- }
-
- public void parseGroupsFromElement(Element item) {
- this.groups = new JSONArray();
- for (Element element : item.getChildren()) {
- if (element.getName().equals("group") && element.getContent() != null) {
- this.groups.put(element.getContent());
- }
- }
- }
-
- public Element asElement() {
- final Element item = new Element("item");
- item.setAttribute("jid", this.jid.toString());
- if (this.serverName != null) {
- item.setAttribute("name", this.serverName);
- }
- for (String group : getGroups()) {
- item.addChild("group").setContent(group);
- }
- return item;
- }
-
- @Override
- public int compareTo(final ListItem another) {
- return this.getDisplayName().compareToIgnoreCase(
- another.getDisplayName());
- }
-
- public Jid getServer() {
- return getJid().toDomainJid();
- }
-
- public boolean setAvatar(Avatar avatar) {
- if (this.avatar != null && this.avatar.equals(avatar)) {
- return false;
- } else {
- if (this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) {
- return false;
- }
- this.avatar = avatar;
- return true;
- }
- }
-
- public String getAvatar() {
- return avatar == null ? null : avatar.getFilename();
- }
-
- public boolean deleteOtrFingerprint(String fingerprint) {
- synchronized (this.keys) {
- boolean success = false;
- try {
- if (this.keys.has("otr_fingerprints")) {
- JSONArray newPrints = new JSONArray();
- JSONArray oldPrints = this.keys
- .getJSONArray("otr_fingerprints");
- for (int i = 0; i < oldPrints.length(); ++i) {
- if (!oldPrints.getString(i).equals(fingerprint)) {
- newPrints.put(oldPrints.getString(i));
- } else {
- success = true;
- }
- }
- this.keys.put("otr_fingerprints", newPrints);
- }
- return success;
- } catch (JSONException e) {
- return false;
- }
- }
- }
-
- public boolean mutualPresenceSubscription() {
- return getOption(Options.FROM) && getOption(Options.TO);
- }
-
- @Override
- public boolean isBlocked() {
- return getAccount().isBlocked(this);
- }
-
- @Override
- public boolean isDomainBlocked() {
- return getAccount().isBlocked(this.getJid().toDomainJid());
- }
-
- @Override
- public Jid getBlockedJid() {
- if (isDomainBlocked()) {
- return getJid().toDomainJid();
- } else {
- return getJid();
- }
- }
-
- public boolean isSelf() {
- return account.getJid().toBareJid().equals(getJid().toBareJid());
- }
-
- public void setCommonName(String cn) {
- this.commonName = cn;
- }
-
- public void flagActive() {
- this.mActive = true;
- }
-
- public void flagInactive() {
- this.mActive = false;
- }
-
- public boolean isActive() {
- return this.mActive;
- }
-
- public void setLastseen(long timestamp) {
- this.mLastseen = Math.max(timestamp, mLastseen);
- }
-
- public long getLastseen() {
- return this.mLastseen;
- }
-
- public void setLastResource(String resource) {
- this.mLastPresence = resource;
- }
-
- public String getLastResource() {
- return this.mLastPresence;
- }
-
- public final class Options {
- public static final int TO = 0;
- public static final int FROM = 1;
- public static final int ASKING = 2;
- public static final int PREEMPTIVE_GRANT = 3;
- public static final int IN_ROSTER = 4;
- public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
- public static final int DIRTY_PUSH = 6;
- public static final int DIRTY_DELETE = 7;
- }
+ return this.presenceName;
+ } else if (jid.hasLocalpart()) {
+ return jid.getLocalpart();
+ } else {
+ return jid.getDomainpart();
+ }
+ }
+
+ @Override
+ public String getDisplayJid() {
+ if (jid != null) {
+ return jid.toString();
+ } else {
+ return null;
+ }
+ }
+
+ public String getProfilePhoto() {
+ return this.photoUri;
+ }
+
+ public Jid getJid() {
+ return jid;
+ }
+
+ @Override
+ public List<Tag> getTags(Context context) {
+ final ArrayList<Tag> tags = new ArrayList<>();
+ for (final String group : getGroups()) {
+ tags.add(new Tag(group, UIHelper.getColorForName(group)));
+ }
+ Presence.Status status = getShownStatus();
+ if (status != Presence.Status.OFFLINE) {
+ tags.add(UIHelper.getTagForStatus(context, status));
+ }
+ if (isBlocked()) {
+ tags.add(new Tag("blocked", 0xff2e2f3b));
+ }
+ return tags;
+ }
+
+ public boolean match(Context context, String needle) {
+ if (needle == null || needle.isEmpty()) {
+ return true;
+ }
+ needle = needle.toLowerCase(Locale.US).trim();
+ String[] parts = needle.split("\\s+");
+ if (parts.length > 1) {
+ for (int i = 0; i < parts.length; ++i) {
+ if (!match(context, parts[i])) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return jid.toString().contains(needle) ||
+ getDisplayName().toLowerCase(Locale.US).contains(needle) ||
+ matchInTag(context, needle);
+ }
+ }
+
+ private boolean matchInTag(Context context, String needle) {
+ needle = needle.toLowerCase(Locale.US);
+ for (Tag tag : getTags(context)) {
+ if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public ContentValues getContentValues() {
+ synchronized (this.keys) {
+ final ContentValues values = new ContentValues();
+ values.put(ACCOUNT, accountUuid);
+ values.put(SYSTEMNAME, systemName);
+ values.put(SERVERNAME, serverName);
+ values.put(JID, jid.toPreppedString());
+ values.put(OPTIONS, subscription);
+ values.put(SYSTEMACCOUNT, systemAccount);
+ values.put(PHOTOURI, photoUri);
+ values.put(KEYS, keys.toString());
+ values.put(AVATAR, avatar == null ? null : avatar.getFilename());
+ values.put(LAST_PRESENCE, mLastPresence);
+ values.put(LAST_TIME, mLastseen);
+ values.put(GROUPS, groups.toString());
+ return values;
+ }
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public void setAccount(Account account) {
+ this.account = account;
+ this.accountUuid = account.getUuid();
+ }
+
+ public Presences getPresences() {
+ return this.presences;
+ }
+
+ public void updatePresence(final String resource, final Presence presence) {
+ this.presences.updatePresence(resource, presence);
+ }
+
+ public void removePresence(final String resource) {
+ this.presences.removePresence(resource);
+ }
+
+ public void clearPresences() {
+ this.presences.clearPresences();
+ this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
+ }
+
+ public Presence.Status getShownStatus() {
+ return this.presences.getShownStatus();
+ }
+
+ public boolean setPhotoUri(String uri) {
+ if (uri != null && !uri.equals(this.photoUri)) {
+ this.photoUri = uri;
+ return true;
+ } else if (this.photoUri != null && uri == null) {
+ this.photoUri = null;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void setServerName(String serverName) {
+ this.serverName = serverName;
+ }
+
+ public void setSystemName(String systemName) {
+ this.systemName = systemName;
+ }
+
+ public void setPresenceName(String presenceName) {
+ this.presenceName = presenceName;
+ }
+
+ public Uri getSystemAccount() {
+ if (systemAccount == null) {
+ return null;
+ } else {
+ String[] parts = systemAccount.split("#");
+ if (parts.length != 2) {
+ return null;
+ } else {
+ long id = Long.parseLong(parts[0]);
+ return ContactsContract.Contacts.getLookupUri(id, parts[1]);
+ }
+ }
+ }
+
+ public void setSystemAccount(String account) {
+ this.systemAccount = account;
+ }
+
+ public List<String> getGroups() {
+ ArrayList<String> groups = new ArrayList<String>();
+ for (int i = 0; i < this.groups.length(); ++i) {
+ try {
+ groups.add(this.groups.getString(i));
+ } catch (final JSONException ignored) {
+ }
+ }
+ return groups;
+ }
+
+ public ArrayList<String> getOtrFingerprints() {
+ synchronized (this.keys) {
+ final ArrayList<String> fingerprints = new ArrayList<String>();
+ try {
+ if (this.keys.has("otr_fingerprints")) {
+ final JSONArray prints = this.keys.getJSONArray("otr_fingerprints");
+ for (int i = 0; i < prints.length(); ++i) {
+ final String print = prints.isNull(i) ? null : prints.getString(i);
+ if (print != null && !print.isEmpty()) {
+ fingerprints.add(prints.getString(i));
+ }
+ }
+ }
+ } catch (final JSONException ignored) {
+
+ }
+ return fingerprints;
+ }
+ }
+
+ public boolean addOtrFingerprint(String print) {
+ synchronized (this.keys) {
+ if (getOtrFingerprints().contains(print)) {
+ return false;
+ }
+ try {
+ JSONArray fingerprints;
+ if (!this.keys.has("otr_fingerprints")) {
+ fingerprints = new JSONArray();
+ } else {
+ fingerprints = this.keys.getJSONArray("otr_fingerprints");
+ }
+ fingerprints.put(print);
+ this.keys.put("otr_fingerprints", fingerprints);
+ return true;
+ } catch (final JSONException ignored) {
+ return false;
+ }
+ }
+ }
+
+ public long getPgpKeyId() {
+ synchronized (this.keys) {
+ if (this.keys.has("pgp_keyid")) {
+ try {
+ return this.keys.getLong("pgp_keyid");
+ } catch (JSONException e) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ public void setPgpKeyId(long keyId) {
+ synchronized (this.keys) {
+ try {
+ this.keys.put("pgp_keyid", keyId);
+ } catch (final JSONException ignored) {
+ }
+ }
+ }
+
+ public void setOption(int option) {
+ this.subscription |= 1 << option;
+ }
+
+ public void resetOption(int option) {
+ this.subscription &= ~(1 << option);
+ }
+
+ public boolean getOption(int option) {
+ return ((this.subscription & (1 << option)) != 0);
+ }
+
+ public boolean showInRoster() {
+ return (this.getOption(Contact.Options.IN_ROSTER) && (!this
+ .getOption(Contact.Options.DIRTY_DELETE)))
+ || (this.getOption(Contact.Options.DIRTY_PUSH));
+ }
+
+ public void parseSubscriptionFromElement(Element item) {
+ String ask = item.getAttribute("ask");
+ String subscription = item.getAttribute("subscription");
+
+ if (subscription != null) {
+ switch (subscription) {
+ case "to":
+ this.resetOption(Options.FROM);
+ this.setOption(Options.TO);
+ break;
+ case "from":
+ this.resetOption(Options.TO);
+ this.setOption(Options.FROM);
+ this.resetOption(Options.PREEMPTIVE_GRANT);
+ this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
+ break;
+ case "both":
+ this.setOption(Options.TO);
+ this.setOption(Options.FROM);
+ this.resetOption(Options.PREEMPTIVE_GRANT);
+ this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
+ break;
+ case "none":
+ this.resetOption(Options.FROM);
+ this.resetOption(Options.TO);
+ break;
+ }
+ }
+
+ // do NOT override asking if pending push request
+ if (!this.getOption(Contact.Options.DIRTY_PUSH)) {
+ if ((ask != null) && (ask.equals("subscribe"))) {
+ this.setOption(Contact.Options.ASKING);
+ } else {
+ this.resetOption(Contact.Options.ASKING);
+ }
+ }
+ }
+
+ public void parseGroupsFromElement(Element item) {
+ this.groups = new JSONArray();
+ for (Element element : item.getChildren()) {
+ if (element.getName().equals("group") && element.getContent() != null) {
+ this.groups.put(element.getContent());
+ }
+ }
+ }
+
+ public Element asElement() {
+ final Element item = new Element("item");
+ item.setAttribute("jid", this.jid.toString());
+ if (this.serverName != null) {
+ item.setAttribute("name", this.serverName);
+ }
+ for (String group : getGroups()) {
+ item.addChild("group").setContent(group);
+ }
+ return item;
+ }
+
+ @Override
+ public int compareTo(final ListItem another) {
+ return this.getDisplayName().compareToIgnoreCase(
+ another.getDisplayName());
+ }
+
+ public Jid getServer() {
+ return getJid().toDomainJid();
+ }
+
+ public boolean setAvatar(Avatar avatar) {
+ if (this.avatar != null && this.avatar.equals(avatar)) {
+ return false;
+ } else {
+ if (this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) {
+ return false;
+ }
+ this.avatar = avatar;
+ return true;
+ }
+ }
+
+ public String getAvatar() {
+ return avatar == null ? null : avatar.getFilename();
+ }
+
+ public boolean deleteOtrFingerprint(String fingerprint) {
+ synchronized (this.keys) {
+ boolean success = false;
+ try {
+ if (this.keys.has("otr_fingerprints")) {
+ JSONArray newPrints = new JSONArray();
+ JSONArray oldPrints = this.keys
+ .getJSONArray("otr_fingerprints");
+ for (int i = 0; i < oldPrints.length(); ++i) {
+ if (!oldPrints.getString(i).equals(fingerprint)) {
+ newPrints.put(oldPrints.getString(i));
+ } else {
+ success = true;
+ }
+ }
+ this.keys.put("otr_fingerprints", newPrints);
+ }
+ return success;
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+ }
+
+ public boolean mutualPresenceSubscription() {
+ return getOption(Options.FROM) && getOption(Options.TO);
+ }
+
+ @Override
+ public boolean isBlocked() {
+ return getAccount().isBlocked(this);
+ }
+
+ @Override
+ public boolean isDomainBlocked() {
+ return getAccount().isBlocked(this.getJid().toDomainJid());
+ }
+
+ @Override
+ public Jid getBlockedJid() {
+ if (isDomainBlocked()) {
+ return getJid().toDomainJid();
+ } else {
+ return getJid();
+ }
+ }
+
+ public boolean isSelf() {
+ return account.getJid().toBareJid().equals(getJid().toBareJid());
+ }
+
+ public void setCommonName(String cn) {
+ this.commonName = cn;
+ }
+
+ public void flagActive() {
+ this.mActive = true;
+ }
+
+ public void flagInactive() {
+ this.mActive = false;
+ }
+
+ public boolean isActive() {
+ return this.mActive;
+ }
+
+ public void setLastseen(long timestamp) {
+ this.mLastseen = Math.max(timestamp, mLastseen);
+ }
+
+ public long getLastseen() {
+ return this.mLastseen;
+ }
+
+ public void setLastResource(String resource) {
+ this.mLastPresence = resource;
+ }
+
+ public String getLastResource() {
+ return this.mLastPresence;
+ }
+
+ public final class Options {
+ public static final int TO = 0;
+ public static final int FROM = 1;
+ public static final int ASKING = 2;
+ public static final int PREEMPTIVE_GRANT = 3;
+ public static final int IN_ROSTER = 4;
+ public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
+ public static final int DIRTY_PUSH = 6;
+ public static final int DIRTY_DELETE = 7;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java
index 5a3dd4cf4..f9932f67d 100644
--- a/src/main/java/de/pixart/messenger/entities/Conversation.java
+++ b/src/main/java/de/pixart/messenger/entities/Conversation.java
@@ -29,948 +29,948 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class Conversation extends AbstractEntity implements Blockable, Comparable<Conversation> {
- public static final String TABLENAME = "conversations";
-
- public static final int STATUS_AVAILABLE = 0;
- public static final int STATUS_ARCHIVED = 1;
- public static final int STATUS_DELETED = 2;
-
- public static final int MODE_MULTI = 1;
- public static final int MODE_SINGLE = 0;
-
- public static final String NAME = "name";
- public static final String ACCOUNT = "accountUuid";
- public static final String CONTACT = "contactUuid";
- public static final String CONTACTJID = "contactJid";
- public static final String STATUS = "status";
- public static final String CREATED = "created";
- public static final String MODE = "mode";
- public static final String ATTRIBUTES = "attributes";
-
- public static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
- public static final String ATTRIBUTE_MUC_PASSWORD = "muc_password";
- public static final String ATTRIBUTE_MUTED_TILL = "muted_till";
- public static final String ATTRIBUTE_ALWAYS_NOTIFY = "always_notify";
- public static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets";
- public static final String ATTRIBUTE_LAST_CLEAR_HISTORY = "last_clear_history";
-
- private String name;
- private String contactUuid;
- private String accountUuid;
- private Jid contactJid;
- private int status;
- private long created;
- private int mode;
-
- private JSONObject attributes = new JSONObject();
-
- private Jid nextCounterpart;
-
- protected final ArrayList<Message> messages = new ArrayList<>();
- protected Account account = null;
-
- private transient SessionImpl otrSession;
-
- private transient String otrFingerprint = null;
- private Smp mSmp = new Smp();
-
- private String nextMessage;
-
- private transient MucOptions mucOptions = null;
-
- private byte[] symmetricKey;
-
- private Bookmark bookmark;
-
- private boolean messagesLeftOnServer = true;
- private ChatState mOutgoingChatState = Config.DEFAULT_CHATSTATE;
- private ChatState mIncomingChatState = Config.DEFAULT_CHATSTATE;
- private String mLastReceivedOtrMessageId = null;
- private String mFirstMamReference = null;
- private Message correctingMessage;
-
- public boolean hasMessagesLeftOnServer() {
- return messagesLeftOnServer;
- }
-
- public void setHasMessagesLeftOnServer(boolean value) {
- this.messagesLeftOnServer = value;
- }
-
-
- public Message getFirstUnreadMessage() {
- Message first = null;
- synchronized (this.messages) {
- for (int i = messages.size() - 1; i >= 0; --i) {
- if (messages.get(i).isRead()) {
- return first;
- } else {
- first = messages.get(i);
- }
- }
- }
- return first;
- }
-
- public Message findUnsentMessageWithUuid(String uuid) {
- synchronized(this.messages) {
- for (final Message message : this.messages) {
- final int s = message.getStatus();
- if ((s == Message.STATUS_UNSEND || s == Message.STATUS_WAITING) && message.getUuid().equals(uuid)) {
- return message;
- }
- }
- }
- return null;
- }
-
- public void findWaitingMessages(OnMessageFound onMessageFound) {
- synchronized (this.messages) {
- for(Message message : this.messages) {
- if (message.getStatus() == Message.STATUS_WAITING) {
- onMessageFound.onMessageFound(message);
- }
- }
- }
- }
-
- public void findUnreadMessages(OnMessageFound onMessageFound) {
- synchronized (this.messages) {
- for(Message message : this.messages) {
- if (!message.isRead()) {
- onMessageFound.onMessageFound(message);
- }
- }
- }
- }
-
- public void findMessagesWithFiles(final OnMessageFound onMessageFound) {
- synchronized (this.messages) {
- for (final Message message : this.messages) {
- if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE)
- && message.getEncryption() != Message.ENCRYPTION_PGP) {
- onMessageFound.onMessageFound(message);
- }
- }
- }
- }
-
- public Message findMessageWithFileAndUuid(final String uuid) {
- synchronized (this.messages) {
- for (final Message message : this.messages) {
- if (message.getUuid().equals(uuid)
- && message.getEncryption() != Message.ENCRYPTION_PGP
- && (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.treatAsDownloadable() != Message.Decision.NEVER)) {
- return message;
- }
- }
- }
- return null;
- }
-
- public void clearMessages() {
- synchronized (this.messages) {
- this.messages.clear();
- }
- }
-
- public boolean setIncomingChatState(ChatState state) {
- if (this.mIncomingChatState == state) {
- return false;
- }
- this.mIncomingChatState = state;
- return true;
- }
-
- public ChatState getIncomingChatState() {
- return this.mIncomingChatState;
- }
-
- public boolean setOutgoingChatState(ChatState state) {
- if (mode == MODE_MULTI) {
- return false;
- }
- if (this.mOutgoingChatState != state) {
- this.mOutgoingChatState = state;
- return true;
- } else {
- return false;
- }
- }
-
- public ChatState getOutgoingChatState() {
- return this.mOutgoingChatState;
- }
-
- public void trim() {
- synchronized (this.messages) {
- final int size = messages.size();
- final int maxsize = Config.PAGE_SIZE * Config.MAX_NUM_PAGES;
- if (size > maxsize) {
- List<Message> discards = this.messages.subList(0, size - maxsize);
- final PgpDecryptionService pgpDecryptionService = account.getPgpDecryptionService();
- if (pgpDecryptionService != null) {
- pgpDecryptionService.discard(discards);
- }
- discards.clear();
- untieMessages();
- }
- }
- }
-
- public void findUnsentMessagesWithEncryption(int encryptionType, OnMessageFound onMessageFound) {
- synchronized (this.messages) {
- for (Message message : this.messages) {
- if ((message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_WAITING)
- && (message.getEncryption() == encryptionType)) {
- onMessageFound.onMessageFound(message);
- }
- }
- }
- }
-
- public void findUnsentTextMessages(OnMessageFound onMessageFound) {
- synchronized (this.messages) {
- for (Message message : this.messages) {
- if (message.getType() != Message.TYPE_IMAGE
- && message.getStatus() == Message.STATUS_UNSEND) {
- onMessageFound.onMessageFound(message);
- }
- }
- }
- }
-
- public Message findSentMessageWithUuidOrRemoteId(String id) {
- synchronized (this.messages) {
- for (Message message : this.messages) {
- if (id.equals(message.getUuid())
- || (message.getStatus() >= Message.STATUS_SEND
- && id.equals(message.getRemoteMsgId()))) {
- return message;
- }
- }
- }
- return null;
- }
-
- public Message findMessageWithRemoteIdAndCounterpart(String id, Jid counterpart, boolean received, boolean carbon) {
- synchronized (this.messages) {
- for(int i = this.messages.size() - 1; i >= 0; --i) {
- Message message = messages.get(i);
- if (counterpart.equals(message.getCounterpart())
- && ((message.getStatus() == Message.STATUS_RECEIVED) == received)
- && (carbon == message.isCarbon() || received) ) {
- if (id.equals(message.getRemoteMsgId())) {
- return message;
- } else {
- return null;
- }
- }
- }
- }
- return null;
- }
-
- public Message findSentMessageWithUuid(String id) {
- synchronized (this.messages) {
- for (Message message : this.messages) {
- if (id.equals(message.getUuid())) {
- return message;
- }
- }
- }
- return null;
- }
-
- public void populateWithMessages(final List<Message> messages) {
- synchronized (this.messages) {
- messages.clear();
- messages.addAll(this.messages);
- }
- for(Iterator<Message> iterator = messages.iterator(); iterator.hasNext();) {
- if (iterator.next().wasMergedIntoPrevious()) {
- iterator.remove();
- }
- }
- }
-
- @Override
- public boolean isBlocked() {
- return getContact().isBlocked();
- }
-
- @Override
- public boolean isDomainBlocked() {
- return getContact().isDomainBlocked();
- }
-
- @Override
- public Jid getBlockedJid() {
- return getContact().getBlockedJid();
- }
-
- public String getLastReceivedOtrMessageId() {
- return this.mLastReceivedOtrMessageId;
- }
-
- public void setLastReceivedOtrMessageId(String id) {
- this.mLastReceivedOtrMessageId = id;
- }
-
- public int countMessages() {
- synchronized (this.messages) {
- return this.messages.size();
- }
- }
-
- public void setFirstMamReference(String reference) {
- this.mFirstMamReference = reference;
- }
-
- public String getFirstMamReference() {
- return this.mFirstMamReference;
- }
-
- public void setLastClearHistory(long time) {
- setAttribute("ATTRIBUTE_LAST_CLEAR_HISTORY",String.valueOf(time));
- }
-
- public long getLastClearHistory() {
- return getLongAttribute("ATTRIBUTE_LAST_CLEAR_HISTORY", 0);
- }
-
- public List<Jid> getAcceptedCryptoTargets() {
- if (mode == MODE_SINGLE) {
- return Arrays.asList(getJid().toBareJid());
- } else {
- return getJidListAttribute(ATTRIBUTE_CRYPTO_TARGETS);
- }
- }
-
- public void setAcceptedCryptoTargets(List<Jid> acceptedTargets) {
- setAttribute(ATTRIBUTE_CRYPTO_TARGETS, acceptedTargets);
- }
-
- public void setCorrectingMessage(Message correctingMessage) {
- this.correctingMessage = correctingMessage;
- }
-
- public Message getCorrectingMessage() {
- return this.correctingMessage;
- }
-
- public boolean withSelf() {
- return getContact().isSelf();
- }
-
- @Override
- public int compareTo(Conversation another) {
- final Message left = getLatestMessage();
- final Message right = another.getLatestMessage();
- if (left.getTimeSent() > right.getTimeSent()) {
- return -1;
- } else if (left.getTimeSent() < right.getTimeSent()) {
- return 1;
- } else {
- return 0;
- }
- }
-
- public interface OnMessageFound {
- void onMessageFound(final Message message);
- }
-
- public Conversation(final String name, final Account account, final Jid contactJid,
- final int mode) {
- this(java.util.UUID.randomUUID().toString(), name, null, account
- .getUuid(), contactJid, System.currentTimeMillis(),
- STATUS_AVAILABLE, mode, "");
- this.account = account;
- }
-
- public Conversation(final String uuid, final String name, final String contactUuid,
- final String accountUuid, final Jid contactJid, final long created, final int status,
- final int mode, final String attributes) {
- this.uuid = uuid;
- this.name = name;
- this.contactUuid = contactUuid;
- this.accountUuid = accountUuid;
- this.contactJid = contactJid;
- this.created = created;
- this.status = status;
- this.mode = mode;
- try {
- this.attributes = new JSONObject(attributes == null ? "" : attributes);
- } catch (JSONException e) {
- this.attributes = new JSONObject();
- }
- }
-
- public boolean isRead() {
- return (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead();
- }
-
- public List<Message> markRead() {
- final List<Message> unread = new ArrayList<>();
- synchronized (this.messages) {
- for(Message message : this.messages) {
- if (!message.isRead()) {
- message.markRead();
- unread.add(message);
- }
- }
- }
- return unread;
- }
-
- public Message getLatestMarkableMessage() {
- for (int i = this.messages.size() - 1; i >= 0; --i) {
- if (this.messages.get(i).getStatus() <= Message.STATUS_RECEIVED
- && this.messages.get(i).markable) {
- if (this.messages.get(i).isRead()) {
- return null;
- } else {
- return this.messages.get(i);
- }
- }
- }
- return null;
- }
-
- public Message getLatestMessage() {
- if (this.messages.size() == 0) {
- Message message = new Message(this, "", Message.ENCRYPTION_NONE);
- message.setTime(getCreated());
- return message;
- } else {
- Message message = this.messages.get(this.messages.size() - 1);
- message.setConversation(this);
- return message;
- }
- }
-
- public String getName() {
- if (getMode() == MODE_MULTI) {
- if (getMucOptions().getSubject() != null) {
- return getMucOptions().getSubject();
- } else if (bookmark != null
- && bookmark.getBookmarkName() != null
- && !bookmark.getBookmarkName().trim().isEmpty()) {
- return bookmark.getBookmarkName().trim();
- } else {
- String generatedName = getMucOptions().createNameFromParticipants();
- if (generatedName != null) {
- return generatedName;
- } else {
- return getJid().getLocalpart();
- }
- }
- } else {
- return this.getContact().getDisplayName();
- }
- }
-
- public String getParticipants() {
- if (getMode() == MODE_MULTI) {
- String generatedName = getMucOptions().createNameFromParticipants();
- if (generatedName != null) {
- return generatedName;
- } else {
- return null;
- }
- } else {
- return null;
- }
- }
-
- public String getAccountUuid() {
- return this.accountUuid;
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public Contact getContact() {
- return this.account.getRoster().getContact(this.contactJid);
- }
-
- public void setAccount(final Account account) {
- this.account = account;
- }
-
- @Override
- public Jid getJid() {
- return this.contactJid;
- }
-
- public int getStatus() {
- return this.status;
- }
-
- public long getCreated() {
- return this.created;
- }
-
- public ContentValues getContentValues() {
- ContentValues values = new ContentValues();
- values.put(UUID, uuid);
- values.put(NAME, name);
- values.put(CONTACT, contactUuid);
- values.put(ACCOUNT, accountUuid);
- values.put(CONTACTJID, contactJid.toPreppedString());
- values.put(CREATED, created);
- values.put(STATUS, status);
- values.put(MODE, mode);
- values.put(ATTRIBUTES, attributes.toString());
- return values;
- }
-
- public static Conversation fromCursor(Cursor cursor) {
- Jid jid;
- try {
- jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(CONTACTJID)), true);
- } catch (final InvalidJidException e) {
- // Borked DB..
- jid = null;
- }
- return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
- cursor.getString(cursor.getColumnIndex(NAME)),
- cursor.getString(cursor.getColumnIndex(CONTACT)),
- cursor.getString(cursor.getColumnIndex(ACCOUNT)),
- jid,
- cursor.getLong(cursor.getColumnIndex(CREATED)),
- cursor.getInt(cursor.getColumnIndex(STATUS)),
- cursor.getInt(cursor.getColumnIndex(MODE)),
- cursor.getString(cursor.getColumnIndex(ATTRIBUTES)));
- }
-
- public void setStatus(int status) {
- this.status = status;
- }
-
- public int getMode() {
- return this.mode;
- }
-
- public void setMode(int mode) {
- this.mode = mode;
- }
-
- public SessionImpl startOtrSession(String presence, boolean sendStart) {
- if (this.otrSession != null) {
- return this.otrSession;
- } else {
- final SessionID sessionId = new SessionID(this.getJid().toBareJid().toString(),
- presence,
- "xmpp");
- this.otrSession = new SessionImpl(sessionId, getAccount().getOtrService());
- try {
- if (sendStart) {
- this.otrSession.startSession();
- return this.otrSession;
- }
- return this.otrSession;
- } catch (OtrException e) {
- return null;
- }
- }
-
- }
-
- public SessionImpl getOtrSession() {
- return this.otrSession;
- }
-
- public void resetOtrSession() {
- this.otrFingerprint = null;
- this.otrSession = null;
- this.mSmp.hint = null;
- this.mSmp.secret = null;
- this.mSmp.status = Smp.STATUS_NONE;
- }
-
- public Smp smp() {
- return mSmp;
- }
-
- public boolean startOtrIfNeeded() {
- if (this.otrSession != null && this.otrSession.getSessionStatus() != SessionStatus.ENCRYPTED) {
- try {
- this.otrSession.startSession();
- return true;
- } catch (OtrException e) {
- this.resetOtrSession();
- return false;
- }
- } else {
- return true;
- }
- }
-
- public boolean endOtrIfNeeded() {
- if (this.otrSession != null) {
- if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
- try {
- this.otrSession.endSession();
- this.resetOtrSession();
- return true;
- } catch (OtrException e) {
- this.resetOtrSession();
- return false;
- }
- } else {
- this.resetOtrSession();
- return false;
- }
- } else {
- return false;
- }
- }
-
- public boolean hasValidOtrSession() {
- return this.otrSession != null;
- }
-
- public synchronized String getOtrFingerprint() {
- if (this.otrFingerprint == null) {
- try {
- if (getOtrSession() == null || getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
- return null;
- }
- DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession().getRemotePublicKey();
- this.otrFingerprint = getAccount().getOtrService().getFingerprint(remotePubKey);
- } catch (final OtrCryptoException | UnsupportedOperationException ignored) {
- return null;
- }
- }
- return this.otrFingerprint;
- }
-
- public boolean verifyOtrFingerprint() {
- final String fingerprint = getOtrFingerprint();
- if (fingerprint != null) {
- getContact().addOtrFingerprint(fingerprint);
- return true;
- } else {
- return false;
- }
- }
-
- public boolean isOtrFingerprintVerified() {
- return getContact().getOtrFingerprints().contains(getOtrFingerprint());
- }
-
- /**
- * short for is Private and Non-anonymous
- */
- private boolean isPnNA() {
- return mode == MODE_SINGLE || (getMucOptions().membersOnly() && getMucOptions().nonanonymous());
- }
-
- public synchronized MucOptions getMucOptions() {
- if (this.mucOptions == null) {
- this.mucOptions = new MucOptions(this);
- }
- return this.mucOptions;
- }
-
- public void resetMucOptions() {
- this.mucOptions = null;
- }
-
- public void setContactJid(final Jid jid) {
- this.contactJid = jid;
- }
-
- public void setNextCounterpart(Jid jid) {
- this.nextCounterpart = jid;
- }
-
- public Jid getNextCounterpart() {
- return this.nextCounterpart;
- }
-
- private int getMostRecentlyUsedIncomingEncryption() {
- synchronized (this.messages) {
- for(int i = this.messages.size() -1; i >= 0; --i) {
- final Message m = this.messages.get(i);
- if (m.getStatus() == Message.STATUS_RECEIVED) {
- final int e = m.getEncryption();
- if (e == Message.ENCRYPTION_DECRYPTED || e == Message.ENCRYPTION_DECRYPTION_FAILED) {
- return Message.ENCRYPTION_PGP;
- } else {
- return e;
- }
- }
- }
- }
- return Message.ENCRYPTION_NONE;
- }
-
- public int getNextEncryption() {
- return Math.max(this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, Message.ENCRYPTION_NONE), Message.ENCRYPTION_NONE);
- }
-
- public void setNextEncryption(int encryption) {
- this.setAttribute(ATTRIBUTE_NEXT_ENCRYPTION, String.valueOf(encryption));
- }
-
- public String getNextMessage() {
- if (this.nextMessage == null) {
- return "";
- } else {
- return this.nextMessage;
- }
- }
-
- public boolean smpRequested() {
- return smp().status == Smp.STATUS_CONTACT_REQUESTED;
- }
-
- public void setNextMessage(String message) {
- this.nextMessage = message;
- }
-
- public void setSymmetricKey(byte[] key) {
- this.symmetricKey = key;
- }
-
- public byte[] getSymmetricKey() {
- return this.symmetricKey;
- }
-
- public void setBookmark(Bookmark bookmark) {
- this.bookmark = bookmark;
- this.bookmark.setConversation(this);
- }
-
- public void deregisterWithBookmark() {
- if (this.bookmark != null) {
- this.bookmark.setConversation(null);
- }
- }
-
- public Bookmark getBookmark() {
- return this.bookmark;
- }
-
- public boolean hasDuplicateMessage(Message message) {
- synchronized (this.messages) {
- for (int i = this.messages.size() - 1; i >= 0; --i) {
- if (this.messages.get(i).similar(message)) {
- return true;
- }
- }
- }
- return false;
- }
-
- public Message findSentMessageWithBody(String body) {
- synchronized (this.messages) {
- for (int i = this.messages.size() - 1; i >= 0; --i) {
- Message message = this.messages.get(i);
- if (message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_SEND) {
- String otherBody;
- if (message.hasFileOnRemoteHost()) {
- otherBody = message.getFileParams().url.toString();
- } else {
- otherBody = message.body;
- }
- if (otherBody != null && otherBody.equals(body)) {
- return message;
- }
- }
- }
- return null;
- }
- }
-
- public long getLastMessageTransmitted() {
- final long last_clear = getLastClearHistory();
- long last_received = 0;
- synchronized (this.messages) {
- for(int i = this.messages.size() - 1; i >= 0; --i) {
- Message message = this.messages.get(i);
- if (message.getStatus() == Message.STATUS_RECEIVED || message.isCarbon()) {
- last_received = message.getTimeSent();
- break;
- }
- }
- }
- return Math.max(last_clear,last_received);
- }
-
- public void setMutedTill(long value) {
- this.setAttribute(ATTRIBUTE_MUTED_TILL, String.valueOf(value));
- }
-
- public boolean isMuted() {
- return System.currentTimeMillis() < this.getLongAttribute(ATTRIBUTE_MUTED_TILL, 0);
- }
-
- public boolean alwaysNotify() {
- return mode == MODE_SINGLE || getBooleanAttribute(ATTRIBUTE_ALWAYS_NOTIFY, Config.ALWAYS_NOTIFY_BY_DEFAULT || isPnNA());
- }
-
- public boolean setAttribute(String key, String value) {
- synchronized (this.attributes) {
- try {
- this.attributes.put(key, value);
- return true;
- } catch (JSONException e) {
- return false;
- }
- }
- }
-
- public boolean setAttribute(String key, List<Jid> jids) {
- JSONArray array = new JSONArray();
- for(Jid jid : jids) {
- array.put(jid.toBareJid().toString());
- }
- synchronized (this.attributes) {
- try {
- this.attributes.put(key, array);
- return true;
- } catch (JSONException e) {
- e.printStackTrace();
- return false;
- }
- }
- }
-
- public String getAttribute(String key) {
- synchronized (this.attributes) {
- try {
- return this.attributes.getString(key);
- } catch (JSONException e) {
- return null;
- }
- }
- }
-
- public List<Jid> getJidListAttribute(String key) {
- ArrayList<Jid> list = new ArrayList<>();
- synchronized (this.attributes) {
- try {
- JSONArray array = this.attributes.getJSONArray(key);
- for (int i = 0; i < array.length(); ++i) {
- try {
- list.add(Jid.fromString(array.getString(i)));
- } catch (InvalidJidException e) {
- //ignored
- }
- }
- } catch (JSONException e) {
- //ignored
- }
- }
- return list;
- }
-
- public int getIntAttribute(String key, int defaultValue) {
- String value = this.getAttribute(key);
- if (value == null) {
- return defaultValue;
- } else {
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
- }
-
- public long getLongAttribute(String key, long defaultValue) {
- String value = this.getAttribute(key);
- if (value == null) {
- return defaultValue;
- } else {
- try {
- return Long.parseLong(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
- }
-
- public boolean getBooleanAttribute(String key, boolean defaultValue) {
- String value = this.getAttribute(key);
- if (value == null) {
- return defaultValue;
- } else {
- return Boolean.parseBoolean(value);
- }
- }
-
- public void add(Message message) {
- message.setConversation(this);
- synchronized (this.messages) {
- this.messages.add(message);
- }
- }
-
- public void prepend(Message message) {
- message.setConversation(this);
- synchronized (this.messages) {
- this.messages.add(0,message);
- }
- }
-
- public void addAll(int index, List<Message> messages) {
- synchronized (this.messages) {
- this.messages.addAll(index, messages);
- }
- account.getPgpDecryptionService().decrypt(messages);
- }
-
- public void sort() {
- synchronized (this.messages) {
- Collections.sort(this.messages, new Comparator<Message>() {
- @Override
- public int compare(Message left, Message right) {
- if (left.getTimeSent() < right.getTimeSent()) {
- return -1;
- } else if (left.getTimeSent() > right.getTimeSent()) {
- return 1;
- } else {
- return 0;
- }
- }
- });
- untieMessages();
- }
- }
-
- private void untieMessages() {
- for(Message message : this.messages) {
- message.untie();
- }
- }
-
- public int unreadCount() {
- synchronized (this.messages) {
- int count = 0;
- for(int i = this.messages.size() - 1; i >= 0; --i) {
- if (this.messages.get(i).isRead()) {
- return count;
- }
- ++count;
- }
- return count;
- }
- }
-
- public class Smp {
- public static final int STATUS_NONE = 0;
- public static final int STATUS_CONTACT_REQUESTED = 1;
- public static final int STATUS_WE_REQUESTED = 2;
- public static final int STATUS_FAILED = 3;
- public static final int STATUS_VERIFIED = 4;
-
- public String secret = null;
- public String hint = null;
- public int status = 0;
- }
+ public static final String TABLENAME = "conversations";
+
+ public static final int STATUS_AVAILABLE = 0;
+ public static final int STATUS_ARCHIVED = 1;
+ public static final int STATUS_DELETED = 2;
+
+ public static final int MODE_MULTI = 1;
+ public static final int MODE_SINGLE = 0;
+
+ public static final String NAME = "name";
+ public static final String ACCOUNT = "accountUuid";
+ public static final String CONTACT = "contactUuid";
+ public static final String CONTACTJID = "contactJid";
+ public static final String STATUS = "status";
+ public static final String CREATED = "created";
+ public static final String MODE = "mode";
+ public static final String ATTRIBUTES = "attributes";
+
+ public static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
+ public static final String ATTRIBUTE_MUC_PASSWORD = "muc_password";
+ public static final String ATTRIBUTE_MUTED_TILL = "muted_till";
+ public static final String ATTRIBUTE_ALWAYS_NOTIFY = "always_notify";
+ public static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets";
+ public static final String ATTRIBUTE_LAST_CLEAR_HISTORY = "last_clear_history";
+
+ private String name;
+ private String contactUuid;
+ private String accountUuid;
+ private Jid contactJid;
+ private int status;
+ private long created;
+ private int mode;
+
+ private JSONObject attributes = new JSONObject();
+
+ private Jid nextCounterpart;
+
+ protected final ArrayList<Message> messages = new ArrayList<>();
+ protected Account account = null;
+
+ private transient SessionImpl otrSession;
+
+ private transient String otrFingerprint = null;
+ private Smp mSmp = new Smp();
+
+ private String nextMessage;
+
+ private transient MucOptions mucOptions = null;
+
+ private byte[] symmetricKey;
+
+ private Bookmark bookmark;
+
+ private boolean messagesLeftOnServer = true;
+ private ChatState mOutgoingChatState = Config.DEFAULT_CHATSTATE;
+ private ChatState mIncomingChatState = Config.DEFAULT_CHATSTATE;
+ private String mLastReceivedOtrMessageId = null;
+ private String mFirstMamReference = null;
+ private Message correctingMessage;
+
+ public boolean hasMessagesLeftOnServer() {
+ return messagesLeftOnServer;
+ }
+
+ public void setHasMessagesLeftOnServer(boolean value) {
+ this.messagesLeftOnServer = value;
+ }
+
+
+ public Message getFirstUnreadMessage() {
+ Message first = null;
+ synchronized (this.messages) {
+ for (int i = messages.size() - 1; i >= 0; --i) {
+ if (messages.get(i).isRead()) {
+ return first;
+ } else {
+ first = messages.get(i);
+ }
+ }
+ }
+ return first;
+ }
+
+ public Message findUnsentMessageWithUuid(String uuid) {
+ synchronized (this.messages) {
+ for (final Message message : this.messages) {
+ final int s = message.getStatus();
+ if ((s == Message.STATUS_UNSEND || s == Message.STATUS_WAITING) && message.getUuid().equals(uuid)) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void findWaitingMessages(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (message.getStatus() == Message.STATUS_WAITING) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public void findUnreadMessages(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (!message.isRead()) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public void findMessagesWithFiles(final OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (final Message message : this.messages) {
+ if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE)
+ && message.getEncryption() != Message.ENCRYPTION_PGP) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public Message findMessageWithFileAndUuid(final String uuid) {
+ synchronized (this.messages) {
+ for (final Message message : this.messages) {
+ if (message.getUuid().equals(uuid)
+ && message.getEncryption() != Message.ENCRYPTION_PGP
+ && (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.treatAsDownloadable() != Message.Decision.NEVER)) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void clearMessages() {
+ synchronized (this.messages) {
+ this.messages.clear();
+ }
+ }
+
+ public boolean setIncomingChatState(ChatState state) {
+ if (this.mIncomingChatState == state) {
+ return false;
+ }
+ this.mIncomingChatState = state;
+ return true;
+ }
+
+ public ChatState getIncomingChatState() {
+ return this.mIncomingChatState;
+ }
+
+ public boolean setOutgoingChatState(ChatState state) {
+ if (mode == MODE_MULTI) {
+ return false;
+ }
+ if (this.mOutgoingChatState != state) {
+ this.mOutgoingChatState = state;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public ChatState getOutgoingChatState() {
+ return this.mOutgoingChatState;
+ }
+
+ public void trim() {
+ synchronized (this.messages) {
+ final int size = messages.size();
+ final int maxsize = Config.PAGE_SIZE * Config.MAX_NUM_PAGES;
+ if (size > maxsize) {
+ List<Message> discards = this.messages.subList(0, size - maxsize);
+ final PgpDecryptionService pgpDecryptionService = account.getPgpDecryptionService();
+ if (pgpDecryptionService != null) {
+ pgpDecryptionService.discard(discards);
+ }
+ discards.clear();
+ untieMessages();
+ }
+ }
+ }
+
+ public void findUnsentMessagesWithEncryption(int encryptionType, OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if ((message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_WAITING)
+ && (message.getEncryption() == encryptionType)) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public void findUnsentTextMessages(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (message.getType() != Message.TYPE_IMAGE
+ && message.getStatus() == Message.STATUS_UNSEND) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public Message findSentMessageWithUuidOrRemoteId(String id) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (id.equals(message.getUuid())
+ || (message.getStatus() >= Message.STATUS_SEND
+ && id.equals(message.getRemoteMsgId()))) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public Message findMessageWithRemoteIdAndCounterpart(String id, Jid counterpart, boolean received, boolean carbon) {
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ Message message = messages.get(i);
+ if (counterpart.equals(message.getCounterpart())
+ && ((message.getStatus() == Message.STATUS_RECEIVED) == received)
+ && (carbon == message.isCarbon() || received)) {
+ if (id.equals(message.getRemoteMsgId())) {
+ return message;
+ } else {
+ return null;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public Message findSentMessageWithUuid(String id) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (id.equals(message.getUuid())) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void populateWithMessages(final List<Message> messages) {
+ synchronized (this.messages) {
+ messages.clear();
+ messages.addAll(this.messages);
+ }
+ for (Iterator<Message> iterator = messages.iterator(); iterator.hasNext(); ) {
+ if (iterator.next().wasMergedIntoPrevious()) {
+ iterator.remove();
+ }
+ }
+ }
+
+ @Override
+ public boolean isBlocked() {
+ return getContact().isBlocked();
+ }
+
+ @Override
+ public boolean isDomainBlocked() {
+ return getContact().isDomainBlocked();
+ }
+
+ @Override
+ public Jid getBlockedJid() {
+ return getContact().getBlockedJid();
+ }
+
+ public String getLastReceivedOtrMessageId() {
+ return this.mLastReceivedOtrMessageId;
+ }
+
+ public void setLastReceivedOtrMessageId(String id) {
+ this.mLastReceivedOtrMessageId = id;
+ }
+
+ public int countMessages() {
+ synchronized (this.messages) {
+ return this.messages.size();
+ }
+ }
+
+ public void setFirstMamReference(String reference) {
+ this.mFirstMamReference = reference;
+ }
+
+ public String getFirstMamReference() {
+ return this.mFirstMamReference;
+ }
+
+ public void setLastClearHistory(long time) {
+ setAttribute("ATTRIBUTE_LAST_CLEAR_HISTORY", String.valueOf(time));
+ }
+
+ public long getLastClearHistory() {
+ return getLongAttribute("ATTRIBUTE_LAST_CLEAR_HISTORY", 0);
+ }
+
+ public List<Jid> getAcceptedCryptoTargets() {
+ if (mode == MODE_SINGLE) {
+ return Arrays.asList(getJid().toBareJid());
+ } else {
+ return getJidListAttribute(ATTRIBUTE_CRYPTO_TARGETS);
+ }
+ }
+
+ public void setAcceptedCryptoTargets(List<Jid> acceptedTargets) {
+ setAttribute(ATTRIBUTE_CRYPTO_TARGETS, acceptedTargets);
+ }
+
+ public void setCorrectingMessage(Message correctingMessage) {
+ this.correctingMessage = correctingMessage;
+ }
+
+ public Message getCorrectingMessage() {
+ return this.correctingMessage;
+ }
+
+ public boolean withSelf() {
+ return getContact().isSelf();
+ }
+
+ @Override
+ public int compareTo(Conversation another) {
+ final Message left = getLatestMessage();
+ final Message right = another.getLatestMessage();
+ if (left.getTimeSent() > right.getTimeSent()) {
+ return -1;
+ } else if (left.getTimeSent() < right.getTimeSent()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public interface OnMessageFound {
+ void onMessageFound(final Message message);
+ }
+
+ public Conversation(final String name, final Account account, final Jid contactJid,
+ final int mode) {
+ this(java.util.UUID.randomUUID().toString(), name, null, account
+ .getUuid(), contactJid, System.currentTimeMillis(),
+ STATUS_AVAILABLE, mode, "");
+ this.account = account;
+ }
+
+ public Conversation(final String uuid, final String name, final String contactUuid,
+ final String accountUuid, final Jid contactJid, final long created, final int status,
+ final int mode, final String attributes) {
+ this.uuid = uuid;
+ this.name = name;
+ this.contactUuid = contactUuid;
+ this.accountUuid = accountUuid;
+ this.contactJid = contactJid;
+ this.created = created;
+ this.status = status;
+ this.mode = mode;
+ try {
+ this.attributes = new JSONObject(attributes == null ? "" : attributes);
+ } catch (JSONException e) {
+ this.attributes = new JSONObject();
+ }
+ }
+
+ public boolean isRead() {
+ return (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead();
+ }
+
+ public List<Message> markRead() {
+ final List<Message> unread = new ArrayList<>();
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (!message.isRead()) {
+ message.markRead();
+ unread.add(message);
+ }
+ }
+ }
+ return unread;
+ }
+
+ public Message getLatestMarkableMessage() {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ if (this.messages.get(i).getStatus() <= Message.STATUS_RECEIVED
+ && this.messages.get(i).markable) {
+ if (this.messages.get(i).isRead()) {
+ return null;
+ } else {
+ return this.messages.get(i);
+ }
+ }
+ }
+ return null;
+ }
+
+ public Message getLatestMessage() {
+ if (this.messages.size() == 0) {
+ Message message = new Message(this, "", Message.ENCRYPTION_NONE);
+ message.setTime(getCreated());
+ return message;
+ } else {
+ Message message = this.messages.get(this.messages.size() - 1);
+ message.setConversation(this);
+ return message;
+ }
+ }
+
+ public String getName() {
+ if (getMode() == MODE_MULTI) {
+ if (getMucOptions().getSubject() != null) {
+ return getMucOptions().getSubject();
+ } else if (bookmark != null
+ && bookmark.getBookmarkName() != null
+ && !bookmark.getBookmarkName().trim().isEmpty()) {
+ return bookmark.getBookmarkName().trim();
+ } else {
+ String generatedName = getMucOptions().createNameFromParticipants();
+ if (generatedName != null) {
+ return generatedName;
+ } else {
+ return getJid().getLocalpart();
+ }
+ }
+ } else {
+ return this.getContact().getDisplayName();
+ }
+ }
+
+ public String getParticipants() {
+ if (getMode() == MODE_MULTI) {
+ String generatedName = getMucOptions().createNameFromParticipants();
+ if (generatedName != null) {
+ return generatedName;
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public String getAccountUuid() {
+ return this.accountUuid;
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public Contact getContact() {
+ return this.account.getRoster().getContact(this.contactJid);
+ }
+
+ public void setAccount(final Account account) {
+ this.account = account;
+ }
+
+ @Override
+ public Jid getJid() {
+ return this.contactJid;
+ }
+
+ public int getStatus() {
+ return this.status;
+ }
+
+ public long getCreated() {
+ return this.created;
+ }
+
+ public ContentValues getContentValues() {
+ ContentValues values = new ContentValues();
+ values.put(UUID, uuid);
+ values.put(NAME, name);
+ values.put(CONTACT, contactUuid);
+ values.put(ACCOUNT, accountUuid);
+ values.put(CONTACTJID, contactJid.toPreppedString());
+ values.put(CREATED, created);
+ values.put(STATUS, status);
+ values.put(MODE, mode);
+ values.put(ATTRIBUTES, attributes.toString());
+ return values;
+ }
+
+ public static Conversation fromCursor(Cursor cursor) {
+ Jid jid;
+ try {
+ jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(CONTACTJID)), true);
+ } catch (final InvalidJidException e) {
+ // Borked DB..
+ jid = null;
+ }
+ return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
+ cursor.getString(cursor.getColumnIndex(NAME)),
+ cursor.getString(cursor.getColumnIndex(CONTACT)),
+ cursor.getString(cursor.getColumnIndex(ACCOUNT)),
+ jid,
+ cursor.getLong(cursor.getColumnIndex(CREATED)),
+ cursor.getInt(cursor.getColumnIndex(STATUS)),
+ cursor.getInt(cursor.getColumnIndex(MODE)),
+ cursor.getString(cursor.getColumnIndex(ATTRIBUTES)));
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+ public int getMode() {
+ return this.mode;
+ }
+
+ public void setMode(int mode) {
+ this.mode = mode;
+ }
+
+ public SessionImpl startOtrSession(String presence, boolean sendStart) {
+ if (this.otrSession != null) {
+ return this.otrSession;
+ } else {
+ final SessionID sessionId = new SessionID(this.getJid().toBareJid().toString(),
+ presence,
+ "xmpp");
+ this.otrSession = new SessionImpl(sessionId, getAccount().getOtrService());
+ try {
+ if (sendStart) {
+ this.otrSession.startSession();
+ return this.otrSession;
+ }
+ return this.otrSession;
+ } catch (OtrException e) {
+ return null;
+ }
+ }
+
+ }
+
+ public SessionImpl getOtrSession() {
+ return this.otrSession;
+ }
+
+ public void resetOtrSession() {
+ this.otrFingerprint = null;
+ this.otrSession = null;
+ this.mSmp.hint = null;
+ this.mSmp.secret = null;
+ this.mSmp.status = Smp.STATUS_NONE;
+ }
+
+ public Smp smp() {
+ return mSmp;
+ }
+
+ public boolean startOtrIfNeeded() {
+ if (this.otrSession != null && this.otrSession.getSessionStatus() != SessionStatus.ENCRYPTED) {
+ try {
+ this.otrSession.startSession();
+ return true;
+ } catch (OtrException e) {
+ this.resetOtrSession();
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public boolean endOtrIfNeeded() {
+ if (this.otrSession != null) {
+ if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
+ try {
+ this.otrSession.endSession();
+ this.resetOtrSession();
+ return true;
+ } catch (OtrException e) {
+ this.resetOtrSession();
+ return false;
+ }
+ } else {
+ this.resetOtrSession();
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ public boolean hasValidOtrSession() {
+ return this.otrSession != null;
+ }
+
+ public synchronized String getOtrFingerprint() {
+ if (this.otrFingerprint == null) {
+ try {
+ if (getOtrSession() == null || getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
+ return null;
+ }
+ DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession().getRemotePublicKey();
+ this.otrFingerprint = getAccount().getOtrService().getFingerprint(remotePubKey);
+ } catch (final OtrCryptoException | UnsupportedOperationException ignored) {
+ return null;
+ }
+ }
+ return this.otrFingerprint;
+ }
+
+ public boolean verifyOtrFingerprint() {
+ final String fingerprint = getOtrFingerprint();
+ if (fingerprint != null) {
+ getContact().addOtrFingerprint(fingerprint);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isOtrFingerprintVerified() {
+ return getContact().getOtrFingerprints().contains(getOtrFingerprint());
+ }
+
+ /**
+ * short for is Private and Non-anonymous
+ */
+ private boolean isPnNA() {
+ return mode == MODE_SINGLE || (getMucOptions().membersOnly() && getMucOptions().nonanonymous());
+ }
+
+ public synchronized MucOptions getMucOptions() {
+ if (this.mucOptions == null) {
+ this.mucOptions = new MucOptions(this);
+ }
+ return this.mucOptions;
+ }
+
+ public void resetMucOptions() {
+ this.mucOptions = null;
+ }
+
+ public void setContactJid(final Jid jid) {
+ this.contactJid = jid;
+ }
+
+ public void setNextCounterpart(Jid jid) {
+ this.nextCounterpart = jid;
+ }
+
+ public Jid getNextCounterpart() {
+ return this.nextCounterpart;
+ }
+
+ private int getMostRecentlyUsedIncomingEncryption() {
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ final Message m = this.messages.get(i);
+ if (m.getStatus() == Message.STATUS_RECEIVED) {
+ final int e = m.getEncryption();
+ if (e == Message.ENCRYPTION_DECRYPTED || e == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ return Message.ENCRYPTION_PGP;
+ } else {
+ return e;
+ }
+ }
+ }
+ }
+ return Message.ENCRYPTION_NONE;
+ }
+
+ public int getNextEncryption() {
+ return Math.max(this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, Message.ENCRYPTION_NONE), Message.ENCRYPTION_NONE);
+ }
+
+ public void setNextEncryption(int encryption) {
+ this.setAttribute(ATTRIBUTE_NEXT_ENCRYPTION, String.valueOf(encryption));
+ }
+
+ public String getNextMessage() {
+ if (this.nextMessage == null) {
+ return "";
+ } else {
+ return this.nextMessage;
+ }
+ }
+
+ public boolean smpRequested() {
+ return smp().status == Smp.STATUS_CONTACT_REQUESTED;
+ }
+
+ public void setNextMessage(String message) {
+ this.nextMessage = message;
+ }
+
+ public void setSymmetricKey(byte[] key) {
+ this.symmetricKey = key;
+ }
+
+ public byte[] getSymmetricKey() {
+ return this.symmetricKey;
+ }
+
+ public void setBookmark(Bookmark bookmark) {
+ this.bookmark = bookmark;
+ this.bookmark.setConversation(this);
+ }
+
+ public void deregisterWithBookmark() {
+ if (this.bookmark != null) {
+ this.bookmark.setConversation(null);
+ }
+ }
+
+ public Bookmark getBookmark() {
+ return this.bookmark;
+ }
+
+ public boolean hasDuplicateMessage(Message message) {
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ if (this.messages.get(i).similar(message)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public Message findSentMessageWithBody(String body) {
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ Message message = this.messages.get(i);
+ if (message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_SEND) {
+ String otherBody;
+ if (message.hasFileOnRemoteHost()) {
+ otherBody = message.getFileParams().url.toString();
+ } else {
+ otherBody = message.body;
+ }
+ if (otherBody != null && otherBody.equals(body)) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ public long getLastMessageTransmitted() {
+ final long last_clear = getLastClearHistory();
+ long last_received = 0;
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ Message message = this.messages.get(i);
+ if (message.getStatus() == Message.STATUS_RECEIVED || message.isCarbon()) {
+ last_received = message.getTimeSent();
+ break;
+ }
+ }
+ }
+ return Math.max(last_clear, last_received);
+ }
+
+ public void setMutedTill(long value) {
+ this.setAttribute(ATTRIBUTE_MUTED_TILL, String.valueOf(value));
+ }
+
+ public boolean isMuted() {
+ return System.currentTimeMillis() < this.getLongAttribute(ATTRIBUTE_MUTED_TILL, 0);
+ }
+
+ public boolean alwaysNotify() {
+ return mode == MODE_SINGLE || getBooleanAttribute(ATTRIBUTE_ALWAYS_NOTIFY, Config.ALWAYS_NOTIFY_BY_DEFAULT || isPnNA());
+ }
+
+ public boolean setAttribute(String key, String value) {
+ synchronized (this.attributes) {
+ try {
+ this.attributes.put(key, value);
+ return true;
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+ }
+
+ public boolean setAttribute(String key, List<Jid> jids) {
+ JSONArray array = new JSONArray();
+ for (Jid jid : jids) {
+ array.put(jid.toBareJid().toString());
+ }
+ synchronized (this.attributes) {
+ try {
+ this.attributes.put(key, array);
+ return true;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+ }
+
+ public String getAttribute(String key) {
+ synchronized (this.attributes) {
+ try {
+ return this.attributes.getString(key);
+ } catch (JSONException e) {
+ return null;
+ }
+ }
+ }
+
+ public List<Jid> getJidListAttribute(String key) {
+ ArrayList<Jid> list = new ArrayList<>();
+ synchronized (this.attributes) {
+ try {
+ JSONArray array = this.attributes.getJSONArray(key);
+ for (int i = 0; i < array.length(); ++i) {
+ try {
+ list.add(Jid.fromString(array.getString(i)));
+ } catch (InvalidJidException e) {
+ //ignored
+ }
+ }
+ } catch (JSONException e) {
+ //ignored
+ }
+ }
+ return list;
+ }
+
+ public int getIntAttribute(String key, int defaultValue) {
+ String value = this.getAttribute(key);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+ }
+
+ public long getLongAttribute(String key, long defaultValue) {
+ String value = this.getAttribute(key);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+ }
+
+ public boolean getBooleanAttribute(String key, boolean defaultValue) {
+ String value = this.getAttribute(key);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ return Boolean.parseBoolean(value);
+ }
+ }
+
+ public void add(Message message) {
+ message.setConversation(this);
+ synchronized (this.messages) {
+ this.messages.add(message);
+ }
+ }
+
+ public void prepend(Message message) {
+ message.setConversation(this);
+ synchronized (this.messages) {
+ this.messages.add(0, message);
+ }
+ }
+
+ public void addAll(int index, List<Message> messages) {
+ synchronized (this.messages) {
+ this.messages.addAll(index, messages);
+ }
+ account.getPgpDecryptionService().decrypt(messages);
+ }
+
+ public void sort() {
+ synchronized (this.messages) {
+ Collections.sort(this.messages, new Comparator<Message>() {
+ @Override
+ public int compare(Message left, Message right) {
+ if (left.getTimeSent() < right.getTimeSent()) {
+ return -1;
+ } else if (left.getTimeSent() > right.getTimeSent()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ });
+ untieMessages();
+ }
+ }
+
+ private void untieMessages() {
+ for (Message message : this.messages) {
+ message.untie();
+ }
+ }
+
+ public int unreadCount() {
+ synchronized (this.messages) {
+ int count = 0;
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ if (this.messages.get(i).isRead()) {
+ return count;
+ }
+ ++count;
+ }
+ return count;
+ }
+ }
+
+ public class Smp {
+ public static final int STATUS_NONE = 0;
+ public static final int STATUS_CONTACT_REQUESTED = 1;
+ public static final int STATUS_WE_REQUESTED = 2;
+ public static final int STATUS_FAILED = 3;
+ public static final int STATUS_VERIFIED = 4;
+
+ public String secret = null;
+ public String hint = null;
+ public int status = 0;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/DownloadableFile.java b/src/main/java/de/pixart/messenger/entities/DownloadableFile.java
index 1a6e88881..ee0694196 100644
--- a/src/main/java/de/pixart/messenger/entities/DownloadableFile.java
+++ b/src/main/java/de/pixart/messenger/entities/DownloadableFile.java
@@ -6,78 +6,78 @@ import de.pixart.messenger.utils.MimeUtils;
public class DownloadableFile extends File {
- private static final long serialVersionUID = 2247012619505115863L;
-
- private long expectedSize = 0;
- private String sha1sum;
- private byte[] aeskey;
-
- private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
-
- public DownloadableFile(String path) {
- super(path);
- }
-
- public long getSize() {
- return super.length();
- }
-
- public long getExpectedSize() {
- return this.expectedSize;
- }
-
- public String getMimeType() {
- String path = this.getAbsolutePath();
- int start = path.lastIndexOf('.') + 1;
- if (start < path.length()) {
- String mime = MimeUtils.guessMimeTypeFromExtension(path.substring(start));
- return mime == null ? "" : mime;
- } else {
- return "";
- }
- }
-
- public void setExpectedSize(long size) {
- this.expectedSize = size;
- }
-
- public String getSha1Sum() {
- return this.sha1sum;
- }
-
- public void setSha1Sum(String sum) {
- this.sha1sum = sum;
- }
-
- public void setKeyAndIv(byte[] keyIvCombo) {
- if (keyIvCombo.length == 48) {
- this.aeskey = new byte[32];
- this.iv = new byte[16];
- System.arraycopy(keyIvCombo, 0, this.iv, 0, 16);
- System.arraycopy(keyIvCombo, 16, this.aeskey, 0, 32);
- } else if (keyIvCombo.length >= 32) {
- this.aeskey = new byte[32];
- System.arraycopy(keyIvCombo, 0, aeskey, 0, 32);
- } else if (keyIvCombo.length >= 16) {
- this.aeskey = new byte[16];
- System.arraycopy(keyIvCombo, 0, this.aeskey, 0, 16);
- }
- }
-
- public void setKey(byte[] key) {
- this.aeskey = key;
- }
-
- public void setIv(byte[] iv) {
- this.iv = iv;
- }
-
- public byte[] getKey() {
- return this.aeskey;
- }
-
- public byte[] getIv() {
- return this.iv;
- }
+ private static final long serialVersionUID = 2247012619505115863L;
+
+ private long expectedSize = 0;
+ private String sha1sum;
+ private byte[] aeskey;
+
+ private byte[] iv = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf};
+
+ public DownloadableFile(String path) {
+ super(path);
+ }
+
+ public long getSize() {
+ return super.length();
+ }
+
+ public long getExpectedSize() {
+ return this.expectedSize;
+ }
+
+ public String getMimeType() {
+ String path = this.getAbsolutePath();
+ int start = path.lastIndexOf('.') + 1;
+ if (start < path.length()) {
+ String mime = MimeUtils.guessMimeTypeFromExtension(path.substring(start));
+ return mime == null ? "" : mime;
+ } else {
+ return "";
+ }
+ }
+
+ public void setExpectedSize(long size) {
+ this.expectedSize = size;
+ }
+
+ public String getSha1Sum() {
+ return this.sha1sum;
+ }
+
+ public void setSha1Sum(String sum) {
+ this.sha1sum = sum;
+ }
+
+ public void setKeyAndIv(byte[] keyIvCombo) {
+ if (keyIvCombo.length == 48) {
+ this.aeskey = new byte[32];
+ this.iv = new byte[16];
+ System.arraycopy(keyIvCombo, 0, this.iv, 0, 16);
+ System.arraycopy(keyIvCombo, 16, this.aeskey, 0, 32);
+ } else if (keyIvCombo.length >= 32) {
+ this.aeskey = new byte[32];
+ System.arraycopy(keyIvCombo, 0, aeskey, 0, 32);
+ } else if (keyIvCombo.length >= 16) {
+ this.aeskey = new byte[16];
+ System.arraycopy(keyIvCombo, 0, this.aeskey, 0, 16);
+ }
+ }
+
+ public void setKey(byte[] key) {
+ this.aeskey = key;
+ }
+
+ public void setIv(byte[] iv) {
+ this.iv = iv;
+ }
+
+ public byte[] getKey() {
+ return this.aeskey;
+ }
+
+ public byte[] getIv() {
+ return this.iv;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/ListItem.java b/src/main/java/de/pixart/messenger/entities/ListItem.java
index cb0644499..7fbd37200 100644
--- a/src/main/java/de/pixart/messenger/entities/ListItem.java
+++ b/src/main/java/de/pixart/messenger/entities/ListItem.java
@@ -7,31 +7,31 @@ import java.util.List;
import de.pixart.messenger.xmpp.jid.Jid;
public interface ListItem extends Comparable<ListItem> {
- String getDisplayName();
+ String getDisplayName();
- String getDisplayJid();
+ String getDisplayJid();
- Jid getJid();
+ Jid getJid();
- List<Tag> getTags(Context context);
+ List<Tag> getTags(Context context);
- final class Tag {
- private final String name;
- private final int color;
+ final class Tag {
+ private final String name;
+ private final int color;
- public Tag(final String name, final int color) {
- this.name = name;
- this.color = color;
- }
+ public Tag(final String name, final int color) {
+ this.name = name;
+ this.color = color;
+ }
- public int getColor() {
- return this.color;
- }
+ public int getColor() {
+ return this.color;
+ }
- public String getName() {
- return this.name;
- }
- }
+ public String getName() {
+ return this.name;
+ }
+ }
- boolean match(Context context, final String needle);
+ boolean match(Context context, final String needle);
}
diff --git a/src/main/java/de/pixart/messenger/entities/Message.java b/src/main/java/de/pixart/messenger/entities/Message.java
index 9cdf57922..4224f6d6f 100644
--- a/src/main/java/de/pixart/messenger/entities/Message.java
+++ b/src/main/java/de/pixart/messenger/entities/Message.java
@@ -230,7 +230,7 @@ public class Message extends AbstractEntity {
values.put(READ, read ? 1 : 0);
values.put(EDITED, edited);
values.put(OOB, oob ? 1 : 0);
- values.put(ERROR_MESSAGE,errorMessage);
+ values.put(ERROR_MESSAGE, errorMessage);
return values;
}
@@ -510,7 +510,8 @@ public class Message extends AbstractEntity {
);
}
- public static class MergeSeparator {}
+ public static class MergeSeparator {
+ }
public SpannableStringBuilder getMergedBody() {
SpannableStringBuilder body = new SpannableStringBuilder(this.body.trim());
diff --git a/src/main/java/de/pixart/messenger/entities/MucOptions.java b/src/main/java/de/pixart/messenger/entities/MucOptions.java
index 4817f4bae..a95debd89 100644
--- a/src/main/java/de/pixart/messenger/entities/MucOptions.java
+++ b/src/main/java/de/pixart/messenger/entities/MucOptions.java
@@ -17,667 +17,667 @@ import de.pixart.messenger.xmpp.pep.Avatar;
@SuppressLint("DefaultLocale")
public class MucOptions {
- private boolean mAutoPushConfiguration = true;
-
- public Account getAccount() {
- return this.conversation.getAccount();
- }
-
- public void setSelf(User user) {
- this.self = user;
- }
-
- public void changeAffiliation(Jid jid, Affiliation affiliation) {
- User user = findUserByRealJid(jid);
- synchronized (users) {
- if (user != null && user.getRole() == Role.NONE) {
- users.remove(user);
- if (affiliation.ranks(Affiliation.MEMBER)) {
- user.affiliation = affiliation;
- users.add(user);
- }
- }
- }
- }
-
- public void flagNoAutoPushConfiguration() {
- mAutoPushConfiguration = false;
- }
-
- public boolean autoPushConfiguration() {
- return mAutoPushConfiguration;
- }
-
- public enum Affiliation {
- OWNER("owner", 4, R.string.owner),
- ADMIN("admin", 3, R.string.admin),
- MEMBER("member", 2, R.string.member),
- OUTCAST("outcast", 0, R.string.outcast),
- NONE("none", 1, R.string.no_affiliation);
-
- Affiliation(String string, int rank, int resId) {
- this.string = string;
- this.resId = resId;
- this.rank = rank;
- }
-
- private String string;
- private int resId;
- private int rank;
-
- public int getResId() {
- return resId;
- }
-
- @Override
- public String toString() {
- return this.string;
- }
-
- public boolean outranks(Affiliation affiliation) {
- return rank > affiliation.rank;
- }
-
- public boolean ranks(Affiliation affiliation) {
- return rank >= affiliation.rank;
- }
- }
-
- public enum Role {
- MODERATOR("moderator", R.string.moderator,3),
- VISITOR("visitor", R.string.visitor,1),
- PARTICIPANT("participant", R.string.participant,2),
- NONE("none", R.string.no_role,0);
-
- Role(String string, int resId, int rank) {
- this.string = string;
- this.resId = resId;
- this.rank = rank;
- }
-
- private String string;
- private int resId;
- private int rank;
-
- public int getResId() {
- return resId;
- }
-
- @Override
- public String toString() {
- return this.string;
- }
-
- public boolean ranks(Role role) {
- return rank >= role.rank;
- }
- }
-
- public enum Error {
- NO_RESPONSE,
- SERVER_NOT_FOUND,
- NONE,
- NICK_IN_USE,
- PASSWORD_REQUIRED,
- BANNED,
- MEMBERS_ONLY,
- KICKED,
- SHUTDOWN,
- UNKNOWN
- }
-
- public static final String STATUS_CODE_SELF_PRESENCE = "110";
- public static final String STATUS_CODE_ROOM_CREATED = "201";
- public static final String STATUS_CODE_BANNED = "301";
- public static final String STATUS_CODE_CHANGED_NICK = "303";
- public static final String STATUS_CODE_KICKED = "307";
- public static final String STATUS_CODE_AFFILIATION_CHANGE = "321";
- public static final String STATUS_CODE_LOST_MEMBERSHIP = "322";
- public static final String STATUS_CODE_SHUTDOWN = "332";
-
- private interface OnEventListener {
- void onSuccess();
-
- void onFailure();
- }
-
- public interface OnRenameListener extends OnEventListener {
-
- }
-
- public static class User implements Comparable<User> {
- private Role role = Role.NONE;
- private Affiliation affiliation = Affiliation.NONE;
- private Jid realJid;
- private Jid fullJid;
- private long pgpKeyId = 0;
- private Avatar avatar;
- private MucOptions options;
-
- public User(MucOptions options, Jid from) {
- this.options = options;
- this.fullJid = from;
- }
-
- public String getName() {
- return fullJid == null ? null : fullJid.getResourcepart();
- }
-
- public void setRealJid(Jid jid) {
- this.realJid = jid != null ? jid.toBareJid() : null;
- }
-
- public Role getRole() {
- return this.role;
- }
-
- public void setRole(String role) {
- if (role == null) {
- this.role = Role.NONE;
- return;
- }
- role = role.toLowerCase();
- switch (role) {
- case "moderator":
- this.role = Role.MODERATOR;
- break;
- case "participant":
- this.role = Role.PARTICIPANT;
- break;
- case "visitor":
- this.role = Role.VISITOR;
- break;
- default:
- this.role = Role.NONE;
- break;
- }
- }
-
- public Affiliation getAffiliation() {
- return this.affiliation;
- }
-
- public void setAffiliation(String affiliation) {
- if (affiliation == null) {
- this.affiliation = Affiliation.NONE;
- return;
- }
- affiliation = affiliation.toLowerCase();
- switch (affiliation) {
- case "admin":
- this.affiliation = Affiliation.ADMIN;
- break;
- case "owner":
- this.affiliation = Affiliation.OWNER;
- break;
- case "member":
- this.affiliation = Affiliation.MEMBER;
- break;
- case "outcast":
- this.affiliation = Affiliation.OUTCAST;
- break;
- default:
- this.affiliation = Affiliation.NONE;
- }
- }
-
- public void setPgpKeyId(long id) {
- this.pgpKeyId = id;
- }
-
- public long getPgpKeyId() {
- return this.pgpKeyId;
- }
-
- public Contact getContact() {
- if (fullJid != null) {
- return getAccount().getRoster().getContactFromRoster(realJid);
- } else if (realJid != null){
- return getAccount().getRoster().getContact(realJid);
- } else {
- return null;
- }
- }
-
- public boolean setAvatar(Avatar avatar) {
- if (this.avatar != null && this.avatar.equals(avatar)) {
- return false;
- } else {
- this.avatar = avatar;
- return true;
- }
- }
-
- public String getAvatar() {
- return avatar == null ? null : avatar.getFilename();
- }
-
- public Account getAccount() {
- return options.getAccount();
- }
-
- public Jid getFullJid() {
- return fullJid;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- User user = (User) o;
-
- if (role != user.role) return false;
- if (affiliation != user.affiliation) return false;
- if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null)
- return false;
- return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null;
-
- }
-
- @Override
- public int hashCode() {
- int result = role != null ? role.hashCode() : 0;
- result = 31 * result + (affiliation != null ? affiliation.hashCode() : 0);
- result = 31 * result + (realJid != null ? realJid.hashCode() : 0);
- result = 31 * result + (fullJid != null ? fullJid.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "[fulljid:"+String.valueOf(fullJid)+",realjid:"+String.valueOf(realJid)+",affiliation"+affiliation.toString()+"]";
- }
-
- public boolean realJidMatchesAccount() {
- return realJid != null && realJid.equals(options.account.getJid().toBareJid());
- }
-
- @Override
- public int compareTo(User another) {
- if (another.getAffiliation().outranks(getAffiliation())) {
- return 1;
- } else if (getAffiliation().outranks(another.getAffiliation())) {
- return -1;
- } else {
- return getComparableName().compareToIgnoreCase(another.getComparableName());
- }
- }
-
-
- private String getComparableName() {
- Contact contact = getContact();
- if (contact != null) {
- return contact.getDisplayName();
- } else {
- String name = getName();
- return name == null ? "" : name;
- }
- }
-
- public Jid getRealJid() {
- return realJid;
- }
- }
-
- private Account account;
- private final Set<User> users = new HashSet<>();
- private final List<String> features = new ArrayList<>();
- private Data form = new Data();
- private Conversation conversation;
- private boolean isOnline = false;
- private Error error = Error.NONE;
- public OnRenameListener onRenameListener = null;
- private User self;
- private String subject = null;
- private String password = null;
- public boolean mNickChangingInProgress = false;
-
- public MucOptions(Conversation conversation) {
- this.account = conversation.getAccount();
- this.conversation = conversation;
- this.self = new User(this,createJoinJid(getProposedNick()));
- }
-
- public void updateFeatures(ArrayList<String> features) {
- this.features.clear();
- this.features.addAll(features);
- }
-
- public void updateFormData(Data form) {
- this.form = form;
- }
-
- public boolean hasFeature(String feature) {
- return this.features.contains(feature);
- }
-
- public boolean canInvite() {
- Field field = this.form.getFieldByName("muc#roomconfig_allowinvites");
- return !membersOnly() || self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
- }
-
- public boolean canChangeSubject() {
- Field field = this.form.getFieldByName("muc#roomconfig_changesubject");
- return self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
- }
-
- public boolean participating() {
- return !online()
- || self.getRole().ranks(Role.PARTICIPANT)
- || hasFeature("muc_unmoderated");
- }
-
- public boolean membersOnly() {
- return hasFeature("muc_membersonly");
- }
-
- public boolean mamSupport() {
- // Update with "urn:xmpp:mam:1" once we support it
- return hasFeature("urn:xmpp:mam:0");
- }
-
- public boolean nonanonymous() {
- return hasFeature("muc_nonanonymous");
- }
-
- public boolean persistent() {
- return hasFeature("muc_persistent");
- }
-
- public boolean moderated() {
- return hasFeature("muc_moderated");
- }
-
- public User deleteUser(Jid jid) {
- User user = findUserByFullJid(jid);
- if (user != null) {
- synchronized (users) {
- users.remove(user);
- if (membersOnly() &&
- nonanonymous() &&
- user.affiliation.ranks(Affiliation.MEMBER) &&
- user.realJid != null) {
- user.role = Role.NONE;
- user.avatar = null;
- user.fullJid = null;
- users.add(user);
- }
- }
- }
- return user;
- }
-
- public void updateUser(User user) {
- User old;
- if (user.fullJid == null && user.realJid != null) {
- old = findUserByRealJid(user.realJid);
- if (old != null) {
- if (old.fullJid != null) {
- return; //don't add. user already exists
- } else {
- synchronized (users) {
- users.remove(old);
- }
- }
- }
- } else if (user.realJid != null) {
- old = findUserByRealJid(user.realJid);
- synchronized (users) {
- if (old != null && old.fullJid == null) {
- users.remove(old);
- }
- }
- }
- old = findUserByFullJid(user.getFullJid());
- synchronized (this.users) {
- if (old != null) {
- users.remove(old);
- }
+ private boolean mAutoPushConfiguration = true;
+
+ public Account getAccount() {
+ return this.conversation.getAccount();
+ }
+
+ public void setSelf(User user) {
+ this.self = user;
+ }
+
+ public void changeAffiliation(Jid jid, Affiliation affiliation) {
+ User user = findUserByRealJid(jid);
+ synchronized (users) {
+ if (user != null && user.getRole() == Role.NONE) {
+ users.remove(user);
+ if (affiliation.ranks(Affiliation.MEMBER)) {
+ user.affiliation = affiliation;
+ users.add(user);
+ }
+ }
+ }
+ }
+
+ public void flagNoAutoPushConfiguration() {
+ mAutoPushConfiguration = false;
+ }
+
+ public boolean autoPushConfiguration() {
+ return mAutoPushConfiguration;
+ }
+
+ public enum Affiliation {
+ OWNER("owner", 4, R.string.owner),
+ ADMIN("admin", 3, R.string.admin),
+ MEMBER("member", 2, R.string.member),
+ OUTCAST("outcast", 0, R.string.outcast),
+ NONE("none", 1, R.string.no_affiliation);
+
+ Affiliation(String string, int rank, int resId) {
+ this.string = string;
+ this.resId = resId;
+ this.rank = rank;
+ }
+
+ private String string;
+ private int resId;
+ private int rank;
+
+ public int getResId() {
+ return resId;
+ }
+
+ @Override
+ public String toString() {
+ return this.string;
+ }
+
+ public boolean outranks(Affiliation affiliation) {
+ return rank > affiliation.rank;
+ }
+
+ public boolean ranks(Affiliation affiliation) {
+ return rank >= affiliation.rank;
+ }
+ }
+
+ public enum Role {
+ MODERATOR("moderator", R.string.moderator, 3),
+ VISITOR("visitor", R.string.visitor, 1),
+ PARTICIPANT("participant", R.string.participant, 2),
+ NONE("none", R.string.no_role, 0);
+
+ Role(String string, int resId, int rank) {
+ this.string = string;
+ this.resId = resId;
+ this.rank = rank;
+ }
+
+ private String string;
+ private int resId;
+ private int rank;
+
+ public int getResId() {
+ return resId;
+ }
+
+ @Override
+ public String toString() {
+ return this.string;
+ }
+
+ public boolean ranks(Role role) {
+ return rank >= role.rank;
+ }
+ }
+
+ public enum Error {
+ NO_RESPONSE,
+ SERVER_NOT_FOUND,
+ NONE,
+ NICK_IN_USE,
+ PASSWORD_REQUIRED,
+ BANNED,
+ MEMBERS_ONLY,
+ KICKED,
+ SHUTDOWN,
+ UNKNOWN
+ }
+
+ public static final String STATUS_CODE_SELF_PRESENCE = "110";
+ public static final String STATUS_CODE_ROOM_CREATED = "201";
+ public static final String STATUS_CODE_BANNED = "301";
+ public static final String STATUS_CODE_CHANGED_NICK = "303";
+ public static final String STATUS_CODE_KICKED = "307";
+ public static final String STATUS_CODE_AFFILIATION_CHANGE = "321";
+ public static final String STATUS_CODE_LOST_MEMBERSHIP = "322";
+ public static final String STATUS_CODE_SHUTDOWN = "332";
+
+ private interface OnEventListener {
+ void onSuccess();
+
+ void onFailure();
+ }
+
+ public interface OnRenameListener extends OnEventListener {
+
+ }
+
+ public static class User implements Comparable<User> {
+ private Role role = Role.NONE;
+ private Affiliation affiliation = Affiliation.NONE;
+ private Jid realJid;
+ private Jid fullJid;
+ private long pgpKeyId = 0;
+ private Avatar avatar;
+ private MucOptions options;
+
+ public User(MucOptions options, Jid from) {
+ this.options = options;
+ this.fullJid = from;
+ }
+
+ public String getName() {
+ return fullJid == null ? null : fullJid.getResourcepart();
+ }
+
+ public void setRealJid(Jid jid) {
+ this.realJid = jid != null ? jid.toBareJid() : null;
+ }
+
+ public Role getRole() {
+ return this.role;
+ }
+
+ public void setRole(String role) {
+ if (role == null) {
+ this.role = Role.NONE;
+ return;
+ }
+ role = role.toLowerCase();
+ switch (role) {
+ case "moderator":
+ this.role = Role.MODERATOR;
+ break;
+ case "participant":
+ this.role = Role.PARTICIPANT;
+ break;
+ case "visitor":
+ this.role = Role.VISITOR;
+ break;
+ default:
+ this.role = Role.NONE;
+ break;
+ }
+ }
+
+ public Affiliation getAffiliation() {
+ return this.affiliation;
+ }
+
+ public void setAffiliation(String affiliation) {
+ if (affiliation == null) {
+ this.affiliation = Affiliation.NONE;
+ return;
+ }
+ affiliation = affiliation.toLowerCase();
+ switch (affiliation) {
+ case "admin":
+ this.affiliation = Affiliation.ADMIN;
+ break;
+ case "owner":
+ this.affiliation = Affiliation.OWNER;
+ break;
+ case "member":
+ this.affiliation = Affiliation.MEMBER;
+ break;
+ case "outcast":
+ this.affiliation = Affiliation.OUTCAST;
+ break;
+ default:
+ this.affiliation = Affiliation.NONE;
+ }
+ }
+
+ public void setPgpKeyId(long id) {
+ this.pgpKeyId = id;
+ }
+
+ public long getPgpKeyId() {
+ return this.pgpKeyId;
+ }
+
+ public Contact getContact() {
+ if (fullJid != null) {
+ return getAccount().getRoster().getContactFromRoster(realJid);
+ } else if (realJid != null) {
+ return getAccount().getRoster().getContact(realJid);
+ } else {
+ return null;
+ }
+ }
+
+ public boolean setAvatar(Avatar avatar) {
+ if (this.avatar != null && this.avatar.equals(avatar)) {
+ return false;
+ } else {
+ this.avatar = avatar;
+ return true;
+ }
+ }
+
+ public String getAvatar() {
+ return avatar == null ? null : avatar.getFilename();
+ }
+
+ public Account getAccount() {
+ return options.getAccount();
+ }
+
+ public Jid getFullJid() {
+ return fullJid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ User user = (User) o;
+
+ if (role != user.role) return false;
+ if (affiliation != user.affiliation) return false;
+ if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null)
+ return false;
+ return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = role != null ? role.hashCode() : 0;
+ result = 31 * result + (affiliation != null ? affiliation.hashCode() : 0);
+ result = 31 * result + (realJid != null ? realJid.hashCode() : 0);
+ result = 31 * result + (fullJid != null ? fullJid.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "[fulljid:" + String.valueOf(fullJid) + ",realjid:" + String.valueOf(realJid) + ",affiliation" + affiliation.toString() + "]";
+ }
+
+ public boolean realJidMatchesAccount() {
+ return realJid != null && realJid.equals(options.account.getJid().toBareJid());
+ }
+
+ @Override
+ public int compareTo(User another) {
+ if (another.getAffiliation().outranks(getAffiliation())) {
+ return 1;
+ } else if (getAffiliation().outranks(another.getAffiliation())) {
+ return -1;
+ } else {
+ return getComparableName().compareToIgnoreCase(another.getComparableName());
+ }
+ }
+
+
+ private String getComparableName() {
+ Contact contact = getContact();
+ if (contact != null) {
+ return contact.getDisplayName();
+ } else {
+ String name = getName();
+ return name == null ? "" : name;
+ }
+ }
+
+ public Jid getRealJid() {
+ return realJid;
+ }
+ }
+
+ private Account account;
+ private final Set<User> users = new HashSet<>();
+ private final List<String> features = new ArrayList<>();
+ private Data form = new Data();
+ private Conversation conversation;
+ private boolean isOnline = false;
+ private Error error = Error.NONE;
+ public OnRenameListener onRenameListener = null;
+ private User self;
+ private String subject = null;
+ private String password = null;
+ public boolean mNickChangingInProgress = false;
+
+ public MucOptions(Conversation conversation) {
+ this.account = conversation.getAccount();
+ this.conversation = conversation;
+ this.self = new User(this, createJoinJid(getProposedNick()));
+ }
+
+ public void updateFeatures(ArrayList<String> features) {
+ this.features.clear();
+ this.features.addAll(features);
+ }
+
+ public void updateFormData(Data form) {
+ this.form = form;
+ }
+
+ public boolean hasFeature(String feature) {
+ return this.features.contains(feature);
+ }
+
+ public boolean canInvite() {
+ Field field = this.form.getFieldByName("muc#roomconfig_allowinvites");
+ return !membersOnly() || self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
+ }
+
+ public boolean canChangeSubject() {
+ Field field = this.form.getFieldByName("muc#roomconfig_changesubject");
+ return self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
+ }
+
+ public boolean participating() {
+ return !online()
+ || self.getRole().ranks(Role.PARTICIPANT)
+ || hasFeature("muc_unmoderated");
+ }
+
+ public boolean membersOnly() {
+ return hasFeature("muc_membersonly");
+ }
+
+ public boolean mamSupport() {
+ // Update with "urn:xmpp:mam:1" once we support it
+ return hasFeature("urn:xmpp:mam:0");
+ }
+
+ public boolean nonanonymous() {
+ return hasFeature("muc_nonanonymous");
+ }
+
+ public boolean persistent() {
+ return hasFeature("muc_persistent");
+ }
+
+ public boolean moderated() {
+ return hasFeature("muc_moderated");
+ }
+
+ public User deleteUser(Jid jid) {
+ User user = findUserByFullJid(jid);
+ if (user != null) {
+ synchronized (users) {
+ users.remove(user);
+ if (membersOnly() &&
+ nonanonymous() &&
+ user.affiliation.ranks(Affiliation.MEMBER) &&
+ user.realJid != null) {
+ user.role = Role.NONE;
+ user.avatar = null;
+ user.fullJid = null;
+ users.add(user);
+ }
+ }
+ }
+ return user;
+ }
+
+ public void updateUser(User user) {
+ User old;
+ if (user.fullJid == null && user.realJid != null) {
+ old = findUserByRealJid(user.realJid);
+ if (old != null) {
+ if (old.fullJid != null) {
+ return; //don't add. user already exists
+ } else {
+ synchronized (users) {
+ users.remove(old);
+ }
+ }
+ }
+ } else if (user.realJid != null) {
+ old = findUserByRealJid(user.realJid);
+ synchronized (users) {
+ if (old != null && old.fullJid == null) {
+ users.remove(old);
+ }
+ }
+ }
+ old = findUserByFullJid(user.getFullJid());
+ synchronized (this.users) {
+ if (old != null) {
+ users.remove(old);
+ }
if ((!membersOnly() || user.getAffiliation().ranks(Affiliation.MEMBER))
- && user.getAffiliation().outranks(Affiliation.OUTCAST)){
+ && user.getAffiliation().outranks(Affiliation.OUTCAST)) {
this.users.add(user);
}
- }
- }
-
- public User findUserByFullJid(Jid jid) {
- if (jid == null) {
- return null;
- }
- synchronized (users) {
- for (User user : users) {
- if (jid.equals(user.getFullJid())) {
- return user;
- }
- }
- }
- return null;
- }
-
- private User findUserByRealJid(Jid jid) {
- if (jid == null) {
- return null;
- }
- synchronized (users) {
- for (User user : users) {
- if (jid.equals(user.realJid)) {
- return user;
- }
- }
- }
- return null;
- }
-
- public boolean isContactInRoom(Contact contact) {
- return findUserByRealJid(contact.getJid().toBareJid()) != null;
- }
-
- public boolean isUserInRoom(Jid jid) {
- return findUserByFullJid(jid) != null;
- }
-
- public void setError(Error error) {
- this.isOnline = isOnline && error == Error.NONE;
- this.error = error;
- }
-
- public void setOnline() {
- this.isOnline = true;
- }
-
- public ArrayList<User> getUsers() {
- return getUsers(true);
- }
-
- public ArrayList<User> getUsers(boolean includeOffline) {
- synchronized (users) {
- if (includeOffline) {
- return new ArrayList<>(users);
- } else {
- ArrayList<User> onlineUsers = new ArrayList<>();
- for (User user : users) {
- if (user.getRole().ranks(Role.PARTICIPANT)) {
- onlineUsers.add(user);
- }
- }
- return onlineUsers;
- }
- }
- }
-
- public List<User> getUsers(int max) {
- ArrayList<User> users = getUsers();
- return users.subList(0, Math.min(max, users.size()));
- }
-
- public int getUserCount() {
- synchronized (users) {
- return users.size();
- }
- }
-
- public String getProposedNick() {
- if (conversation.getBookmark() != null
- && conversation.getBookmark().getNick() != null
- && !conversation.getBookmark().getNick().trim().isEmpty()) {
- return conversation.getBookmark().getNick().trim();
- } else if (!conversation.getJid().isBareJid()) {
- return conversation.getJid().getResourcepart();
- } else {
- return account.getUsername();
- }
- }
-
- public String getActualNick() {
- if (this.self.getName() != null) {
- return this.self.getName();
- } else {
- return this.getProposedNick();
- }
- }
-
- public boolean online() {
- return this.isOnline;
- }
-
- public Error getError() {
- return this.error;
- }
-
- public void setOnRenameListener(OnRenameListener listener) {
- this.onRenameListener = listener;
- }
-
- public void setOffline() {
- synchronized (users) {
- this.users.clear();
- }
- this.error = Error.NO_RESPONSE;
- this.isOnline = false;
- }
-
- public User getSelf() {
- return self;
- }
-
- public void setSubject(String content) {
- this.subject = content;
- }
-
- public String getSubject() {
- return this.subject;
- }
-
- public String createNameFromParticipants() {
- if (users.size() >= 1) {
- List<String> names = new ArrayList<>();
- for (User user : getUsers(5)) {
- Contact contact = user.getContact();
- if (contact != null && !contact.getDisplayName().isEmpty()) {
- names.add(contact.getDisplayName().split("\\s+")[0]);
- } else if (user.getName() != null){
- names.add(user.getName());
- }
- }
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < names.size(); ++i) {
- builder.append(names.get(i));
- if (i != names.size() - 1) {
- builder.append(", ");
- }
- }
- return builder.toString();
- } else {
- return null;
- }
- }
-
- public long[] getPgpKeyIds() {
- List<Long> ids = new ArrayList<>();
- for (User user : this.users) {
- if (user.getPgpKeyId() != 0) {
- ids.add(user.getPgpKeyId());
- }
- }
- ids.add(account.getPgpId());
- long[] primitiveLongArray = new long[ids.size()];
- for (int i = 0; i < ids.size(); ++i) {
- primitiveLongArray[i] = ids.get(i);
- }
- return primitiveLongArray;
- }
-
- public boolean pgpKeysInUse() {
- synchronized (users) {
- for (User user : users) {
- if (user.getPgpKeyId() != 0) {
- return true;
- }
- }
- }
- return false;
- }
-
- public boolean everybodyHasKeys() {
- synchronized (users) {
- for (User user : users) {
- if (user.getPgpKeyId() == 0) {
- return false;
- }
- }
- }
- return true;
- }
-
- public Jid createJoinJid(String nick) {
- try {
- return Jid.fromString(this.conversation.getJid().toBareJid().toString() + "/" + nick);
- } catch (final InvalidJidException e) {
- return null;
- }
- }
-
- public Jid getTrueCounterpart(Jid jid) {
- if (jid.equals(getSelf().getFullJid())) {
- return account.getJid().toBareJid();
- }
- User user = findUserByFullJid(jid);
- return user == null ? null : user.realJid;
- }
-
- public String getPassword() {
- this.password = conversation.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD);
- if (this.password == null && conversation.getBookmark() != null
- && conversation.getBookmark().getPassword() != null) {
- return conversation.getBookmark().getPassword();
- } else {
- return this.password;
- }
- }
-
- public void setPassword(String password) {
- if (conversation.getBookmark() != null) {
- conversation.getBookmark().setPassword(password);
- } else {
- this.password = password;
- }
- conversation.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password);
- }
-
- public Conversation getConversation() {
- return this.conversation;
- }
-
- public List<Jid> getMembers() {
- ArrayList<Jid> members = new ArrayList<>();
- synchronized (users) {
- for (User user : users) {
- if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null) {
- members.add(user.realJid);
- }
- }
- }
- return members;
- }
+ }
+ }
+
+ public User findUserByFullJid(Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ synchronized (users) {
+ for (User user : users) {
+ if (jid.equals(user.getFullJid())) {
+ return user;
+ }
+ }
+ }
+ return null;
+ }
+
+ private User findUserByRealJid(Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ synchronized (users) {
+ for (User user : users) {
+ if (jid.equals(user.realJid)) {
+ return user;
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean isContactInRoom(Contact contact) {
+ return findUserByRealJid(contact.getJid().toBareJid()) != null;
+ }
+
+ public boolean isUserInRoom(Jid jid) {
+ return findUserByFullJid(jid) != null;
+ }
+
+ public void setError(Error error) {
+ this.isOnline = isOnline && error == Error.NONE;
+ this.error = error;
+ }
+
+ public void setOnline() {
+ this.isOnline = true;
+ }
+
+ public ArrayList<User> getUsers() {
+ return getUsers(true);
+ }
+
+ public ArrayList<User> getUsers(boolean includeOffline) {
+ synchronized (users) {
+ if (includeOffline) {
+ return new ArrayList<>(users);
+ } else {
+ ArrayList<User> onlineUsers = new ArrayList<>();
+ for (User user : users) {
+ if (user.getRole().ranks(Role.PARTICIPANT)) {
+ onlineUsers.add(user);
+ }
+ }
+ return onlineUsers;
+ }
+ }
+ }
+
+ public List<User> getUsers(int max) {
+ ArrayList<User> users = getUsers();
+ return users.subList(0, Math.min(max, users.size()));
+ }
+
+ public int getUserCount() {
+ synchronized (users) {
+ return users.size();
+ }
+ }
+
+ public String getProposedNick() {
+ if (conversation.getBookmark() != null
+ && conversation.getBookmark().getNick() != null
+ && !conversation.getBookmark().getNick().trim().isEmpty()) {
+ return conversation.getBookmark().getNick().trim();
+ } else if (!conversation.getJid().isBareJid()) {
+ return conversation.getJid().getResourcepart();
+ } else {
+ return account.getUsername();
+ }
+ }
+
+ public String getActualNick() {
+ if (this.self.getName() != null) {
+ return this.self.getName();
+ } else {
+ return this.getProposedNick();
+ }
+ }
+
+ public boolean online() {
+ return this.isOnline;
+ }
+
+ public Error getError() {
+ return this.error;
+ }
+
+ public void setOnRenameListener(OnRenameListener listener) {
+ this.onRenameListener = listener;
+ }
+
+ public void setOffline() {
+ synchronized (users) {
+ this.users.clear();
+ }
+ this.error = Error.NO_RESPONSE;
+ this.isOnline = false;
+ }
+
+ public User getSelf() {
+ return self;
+ }
+
+ public void setSubject(String content) {
+ this.subject = content;
+ }
+
+ public String getSubject() {
+ return this.subject;
+ }
+
+ public String createNameFromParticipants() {
+ if (users.size() >= 1) {
+ List<String> names = new ArrayList<>();
+ for (User user : getUsers(5)) {
+ Contact contact = user.getContact();
+ if (contact != null && !contact.getDisplayName().isEmpty()) {
+ names.add(contact.getDisplayName().split("\\s+")[0]);
+ } else if (user.getName() != null) {
+ names.add(user.getName());
+ }
+ }
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < names.size(); ++i) {
+ builder.append(names.get(i));
+ if (i != names.size() - 1) {
+ builder.append(", ");
+ }
+ }
+ return builder.toString();
+ } else {
+ return null;
+ }
+ }
+
+ public long[] getPgpKeyIds() {
+ List<Long> ids = new ArrayList<>();
+ for (User user : this.users) {
+ if (user.getPgpKeyId() != 0) {
+ ids.add(user.getPgpKeyId());
+ }
+ }
+ ids.add(account.getPgpId());
+ long[] primitiveLongArray = new long[ids.size()];
+ for (int i = 0; i < ids.size(); ++i) {
+ primitiveLongArray[i] = ids.get(i);
+ }
+ return primitiveLongArray;
+ }
+
+ public boolean pgpKeysInUse() {
+ synchronized (users) {
+ for (User user : users) {
+ if (user.getPgpKeyId() != 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean everybodyHasKeys() {
+ synchronized (users) {
+ for (User user : users) {
+ if (user.getPgpKeyId() == 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public Jid createJoinJid(String nick) {
+ try {
+ return Jid.fromString(this.conversation.getJid().toBareJid().toString() + "/" + nick);
+ } catch (final InvalidJidException e) {
+ return null;
+ }
+ }
+
+ public Jid getTrueCounterpart(Jid jid) {
+ if (jid.equals(getSelf().getFullJid())) {
+ return account.getJid().toBareJid();
+ }
+ User user = findUserByFullJid(jid);
+ return user == null ? null : user.realJid;
+ }
+
+ public String getPassword() {
+ this.password = conversation.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD);
+ if (this.password == null && conversation.getBookmark() != null
+ && conversation.getBookmark().getPassword() != null) {
+ return conversation.getBookmark().getPassword();
+ } else {
+ return this.password;
+ }
+ }
+
+ public void setPassword(String password) {
+ if (conversation.getBookmark() != null) {
+ conversation.getBookmark().setPassword(password);
+ } else {
+ this.password = password;
+ }
+ conversation.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password);
+ }
+
+ public Conversation getConversation() {
+ return this.conversation;
+ }
+
+ public List<Jid> getMembers() {
+ ArrayList<Jid> members = new ArrayList<>();
+ synchronized (users) {
+ for (User user : users) {
+ if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null) {
+ members.add(user.realJid);
+ }
+ }
+ }
+ return members;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Presence.java b/src/main/java/de/pixart/messenger/entities/Presence.java
index fe7bac6ee..af467f1f6 100644
--- a/src/main/java/de/pixart/messenger/entities/Presence.java
+++ b/src/main/java/de/pixart/messenger/entities/Presence.java
@@ -6,87 +6,91 @@ import de.pixart.messenger.xml.Element;
public class Presence implements Comparable {
- public enum Status {
- CHAT, ONLINE, AWAY, XA, DND, OFFLINE;
-
- public String toShowString() {
- switch(this) {
- case CHAT: return "chat";
- case AWAY: return "away";
- case XA: return "xa";
- case DND: return "dnd";
- }
- return null;
- }
-
- public static Status fromShowString(String show) {
- if (show == null) {
- return ONLINE;
- } else {
- switch (show.toLowerCase(Locale.US)) {
- case "away":
- return AWAY;
- case "xa":
- return XA;
- case "dnd":
- return DND;
- case "chat":
- return CHAT;
- default:
- return ONLINE;
- }
- }
- }
- }
-
- private final Status status;
- private ServiceDiscoveryResult disco;
- private final String ver;
- private final String hash;
- private final String message;
-
- private Presence(Status status, String ver, String hash, String message) {
- this.status = status;
- this.ver = ver;
- this.hash = hash;
- this.message = message;
- }
-
- public static Presence parse(String show, Element caps, String message) {
- final String hash = caps == null ? null : caps.getAttribute("hash");
- final String ver = caps == null ? null : caps.getAttribute("ver");
- return new Presence(Status.fromShowString(show), ver, hash, message);
- }
-
- public int compareTo(Object other) {
- return this.status.compareTo(((Presence)other).status);
- }
-
- public Status getStatus() {
- return this.status;
- }
-
- public boolean hasCaps() {
- return ver != null && hash != null;
- }
-
- public String getVer() {
- return this.ver;
- }
-
- public String getHash() {
- return this.hash;
- }
-
- public String getMessage() {
- return this.message;
- }
-
- public void setServiceDiscoveryResult(ServiceDiscoveryResult disco) {
- this.disco = disco;
- }
-
- public ServiceDiscoveryResult getServiceDiscoveryResult() {
- return disco;
- }
+ public enum Status {
+ CHAT, ONLINE, AWAY, XA, DND, OFFLINE;
+
+ public String toShowString() {
+ switch (this) {
+ case CHAT:
+ return "chat";
+ case AWAY:
+ return "away";
+ case XA:
+ return "xa";
+ case DND:
+ return "dnd";
+ }
+ return null;
+ }
+
+ public static Status fromShowString(String show) {
+ if (show == null) {
+ return ONLINE;
+ } else {
+ switch (show.toLowerCase(Locale.US)) {
+ case "away":
+ return AWAY;
+ case "xa":
+ return XA;
+ case "dnd":
+ return DND;
+ case "chat":
+ return CHAT;
+ default:
+ return ONLINE;
+ }
+ }
+ }
+ }
+
+ private final Status status;
+ private ServiceDiscoveryResult disco;
+ private final String ver;
+ private final String hash;
+ private final String message;
+
+ private Presence(Status status, String ver, String hash, String message) {
+ this.status = status;
+ this.ver = ver;
+ this.hash = hash;
+ this.message = message;
+ }
+
+ public static Presence parse(String show, Element caps, String message) {
+ final String hash = caps == null ? null : caps.getAttribute("hash");
+ final String ver = caps == null ? null : caps.getAttribute("ver");
+ return new Presence(Status.fromShowString(show), ver, hash, message);
+ }
+
+ public int compareTo(Object other) {
+ return this.status.compareTo(((Presence) other).status);
+ }
+
+ public Status getStatus() {
+ return this.status;
+ }
+
+ public boolean hasCaps() {
+ return ver != null && hash != null;
+ }
+
+ public String getVer() {
+ return this.ver;
+ }
+
+ public String getHash() {
+ return this.hash;
+ }
+
+ public String getMessage() {
+ return this.message;
+ }
+
+ public void setServiceDiscoveryResult(ServiceDiscoveryResult disco) {
+ this.disco = disco;
+ }
+
+ public ServiceDiscoveryResult getServiceDiscoveryResult() {
+ return disco;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/PresenceTemplate.java b/src/main/java/de/pixart/messenger/entities/PresenceTemplate.java
index b8bd167db..ab71a41fd 100644
--- a/src/main/java/de/pixart/messenger/entities/PresenceTemplate.java
+++ b/src/main/java/de/pixart/messenger/entities/PresenceTemplate.java
@@ -6,71 +6,71 @@ import android.database.Cursor;
public class PresenceTemplate extends AbstractEntity {
- public static final String TABELNAME = "presence_templates";
- public static final String LAST_USED = "last_used";
- public static final String MESSAGE = "message";
- public static final String STATUS = "status";
-
- private long lastUsed = 0;
- private String statusMessage;
- private Presence.Status status = Presence.Status.ONLINE;
-
- public PresenceTemplate(Presence.Status status, String statusMessage) {
- this.status = status;
- this.statusMessage = statusMessage;
- this.lastUsed = System.currentTimeMillis();
- this.uuid = java.util.UUID.randomUUID().toString();
- }
-
- private PresenceTemplate() {
-
- }
-
- @Override
- public ContentValues getContentValues() {
- final String show = status.toShowString();
- ContentValues values = new ContentValues();
- values.put(LAST_USED, lastUsed);
- values.put(MESSAGE, statusMessage);
- values.put(STATUS, show == null ? "" : show);
- values.put(UUID, uuid);
- return values;
- }
-
- public static PresenceTemplate fromCursor(Cursor cursor) {
- PresenceTemplate template = new PresenceTemplate();
- template.uuid = cursor.getString(cursor.getColumnIndex(UUID));
- template.lastUsed = cursor.getLong(cursor.getColumnIndex(LAST_USED));
- template.statusMessage = cursor.getString(cursor.getColumnIndex(MESSAGE));
- template.status = Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS)));
- return template;
- }
-
- public Presence.Status getStatus() {
- return status;
- }
-
- public String getStatusMessage() {
- return statusMessage;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- PresenceTemplate template = (PresenceTemplate) o;
-
- if (statusMessage != null ? !statusMessage.equals(template.statusMessage) : template.statusMessage != null)
- return false;
- return status == template.status;
-
- }
-
- @Override
- public int hashCode() {
- int result = statusMessage != null ? statusMessage.hashCode() : 0;
- result = 31 * result + status.hashCode();
- return result;
- }
+ public static final String TABELNAME = "presence_templates";
+ public static final String LAST_USED = "last_used";
+ public static final String MESSAGE = "message";
+ public static final String STATUS = "status";
+
+ private long lastUsed = 0;
+ private String statusMessage;
+ private Presence.Status status = Presence.Status.ONLINE;
+
+ public PresenceTemplate(Presence.Status status, String statusMessage) {
+ this.status = status;
+ this.statusMessage = statusMessage;
+ this.lastUsed = System.currentTimeMillis();
+ this.uuid = java.util.UUID.randomUUID().toString();
+ }
+
+ private PresenceTemplate() {
+
+ }
+
+ @Override
+ public ContentValues getContentValues() {
+ final String show = status.toShowString();
+ ContentValues values = new ContentValues();
+ values.put(LAST_USED, lastUsed);
+ values.put(MESSAGE, statusMessage);
+ values.put(STATUS, show == null ? "" : show);
+ values.put(UUID, uuid);
+ return values;
+ }
+
+ public static PresenceTemplate fromCursor(Cursor cursor) {
+ PresenceTemplate template = new PresenceTemplate();
+ template.uuid = cursor.getString(cursor.getColumnIndex(UUID));
+ template.lastUsed = cursor.getLong(cursor.getColumnIndex(LAST_USED));
+ template.statusMessage = cursor.getString(cursor.getColumnIndex(MESSAGE));
+ template.status = Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS)));
+ return template;
+ }
+
+ public Presence.Status getStatus() {
+ return status;
+ }
+
+ public String getStatusMessage() {
+ return statusMessage;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ PresenceTemplate template = (PresenceTemplate) o;
+
+ if (statusMessage != null ? !statusMessage.equals(template.statusMessage) : template.statusMessage != null)
+ return false;
+ return status == template.status;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = statusMessage != null ? statusMessage.hashCode() : 0;
+ result = 31 * result + status.hashCode();
+ return result;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Presences.java b/src/main/java/de/pixart/messenger/entities/Presences.java
index 98ea58fcf..6afd973fc 100644
--- a/src/main/java/de/pixart/messenger/entities/Presences.java
+++ b/src/main/java/de/pixart/messenger/entities/Presences.java
@@ -9,122 +9,122 @@ import java.util.List;
import java.util.Map;
public class Presences {
- private final Hashtable<String, Presence> presences = new Hashtable<>();
+ private final Hashtable<String, Presence> presences = new Hashtable<>();
- public Hashtable<String, Presence> getPresences() {
- return this.presences;
- }
+ public Hashtable<String, Presence> getPresences() {
+ return this.presences;
+ }
- public void updatePresence(String resource, Presence presence) {
- synchronized (this.presences) {
- this.presences.put(resource, presence);
- }
- }
+ public void updatePresence(String resource, Presence presence) {
+ synchronized (this.presences) {
+ this.presences.put(resource, presence);
+ }
+ }
- public void removePresence(String resource) {
- synchronized (this.presences) {
- this.presences.remove(resource);
- }
- }
+ public void removePresence(String resource) {
+ synchronized (this.presences) {
+ this.presences.remove(resource);
+ }
+ }
- public void clearPresences() {
- synchronized (this.presences) {
- this.presences.clear();
- }
- }
+ public void clearPresences() {
+ synchronized (this.presences) {
+ this.presences.clear();
+ }
+ }
- public Presence.Status getShownStatus() {
- Presence.Status status = Presence.Status.OFFLINE;
- synchronized (this.presences) {
- for(Presence p : presences.values()) {
- if (p.getStatus() == Presence.Status.DND) {
- return p.getStatus();
- } else if (p.getStatus().compareTo(status) < 0){
- status = p.getStatus();
- }
- }
- }
- return status;
- }
+ public Presence.Status getShownStatus() {
+ Presence.Status status = Presence.Status.OFFLINE;
+ synchronized (this.presences) {
+ for (Presence p : presences.values()) {
+ if (p.getStatus() == Presence.Status.DND) {
+ return p.getStatus();
+ } else if (p.getStatus().compareTo(status) < 0) {
+ status = p.getStatus();
+ }
+ }
+ }
+ return status;
+ }
- public int size() {
- synchronized (this.presences) {
- return presences.size();
- }
- }
+ public int size() {
+ synchronized (this.presences) {
+ return presences.size();
+ }
+ }
- public String[] toResourceArray() {
- synchronized (this.presences) {
- final String[] presencesArray = new String[presences.size()];
- presences.keySet().toArray(presencesArray);
- return presencesArray;
- }
- }
+ public String[] toResourceArray() {
+ synchronized (this.presences) {
+ final String[] presencesArray = new String[presences.size()];
+ presences.keySet().toArray(presencesArray);
+ return presencesArray;
+ }
+ }
- public List<PresenceTemplate> asTemplates() {
- synchronized (this.presences) {
- ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size());
- for(Presence p : presences.values()) {
- if (p.getMessage() != null && !p.getMessage().trim().isEmpty()) {
- templates.add(new PresenceTemplate(p.getStatus(), p.getMessage()));
- }
- }
- return templates;
- }
- }
+ public List<PresenceTemplate> asTemplates() {
+ synchronized (this.presences) {
+ ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size());
+ for (Presence p : presences.values()) {
+ if (p.getMessage() != null && !p.getMessage().trim().isEmpty()) {
+ templates.add(new PresenceTemplate(p.getStatus(), p.getMessage()));
+ }
+ }
+ return templates;
+ }
+ }
- public boolean has(String presence) {
- synchronized (this.presences) {
- return presences.containsKey(presence);
- }
- }
+ public boolean has(String presence) {
+ synchronized (this.presences) {
+ return presences.containsKey(presence);
+ }
+ }
- public List<String> getStatusMessages() {
- ArrayList<String> messages = new ArrayList<>();
- synchronized (this.presences) {
- for(Presence presence : this.presences.values()) {
- String message = presence.getMessage() == null ? null : presence.getMessage().trim();
- if (message != null && !message.isEmpty() && !messages.contains(message)) {
- messages.add(message);
- }
- }
- }
- return messages;
- }
+ public List<String> getStatusMessages() {
+ ArrayList<String> messages = new ArrayList<>();
+ synchronized (this.presences) {
+ for (Presence presence : this.presences.values()) {
+ String message = presence.getMessage() == null ? null : presence.getMessage().trim();
+ if (message != null && !message.isEmpty() && !messages.contains(message)) {
+ messages.add(message);
+ }
+ }
+ }
+ return messages;
+ }
- public boolean allOrNonSupport(String namespace) {
- synchronized (this.presences) {
- for(Presence presence : this.presences.values()) {
- ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
- if (disco == null || !disco.getFeatures().contains(namespace)) {
- return false;
- }
- }
- }
- return true;
- }
+ public boolean allOrNonSupport(String namespace) {
+ synchronized (this.presences) {
+ for (Presence presence : this.presences.values()) {
+ ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
+ if (disco == null || !disco.getFeatures().contains(namespace)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
- public Pair<Map<String, String>,Map<String,String>> toTypeAndNameMap() {
- Map<String,String> typeMap = new HashMap<>();
- Map<String,String> nameMap = new HashMap<>();
- synchronized (this.presences) {
- for(Map.Entry<String,Presence> presenceEntry : this.presences.entrySet()) {
- String resource = presenceEntry.getKey();
- Presence presence = presenceEntry.getValue();
- ServiceDiscoveryResult serviceDiscoveryResult = presence == null ? null : presence.getServiceDiscoveryResult();
- if (serviceDiscoveryResult != null && serviceDiscoveryResult.getIdentities().size() > 0) {
- ServiceDiscoveryResult.Identity identity = serviceDiscoveryResult.getIdentities().get(0);
- String type = identity.getType();
- String name = identity.getName();
- if (type != null) {
- typeMap.put(resource,type);
- }
- if (name != null) {
- nameMap.put(resource, name);
- }
- }
- }
- }
- return new Pair(typeMap,nameMap);
- }
+ public Pair<Map<String, String>, Map<String, String>> toTypeAndNameMap() {
+ Map<String, String> typeMap = new HashMap<>();
+ Map<String, String> nameMap = new HashMap<>();
+ synchronized (this.presences) {
+ for (Map.Entry<String, Presence> presenceEntry : this.presences.entrySet()) {
+ String resource = presenceEntry.getKey();
+ Presence presence = presenceEntry.getValue();
+ ServiceDiscoveryResult serviceDiscoveryResult = presence == null ? null : presence.getServiceDiscoveryResult();
+ if (serviceDiscoveryResult != null && serviceDiscoveryResult.getIdentities().size() > 0) {
+ ServiceDiscoveryResult.Identity identity = serviceDiscoveryResult.getIdentities().get(0);
+ String type = identity.getType();
+ String name = identity.getName();
+ if (type != null) {
+ typeMap.put(resource, type);
+ }
+ if (name != null) {
+ nameMap.put(resource, name);
+ }
+ }
+ }
+ }
+ return new Pair(typeMap, nameMap);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Roster.java b/src/main/java/de/pixart/messenger/entities/Roster.java
index e267142bc..bdb7265f3 100644
--- a/src/main/java/de/pixart/messenger/entities/Roster.java
+++ b/src/main/java/de/pixart/messenger/entities/Roster.java
@@ -8,89 +8,89 @@ import java.util.List;
import de.pixart.messenger.xmpp.jid.Jid;
public class Roster {
- final Account account;
- final HashMap<Jid, Contact> contacts = new HashMap<>();
- private String version = null;
+ final Account account;
+ final HashMap<Jid, Contact> contacts = new HashMap<>();
+ private String version = null;
- public Roster(Account account) {
- this.account = account;
- }
+ public Roster(Account account) {
+ this.account = account;
+ }
- public Contact getContactFromRoster(Jid jid) {
- if (jid == null) {
- return null;
- }
- synchronized (this.contacts) {
- Contact contact = contacts.get(jid.toBareJid());
- if (contact != null && contact.showInRoster()) {
- return contact;
- } else {
- return null;
- }
- }
- }
+ public Contact getContactFromRoster(Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ synchronized (this.contacts) {
+ Contact contact = contacts.get(jid.toBareJid());
+ if (contact != null && contact.showInRoster()) {
+ return contact;
+ } else {
+ return null;
+ }
+ }
+ }
- public Contact getContact(final Jid jid) {
- synchronized (this.contacts) {
- if (!contacts.containsKey(jid.toBareJid())) {
- Contact contact = new Contact(jid.toBareJid());
- contact.setAccount(account);
- contacts.put(contact.getJid().toBareJid(), contact);
- return contact;
- }
- return contacts.get(jid.toBareJid());
- }
- }
+ public Contact getContact(final Jid jid) {
+ synchronized (this.contacts) {
+ if (!contacts.containsKey(jid.toBareJid())) {
+ Contact contact = new Contact(jid.toBareJid());
+ contact.setAccount(account);
+ contacts.put(contact.getJid().toBareJid(), contact);
+ return contact;
+ }
+ return contacts.get(jid.toBareJid());
+ }
+ }
- public void clearPresences() {
- for (Contact contact : getContacts()) {
- contact.clearPresences();
- }
- }
+ public void clearPresences() {
+ for (Contact contact : getContacts()) {
+ contact.clearPresences();
+ }
+ }
- public void markAllAsNotInRoster() {
- for (Contact contact : getContacts()) {
- contact.resetOption(Contact.Options.IN_ROSTER);
- }
- }
+ public void markAllAsNotInRoster() {
+ for (Contact contact : getContacts()) {
+ contact.resetOption(Contact.Options.IN_ROSTER);
+ }
+ }
- public List<Contact> getWithSystemAccounts() {
- List<Contact> with = getContacts();
- for(Iterator<Contact> iterator = with.iterator(); iterator.hasNext();) {
- Contact contact = iterator.next();
- if (contact.getSystemAccount() == null) {
- iterator.remove();
- }
- }
- return with;
- }
+ public List<Contact> getWithSystemAccounts() {
+ List<Contact> with = getContacts();
+ for (Iterator<Contact> iterator = with.iterator(); iterator.hasNext(); ) {
+ Contact contact = iterator.next();
+ if (contact.getSystemAccount() == null) {
+ iterator.remove();
+ }
+ }
+ return with;
+ }
- public List<Contact> getContacts() {
- synchronized (this.contacts) {
- return new ArrayList<>(this.contacts.values());
- }
- }
+ public List<Contact> getContacts() {
+ synchronized (this.contacts) {
+ return new ArrayList<>(this.contacts.values());
+ }
+ }
- public void initContact(final Contact contact) {
- if (contact == null) {
- return;
- }
- contact.setAccount(account);
- contact.setOption(Contact.Options.IN_ROSTER);
- synchronized (this.contacts) {
- contacts.put(contact.getJid().toBareJid(), contact);
- }
- }
+ public void initContact(final Contact contact) {
+ if (contact == null) {
+ return;
+ }
+ contact.setAccount(account);
+ contact.setOption(Contact.Options.IN_ROSTER);
+ synchronized (this.contacts) {
+ contacts.put(contact.getJid().toBareJid(), contact);
+ }
+ }
- public void setVersion(String version) {
- this.version = version;
- }
+ public void setVersion(String version) {
+ this.version = version;
+ }
- public String getVersion() {
- return this.version;
- }
+ public String getVersion() {
+ return this.version;
+ }
- public Account getAccount() {
- return this.account;
- }
+ public Account getAccount() {
+ return this.account;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/ServiceDiscoveryResult.java b/src/main/java/de/pixart/messenger/entities/ServiceDiscoveryResult.java
index cac20ba90..b0c3ebb77 100644
--- a/src/main/java/de/pixart/messenger/entities/ServiceDiscoveryResult.java
+++ b/src/main/java/de/pixart/messenger/entities/ServiceDiscoveryResult.java
@@ -22,328 +22,328 @@ import de.pixart.messenger.xmpp.forms.Field;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class ServiceDiscoveryResult {
- public static final String TABLENAME = "discovery_results";
- public static final String HASH = "hash";
- public static final String VER = "ver";
- public static final String RESULT = "result";
-
- protected static String blankNull(String s) {
- return s == null ? "" : s;
- }
-
- public static class Identity implements Comparable {
- protected final String category;
- protected final String type;
- protected final String lang;
- protected final String name;
-
- public Identity(final String category, final String type, final String lang, final String name) {
- this.category = category;
- this.type = type;
- this.lang = lang;
- this.name = name;
- }
-
- public Identity(final Element el) {
- this(
- el.getAttribute("category"),
- el.getAttribute("type"),
- el.getAttribute("xml:lang"),
- el.getAttribute("name")
- );
- }
-
- public Identity(final JSONObject o) {
-
- this(
- o.optString("category", null),
- o.optString("type", null),
- o.optString("lang", null),
- o.optString("name", null)
- );
- }
-
- public String getCategory() {
- return this.category;
- }
-
- public String getType() {
- return this.type;
- }
-
- public String getLang() {
- return this.lang;
- }
-
- public String getName() {
- return this.name;
- }
-
- public int compareTo(Object other) {
- Identity o = (Identity)other;
- int r = blankNull(this.getCategory()).compareTo(blankNull(o.getCategory()));
- if(r == 0) {
- r = blankNull(this.getType()).compareTo(blankNull(o.getType()));
- }
- if(r == 0) {
- r = blankNull(this.getLang()).compareTo(blankNull(o.getLang()));
- }
- if(r == 0) {
- r = blankNull(this.getName()).compareTo(blankNull(o.getName()));
- }
-
- return r;
- }
-
- public JSONObject toJSON() {
- try {
- JSONObject o = new JSONObject();
- o.put("category", this.getCategory());
- o.put("type", this.getType());
- o.put("lang", this.getLang());
- o.put("name", this.getName());
- return o;
- } catch(JSONException e) {
- return null;
- }
- }
- }
-
- protected final String hash;
- protected final byte[] ver;
- protected final List<Identity> identities;
- protected final List<String> features;
- protected final List<Data> forms;
-
- public ServiceDiscoveryResult(final IqPacket packet) {
- this.identities = new ArrayList<>();
- this.features = new ArrayList<>();
- this.forms = new ArrayList<>();
- this.hash = "sha-1"; // We only support sha-1 for now
-
- final List<Element> elements = packet.query().getChildren();
-
- for (final Element element : elements) {
- if (element.getName().equals("identity")) {
- Identity id = new Identity(element);
- if (id.getType() != null && id.getCategory() != null) {
- identities.add(id);
- }
- } else if (element.getName().equals("feature")) {
- if (element.getAttribute("var") != null) {
- features.add(element.getAttribute("var"));
- }
- } else if (element.getName().equals("x") && "jabber:x:data".equals(element.getAttribute("xmlns"))) {
- forms.add(Data.parse(element));
- }
- }
- this.ver = this.mkCapHash();
- }
-
- public ServiceDiscoveryResult(String hash, byte[] ver, JSONObject o) throws JSONException {
- this.identities = new ArrayList<>();
- this.features = new ArrayList<>();
- this.forms = new ArrayList<>();
- this.hash = hash;
- this.ver = ver;
-
- JSONArray identities = o.optJSONArray("identities");
- if (identities != null) {
- for (int i = 0; i < identities.length(); i++) {
- this.identities.add(new Identity(identities.getJSONObject(i)));
- }
- }
- JSONArray features = o.optJSONArray("features");
- if (features != null) {
- for (int i = 0; i < features.length(); i++) {
- this.features.add(features.getString(i));
- }
- }
- JSONArray forms = o.optJSONArray("forms");
- if (forms != null) {
- for(int i = 0; i < forms.length(); i++) {
- this.forms.add(createFormFromJSONObject(forms.getJSONObject(i)));
- }
- }
- }
-
- private static Data createFormFromJSONObject(JSONObject o) {
- Data data = new Data();
- JSONArray names = o.names();
- for(int i = 0; i < names.length(); ++i) {
- try {
- String name = names.getString(i);
- JSONArray jsonValues = o.getJSONArray(name);
- ArrayList<String> values = new ArrayList<>(jsonValues.length());
- for(int j = 0; j < jsonValues.length(); ++j) {
- values.add(jsonValues.getString(j));
- }
- data.put(name, values);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return data;
- }
-
- private static JSONObject createJSONFromForm(Data data) {
- JSONObject object = new JSONObject();
- for(Field field : data.getFields()) {
- try {
- JSONArray jsonValues = new JSONArray();
- for(String value : field.getValues()) {
- jsonValues.put(value);
- }
- object.put(field.getFieldName(), jsonValues);
- } catch(Exception e) {
- e.printStackTrace();
- }
- }
- try {
- JSONArray jsonValues = new JSONArray();
- jsonValues.put(data.getFormType());
- object.put(Data.FORM_TYPE, jsonValues);
- } catch(Exception e) {
- e.printStackTrace();
- }
- return object;
- }
-
- public String getVer() {
- return new String(Base64.encode(this.ver, Base64.DEFAULT)).trim();
- }
-
- public ServiceDiscoveryResult(Cursor cursor) throws JSONException {
- this(
- cursor.getString(cursor.getColumnIndex(HASH)),
- Base64.decode(cursor.getString(cursor.getColumnIndex(VER)), Base64.DEFAULT),
- new JSONObject(cursor.getString(cursor.getColumnIndex(RESULT)))
- );
- }
-
- public List<Identity> getIdentities() {
- return this.identities;
- }
-
- public List<String> getFeatures() {
- return this.features;
- }
-
- public boolean hasIdentity(String category, String type) {
- for(Identity id : this.getIdentities()) {
- if((category == null || id.getCategory().equals(category)) &&
- (type == null || id.getType().equals(type))) {
- return true;
- }
- }
-
- return false;
- }
-
- public String getExtendedDiscoInformation(String formType, String name) {
- for(Data form : this.forms) {
- if (formType.equals(form.getFormType())) {
- for(Field field: form.getFields()) {
- if (name.equals(field.getFieldName())) {
- return field.getValue();
- }
- }
- }
- }
- return null;
- }
-
- protected byte[] mkCapHash() {
- StringBuilder s = new StringBuilder();
-
- List<Identity> identities = this.getIdentities();
- Collections.sort(identities);
-
- for(Identity id : identities) {
- s.append(
- blankNull(id.getCategory()) + "/" +
- blankNull(id.getType()) + "/" +
- blankNull(id.getLang()) + "/" +
- blankNull(id.getName()) + "<"
- );
- }
-
- List<String> features = this.getFeatures();
- Collections.sort(features);
-
- for (String feature : features) {
- s.append(feature + "<");
- }
-
- Collections.sort(forms, new Comparator<Data>() {
- @Override
- public int compare(Data lhs, Data rhs) {
- return lhs.getFormType().compareTo(rhs.getFormType());
- }
- });
-
- for(Data form : forms) {
- s.append(form.getFormType() + "<");
- List<Field> fields = form.getFields();
- Collections.sort(fields, new Comparator<Field>() {
- @Override
- public int compare(Field lhs, Field rhs) {
- return lhs.getFieldName().compareTo(rhs.getFieldName());
- }
- });
- for(Field field : fields) {
- s.append(field.getFieldName()+"<");
- List<String> values = field.getValues();
- Collections.sort(values);
- for(String value : values) {
- s.append(value+"<");
- }
- }
- }
-
- MessageDigest md;
- try {
- md = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- return null;
- }
-
- try {
- return md.digest(s.toString().getBytes("UTF-8"));
- } catch(UnsupportedEncodingException e) {
- return null;
- }
- }
-
- public JSONObject toJSON() {
- try {
- JSONObject o = new JSONObject();
-
- JSONArray ids = new JSONArray();
- for(Identity id : this.getIdentities()) {
- ids.put(id.toJSON());
- }
- o.put("identities", ids);
-
- o.put("features", new JSONArray(this.getFeatures()));
-
- JSONArray forms = new JSONArray();
- for(Data data : this.forms) {
- forms.put(createJSONFromForm(data));
- }
- o.put("forms", forms);
-
- return o;
- } catch(JSONException e) {
- return null;
- }
- }
-
- public ContentValues getContentValues() {
- final ContentValues values = new ContentValues();
- values.put(HASH, this.hash);
- values.put(VER, getVer());
- values.put(RESULT, this.toJSON().toString());
- return values;
- }
+ public static final String TABLENAME = "discovery_results";
+ public static final String HASH = "hash";
+ public static final String VER = "ver";
+ public static final String RESULT = "result";
+
+ protected static String blankNull(String s) {
+ return s == null ? "" : s;
+ }
+
+ public static class Identity implements Comparable {
+ protected final String category;
+ protected final String type;
+ protected final String lang;
+ protected final String name;
+
+ public Identity(final String category, final String type, final String lang, final String name) {
+ this.category = category;
+ this.type = type;
+ this.lang = lang;
+ this.name = name;
+ }
+
+ public Identity(final Element el) {
+ this(
+ el.getAttribute("category"),
+ el.getAttribute("type"),
+ el.getAttribute("xml:lang"),
+ el.getAttribute("name")
+ );
+ }
+
+ public Identity(final JSONObject o) {
+
+ this(
+ o.optString("category", null),
+ o.optString("type", null),
+ o.optString("lang", null),
+ o.optString("name", null)
+ );
+ }
+
+ public String getCategory() {
+ return this.category;
+ }
+
+ public String getType() {
+ return this.type;
+ }
+
+ public String getLang() {
+ return this.lang;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public int compareTo(Object other) {
+ Identity o = (Identity) other;
+ int r = blankNull(this.getCategory()).compareTo(blankNull(o.getCategory()));
+ if (r == 0) {
+ r = blankNull(this.getType()).compareTo(blankNull(o.getType()));
+ }
+ if (r == 0) {
+ r = blankNull(this.getLang()).compareTo(blankNull(o.getLang()));
+ }
+ if (r == 0) {
+ r = blankNull(this.getName()).compareTo(blankNull(o.getName()));
+ }
+
+ return r;
+ }
+
+ public JSONObject toJSON() {
+ try {
+ JSONObject o = new JSONObject();
+ o.put("category", this.getCategory());
+ o.put("type", this.getType());
+ o.put("lang", this.getLang());
+ o.put("name", this.getName());
+ return o;
+ } catch (JSONException e) {
+ return null;
+ }
+ }
+ }
+
+ protected final String hash;
+ protected final byte[] ver;
+ protected final List<Identity> identities;
+ protected final List<String> features;
+ protected final List<Data> forms;
+
+ public ServiceDiscoveryResult(final IqPacket packet) {
+ this.identities = new ArrayList<>();
+ this.features = new ArrayList<>();
+ this.forms = new ArrayList<>();
+ this.hash = "sha-1"; // We only support sha-1 for now
+
+ final List<Element> elements = packet.query().getChildren();
+
+ for (final Element element : elements) {
+ if (element.getName().equals("identity")) {
+ Identity id = new Identity(element);
+ if (id.getType() != null && id.getCategory() != null) {
+ identities.add(id);
+ }
+ } else if (element.getName().equals("feature")) {
+ if (element.getAttribute("var") != null) {
+ features.add(element.getAttribute("var"));
+ }
+ } else if (element.getName().equals("x") && "jabber:x:data".equals(element.getAttribute("xmlns"))) {
+ forms.add(Data.parse(element));
+ }
+ }
+ this.ver = this.mkCapHash();
+ }
+
+ public ServiceDiscoveryResult(String hash, byte[] ver, JSONObject o) throws JSONException {
+ this.identities = new ArrayList<>();
+ this.features = new ArrayList<>();
+ this.forms = new ArrayList<>();
+ this.hash = hash;
+ this.ver = ver;
+
+ JSONArray identities = o.optJSONArray("identities");
+ if (identities != null) {
+ for (int i = 0; i < identities.length(); i++) {
+ this.identities.add(new Identity(identities.getJSONObject(i)));
+ }
+ }
+ JSONArray features = o.optJSONArray("features");
+ if (features != null) {
+ for (int i = 0; i < features.length(); i++) {
+ this.features.add(features.getString(i));
+ }
+ }
+ JSONArray forms = o.optJSONArray("forms");
+ if (forms != null) {
+ for (int i = 0; i < forms.length(); i++) {
+ this.forms.add(createFormFromJSONObject(forms.getJSONObject(i)));
+ }
+ }
+ }
+
+ private static Data createFormFromJSONObject(JSONObject o) {
+ Data data = new Data();
+ JSONArray names = o.names();
+ for (int i = 0; i < names.length(); ++i) {
+ try {
+ String name = names.getString(i);
+ JSONArray jsonValues = o.getJSONArray(name);
+ ArrayList<String> values = new ArrayList<>(jsonValues.length());
+ for (int j = 0; j < jsonValues.length(); ++j) {
+ values.add(jsonValues.getString(j));
+ }
+ data.put(name, values);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return data;
+ }
+
+ private static JSONObject createJSONFromForm(Data data) {
+ JSONObject object = new JSONObject();
+ for (Field field : data.getFields()) {
+ try {
+ JSONArray jsonValues = new JSONArray();
+ for (String value : field.getValues()) {
+ jsonValues.put(value);
+ }
+ object.put(field.getFieldName(), jsonValues);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ JSONArray jsonValues = new JSONArray();
+ jsonValues.put(data.getFormType());
+ object.put(Data.FORM_TYPE, jsonValues);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return object;
+ }
+
+ public String getVer() {
+ return new String(Base64.encode(this.ver, Base64.DEFAULT)).trim();
+ }
+
+ public ServiceDiscoveryResult(Cursor cursor) throws JSONException {
+ this(
+ cursor.getString(cursor.getColumnIndex(HASH)),
+ Base64.decode(cursor.getString(cursor.getColumnIndex(VER)), Base64.DEFAULT),
+ new JSONObject(cursor.getString(cursor.getColumnIndex(RESULT)))
+ );
+ }
+
+ public List<Identity> getIdentities() {
+ return this.identities;
+ }
+
+ public List<String> getFeatures() {
+ return this.features;
+ }
+
+ public boolean hasIdentity(String category, String type) {
+ for (Identity id : this.getIdentities()) {
+ if ((category == null || id.getCategory().equals(category)) &&
+ (type == null || id.getType().equals(type))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public String getExtendedDiscoInformation(String formType, String name) {
+ for (Data form : this.forms) {
+ if (formType.equals(form.getFormType())) {
+ for (Field field : form.getFields()) {
+ if (name.equals(field.getFieldName())) {
+ return field.getValue();
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ protected byte[] mkCapHash() {
+ StringBuilder s = new StringBuilder();
+
+ List<Identity> identities = this.getIdentities();
+ Collections.sort(identities);
+
+ for (Identity id : identities) {
+ s.append(
+ blankNull(id.getCategory()) + "/" +
+ blankNull(id.getType()) + "/" +
+ blankNull(id.getLang()) + "/" +
+ blankNull(id.getName()) + "<"
+ );
+ }
+
+ List<String> features = this.getFeatures();
+ Collections.sort(features);
+
+ for (String feature : features) {
+ s.append(feature + "<");
+ }
+
+ Collections.sort(forms, new Comparator<Data>() {
+ @Override
+ public int compare(Data lhs, Data rhs) {
+ return lhs.getFormType().compareTo(rhs.getFormType());
+ }
+ });
+
+ for (Data form : forms) {
+ s.append(form.getFormType() + "<");
+ List<Field> fields = form.getFields();
+ Collections.sort(fields, new Comparator<Field>() {
+ @Override
+ public int compare(Field lhs, Field rhs) {
+ return lhs.getFieldName().compareTo(rhs.getFieldName());
+ }
+ });
+ for (Field field : fields) {
+ s.append(field.getFieldName() + "<");
+ List<String> values = field.getValues();
+ Collections.sort(values);
+ for (String value : values) {
+ s.append(value + "<");
+ }
+ }
+ }
+
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+
+ try {
+ return md.digest(s.toString().getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
+ }
+
+ public JSONObject toJSON() {
+ try {
+ JSONObject o = new JSONObject();
+
+ JSONArray ids = new JSONArray();
+ for (Identity id : this.getIdentities()) {
+ ids.put(id.toJSON());
+ }
+ o.put("identities", ids);
+
+ o.put("features", new JSONArray(this.getFeatures()));
+
+ JSONArray forms = new JSONArray();
+ for (Data data : this.forms) {
+ forms.put(createJSONFromForm(data));
+ }
+ o.put("forms", forms);
+
+ return o;
+ } catch (JSONException e) {
+ return null;
+ }
+ }
+
+ public ContentValues getContentValues() {
+ final ContentValues values = new ContentValues();
+ values.put(HASH, this.hash);
+ values.put(VER, getVer());
+ values.put(RESULT, this.toJSON().toString());
+ return values;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/entities/Transferable.java b/src/main/java/de/pixart/messenger/entities/Transferable.java
index 14132ac38..c07ec750d 100644
--- a/src/main/java/de/pixart/messenger/entities/Transferable.java
+++ b/src/main/java/de/pixart/messenger/entities/Transferable.java
@@ -54,7 +54,7 @@ public interface Transferable {
//compressed
"zip",
"rar"
- );
+ );
int STATUS_UNKNOWN = 0x200;
int STATUS_CHECKING = 0x201;
diff --git a/src/main/java/de/pixart/messenger/entities/TransferablePlaceholder.java b/src/main/java/de/pixart/messenger/entities/TransferablePlaceholder.java
index 59f1dc846..e22984f5a 100644
--- a/src/main/java/de/pixart/messenger/entities/TransferablePlaceholder.java
+++ b/src/main/java/de/pixart/messenger/entities/TransferablePlaceholder.java
@@ -2,33 +2,34 @@ package de.pixart.messenger.entities;
public class TransferablePlaceholder implements Transferable {
- private int status;
-
- public TransferablePlaceholder(int status) {
- this.status = status;
- }
- @Override
- public boolean start() {
- return false;
- }
-
- @Override
- public int getStatus() {
- return status;
- }
-
- @Override
- public long getFileSize() {
- return 0;
- }
-
- @Override
- public int getProgress() {
- return 0;
- }
-
- @Override
- public void cancel() {
-
- }
+ private int status;
+
+ public TransferablePlaceholder(int status) {
+ this.status = status;
+ }
+
+ @Override
+ public boolean start() {
+ return false;
+ }
+
+ @Override
+ public int getStatus() {
+ return status;
+ }
+
+ @Override
+ public long getFileSize() {
+ return 0;
+ }
+
+ @Override
+ public int getProgress() {
+ return 0;
+ }
+
+ @Override
+ public void cancel() {
+
+ }
}
diff --git a/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java b/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java
index 829b033fe..33ce81859 100644
--- a/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java
+++ b/src/main/java/de/pixart/messenger/generator/AbstractGenerator.java
@@ -20,105 +20,105 @@ import de.pixart.messenger.utils.PhoneHelper;
import de.pixart.messenger.xmpp.jingle.stanzas.Content;
public abstract class AbstractGenerator {
- private final String[] FEATURES = {
- "urn:xmpp:jingle:1",
- Content.Version.FT_3.getNamespace(),
- Content.Version.FT_4.getNamespace(),
- "urn:xmpp:jingle:transports:s5b:1",
- "urn:xmpp:jingle:transports:ibb:1",
- "http://jabber.org/protocol/muc",
- "jabber:x:conference",
- "http://jabber.org/protocol/caps",
- "http://jabber.org/protocol/disco#info",
- "urn:xmpp:avatar:metadata+notify",
- "http://jabber.org/protocol/nick+notify",
- "urn:xmpp:ping",
- "jabber:iq:version",
- "http://jabber.org/protocol/chatstates"
- };
- private final String[] MESSAGE_CONFIRMATION_FEATURES = {
- "urn:xmpp:chat-markers:0",
- "urn:xmpp:receipts"
- };
- private final String[] MESSAGE_CORRECTION_FEATURES = {
- "urn:xmpp:message-correct:0"
- };
- private final String[] PRIVACY_SENSITIVE = {
- "urn:xmpp:time" //XEP-0202: Entity Time leaks time zone
- };
- private final String[] OTR = {
- "urn:xmpp:otr:0"
- };
- private String mVersion = null;
+ private final String[] FEATURES = {
+ "urn:xmpp:jingle:1",
+ Content.Version.FT_3.getNamespace(),
+ Content.Version.FT_4.getNamespace(),
+ "urn:xmpp:jingle:transports:s5b:1",
+ "urn:xmpp:jingle:transports:ibb:1",
+ "http://jabber.org/protocol/muc",
+ "jabber:x:conference",
+ "http://jabber.org/protocol/caps",
+ "http://jabber.org/protocol/disco#info",
+ "urn:xmpp:avatar:metadata+notify",
+ "http://jabber.org/protocol/nick+notify",
+ "urn:xmpp:ping",
+ "jabber:iq:version",
+ "http://jabber.org/protocol/chatstates"
+ };
+ private final String[] MESSAGE_CONFIRMATION_FEATURES = {
+ "urn:xmpp:chat-markers:0",
+ "urn:xmpp:receipts"
+ };
+ private final String[] MESSAGE_CORRECTION_FEATURES = {
+ "urn:xmpp:message-correct:0"
+ };
+ private final String[] PRIVACY_SENSITIVE = {
+ "urn:xmpp:time" //XEP-0202: Entity Time leaks time zone
+ };
+ private final String[] OTR = {
+ "urn:xmpp:otr:0"
+ };
+ private String mVersion = null;
- private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
- protected XmppConnectionService mXmppConnectionService;
+ protected XmppConnectionService mXmppConnectionService;
- protected AbstractGenerator(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
+ protected AbstractGenerator(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
- protected String getIdentityVersion() {
- if (mVersion == null) {
- this.mVersion = PhoneHelper.getVersionName(mXmppConnectionService);
- }
- return this.mVersion;
- }
+ protected String getIdentityVersion() {
+ if (mVersion == null) {
+ this.mVersion = PhoneHelper.getVersionName(mXmppConnectionService);
+ }
+ return this.mVersion;
+ }
- public String getIdentityName() {
- return mXmppConnectionService.getString(R.string.app_name) + " " + getIdentityVersion();
- }
+ public String getIdentityName() {
+ return mXmppConnectionService.getString(R.string.app_name) + " " + getIdentityVersion();
+ }
- public String getIdentityType() {
- if ("chromium".equals(android.os.Build.BRAND)) {
- return "pc";
- } else {
- return mXmppConnectionService.getString(R.string.default_resource).toLowerCase();
- }
- }
+ public String getIdentityType() {
+ if ("chromium".equals(android.os.Build.BRAND)) {
+ return "pc";
+ } else {
+ return mXmppConnectionService.getString(R.string.default_resource).toLowerCase();
+ }
+ }
- public String getCapHash() {
- StringBuilder s = new StringBuilder();
- s.append("client/" + getIdentityType() + "//" + getIdentityName() + "<");
- MessageDigest md;
- try {
- md = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- return null;
- }
+ public String getCapHash() {
+ StringBuilder s = new StringBuilder();
+ s.append("client/" + getIdentityType() + "//" + getIdentityName() + "<");
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
- for (String feature : getFeatures()) {
- s.append(feature + "<");
- }
- byte[] sha1 = md.digest(s.toString().getBytes());
- return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
- }
+ for (String feature : getFeatures()) {
+ s.append(feature + "<");
+ }
+ byte[] sha1 = md.digest(s.toString().getBytes());
+ return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
+ }
- public static String getTimestamp(long time) {
- DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
- return DATE_FORMAT.format(time);
- }
+ public static String getTimestamp(long time) {
+ DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return DATE_FORMAT.format(time);
+ }
- public List<String> getFeatures() {
- ArrayList<String> features = new ArrayList<>();
- features.addAll(Arrays.asList(FEATURES));
- if (mXmppConnectionService.confirmMessages()) {
- features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES));
- }
- if (mXmppConnectionService.allowMessageCorrection()) {
- features.addAll(Arrays.asList(MESSAGE_CORRECTION_FEATURES));
- }
- if (Config.supportOmemo()) {
- features.add(AxolotlService.PEP_DEVICE_LIST_NOTIFY);
- }
- if (!mXmppConnectionService.useTorToConnect()) {
- features.addAll(Arrays.asList(PRIVACY_SENSITIVE));
- }
- if (Config.supportOtr()) {
- features.addAll(Arrays.asList(OTR));
- }
- Collections.sort(features);
- return features;
- }
+ public List<String> getFeatures() {
+ ArrayList<String> features = new ArrayList<>();
+ features.addAll(Arrays.asList(FEATURES));
+ if (mXmppConnectionService.confirmMessages()) {
+ features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES));
+ }
+ if (mXmppConnectionService.allowMessageCorrection()) {
+ features.addAll(Arrays.asList(MESSAGE_CORRECTION_FEATURES));
+ }
+ if (Config.supportOmemo()) {
+ features.add(AxolotlService.PEP_DEVICE_LIST_NOTIFY);
+ }
+ if (!mXmppConnectionService.useTorToConnect()) {
+ features.addAll(Arrays.asList(PRIVACY_SENSITIVE));
+ }
+ if (Config.supportOtr()) {
+ features.addAll(Arrays.asList(OTR));
+ }
+ Collections.sort(features);
+ return features;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/generator/IqGenerator.java b/src/main/java/de/pixart/messenger/generator/IqGenerator.java
index fd7177a8c..e2b462f76 100644
--- a/src/main/java/de/pixart/messenger/generator/IqGenerator.java
+++ b/src/main/java/de/pixart/messenger/generator/IqGenerator.java
@@ -34,347 +34,348 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class IqGenerator extends AbstractGenerator {
- public IqGenerator(final XmppConnectionService service) {
- super(service);
- }
-
- public IqPacket discoResponse(final IqPacket request) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.RESULT);
- packet.setId(request.getId());
- packet.setTo(request.getFrom());
- final Element query = packet.addChild("query",
- "http://jabber.org/protocol/disco#info");
- query.setAttribute("node", request.query().getAttribute("node"));
- final Element identity = query.addChild("identity");
- identity.setAttribute("category", "client");
- identity.setAttribute("type", getIdentityType());
- identity.setAttribute("name", getIdentityName());
- for (final String feature : getFeatures()) {
- query.addChild("feature").setAttribute("var", feature);
- }
- return packet;
- }
-
- public IqPacket versionResponse(final IqPacket request) {
- final IqPacket packet = request.generateResponse(IqPacket.TYPE.RESULT);
- Element query = packet.query("jabber:iq:version");
- query.addChild("name").setContent(mXmppConnectionService.getString(R.string.app_name));
- query.addChild("version").setContent(getIdentityVersion());
- if ("chromium".equals(android.os.Build.BRAND)) {
- query.addChild("os").setContent("Chrome OS");
- } else{
- query.addChild("os").setContent("Android");
- }
- return packet;
- }
-
- public IqPacket entityTimeResponse(IqPacket request) {
- final IqPacket packet = request.generateResponse(IqPacket.TYPE.RESULT);
- Element time = packet.addChild("time","urn:xmpp:time");
- final long now = System.currentTimeMillis();
- time.addChild("utc").setContent(getTimestamp(now));
- TimeZone ourTimezone = TimeZone.getDefault();
- long offsetSeconds = ourTimezone.getOffset(now) / 1000;
- long offsetMinutes = offsetSeconds % (60 * 60);
- long offsetHours = offsetSeconds / (60 * 60);
- time.addChild("tzo").setContent(String.format("%02d",offsetHours)+":"+String.format("%02d",offsetMinutes));
- return packet;
- }
-
- protected IqPacket publish(final String node, final Element item) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- final Element pubsub = packet.addChild("pubsub",
- "http://jabber.org/protocol/pubsub");
- final Element publish = pubsub.addChild("publish");
- publish.setAttribute("node", node);
- publish.addChild(item);
- return packet;
- }
-
- protected IqPacket retrieve(String node, Element item) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
- final Element pubsub = packet.addChild("pubsub",
- "http://jabber.org/protocol/pubsub");
- final Element items = pubsub.addChild("items");
- items.setAttribute("node", node);
- if (item != null) {
- items.addChild(item);
- }
- return packet;
- }
-
- public IqPacket publishNick(String nick) {
- final Element item = new Element("item");
- item.addChild("nick","http://jabber.org/protocol/nick").setContent(nick);
- return publish("http://jabber.org/protocol/nick", item);
- }
-
- public IqPacket publishAvatar(Avatar avatar) {
- final Element item = new Element("item");
- item.setAttribute("id", avatar.sha1sum);
- final Element data = item.addChild("data", "urn:xmpp:avatar:data");
- data.setContent(avatar.image);
- return publish("urn:xmpp:avatar:data", item);
- }
-
- public IqPacket publishAvatarMetadata(final Avatar avatar) {
- final Element item = new Element("item");
- item.setAttribute("id", avatar.sha1sum);
- final Element metadata = item
- .addChild("metadata", "urn:xmpp: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);
- }
-
- 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);
- packet.setTo(avatar.owner);
- return packet;
- }
-
- public IqPacket retrieveVcardAvatar(final Avatar avatar) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
- packet.setTo(avatar.owner);
- 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) {
- packet.setTo(to);
- }
- return packet;
- }
-
- public IqPacket retrieveDeviceIds(final Jid to) {
- final IqPacket packet = retrieve(AxolotlService.PEP_DEVICE_LIST, null);
- if(to != null) {
- packet.setTo(to);
- }
- return packet;
- }
-
- public IqPacket retrieveBundlesForDevice(final Jid to, final int deviceid) {
- final IqPacket packet = retrieve(AxolotlService.PEP_BUNDLES+":"+deviceid, null);
- packet.setTo(to);
- return packet;
- }
-
- public IqPacket retrieveVerificationForDevice(final Jid to, final int deviceid) {
- final IqPacket packet = retrieve(AxolotlService.PEP_VERIFICATION+":"+deviceid, null);
- packet.setTo(to);
- return packet;
- }
-
- public IqPacket publishDeviceIds(final Set<Integer> ids) {
- final Element item = new Element("item");
- final Element list = item.addChild("list", AxolotlService.PEP_PREFIX);
- for(Integer id:ids) {
- final Element device = new Element("device");
- device.setAttribute("id", id);
- list.addChild(device);
- }
- return publish(AxolotlService.PEP_DEVICE_LIST, item);
- }
-
- public IqPacket publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey,
- final Set<PreKeyRecord> preKeyRecords, final int deviceId) {
- final Element item = new Element("item");
- final Element bundle = item.addChild("bundle", AxolotlService.PEP_PREFIX);
- final Element signedPreKeyPublic = bundle.addChild("signedPreKeyPublic");
- signedPreKeyPublic.setAttribute("signedPreKeyId", signedPreKeyRecord.getId());
- ECPublicKey publicKey = signedPreKeyRecord.getKeyPair().getPublicKey();
- signedPreKeyPublic.setContent(Base64.encodeToString(publicKey.serialize(),Base64.DEFAULT));
- final Element signedPreKeySignature = bundle.addChild("signedPreKeySignature");
- signedPreKeySignature.setContent(Base64.encodeToString(signedPreKeyRecord.getSignature(),Base64.DEFAULT));
- final Element identityKeyElement = bundle.addChild("identityKey");
- identityKeyElement.setContent(Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT));
-
- final Element prekeys = bundle.addChild("prekeys", AxolotlService.PEP_PREFIX);
- for(PreKeyRecord preKeyRecord:preKeyRecords) {
- final Element prekey = prekeys.addChild("preKeyPublic");
- prekey.setAttribute("preKeyId", preKeyRecord.getId());
- prekey.setContent(Base64.encodeToString(preKeyRecord.getKeyPair().getPublicKey().serialize(), Base64.DEFAULT));
- }
-
- return publish(AxolotlService.PEP_BUNDLES+":"+deviceId, item);
- }
-
- public IqPacket publishVerification(byte[] signature, X509Certificate[] certificates, final int deviceId) {
- final Element item = new Element("item");
- final Element verification = item.addChild("verification", AxolotlService.PEP_PREFIX);
- final Element chain = verification.addChild("chain");
- for(int i = 0; i < certificates.length; ++i) {
- try {
- Element certificate = chain.addChild("certificate");
- certificate.setContent(Base64.encodeToString(certificates[i].getEncoded(), Base64.DEFAULT));
- certificate.setAttribute("index",i);
- } catch (CertificateEncodingException e) {
- Log.d(Config.LOGTAG, "could not encode certificate");
- }
- }
- verification.addChild("signature").setContent(Base64.encodeToString(signature, Base64.DEFAULT));
- return publish(AxolotlService.PEP_VERIFICATION+":"+deviceId, item);
- }
-
- public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- final Element query = packet.query("urn:xmpp:mam:0");
- query.setAttribute("queryid", mam.getQueryId());
- final Data data = new Data();
- data.setFormType("urn:xmpp:mam:0");
- if (mam.muc()) {
- packet.setTo(mam.getWith());
- } else if (mam.getWith()!=null) {
- data.put("with", mam.getWith().toString());
- }
- data.put("start", getTimestamp(mam.getStart()));
- data.put("end", getTimestamp(mam.getEnd()));
- data.submit();
- query.addChild(data);
- if (mam.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
- query.addChild("set", "http://jabber.org/protocol/rsm").addChild("before").setContent(mam.getReference());
- } else if (mam.getReference() != null) {
- query.addChild("set", "http://jabber.org/protocol/rsm").addChild("after").setContent(mam.getReference());
- }
- return packet;
- }
- public IqPacket generateGetBlockList() {
- final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
- iq.addChild("blocklist", Xmlns.BLOCKING);
-
- return iq;
- }
-
- public IqPacket generateSetBlockRequest(final Jid jid, boolean reportSpam) {
- final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- final Element block = iq.addChild("block", Xmlns.BLOCKING);
- final Element item = block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
- if (reportSpam) {
- item.addChild("report", "urn:xmpp:reporting:0").addChild("spam");
- }
- Log.d(Config.LOGTAG,iq.toString());
- return iq;
- }
-
- public IqPacket generateSetUnblockRequest(final Jid jid) {
- final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- final Element block = iq.addChild("unblock", Xmlns.BLOCKING);
- block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
- return iq;
- }
-
- public IqPacket generateSetPassword(final Account account, final String newPassword) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- packet.setTo(account.getServer());
- final Element query = packet.addChild("query", Xmlns.REGISTER);
- final Jid jid = account.getJid();
- query.addChild("username").setContent(jid.getLocalpart());
- query.addChild("password").setContent(newPassword);
- return packet;
- }
-
- public IqPacket changeAffiliation(Conversation conference, Jid jid, String affiliation) {
- List<Jid> jids = new ArrayList<>();
- jids.add(jid);
- return changeAffiliation(conference,jids,affiliation);
- }
-
- public IqPacket changeAffiliation(Conversation conference, List<Jid> jids, String affiliation) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- packet.setTo(conference.getJid().toBareJid());
- packet.setFrom(conference.getAccount().getJid());
- Element query = packet.query("http://jabber.org/protocol/muc#admin");
- for(Jid jid : jids) {
- Element item = query.addChild("item");
- item.setAttribute("jid", jid.toString());
- item.setAttribute("affiliation", affiliation);
- }
- return packet;
- }
-
- public IqPacket changeRole(Conversation conference, String nick, String role) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- packet.setTo(conference.getJid().toBareJid());
- packet.setFrom(conference.getAccount().getJid());
- Element item = packet.query("http://jabber.org/protocol/muc#admin").addChild("item");
- item.setAttribute("nick", nick);
- item.setAttribute("role", role);
- return packet;
- }
-
- public IqPacket requestHttpUploadSlot(Jid host, DownloadableFile file, String mime) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
- packet.setTo(host);
- Element request = packet.addChild("request", Xmlns.HTTP_UPLOAD);
- request.addChild("filename").setContent(file.getName());
- request.addChild("size").setContent(String.valueOf(file.getExpectedSize()));
- if (mime != null) {
- request.addChild("content-type").setContent(mime);
- }
- return packet;
- }
-
- public IqPacket generateCreateAccountWithCaptcha(Account account, String id, Data data) {
- final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
- register.setFrom(account.getJid().toBareJid());
- register.setTo(account.getServer());
- register.setId(id);
- Element query = register.query("jabber:iq:register");
- if (data != null) {
- query.addChild(data);
- }
- return register;
- }
-
- public IqPacket pushTokenToAppServer(Jid appServer, String token, String deviceId) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- packet.setTo(appServer);
- Element command = packet.addChild("command", "http://jabber.org/protocol/commands");
- command.setAttribute("node","register-push-gcm");
- command.setAttribute("action","execute");
- Data data = new Data();
- data.put("token", token);
- data.put("device-id", deviceId);
- data.submit();
- command.addChild(data);
- return packet;
- }
-
- public IqPacket enablePush(Jid jid, String node, String secret) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
- Element enable = packet.addChild("enable","urn:xmpp:push:0");
- enable.setAttribute("jid",jid.toString());
- enable.setAttribute("node", node);
- Data data = new Data();
- data.setFormType("http://jabber.org/protocol/pubsub#publish-options");
- data.put("secret",secret);
- data.submit();
- enable.addChild(data);
- return packet;
- }
-
- public IqPacket queryAffiliation(Conversation conversation, String affiliation) {
- IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
- packet.setTo(conversation.getJid().toBareJid());
- packet.query("http://jabber.org/protocol/muc#admin").addChild("item").setAttribute("affiliation",affiliation);
- return packet;
- }
-
- public static Bundle defaultRoomConfiguration() {
- Bundle options = new Bundle();
- options.putString("muc#roomconfig_persistentroom", "1");
- options.putString("muc#roomconfig_membersonly", "1");
- options.putString("muc#roomconfig_publicroom", "0");
- options.putString("muc#roomconfig_whois", "anyone");
- return options;
- }
+ public IqGenerator(final XmppConnectionService service) {
+ super(service);
+ }
+
+ public IqPacket discoResponse(final IqPacket request) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE.RESULT);
+ packet.setId(request.getId());
+ packet.setTo(request.getFrom());
+ final Element query = packet.addChild("query",
+ "http://jabber.org/protocol/disco#info");
+ query.setAttribute("node", request.query().getAttribute("node"));
+ final Element identity = query.addChild("identity");
+ identity.setAttribute("category", "client");
+ identity.setAttribute("type", getIdentityType());
+ identity.setAttribute("name", getIdentityName());
+ for (final String feature : getFeatures()) {
+ query.addChild("feature").setAttribute("var", feature);
+ }
+ return packet;
+ }
+
+ public IqPacket versionResponse(final IqPacket request) {
+ final IqPacket packet = request.generateResponse(IqPacket.TYPE.RESULT);
+ Element query = packet.query("jabber:iq:version");
+ query.addChild("name").setContent(mXmppConnectionService.getString(R.string.app_name));
+ query.addChild("version").setContent(getIdentityVersion());
+ if ("chromium".equals(android.os.Build.BRAND)) {
+ query.addChild("os").setContent("Chrome OS");
+ } else {
+ query.addChild("os").setContent("Android");
+ }
+ return packet;
+ }
+
+ public IqPacket entityTimeResponse(IqPacket request) {
+ final IqPacket packet = request.generateResponse(IqPacket.TYPE.RESULT);
+ Element time = packet.addChild("time", "urn:xmpp:time");
+ final long now = System.currentTimeMillis();
+ time.addChild("utc").setContent(getTimestamp(now));
+ TimeZone ourTimezone = TimeZone.getDefault();
+ long offsetSeconds = ourTimezone.getOffset(now) / 1000;
+ long offsetMinutes = offsetSeconds % (60 * 60);
+ long offsetHours = offsetSeconds / (60 * 60);
+ time.addChild("tzo").setContent(String.format("%02d", offsetHours) + ":" + String.format("%02d", offsetMinutes));
+ return packet;
+ }
+
+ protected IqPacket publish(final String node, final Element item) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ final Element pubsub = packet.addChild("pubsub",
+ "http://jabber.org/protocol/pubsub");
+ final Element publish = pubsub.addChild("publish");
+ publish.setAttribute("node", node);
+ publish.addChild(item);
+ return packet;
+ }
+
+ protected IqPacket retrieve(String node, Element item) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
+ final Element pubsub = packet.addChild("pubsub",
+ "http://jabber.org/protocol/pubsub");
+ final Element items = pubsub.addChild("items");
+ items.setAttribute("node", node);
+ if (item != null) {
+ items.addChild(item);
+ }
+ return packet;
+ }
+
+ public IqPacket publishNick(String nick) {
+ final Element item = new Element("item");
+ item.addChild("nick", "http://jabber.org/protocol/nick").setContent(nick);
+ return publish("http://jabber.org/protocol/nick", item);
+ }
+
+ public IqPacket publishAvatar(Avatar avatar) {
+ final Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ final Element data = item.addChild("data", "urn:xmpp:avatar:data");
+ data.setContent(avatar.image);
+ return publish("urn:xmpp:avatar:data", item);
+ }
+
+ public IqPacket publishAvatarMetadata(final Avatar avatar) {
+ final Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ final Element metadata = item
+ .addChild("metadata", "urn:xmpp: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);
+ }
+
+ 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);
+ packet.setTo(avatar.owner);
+ return packet;
+ }
+
+ public IqPacket retrieveVcardAvatar(final Avatar avatar) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
+ packet.setTo(avatar.owner);
+ 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) {
+ packet.setTo(to);
+ }
+ return packet;
+ }
+
+ public IqPacket retrieveDeviceIds(final Jid to) {
+ final IqPacket packet = retrieve(AxolotlService.PEP_DEVICE_LIST, null);
+ if (to != null) {
+ packet.setTo(to);
+ }
+ return packet;
+ }
+
+ public IqPacket retrieveBundlesForDevice(final Jid to, final int deviceid) {
+ final IqPacket packet = retrieve(AxolotlService.PEP_BUNDLES + ":" + deviceid, null);
+ packet.setTo(to);
+ return packet;
+ }
+
+ public IqPacket retrieveVerificationForDevice(final Jid to, final int deviceid) {
+ final IqPacket packet = retrieve(AxolotlService.PEP_VERIFICATION + ":" + deviceid, null);
+ packet.setTo(to);
+ return packet;
+ }
+
+ public IqPacket publishDeviceIds(final Set<Integer> ids) {
+ final Element item = new Element("item");
+ final Element list = item.addChild("list", AxolotlService.PEP_PREFIX);
+ for (Integer id : ids) {
+ final Element device = new Element("device");
+ device.setAttribute("id", id);
+ list.addChild(device);
+ }
+ return publish(AxolotlService.PEP_DEVICE_LIST, item);
+ }
+
+ public IqPacket publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey,
+ final Set<PreKeyRecord> preKeyRecords, final int deviceId) {
+ final Element item = new Element("item");
+ final Element bundle = item.addChild("bundle", AxolotlService.PEP_PREFIX);
+ final Element signedPreKeyPublic = bundle.addChild("signedPreKeyPublic");
+ signedPreKeyPublic.setAttribute("signedPreKeyId", signedPreKeyRecord.getId());
+ ECPublicKey publicKey = signedPreKeyRecord.getKeyPair().getPublicKey();
+ signedPreKeyPublic.setContent(Base64.encodeToString(publicKey.serialize(), Base64.DEFAULT));
+ final Element signedPreKeySignature = bundle.addChild("signedPreKeySignature");
+ signedPreKeySignature.setContent(Base64.encodeToString(signedPreKeyRecord.getSignature(), Base64.DEFAULT));
+ final Element identityKeyElement = bundle.addChild("identityKey");
+ identityKeyElement.setContent(Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT));
+
+ final Element prekeys = bundle.addChild("prekeys", AxolotlService.PEP_PREFIX);
+ for (PreKeyRecord preKeyRecord : preKeyRecords) {
+ final Element prekey = prekeys.addChild("preKeyPublic");
+ prekey.setAttribute("preKeyId", preKeyRecord.getId());
+ prekey.setContent(Base64.encodeToString(preKeyRecord.getKeyPair().getPublicKey().serialize(), Base64.DEFAULT));
+ }
+
+ return publish(AxolotlService.PEP_BUNDLES + ":" + deviceId, item);
+ }
+
+ public IqPacket publishVerification(byte[] signature, X509Certificate[] certificates, final int deviceId) {
+ final Element item = new Element("item");
+ final Element verification = item.addChild("verification", AxolotlService.PEP_PREFIX);
+ final Element chain = verification.addChild("chain");
+ for (int i = 0; i < certificates.length; ++i) {
+ try {
+ Element certificate = chain.addChild("certificate");
+ certificate.setContent(Base64.encodeToString(certificates[i].getEncoded(), Base64.DEFAULT));
+ certificate.setAttribute("index", i);
+ } catch (CertificateEncodingException e) {
+ Log.d(Config.LOGTAG, "could not encode certificate");
+ }
+ }
+ verification.addChild("signature").setContent(Base64.encodeToString(signature, Base64.DEFAULT));
+ return publish(AxolotlService.PEP_VERIFICATION + ":" + deviceId, item);
+ }
+
+ public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ final Element query = packet.query("urn:xmpp:mam:0");
+ query.setAttribute("queryid", mam.getQueryId());
+ final Data data = new Data();
+ data.setFormType("urn:xmpp:mam:0");
+ if (mam.muc()) {
+ packet.setTo(mam.getWith());
+ } else if (mam.getWith() != null) {
+ data.put("with", mam.getWith().toString());
+ }
+ data.put("start", getTimestamp(mam.getStart()));
+ data.put("end", getTimestamp(mam.getEnd()));
+ data.submit();
+ query.addChild(data);
+ if (mam.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
+ query.addChild("set", "http://jabber.org/protocol/rsm").addChild("before").setContent(mam.getReference());
+ } else if (mam.getReference() != null) {
+ query.addChild("set", "http://jabber.org/protocol/rsm").addChild("after").setContent(mam.getReference());
+ }
+ return packet;
+ }
+
+ public IqPacket generateGetBlockList() {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
+ iq.addChild("blocklist", Xmlns.BLOCKING);
+
+ return iq;
+ }
+
+ public IqPacket generateSetBlockRequest(final Jid jid, boolean reportSpam) {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ final Element block = iq.addChild("block", Xmlns.BLOCKING);
+ final Element item = block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
+ if (reportSpam) {
+ item.addChild("report", "urn:xmpp:reporting:0").addChild("spam");
+ }
+ Log.d(Config.LOGTAG, iq.toString());
+ return iq;
+ }
+
+ public IqPacket generateSetUnblockRequest(final Jid jid) {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ final Element block = iq.addChild("unblock", Xmlns.BLOCKING);
+ block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
+ return iq;
+ }
+
+ public IqPacket generateSetPassword(final Account account, final String newPassword) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ packet.setTo(account.getServer());
+ final Element query = packet.addChild("query", Xmlns.REGISTER);
+ final Jid jid = account.getJid();
+ query.addChild("username").setContent(jid.getLocalpart());
+ query.addChild("password").setContent(newPassword);
+ return packet;
+ }
+
+ public IqPacket changeAffiliation(Conversation conference, Jid jid, String affiliation) {
+ List<Jid> jids = new ArrayList<>();
+ jids.add(jid);
+ return changeAffiliation(conference, jids, affiliation);
+ }
+
+ public IqPacket changeAffiliation(Conversation conference, List<Jid> jids, String affiliation) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ packet.setTo(conference.getJid().toBareJid());
+ packet.setFrom(conference.getAccount().getJid());
+ Element query = packet.query("http://jabber.org/protocol/muc#admin");
+ for (Jid jid : jids) {
+ Element item = query.addChild("item");
+ item.setAttribute("jid", jid.toString());
+ item.setAttribute("affiliation", affiliation);
+ }
+ return packet;
+ }
+
+ public IqPacket changeRole(Conversation conference, String nick, String role) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ packet.setTo(conference.getJid().toBareJid());
+ packet.setFrom(conference.getAccount().getJid());
+ Element item = packet.query("http://jabber.org/protocol/muc#admin").addChild("item");
+ item.setAttribute("nick", nick);
+ item.setAttribute("role", role);
+ return packet;
+ }
+
+ public IqPacket requestHttpUploadSlot(Jid host, DownloadableFile file, String mime) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
+ packet.setTo(host);
+ Element request = packet.addChild("request", Xmlns.HTTP_UPLOAD);
+ request.addChild("filename").setContent(file.getName());
+ request.addChild("size").setContent(String.valueOf(file.getExpectedSize()));
+ if (mime != null) {
+ request.addChild("content-type").setContent(mime);
+ }
+ return packet;
+ }
+
+ public IqPacket generateCreateAccountWithCaptcha(Account account, String id, Data data) {
+ final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
+ register.setFrom(account.getJid().toBareJid());
+ register.setTo(account.getServer());
+ register.setId(id);
+ Element query = register.query("jabber:iq:register");
+ if (data != null) {
+ query.addChild(data);
+ }
+ return register;
+ }
+
+ public IqPacket pushTokenToAppServer(Jid appServer, String token, String deviceId) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ packet.setTo(appServer);
+ Element command = packet.addChild("command", "http://jabber.org/protocol/commands");
+ command.setAttribute("node", "register-push-gcm");
+ command.setAttribute("action", "execute");
+ Data data = new Data();
+ data.put("token", token);
+ data.put("device-id", deviceId);
+ data.submit();
+ command.addChild(data);
+ return packet;
+ }
+
+ public IqPacket enablePush(Jid jid, String node, String secret) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ Element enable = packet.addChild("enable", "urn:xmpp:push:0");
+ enable.setAttribute("jid", jid.toString());
+ enable.setAttribute("node", node);
+ Data data = new Data();
+ data.setFormType("http://jabber.org/protocol/pubsub#publish-options");
+ data.put("secret", secret);
+ data.submit();
+ enable.addChild(data);
+ return packet;
+ }
+
+ public IqPacket queryAffiliation(Conversation conversation, String affiliation) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
+ packet.setTo(conversation.getJid().toBareJid());
+ packet.query("http://jabber.org/protocol/muc#admin").addChild("item").setAttribute("affiliation", affiliation);
+ return packet;
+ }
+
+ public static Bundle defaultRoomConfiguration() {
+ Bundle options = new Bundle();
+ options.putString("muc#roomconfig_persistentroom", "1");
+ options.putString("muc#roomconfig_membersonly", "1");
+ options.putString("muc#roomconfig_publicroom", "0");
+ options.putString("muc#roomconfig_whois", "anyone");
+ return options;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/generator/MessageGenerator.java b/src/main/java/de/pixart/messenger/generator/MessageGenerator.java
index ce0a81a72..bdb4b7837 100644
--- a/src/main/java/de/pixart/messenger/generator/MessageGenerator.java
+++ b/src/main/java/de/pixart/messenger/generator/MessageGenerator.java
@@ -23,207 +23,207 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.pixart.messenger.xmpp.stanzas.MessagePacket;
public class MessageGenerator extends AbstractGenerator {
- public static final String OTR_FALLBACK_MESSAGE = "I would like to start a private (OTR encrypted) conversation but your client doesn’t seem to support that";
- private static final String OMEMO_FALLBACK_MESSAGE = "I sent you an OMEMO encrypted message but your client doesn’t seem to support that. Find more information on https://conversations.im/omemo";
- private static final String PGP_FALLBACK_MESSAGE = "I sent you a PGP encrypted message but your client doesn’t seem to support that.";
-
- public MessageGenerator(XmppConnectionService service) {
- super(service);
- }
-
- private MessagePacket preparePacket(Message message) {
- Conversation conversation = message.getConversation();
- Account account = conversation.getAccount();
- MessagePacket packet = new MessagePacket();
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- packet.setTo(message.getCounterpart());
- packet.setType(MessagePacket.TYPE_CHAT);
- packet.addChild("markable", "urn:xmpp:chat-markers:0");
- if (this.mXmppConnectionService.indicateReceived()) {
- packet.addChild("request", "urn:xmpp:receipts");
- }
- } else if (message.getType() == Message.TYPE_PRIVATE) {
- packet.setTo(message.getCounterpart());
- packet.setType(MessagePacket.TYPE_CHAT);
- if (this.mXmppConnectionService.indicateReceived()) {
- packet.addChild("request", "urn:xmpp:receipts");
- }
- } else {
- packet.setTo(message.getCounterpart().toBareJid());
- packet.setType(MessagePacket.TYPE_GROUPCHAT);
- }
- packet.setFrom(account.getJid());
- packet.setId(message.getUuid());
- if (message.edited()) {
- packet.addChild("replace","urn:xmpp:message-correct:0").setAttribute("id",message.getEditedId());
- }
- return packet;
- }
-
- public void addDelay(MessagePacket packet, long timestamp) {
- final SimpleDateFormat mDateFormat = new SimpleDateFormat(
- "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
- mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- Element delay = packet.addChild("delay", "urn:xmpp:delay");
- Date date = new Date(timestamp);
- delay.setAttribute("stamp", mDateFormat.format(date));
- }
-
- public MessagePacket generateAxolotlChat(Message message, XmppAxolotlMessage axolotlMessage) {
- MessagePacket packet = preparePacket(message);
- if (axolotlMessage == null) {
- return null;
- }
- packet.setAxolotlMessage(axolotlMessage.toElement());
- if (Config.supportUnencrypted() && !recipientSupportsOmemo(message)) {
- packet.setBody(OMEMO_FALLBACK_MESSAGE);
- }
- packet.addChild("store", "urn:xmpp:hints");
- return packet;
- }
-
- private static boolean recipientSupportsOmemo(Message message) {
- Contact c = message.getContact();
- return c != null && c.getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY);
- }
-
- public static void addMessageHints(MessagePacket packet) {
- packet.addChild("private", "urn:xmpp:carbons:2");
- packet.addChild("no-copy", "urn:xmpp:hints");
- packet.addChild("no-permanent-store", "urn:xmpp:hints");
- packet.addChild("no-permanent-storage", "urn:xmpp:hints"); //do not copy this. this is wrong. it is *store*
- }
-
- public MessagePacket generateOtrChat(Message message) {
- Session otrSession = message.getConversation().getOtrSession();
- if (otrSession == null) {
- return null;
- }
- MessagePacket packet = preparePacket(message);
- addMessageHints(packet);
- try {
- String content;
- if (message.hasFileOnRemoteHost()) {
- content = message.getFileParams().url.toString();
- } else {
- content = message.getBody();
- }
- packet.setBody(otrSession.transformSending(content)[0]);
- return packet;
- } catch (OtrException e) {
- return null;
- }
- }
-
- public MessagePacket generateChat(Message message) {
- MessagePacket packet = preparePacket(message);
- String content;
- if (message.hasFileOnRemoteHost()) {
- Message.FileParams fileParams = message.getFileParams();
- content = fileParams.url.toString();
- packet.addChild("x","jabber:x:oob").addChild("url").setContent(content);
- } else {
- content = message.getBody();
- }
- packet.setBody(content);
- return packet;
- }
-
- public MessagePacket generatePgpChat(Message message) {
- MessagePacket packet = preparePacket(message);
- if (Config.supportUnencrypted()) {
- packet.setBody(PGP_FALLBACK_MESSAGE);
- }
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- packet.addChild("x", "jabber:x:encrypted").setContent(message.getEncryptedBody());
- } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- packet.addChild("x", "jabber:x:encrypted").setContent(message.getBody());
- }
- return packet;
- }
-
- public MessagePacket generateChatState(Conversation conversation) {
- final Account account = conversation.getAccount();
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_CHAT);
- packet.setTo(conversation.getJid().toBareJid());
- packet.setFrom(account.getJid());
- packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
- packet.addChild("no-store", "urn:xmpp:hints");
- packet.addChild("no-storage", "urn:xmpp:hints"); //wrong! don't copy this. Its *store*
- return packet;
- }
-
- public MessagePacket confirm(final Account account, final Jid to, final String id) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_CHAT);
- packet.setTo(to);
- packet.setFrom(account.getJid());
- Element received = packet.addChild("displayed","urn:xmpp:chat-markers:0");
- received.setAttribute("id", id);
- packet.addChild("store", "urn:xmpp:hints");
- return packet;
- }
-
- public MessagePacket conferenceSubject(Conversation conversation,String subject) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_GROUPCHAT);
- packet.setTo(conversation.getJid().toBareJid());
- Element subjectChild = new Element("subject");
- subjectChild.setContent(subject);
- packet.addChild(subjectChild);
- packet.setFrom(conversation.getAccount().getJid().toBareJid());
- return packet;
- }
-
- public MessagePacket directInvite(final Conversation conversation, final Jid contact) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_NORMAL);
- packet.setTo(contact);
- packet.setFrom(conversation.getAccount().getJid());
- Element x = packet.addChild("x", "jabber:x:conference");
- x.setAttribute("jid", conversation.getJid().toBareJid().toString());
+ public static final String OTR_FALLBACK_MESSAGE = "I would like to start a private (OTR encrypted) conversation but your client doesn’t seem to support that";
+ private static final String OMEMO_FALLBACK_MESSAGE = "I sent you an OMEMO encrypted message but your client doesn’t seem to support that. Find more information on https://conversations.im/omemo";
+ private static final String PGP_FALLBACK_MESSAGE = "I sent you a PGP encrypted message but your client doesn’t seem to support that.";
+
+ public MessageGenerator(XmppConnectionService service) {
+ super(service);
+ }
+
+ private MessagePacket preparePacket(Message message) {
+ Conversation conversation = message.getConversation();
+ Account account = conversation.getAccount();
+ MessagePacket packet = new MessagePacket();
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ packet.setTo(message.getCounterpart());
+ packet.setType(MessagePacket.TYPE_CHAT);
+ packet.addChild("markable", "urn:xmpp:chat-markers:0");
+ if (this.mXmppConnectionService.indicateReceived()) {
+ packet.addChild("request", "urn:xmpp:receipts");
+ }
+ } else if (message.getType() == Message.TYPE_PRIVATE) {
+ packet.setTo(message.getCounterpart());
+ packet.setType(MessagePacket.TYPE_CHAT);
+ if (this.mXmppConnectionService.indicateReceived()) {
+ packet.addChild("request", "urn:xmpp:receipts");
+ }
+ } else {
+ packet.setTo(message.getCounterpart().toBareJid());
+ packet.setType(MessagePacket.TYPE_GROUPCHAT);
+ }
+ packet.setFrom(account.getJid());
+ packet.setId(message.getUuid());
+ if (message.edited()) {
+ packet.addChild("replace", "urn:xmpp:message-correct:0").setAttribute("id", message.getEditedId());
+ }
+ return packet;
+ }
+
+ public void addDelay(MessagePacket packet, long timestamp) {
+ final SimpleDateFormat mDateFormat = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
+ mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Element delay = packet.addChild("delay", "urn:xmpp:delay");
+ Date date = new Date(timestamp);
+ delay.setAttribute("stamp", mDateFormat.format(date));
+ }
+
+ public MessagePacket generateAxolotlChat(Message message, XmppAxolotlMessage axolotlMessage) {
+ MessagePacket packet = preparePacket(message);
+ if (axolotlMessage == null) {
+ return null;
+ }
+ packet.setAxolotlMessage(axolotlMessage.toElement());
+ if (Config.supportUnencrypted() && !recipientSupportsOmemo(message)) {
+ packet.setBody(OMEMO_FALLBACK_MESSAGE);
+ }
+ packet.addChild("store", "urn:xmpp:hints");
+ return packet;
+ }
+
+ private static boolean recipientSupportsOmemo(Message message) {
+ Contact c = message.getContact();
+ return c != null && c.getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY);
+ }
+
+ public static void addMessageHints(MessagePacket packet) {
+ packet.addChild("private", "urn:xmpp:carbons:2");
+ packet.addChild("no-copy", "urn:xmpp:hints");
+ packet.addChild("no-permanent-store", "urn:xmpp:hints");
+ packet.addChild("no-permanent-storage", "urn:xmpp:hints"); //do not copy this. this is wrong. it is *store*
+ }
+
+ public MessagePacket generateOtrChat(Message message) {
+ Session otrSession = message.getConversation().getOtrSession();
+ if (otrSession == null) {
+ return null;
+ }
+ MessagePacket packet = preparePacket(message);
+ addMessageHints(packet);
+ try {
+ String content;
+ if (message.hasFileOnRemoteHost()) {
+ content = message.getFileParams().url.toString();
+ } else {
+ content = message.getBody();
+ }
+ packet.setBody(otrSession.transformSending(content)[0]);
+ return packet;
+ } catch (OtrException e) {
+ return null;
+ }
+ }
+
+ public MessagePacket generateChat(Message message) {
+ MessagePacket packet = preparePacket(message);
+ String content;
+ if (message.hasFileOnRemoteHost()) {
+ Message.FileParams fileParams = message.getFileParams();
+ content = fileParams.url.toString();
+ packet.addChild("x", "jabber:x:oob").addChild("url").setContent(content);
+ } else {
+ content = message.getBody();
+ }
+ packet.setBody(content);
+ return packet;
+ }
+
+ public MessagePacket generatePgpChat(Message message) {
+ MessagePacket packet = preparePacket(message);
+ if (Config.supportUnencrypted()) {
+ packet.setBody(PGP_FALLBACK_MESSAGE);
+ }
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ packet.addChild("x", "jabber:x:encrypted").setContent(message.getEncryptedBody());
+ } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ packet.addChild("x", "jabber:x:encrypted").setContent(message.getBody());
+ }
+ return packet;
+ }
+
+ public MessagePacket generateChatState(Conversation conversation) {
+ final Account account = conversation.getAccount();
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_CHAT);
+ packet.setTo(conversation.getJid().toBareJid());
+ packet.setFrom(account.getJid());
+ packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
+ packet.addChild("no-store", "urn:xmpp:hints");
+ packet.addChild("no-storage", "urn:xmpp:hints"); //wrong! don't copy this. Its *store*
+ return packet;
+ }
+
+ public MessagePacket confirm(final Account account, final Jid to, final String id) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_CHAT);
+ packet.setTo(to);
+ packet.setFrom(account.getJid());
+ Element received = packet.addChild("displayed", "urn:xmpp:chat-markers:0");
+ received.setAttribute("id", id);
+ packet.addChild("store", "urn:xmpp:hints");
+ return packet;
+ }
+
+ public MessagePacket conferenceSubject(Conversation conversation, String subject) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_GROUPCHAT);
+ packet.setTo(conversation.getJid().toBareJid());
+ Element subjectChild = new Element("subject");
+ subjectChild.setContent(subject);
+ packet.addChild(subjectChild);
+ packet.setFrom(conversation.getAccount().getJid().toBareJid());
+ return packet;
+ }
+
+ public MessagePacket directInvite(final Conversation conversation, final Jid contact) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_NORMAL);
+ packet.setTo(contact);
+ packet.setFrom(conversation.getAccount().getJid());
+ Element x = packet.addChild("x", "jabber:x:conference");
+ x.setAttribute("jid", conversation.getJid().toBareJid().toString());
String password = conversation.getMucOptions().getPassword();
if (password != null) {
- x.setAttribute("password",password);
+ x.setAttribute("password", password);
+ }
+ return packet;
+ }
+
+ public MessagePacket invite(Conversation conversation, Jid contact) {
+ MessagePacket packet = new MessagePacket();
+ packet.setTo(conversation.getJid().toBareJid());
+ packet.setFrom(conversation.getAccount().getJid());
+ Element x = new Element("x");
+ x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user");
+ Element invite = new Element("invite");
+ invite.setAttribute("to", contact.toBareJid().toString());
+ x.addChild(invite);
+ packet.addChild(x);
+ return packet;
+ }
+
+ public MessagePacket received(Account account, MessagePacket originalMessage, ArrayList<String> namespaces, int type) {
+ MessagePacket receivedPacket = new MessagePacket();
+ receivedPacket.setType(type);
+ receivedPacket.setTo(originalMessage.getFrom());
+ receivedPacket.setFrom(account.getJid());
+ for (String namespace : namespaces) {
+ receivedPacket.addChild("received", namespace).setAttribute("id", originalMessage.getId());
}
- return packet;
- }
-
- public MessagePacket invite(Conversation conversation, Jid contact) {
- MessagePacket packet = new MessagePacket();
- packet.setTo(conversation.getJid().toBareJid());
- packet.setFrom(conversation.getAccount().getJid());
- Element x = new Element("x");
- x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user");
- Element invite = new Element("invite");
- invite.setAttribute("to", contact.toBareJid().toString());
- x.addChild(invite);
- packet.addChild(x);
- return packet;
- }
-
- public MessagePacket received(Account account, MessagePacket originalMessage, ArrayList<String> namespaces, int type) {
- MessagePacket receivedPacket = new MessagePacket();
- receivedPacket.setType(type);
- receivedPacket.setTo(originalMessage.getFrom());
- receivedPacket.setFrom(account.getJid());
- for(String namespace : namespaces) {
- receivedPacket.addChild("received", namespace).setAttribute("id", originalMessage.getId());
- }
- return receivedPacket;
- }
-
- public MessagePacket generateOtrError(Jid to, String id, String errorText) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_ERROR);
- packet.setAttribute("id",id);
- packet.setTo(to);
- Element error = packet.addChild("error");
- error.setAttribute("code","406");
- error.setAttribute("type","modify");
- error.addChild("not-acceptable","urn:ietf:params:xml:ns:xmpp-stanzas");
- error.addChild("text").setContent("?OTR Error:" + errorText);
- return packet;
- }
+ return receivedPacket;
+ }
+
+ public MessagePacket generateOtrError(Jid to, String id, String errorText) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_ERROR);
+ packet.setAttribute("id", id);
+ packet.setTo(to);
+ Element error = packet.addChild("error");
+ error.setAttribute("code", "406");
+ error.setAttribute("type", "modify");
+ error.addChild("not-acceptable", "urn:ietf:params:xml:ns:xmpp-stanzas");
+ error.addChild("text").setContent("?OTR Error:" + errorText);
+ return packet;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/generator/PresenceGenerator.java b/src/main/java/de/pixart/messenger/generator/PresenceGenerator.java
index bf84444a5..2234d54e6 100644
--- a/src/main/java/de/pixart/messenger/generator/PresenceGenerator.java
+++ b/src/main/java/de/pixart/messenger/generator/PresenceGenerator.java
@@ -9,63 +9,63 @@ import de.pixart.messenger.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().toBareJid());
- 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().toBareJid());
+ return packet;
+ }
- public PresencePacket requestPresenceUpdatesFrom(Contact contact) {
- return subscription("subscribe", contact);
- }
+ public PresencePacket requestPresenceUpdatesFrom(Contact contact) {
+ return subscription("subscribe", contact);
+ }
- 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) {
+ public PresencePacket selfPresence(Account account, Presence.Status status) {
return selfPresence(account, status, true);
}
public PresencePacket selfPresence(Account account, Presence.Status status, boolean includePgpAnnouncement) {
- PresencePacket packet = new PresencePacket();
- if(status.toShowString() != null) {
- packet.addChild("show").setContent(status.toShowString());
- }
- packet.setFrom(account.getJid());
- String sig = account.getPgpSignature();
+ PresencePacket packet = new PresencePacket();
+ if (status.toShowString() != null) {
+ packet.addChild("show").setContent(status.toShowString());
+ }
+ packet.setFrom(account.getJid());
+ String sig = account.getPgpSignature();
if (includePgpAnnouncement && sig != null && mXmppConnectionService.getPgpEngine() != null) {
- packet.addChild("x", "jabber:x:signed").setContent(sig);
- }
- String capHash = getCapHash();
- 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;
- }
+ packet.addChild("x", "jabber:x:signed").setContent(sig);
+ }
+ String capHash = getCapHash();
+ 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 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/de/pixart/messenger/http/HttpConnectionManager.java b/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java
index 921b20571..a03e36991 100644
--- a/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java
+++ b/src/main/java/de/pixart/messenger/http/HttpConnectionManager.java
@@ -25,75 +25,75 @@ import de.pixart.messenger.utils.SSLSocketHelper;
public class HttpConnectionManager extends AbstractConnectionManager {
- public HttpConnectionManager(XmppConnectionService service) {
- super(service);
- }
-
- private List<HttpDownloadConnection> downloadConnections = new CopyOnWriteArrayList<>();
- private List<HttpUploadConnection> uploadConnections = new CopyOnWriteArrayList<>();
-
- public HttpDownloadConnection createNewDownloadConnection(Message message) {
- return this.createNewDownloadConnection(message, false);
- }
-
- public HttpDownloadConnection createNewDownloadConnection(Message message, boolean interactive) {
- HttpDownloadConnection connection = new HttpDownloadConnection(this);
- connection.init(message,interactive);
- this.downloadConnections.add(connection);
- return connection;
- }
-
- public HttpUploadConnection createNewUploadConnection(Message message, boolean delay) {
- HttpUploadConnection connection = new HttpUploadConnection(this);
- connection.init(message,delay);
- this.uploadConnections.add(connection);
- return connection;
- }
-
- public void finishConnection(HttpDownloadConnection connection) {
- this.downloadConnections.remove(connection);
- }
-
- public void finishUploadConnection(HttpUploadConnection httpUploadConnection) {
- this.uploadConnections.remove(httpUploadConnection);
- }
-
- public void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) {
- final X509TrustManager trustManager;
- final HostnameVerifier hostnameVerifier;
- if (interactive) {
- trustManager = mXmppConnectionService.getMemorizingTrustManager();
- hostnameVerifier = mXmppConnectionService
- .getMemorizingTrustManager().wrapHostnameVerifier(
- new StrictHostnameVerifier());
- } else {
- trustManager = mXmppConnectionService.getMemorizingTrustManager()
- .getNonInteractive();
- hostnameVerifier = mXmppConnectionService
- .getMemorizingTrustManager()
- .wrapHostnameVerifierNonInteractive(
- new StrictHostnameVerifier());
- }
- try {
- final SSLContext sc = SSLSocketHelper.getSSLContext();
- sc.init(null, new X509TrustManager[]{trustManager},
- mXmppConnectionService.getRNG());
-
- final SSLSocketFactory sf = sc.getSocketFactory();
- final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
- sf.getSupportedCipherSuites());
- if (cipherSuites.length > 0) {
- sc.getDefaultSSLParameters().setCipherSuites(cipherSuites);
-
- }
-
- connection.setSSLSocketFactory(sf);
- connection.setHostnameVerifier(hostnameVerifier);
- } catch (final KeyManagementException | NoSuchAlgorithmException ignored) {
- }
- }
-
- public Proxy getProxy() throws IOException {
- return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(InetAddress.getByAddress(new byte[]{127,0,0,1}), 8118));
- }
+ public HttpConnectionManager(XmppConnectionService service) {
+ super(service);
+ }
+
+ private List<HttpDownloadConnection> downloadConnections = new CopyOnWriteArrayList<>();
+ private List<HttpUploadConnection> uploadConnections = new CopyOnWriteArrayList<>();
+
+ public HttpDownloadConnection createNewDownloadConnection(Message message) {
+ return this.createNewDownloadConnection(message, false);
+ }
+
+ public HttpDownloadConnection createNewDownloadConnection(Message message, boolean interactive) {
+ HttpDownloadConnection connection = new HttpDownloadConnection(this);
+ connection.init(message, interactive);
+ this.downloadConnections.add(connection);
+ return connection;
+ }
+
+ public HttpUploadConnection createNewUploadConnection(Message message, boolean delay) {
+ HttpUploadConnection connection = new HttpUploadConnection(this);
+ connection.init(message, delay);
+ this.uploadConnections.add(connection);
+ return connection;
+ }
+
+ public void finishConnection(HttpDownloadConnection connection) {
+ this.downloadConnections.remove(connection);
+ }
+
+ public void finishUploadConnection(HttpUploadConnection httpUploadConnection) {
+ this.uploadConnections.remove(httpUploadConnection);
+ }
+
+ public void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) {
+ final X509TrustManager trustManager;
+ final HostnameVerifier hostnameVerifier;
+ if (interactive) {
+ trustManager = mXmppConnectionService.getMemorizingTrustManager();
+ hostnameVerifier = mXmppConnectionService
+ .getMemorizingTrustManager().wrapHostnameVerifier(
+ new StrictHostnameVerifier());
+ } else {
+ trustManager = mXmppConnectionService.getMemorizingTrustManager()
+ .getNonInteractive();
+ hostnameVerifier = mXmppConnectionService
+ .getMemorizingTrustManager()
+ .wrapHostnameVerifierNonInteractive(
+ new StrictHostnameVerifier());
+ }
+ try {
+ final SSLContext sc = SSLSocketHelper.getSSLContext();
+ sc.init(null, new X509TrustManager[]{trustManager},
+ mXmppConnectionService.getRNG());
+
+ final SSLSocketFactory sf = sc.getSocketFactory();
+ final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
+ sf.getSupportedCipherSuites());
+ if (cipherSuites.length > 0) {
+ sc.getDefaultSSLParameters().setCipherSuites(cipherSuites);
+
+ }
+
+ connection.setSSLSocketFactory(sf);
+ connection.setHostnameVerifier(hostnameVerifier);
+ } catch (final KeyManagementException | NoSuchAlgorithmException ignored) {
+ }
+ }
+
+ public Proxy getProxy() throws IOException {
+ return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 8118));
+ }
}
diff --git a/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java b/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java
index f091c9dad..8c342e6f2 100644
--- a/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java
+++ b/src/main/java/de/pixart/messenger/http/HttpDownloadConnection.java
@@ -32,335 +32,335 @@ import de.pixart.messenger.utils.FileWriterException;
public class HttpDownloadConnection implements Transferable {
- private HttpConnectionManager mHttpConnectionManager;
- private XmppConnectionService mXmppConnectionService;
-
- private URL mUrl;
- private Message message;
- private DownloadableFile file;
- private int mStatus = Transferable.STATUS_UNKNOWN;
- private boolean acceptedAutomatically = false;
- private int mProgress = 0;
- private boolean mUseTor = false;
- private boolean canceled = false;
-
- private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
-
- public HttpDownloadConnection(HttpConnectionManager manager) {
- this.mHttpConnectionManager = manager;
- this.mXmppConnectionService = manager.getXmppConnectionService();
- this.mUseTor = mXmppConnectionService.useTorToConnect();
- }
-
- @Override
- public boolean start() {
- if (mXmppConnectionService.hasInternetConnection()) {
- if (this.mStatus == STATUS_OFFER_CHECK_FILESIZE) {
- checkFileSize(true);
- } else {
- new Thread(new FileDownloader(true)).start();
- }
- return true;
- } else {
- return false;
- }
- }
-
- public void init(Message message) {
- init(message, false);
- }
-
- public void init(Message message, boolean interactive) {
- this.message = message;
- this.message.setTransferable(this);
- try {
- if (message.hasFileOnRemoteHost()) {
- mUrl = message.getFileParams().url;
- } else {
- mUrl = new URL(message.getBody());
- }
- String[] parts = mUrl.getPath().toLowerCase().split("\\.");
- String lastPart = parts.length >= 1 ? parts[parts.length - 1] : null;
- String secondToLast = parts.length >= 2 ? parts[parts.length -2] : null;
- if ("pgp".equals(lastPart) || "gpg".equals(lastPart)) {
- this.message.setEncryption(Message.ENCRYPTION_PGP);
- } else if (message.getEncryption() != Message.ENCRYPTION_OTR
- && message.getEncryption() != Message.ENCRYPTION_AXOLOTL) {
- this.message.setEncryption(Message.ENCRYPTION_NONE);
- }
- String extension;
- if (VALID_CRYPTO_EXTENSIONS.contains(lastPart)) {
- extension = secondToLast;
- } else {
- extension = lastPart;
- }
- String filename = fileDateFormat.format(new Date(message.getTimeSent()))+"_"+message.getUuid().substring(0,4);
- message.setRelativeFilePath(filename + "." + extension);
- this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
- String reference = mUrl.getRef();
- if (reference != null && reference.length() == 96) {
- this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
- }
-
- if ((this.message.getEncryption() == Message.ENCRYPTION_OTR
- || this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL)
- && this.file.getKey() == null) {
- this.message.setEncryption(Message.ENCRYPTION_NONE);
- }
- checkFileSize(interactive);
- } catch (MalformedURLException e) {
- this.cancel();
- }
- }
-
- private void checkFileSize(boolean interactive) {
- new Thread(new FileSizeChecker(interactive)).start();
- }
-
- @Override
- public void cancel() {
- this.canceled = true;
- mHttpConnectionManager.finishConnection(this);
- if (message.isFileOrImage()) {
- message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- } else {
- message.setTransferable(null);
- }
- mXmppConnectionService.updateConversationUi();
- }
-
- private void finish() {
- mXmppConnectionService.getFileBackend().updateMediaScanner(file);
- message.setTransferable(null);
- mHttpConnectionManager.finishConnection(this);
- boolean notify = acceptedAutomatically && !message.isRead();
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- notify = message.getConversation().getAccount().getPgpDecryptionService().decrypt(message, notify);
- }
- mXmppConnectionService.updateConversationUi();
- if (notify) {
- mXmppConnectionService.getNotificationService().push(message);
- }
- }
-
- private void changeStatus(int status) {
- this.mStatus = status;
- mXmppConnectionService.updateConversationUi();
- }
-
- private void showToastForException(Exception e) {
- if (e instanceof java.net.UnknownHostException) {
- mXmppConnectionService.showErrorToastInUi(R.string.download_failed_server_not_found);
- } else if (e instanceof java.net.ConnectException) {
- mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_connect);
- } else if (e instanceof FileWriterException) {
- mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_write_file);
- } else if (!(e instanceof CancellationException)) {
- mXmppConnectionService.showErrorToastInUi(R.string.download_failed_file_not_found);
- }
- }
-
- private class FileSizeChecker implements Runnable {
-
- private boolean interactive = false;
-
- public FileSizeChecker(boolean interactive) {
- this.interactive = interactive;
- }
-
- @Override
- public void run() {
- long size;
- try {
- size = retrieveFileSize();
- } catch (Exception e) {
- changeStatus(STATUS_OFFER_CHECK_FILESIZE);
- Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage());
- if (interactive) {
- showToastForException(e);
- } else {
- HttpDownloadConnection.this.acceptedAutomatically = false;
- HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
- }
- cancel();
- return;
- }
- file.setExpectedSize(size);
- if (mHttpConnectionManager.hasStoragePermission()
- && size <= mHttpConnectionManager.getAutoAcceptFileSize()
- && mXmppConnectionService.isDataSaverDisabled()) {
- HttpDownloadConnection.this.acceptedAutomatically = true;
- new Thread(new FileDownloader(interactive)).start();
- } else {
- changeStatus(STATUS_OFFER);
- HttpDownloadConnection.this.acceptedAutomatically = false;
- HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
- }
- }
-
- private long retrieveFileSize() throws IOException {
- PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid());
- try {
- wakeLock.acquire();
- Log.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive));
- changeStatus(STATUS_CHECKING);
- HttpURLConnection connection;
- if (mUseTor) {
- connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy());
- } else {
- connection = (HttpURLConnection) mUrl.openConnection();
- }
- connection.setRequestMethod("HEAD");
- Log.d(Config.LOGTAG,"url: "+connection.getURL().toString());
- Log.d(Config.LOGTAG,"connection: "+connection.toString());
- connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName());
- if (connection instanceof HttpsURLConnection) {
- mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
- }
- connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
- connection.setReadTimeout(Config.CONNECT_TIMEOUT * 1000);
- connection.connect();
- String contentLength = connection.getHeaderField("Content-Length");
- connection.disconnect();
- if (contentLength == null) {
- throw new IOException("no content-length found in HEAD response");
- }
- wakeLock.release();
- return Long.parseLong(contentLength, 10);
- } catch (IOException e) {
- throw e;
- } catch (NumberFormatException e) {
- throw new IOException();
- }
- }
-
- }
-
- private class FileDownloader implements Runnable {
-
- private boolean interactive = false;
-
- private OutputStream os;
-
- public FileDownloader(boolean interactive) {
- this.interactive = interactive;
- }
-
- @Override
- public void run() {
- try {
- changeStatus(STATUS_DOWNLOADING);
- download();
- updateImageBounds();
- finish();
- } catch (SSLHandshakeException e) {
- changeStatus(STATUS_OFFER);
- } catch (Exception e) {
- if (interactive) {
- showToastForException(e);
- } else {
- HttpDownloadConnection.this.acceptedAutomatically = false;
- HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
- }
- cancel();
- }
- }
-
- private void download() throws Exception {
- InputStream is = null;
- PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid());
- try {
- wakeLock.acquire();
- HttpURLConnection connection;
- if (mUseTor) {
- connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy());
- } else {
- connection = (HttpURLConnection) mUrl.openConnection();
- }
- if (connection instanceof HttpsURLConnection) {
- mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
- }
- connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getIdentityName());
- final boolean tryResume = file.exists() && file.getKey() == null;
- if (tryResume) {
- Log.d(Config.LOGTAG,"http download trying resume");
- long size = file.getSize();
- connection.setRequestProperty("Range", "bytes="+size+"-");
- }
- connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
- connection.setReadTimeout(Config.CONNECT_TIMEOUT * 1000);
- connection.connect();
- is = new BufferedInputStream(connection.getInputStream());
- boolean serverResumed = "bytes".equals(connection.getHeaderField("Accept-Ranges"));
- long transmitted = 0;
- long expected = file.getExpectedSize();
- if (tryResume && serverResumed) {
- Log.d(Config.LOGTAG,"server resumed");
- transmitted = file.getSize();
- updateProgress((int) ((((double) transmitted) / expected) * 100));
- os = AbstractConnectionManager.createAppendedOutputStream(file);
- } else {
- file.getParentFile().mkdirs();
- file.createNewFile();
- os = AbstractConnectionManager.createOutputStream(file, true);
- }
- int count;
- byte[] buffer = new byte[1024];
- while ((count = is.read(buffer)) != -1) {
- transmitted += count;
- try {
- os.write(buffer, 0, count);
- } catch (IOException e) {
- throw new FileWriterException();
- }
- updateProgress((int) ((((double) transmitted) / expected) * 100));
- if (canceled) {
- throw new CancellationException();
- }
- }
- try {
- os.flush();
- } catch (IOException e) {
- throw new FileWriterException();
- }
- } catch (CancellationException | IOException e) {
- throw e;
- } finally {
- FileBackend.close(os);
- FileBackend.close(is);
- wakeLock.release();
- }
- }
-
- private void updateImageBounds() {
- message.setType(Message.TYPE_FILE);
- mXmppConnectionService.getFileBackend().updateFileParams(message, mUrl);
- mXmppConnectionService.updateMessage(message);
- }
-
- }
-
- public void updateProgress(int i) {
- this.mProgress = i;
- mXmppConnectionService.updateConversationUi();
- }
-
- @Override
- public int getStatus() {
- return this.mStatus;
- }
-
- @Override
- public long getFileSize() {
- if (this.file != null) {
- return this.file.getExpectedSize();
- } else {
- return 0;
- }
- }
-
- @Override
- public int getProgress() {
- return this.mProgress;
- }
+ private HttpConnectionManager mHttpConnectionManager;
+ private XmppConnectionService mXmppConnectionService;
+
+ private URL mUrl;
+ private Message message;
+ private DownloadableFile file;
+ private int mStatus = Transferable.STATUS_UNKNOWN;
+ private boolean acceptedAutomatically = false;
+ private int mProgress = 0;
+ private boolean mUseTor = false;
+ private boolean canceled = false;
+
+ private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+
+ public HttpDownloadConnection(HttpConnectionManager manager) {
+ this.mHttpConnectionManager = manager;
+ this.mXmppConnectionService = manager.getXmppConnectionService();
+ this.mUseTor = mXmppConnectionService.useTorToConnect();
+ }
+
+ @Override
+ public boolean start() {
+ if (mXmppConnectionService.hasInternetConnection()) {
+ if (this.mStatus == STATUS_OFFER_CHECK_FILESIZE) {
+ checkFileSize(true);
+ } else {
+ new Thread(new FileDownloader(true)).start();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void init(Message message) {
+ init(message, false);
+ }
+
+ public void init(Message message, boolean interactive) {
+ this.message = message;
+ this.message.setTransferable(this);
+ try {
+ if (message.hasFileOnRemoteHost()) {
+ mUrl = message.getFileParams().url;
+ } else {
+ mUrl = new URL(message.getBody());
+ }
+ String[] parts = mUrl.getPath().toLowerCase().split("\\.");
+ String lastPart = parts.length >= 1 ? parts[parts.length - 1] : null;
+ String secondToLast = parts.length >= 2 ? parts[parts.length - 2] : null;
+ if ("pgp".equals(lastPart) || "gpg".equals(lastPart)) {
+ this.message.setEncryption(Message.ENCRYPTION_PGP);
+ } else if (message.getEncryption() != Message.ENCRYPTION_OTR
+ && message.getEncryption() != Message.ENCRYPTION_AXOLOTL) {
+ this.message.setEncryption(Message.ENCRYPTION_NONE);
+ }
+ String extension;
+ if (VALID_CRYPTO_EXTENSIONS.contains(lastPart)) {
+ extension = secondToLast;
+ } else {
+ extension = lastPart;
+ }
+ String filename = fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4);
+ message.setRelativeFilePath(filename + "." + extension);
+ this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
+ String reference = mUrl.getRef();
+ if (reference != null && reference.length() == 96) {
+ this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
+ }
+
+ if ((this.message.getEncryption() == Message.ENCRYPTION_OTR
+ || this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL)
+ && this.file.getKey() == null) {
+ this.message.setEncryption(Message.ENCRYPTION_NONE);
+ }
+ checkFileSize(interactive);
+ } catch (MalformedURLException e) {
+ this.cancel();
+ }
+ }
+
+ private void checkFileSize(boolean interactive) {
+ new Thread(new FileSizeChecker(interactive)).start();
+ }
+
+ @Override
+ public void cancel() {
+ this.canceled = true;
+ mHttpConnectionManager.finishConnection(this);
+ if (message.isFileOrImage()) {
+ message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ } else {
+ message.setTransferable(null);
+ }
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ private void finish() {
+ mXmppConnectionService.getFileBackend().updateMediaScanner(file);
+ message.setTransferable(null);
+ mHttpConnectionManager.finishConnection(this);
+ boolean notify = acceptedAutomatically && !message.isRead();
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ notify = message.getConversation().getAccount().getPgpDecryptionService().decrypt(message, notify);
+ }
+ mXmppConnectionService.updateConversationUi();
+ if (notify) {
+ mXmppConnectionService.getNotificationService().push(message);
+ }
+ }
+
+ private void changeStatus(int status) {
+ this.mStatus = status;
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ private void showToastForException(Exception e) {
+ if (e instanceof java.net.UnknownHostException) {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_server_not_found);
+ } else if (e instanceof java.net.ConnectException) {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_connect);
+ } else if (e instanceof FileWriterException) {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_write_file);
+ } else if (!(e instanceof CancellationException)) {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_file_not_found);
+ }
+ }
+
+ private class FileSizeChecker implements Runnable {
+
+ private boolean interactive = false;
+
+ public FileSizeChecker(boolean interactive) {
+ this.interactive = interactive;
+ }
+
+ @Override
+ public void run() {
+ long size;
+ try {
+ size = retrieveFileSize();
+ } catch (Exception e) {
+ changeStatus(STATUS_OFFER_CHECK_FILESIZE);
+ Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage());
+ if (interactive) {
+ showToastForException(e);
+ } else {
+ HttpDownloadConnection.this.acceptedAutomatically = false;
+ HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ cancel();
+ return;
+ }
+ file.setExpectedSize(size);
+ if (mHttpConnectionManager.hasStoragePermission()
+ && size <= mHttpConnectionManager.getAutoAcceptFileSize()
+ && mXmppConnectionService.isDataSaverDisabled()) {
+ HttpDownloadConnection.this.acceptedAutomatically = true;
+ new Thread(new FileDownloader(interactive)).start();
+ } else {
+ changeStatus(STATUS_OFFER);
+ HttpDownloadConnection.this.acceptedAutomatically = false;
+ HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ }
+
+ private long retrieveFileSize() throws IOException {
+ PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_" + message.getUuid());
+ try {
+ wakeLock.acquire();
+ Log.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive));
+ changeStatus(STATUS_CHECKING);
+ HttpURLConnection connection;
+ if (mUseTor) {
+ connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy());
+ } else {
+ connection = (HttpURLConnection) mUrl.openConnection();
+ }
+ connection.setRequestMethod("HEAD");
+ Log.d(Config.LOGTAG, "url: " + connection.getURL().toString());
+ Log.d(Config.LOGTAG, "connection: " + connection.toString());
+ connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName());
+ if (connection instanceof HttpsURLConnection) {
+ mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
+ }
+ connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
+ connection.setReadTimeout(Config.CONNECT_TIMEOUT * 1000);
+ connection.connect();
+ String contentLength = connection.getHeaderField("Content-Length");
+ connection.disconnect();
+ if (contentLength == null) {
+ throw new IOException("no content-length found in HEAD response");
+ }
+ wakeLock.release();
+ return Long.parseLong(contentLength, 10);
+ } catch (IOException e) {
+ throw e;
+ } catch (NumberFormatException e) {
+ throw new IOException();
+ }
+ }
+
+ }
+
+ private class FileDownloader implements Runnable {
+
+ private boolean interactive = false;
+
+ private OutputStream os;
+
+ public FileDownloader(boolean interactive) {
+ this.interactive = interactive;
+ }
+
+ @Override
+ public void run() {
+ try {
+ changeStatus(STATUS_DOWNLOADING);
+ download();
+ updateImageBounds();
+ finish();
+ } catch (SSLHandshakeException e) {
+ changeStatus(STATUS_OFFER);
+ } catch (Exception e) {
+ if (interactive) {
+ showToastForException(e);
+ } else {
+ HttpDownloadConnection.this.acceptedAutomatically = false;
+ HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ cancel();
+ }
+ }
+
+ private void download() throws Exception {
+ InputStream is = null;
+ PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_" + message.getUuid());
+ try {
+ wakeLock.acquire();
+ HttpURLConnection connection;
+ if (mUseTor) {
+ connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy());
+ } else {
+ connection = (HttpURLConnection) mUrl.openConnection();
+ }
+ if (connection instanceof HttpsURLConnection) {
+ mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
+ }
+ connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName());
+ final boolean tryResume = file.exists() && file.getKey() == null;
+ if (tryResume) {
+ Log.d(Config.LOGTAG, "http download trying resume");
+ long size = file.getSize();
+ connection.setRequestProperty("Range", "bytes=" + size + "-");
+ }
+ connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
+ connection.setReadTimeout(Config.CONNECT_TIMEOUT * 1000);
+ connection.connect();
+ is = new BufferedInputStream(connection.getInputStream());
+ boolean serverResumed = "bytes".equals(connection.getHeaderField("Accept-Ranges"));
+ long transmitted = 0;
+ long expected = file.getExpectedSize();
+ if (tryResume && serverResumed) {
+ Log.d(Config.LOGTAG, "server resumed");
+ transmitted = file.getSize();
+ updateProgress((int) ((((double) transmitted) / expected) * 100));
+ os = AbstractConnectionManager.createAppendedOutputStream(file);
+ } else {
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ os = AbstractConnectionManager.createOutputStream(file, true);
+ }
+ int count;
+ byte[] buffer = new byte[1024];
+ while ((count = is.read(buffer)) != -1) {
+ transmitted += count;
+ try {
+ os.write(buffer, 0, count);
+ } catch (IOException e) {
+ throw new FileWriterException();
+ }
+ updateProgress((int) ((((double) transmitted) / expected) * 100));
+ if (canceled) {
+ throw new CancellationException();
+ }
+ }
+ try {
+ os.flush();
+ } catch (IOException e) {
+ throw new FileWriterException();
+ }
+ } catch (CancellationException | IOException e) {
+ throw e;
+ } finally {
+ FileBackend.close(os);
+ FileBackend.close(is);
+ wakeLock.release();
+ }
+ }
+
+ private void updateImageBounds() {
+ message.setType(Message.TYPE_FILE);
+ mXmppConnectionService.getFileBackend().updateFileParams(message, mUrl);
+ mXmppConnectionService.updateMessage(message);
+ }
+
+ }
+
+ public void updateProgress(int i) {
+ this.mProgress = i;
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ @Override
+ public int getStatus() {
+ return this.mStatus;
+ }
+
+ @Override
+ public long getFileSize() {
+ if (this.file != null) {
+ return this.file.getExpectedSize();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int getProgress() {
+ return this.mProgress;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
index 95f840d67..afb0b04df 100644
--- a/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
+++ b/src/main/java/de/pixart/messenger/http/HttpUploadConnection.java
@@ -34,208 +34,208 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class HttpUploadConnection implements Transferable {
- private HttpConnectionManager mHttpConnectionManager;
- private XmppConnectionService mXmppConnectionService;
-
- private boolean canceled = false;
- private boolean delayed = false;
- private Account account;
- private DownloadableFile file;
- private Message message;
- private String mime;
- private URL mGetUrl;
- private URL mPutUrl;
- private boolean mUseTor = false;
-
- private byte[] key = null;
-
- private long transmitted = 0;
-
- private InputStream mFileInputStream;
-
- public HttpUploadConnection(HttpConnectionManager httpConnectionManager) {
- this.mHttpConnectionManager = httpConnectionManager;
- this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService();
- this.mUseTor = mXmppConnectionService.useTorToConnect();
- }
-
- @Override
- public boolean start() {
- return false;
- }
-
- @Override
- public int getStatus() {
- return STATUS_UPLOADING;
- }
-
- @Override
- public long getFileSize() {
- return file == null ? 0 : file.getExpectedSize();
- }
-
- @Override
- public int getProgress() {
- if (file == null) {
- return 0;
- }
- return (int) ((((double) transmitted) / file.getExpectedSize()) * 100);
- }
-
- @Override
- public void cancel() {
- this.canceled = true;
- }
+ private HttpConnectionManager mHttpConnectionManager;
+ private XmppConnectionService mXmppConnectionService;
+
+ private boolean canceled = false;
+ private boolean delayed = false;
+ private Account account;
+ private DownloadableFile file;
+ private Message message;
+ private String mime;
+ private URL mGetUrl;
+ private URL mPutUrl;
+ private boolean mUseTor = false;
+
+ private byte[] key = null;
+
+ private long transmitted = 0;
+
+ private InputStream mFileInputStream;
+
+ public HttpUploadConnection(HttpConnectionManager httpConnectionManager) {
+ this.mHttpConnectionManager = httpConnectionManager;
+ this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService();
+ this.mUseTor = mXmppConnectionService.useTorToConnect();
+ }
+
+ @Override
+ public boolean start() {
+ return false;
+ }
+
+ @Override
+ public int getStatus() {
+ return STATUS_UPLOADING;
+ }
+
+ @Override
+ public long getFileSize() {
+ return file == null ? 0 : file.getExpectedSize();
+ }
+
+ @Override
+ public int getProgress() {
+ if (file == null) {
+ return 0;
+ }
+ return (int) ((((double) transmitted) / file.getExpectedSize()) * 100);
+ }
+
+ @Override
+ public void cancel() {
+ this.canceled = true;
+ }
private void fail(String errorMessage) {
- mHttpConnectionManager.finishUploadConnection(this);
- message.setTransferable(null);
+ mHttpConnectionManager.finishUploadConnection(this);
+ message.setTransferable(null);
mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, errorMessage);
FileBackend.close(mFileInputStream);
- }
-
- public void init(Message message, boolean delay) {
- this.message = message;
- this.account = message.getConversation().getAccount();
- this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
- this.mime = this.file.getMimeType();
- this.delayed = delay;
- if (Config.ENCRYPT_ON_HTTP_UPLOADED
- || message.getEncryption() == Message.ENCRYPTION_AXOLOTL
- || message.getEncryption() == Message.ENCRYPTION_OTR) {
- this.key = new byte[48];
- mXmppConnectionService.getRNG().nextBytes(this.key);
- this.file.setKeyAndIv(this.key);
- }
- Pair<InputStream,Integer> pair;
- try {
- pair = AbstractConnectionManager.createInputStream(file, true);
- } catch (FileNotFoundException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not find file to upload - "+e.getMessage());
- fail(e.getMessage());
- return;
- }
- if (pair != null) {
- this.file.setExpectedSize(pair.second);
- this.mFileInputStream = pair.first;
- }
- Jid host = account.getXmppConnection().findDiscoItemByFeature(Xmlns.HTTP_UPLOAD);
- IqPacket request = mXmppConnectionService.getIqGenerator().requestHttpUploadSlot(host,file,mime);
- mXmppConnectionService.sendIqPacket(account, request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Element slot = packet.findChild("slot",Xmlns.HTTP_UPLOAD);
- if (slot != null) {
- try {
- mGetUrl = new URL(slot.findChildContent("get"));
- mPutUrl = new URL(slot.findChildContent("put"));
- if (!canceled) {
- new Thread(new FileUploader()).start();
- }
- return;
- } catch (MalformedURLException e) {
- //fall through
- }
- }
- }
- Log.d(Config.LOGTAG,account.getJid().toString()+": invalid response to slot request "+packet);
+ }
+
+ public void init(Message message, boolean delay) {
+ this.message = message;
+ this.account = message.getConversation().getAccount();
+ this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
+ this.mime = this.file.getMimeType();
+ this.delayed = delay;
+ if (Config.ENCRYPT_ON_HTTP_UPLOADED
+ || message.getEncryption() == Message.ENCRYPTION_AXOLOTL
+ || message.getEncryption() == Message.ENCRYPTION_OTR) {
+ this.key = new byte[48];
+ mXmppConnectionService.getRNG().nextBytes(this.key);
+ this.file.setKeyAndIv(this.key);
+ }
+ Pair<InputStream, Integer> pair;
+ try {
+ pair = AbstractConnectionManager.createInputStream(file, true);
+ } catch (FileNotFoundException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not find file to upload - " + e.getMessage());
+ fail(e.getMessage());
+ return;
+ }
+ if (pair != null) {
+ this.file.setExpectedSize(pair.second);
+ this.mFileInputStream = pair.first;
+ }
+ Jid host = account.getXmppConnection().findDiscoItemByFeature(Xmlns.HTTP_UPLOAD);
+ IqPacket request = mXmppConnectionService.getIqGenerator().requestHttpUploadSlot(host, file, mime);
+ mXmppConnectionService.sendIqPacket(account, request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Element slot = packet.findChild("slot", Xmlns.HTTP_UPLOAD);
+ if (slot != null) {
+ try {
+ mGetUrl = new URL(slot.findChildContent("get"));
+ mPutUrl = new URL(slot.findChildContent("put"));
+ if (!canceled) {
+ new Thread(new FileUploader()).start();
+ }
+ return;
+ } catch (MalformedURLException e) {
+ //fall through
+ }
+ }
+ }
+ Log.d(Config.LOGTAG, account.getJid().toString() + ": invalid response to slot request " + packet);
fail(IqParser.extractErrorMessage(packet));
- }
- });
- message.setTransferable(this);
- mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
- }
-
- private class FileUploader implements Runnable {
-
- @Override
- public void run() {
- this.upload();
- }
-
- private void upload() {
- OutputStream os = null;
- HttpURLConnection connection = null;
- PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_"+message.getUuid());
- try {
- wakeLock.acquire();
- Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString());
- if (mUseTor) {
- connection = (HttpURLConnection) mPutUrl.openConnection(mHttpConnectionManager.getProxy());
- } else {
- connection = (HttpURLConnection) mPutUrl.openConnection();
- }
- if (connection instanceof HttpsURLConnection) {
- mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true);
- }
- connection.setRequestMethod("PUT");
- connection.setFixedLengthStreamingMode((int) file.getExpectedSize());
- connection.setRequestProperty("Content-Type", mime == null ? "application/octet-stream" : mime);
- connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getIdentityName());
- connection.setDoOutput(true);
+ }
+ });
+ message.setTransferable(this);
+ mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
+ }
+
+ private class FileUploader implements Runnable {
+
+ @Override
+ public void run() {
+ this.upload();
+ }
+
+ private void upload() {
+ OutputStream os = null;
+ HttpURLConnection connection = null;
+ PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_" + message.getUuid());
+ try {
+ wakeLock.acquire();
+ Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString());
+ if (mUseTor) {
+ connection = (HttpURLConnection) mPutUrl.openConnection(mHttpConnectionManager.getProxy());
+ } else {
+ connection = (HttpURLConnection) mPutUrl.openConnection();
+ }
+ if (connection instanceof HttpsURLConnection) {
+ mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true);
+ }
+ connection.setRequestMethod("PUT");
+ connection.setFixedLengthStreamingMode((int) file.getExpectedSize());
+ connection.setRequestProperty("Content-Type", mime == null ? "application/octet-stream" : mime);
+ connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName());
+ connection.setDoOutput(true);
connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.setReadTimeout(Config.CONNECT_TIMEOUT * 1000);
- connection.connect();
- os = connection.getOutputStream();
- transmitted = 0;
- int count;
- byte[] buffer = new byte[4096];
- while (((count = mFileInputStream.read(buffer)) != -1) && !canceled) {
- transmitted += count;
- os.write(buffer, 0, count);
- mXmppConnectionService.updateConversationUi();
- }
- os.flush();
- os.close();
- mFileInputStream.close();
- int code = connection.getResponseCode();
- if (code == 200 || code == 201) {
- Log.d(Config.LOGTAG, "finished uploading file");
- if (key != null) {
- mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key));
- }
- mXmppConnectionService.getFileBackend().updateFileParams(message, mGetUrl);
- mXmppConnectionService.getFileBackend().updateMediaScanner(file);
- message.setTransferable(null);
- message.setCounterpart(message.getConversation().getJid().toBareJid());
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- mXmppConnectionService.getPgpEngine().encrypt(message, new UiCallback<Message>() {
- @Override
- public void success(Message message) {
- mXmppConnectionService.resendMessage(message,delayed);
- }
-
- @Override
- public void error(int errorCode, Message object) {
- Log.d(Config.LOGTAG,"pgp encryption failed");
+ connection.connect();
+ os = connection.getOutputStream();
+ transmitted = 0;
+ int count;
+ byte[] buffer = new byte[4096];
+ while (((count = mFileInputStream.read(buffer)) != -1) && !canceled) {
+ transmitted += count;
+ os.write(buffer, 0, count);
+ mXmppConnectionService.updateConversationUi();
+ }
+ os.flush();
+ os.close();
+ mFileInputStream.close();
+ int code = connection.getResponseCode();
+ if (code == 200 || code == 201) {
+ Log.d(Config.LOGTAG, "finished uploading file");
+ if (key != null) {
+ mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key));
+ }
+ mXmppConnectionService.getFileBackend().updateFileParams(message, mGetUrl);
+ mXmppConnectionService.getFileBackend().updateMediaScanner(file);
+ message.setTransferable(null);
+ message.setCounterpart(message.getConversation().getJid().toBareJid());
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ mXmppConnectionService.getPgpEngine().encrypt(message, new UiCallback<Message>() {
+ @Override
+ public void success(Message message) {
+ mXmppConnectionService.resendMessage(message, delayed);
+ }
+
+ @Override
+ public void error(int errorCode, Message object) {
+ Log.d(Config.LOGTAG, "pgp encryption failed");
fail("pgp encryption failed");
- }
+ }
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
fail("pgp encryption failed");
- }
- });
- } else {
- mXmppConnectionService.resendMessage(message, delayed);
- }
- } else {
- Log.d(Config.LOGTAG,"http upload failed because response code was "+code);
- fail("http upload failed because response code was "+code);
- }
- } catch (IOException e) {
- e.printStackTrace();
- Log.d(Config.LOGTAG,"http upload failed "+e.getMessage());
- fail(e.getMessage());
- } finally {
- FileBackend.close(mFileInputStream);
- FileBackend.close(os);
- if (connection != null) {
- connection.disconnect();
- }
- wakeLock.release();
- }
- }
- }
+ }
+ });
+ } else {
+ mXmppConnectionService.resendMessage(message, delayed);
+ }
+ } else {
+ Log.d(Config.LOGTAG, "http upload failed because response code was " + code);
+ fail("http upload failed because response code was " + code);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.d(Config.LOGTAG, "http upload failed " + e.getMessage());
+ fail(e.getMessage());
+ } finally {
+ FileBackend.close(mFileInputStream);
+ FileBackend.close(os);
+ if (connection != null) {
+ connection.disconnect();
+ }
+ wakeLock.release();
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/parser/AbstractParser.java b/src/main/java/de/pixart/messenger/parser/AbstractParser.java
index 3ad20783b..21da7fe67 100644
--- a/src/main/java/de/pixart/messenger/parser/AbstractParser.java
+++ b/src/main/java/de/pixart/messenger/parser/AbstractParser.java
@@ -15,88 +15,88 @@ import de.pixart.messenger.xmpp.jid.Jid;
public abstract class AbstractParser {
- protected XmppConnectionService mXmppConnectionService;
+ protected XmppConnectionService mXmppConnectionService;
- protected AbstractParser(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
+ protected AbstractParser(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
- public static Long parseTimestamp(Element element, Long d) {
- Element delay = element.findChild("delay","urn:xmpp:delay");
- if (delay != null) {
- String stamp = delay.getAttribute("stamp");
- if (stamp != null) {
- try {
- return AbstractParser.parseTimestamp(delay.getAttribute("stamp"));
- } catch (ParseException e) {
- return d;
- }
- }
- }
- return d;
- }
+ public static Long parseTimestamp(Element element, Long d) {
+ Element delay = element.findChild("delay", "urn:xmpp:delay");
+ if (delay != null) {
+ String stamp = delay.getAttribute("stamp");
+ if (stamp != null) {
+ try {
+ return AbstractParser.parseTimestamp(delay.getAttribute("stamp"));
+ } catch (ParseException e) {
+ return d;
+ }
+ }
+ }
+ return d;
+ }
- public static long parseTimestamp(Element element) {
- return parseTimestamp(element, System.currentTimeMillis());
- }
+ public static long parseTimestamp(Element element) {
+ return parseTimestamp(element, System.currentTimeMillis());
+ }
- public static long parseTimestamp(String timestamp) throws ParseException {
- timestamp = timestamp.replace("Z", "+0000");
- SimpleDateFormat dateFormat;
- long ms;
- if (timestamp.charAt(19) == '.' && timestamp.length() >= 25) {
- String millis = timestamp.substring(19,timestamp.length() - 5);
- try {
- double fractions = Double.parseDouble("0" + millis);
- ms = Math.round(1000 * fractions);
- } catch (NumberFormatException e) {
- ms = 0;
- }
- } else {
- ms = 0;
- }
- timestamp = timestamp.substring(0,19)+timestamp.substring(timestamp.length() -5,timestamp.length());
- dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US);
- return Math.min(dateFormat.parse(timestamp).getTime()+ms, System.currentTimeMillis());
- }
+ public static long parseTimestamp(String timestamp) throws ParseException {
+ timestamp = timestamp.replace("Z", "+0000");
+ SimpleDateFormat dateFormat;
+ long ms;
+ if (timestamp.charAt(19) == '.' && timestamp.length() >= 25) {
+ String millis = timestamp.substring(19, timestamp.length() - 5);
+ try {
+ double fractions = Double.parseDouble("0" + millis);
+ ms = Math.round(1000 * fractions);
+ } catch (NumberFormatException e) {
+ ms = 0;
+ }
+ } else {
+ ms = 0;
+ }
+ timestamp = timestamp.substring(0, 19) + timestamp.substring(timestamp.length() - 5, timestamp.length());
+ dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
+ return Math.min(dateFormat.parse(timestamp).getTime() + ms, System.currentTimeMillis());
+ }
- protected void updateLastseen(final Account account, final Jid from) {
- final Contact contact = account.getRoster().getContact(from);
- contact.setLastResource(from.isBareJid() ? "" : from.getResourcepart());
- }
+ protected void updateLastseen(final Account account, final Jid from) {
+ final Contact contact = account.getRoster().getContact(from);
+ contact.setLastResource(from.isBareJid() ? "" : from.getResourcepart());
+ }
- protected String avatarData(Element items) {
- Element item = items.findChild("item");
- if (item == null) {
- return null;
- }
- return item.findChildContent("data", "urn:xmpp:avatar:data");
- }
+ protected String avatarData(Element items) {
+ Element item = items.findChild("item");
+ if (item == null) {
+ return null;
+ }
+ return item.findChildContent("data", "urn:xmpp:avatar:data");
+ }
- public static MucOptions.User parseItem(Conversation conference, Element item) {
+ public static MucOptions.User parseItem(Conversation conference, Element item) {
return parseItem(conference, item, null);
}
public static MucOptions.User parseItem(Conversation conference, Element item, Jid fullJid) {
- final String local = conference.getJid().getLocalpart();
- final String domain = conference.getJid().getDomainpart();
- String affiliation = item.getAttribute("affiliation");
- String role = item.getAttribute("role");
- String nick = item.getAttribute("nick");
+ final String local = conference.getJid().getLocalpart();
+ final String domain = conference.getJid().getDomainpart();
+ String affiliation = item.getAttribute("affiliation");
+ String role = item.getAttribute("role");
+ String nick = item.getAttribute("nick");
if (nick != null && fullJid == null) {
try {
fullJid = Jid.fromParts(local, domain, nick);
} catch (InvalidJidException e) {
fullJid = null;
}
- }
- Jid realJid = item.getAttributeAsJid("jid");
+ }
+ Jid realJid = item.getAttributeAsJid("jid");
MucOptions.User user = new MucOptions.User(conference.getMucOptions(), fullJid);
- user.setRealJid(realJid);
- user.setAffiliation(affiliation);
- user.setRole(role);
- return user;
- }
+ user.setRealJid(realJid);
+ user.setAffiliation(affiliation);
+ user.setRole(role);
+ return user;
+ }
public static String extractErrorMessage(Element packet) {
final Element error = packet.findChild("error");
@@ -105,7 +105,7 @@ public abstract class AbstractParser {
if (text != null && !text.trim().isEmpty()) {
return text;
} else {
- return error.getChildren().get(0).getName().replace("-"," ");
+ return error.getChildren().get(0).getName().replace("-", " ");
}
} else {
return null;
diff --git a/src/main/java/de/pixart/messenger/parser/IqParser.java b/src/main/java/de/pixart/messenger/parser/IqParser.java
index 5617973d6..25d946c1d 100644
--- a/src/main/java/de/pixart/messenger/parser/IqParser.java
+++ b/src/main/java/de/pixart/messenger/parser/IqParser.java
@@ -36,351 +36,351 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class IqParser extends AbstractParser implements OnIqPacketReceived {
- public IqParser(final XmppConnectionService service) {
- super(service);
- }
+ public IqParser(final XmppConnectionService service) {
+ super(service);
+ }
- private void rosterItems(final Account account, final Element query) {
- final String version = query.getAttribute("ver");
- if (version != null) {
- account.getRoster().setVersion(version);
- }
- for (final Element item : query.getChildren()) {
- if (item.getName().equals("item")) {
- final Jid jid = item.getAttributeAsJid("jid");
- if (jid == null) {
- continue;
- }
- final String name = item.getAttribute("name");
- final String subscription = item.getAttribute("subscription");
- final Contact contact = account.getRoster().getContact(jid);
- boolean bothPre = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
- if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
- contact.setServerName(name);
- contact.parseGroupsFromElement(item);
- }
- if (subscription != null) {
- if (subscription.equals("remove")) {
- contact.resetOption(Contact.Options.IN_ROSTER);
- contact.resetOption(Contact.Options.DIRTY_DELETE);
- contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- } else {
- contact.setOption(Contact.Options.IN_ROSTER);
- contact.resetOption(Contact.Options.DIRTY_PUSH);
- contact.parseSubscriptionFromElement(item);
- }
- }
- boolean both = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
- if ((both != bothPre) && both) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": gained mutual presence subscription with "+contact.getJid());
- AxolotlService axolotlService = account.getAxolotlService();
- if (axolotlService != null) {
- axolotlService.clearErrorsInFetchStatusMap(contact.getJid());
- }
- }
- mXmppConnectionService.getAvatarService().clear(contact);
- }
- }
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateRosterUi();
- }
+ private void rosterItems(final Account account, final Element query) {
+ final String version = query.getAttribute("ver");
+ if (version != null) {
+ account.getRoster().setVersion(version);
+ }
+ for (final Element item : query.getChildren()) {
+ if (item.getName().equals("item")) {
+ final Jid jid = item.getAttributeAsJid("jid");
+ if (jid == null) {
+ continue;
+ }
+ final String name = item.getAttribute("name");
+ final String subscription = item.getAttribute("subscription");
+ final Contact contact = account.getRoster().getContact(jid);
+ boolean bothPre = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
+ if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
+ contact.setServerName(name);
+ contact.parseGroupsFromElement(item);
+ }
+ if (subscription != null) {
+ if (subscription.equals("remove")) {
+ contact.resetOption(Contact.Options.IN_ROSTER);
+ contact.resetOption(Contact.Options.DIRTY_DELETE);
+ contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
+ } else {
+ contact.setOption(Contact.Options.IN_ROSTER);
+ contact.resetOption(Contact.Options.DIRTY_PUSH);
+ contact.parseSubscriptionFromElement(item);
+ }
+ }
+ boolean both = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
+ if ((both != bothPre) && both) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": gained mutual presence subscription with " + contact.getJid());
+ AxolotlService axolotlService = account.getAxolotlService();
+ if (axolotlService != null) {
+ axolotlService.clearErrorsInFetchStatusMap(contact.getJid());
+ }
+ }
+ mXmppConnectionService.getAvatarService().clear(contact);
+ }
+ }
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateRosterUi();
+ }
- public String avatarData(final IqPacket packet) {
- final Element pubsub = packet.findChild("pubsub",
- "http://jabber.org/protocol/pubsub");
- if (pubsub == null) {
- return null;
- }
- final Element items = pubsub.findChild("items");
- if (items == null) {
- return null;
- }
- return super.avatarData(items);
- }
+ public String avatarData(final IqPacket packet) {
+ final Element pubsub = packet.findChild("pubsub",
+ "http://jabber.org/protocol/pubsub");
+ if (pubsub == null) {
+ return null;
+ }
+ final Element items = pubsub.findChild("items");
+ if (items == null) {
+ return null;
+ }
+ return super.avatarData(items);
+ }
- public Element getItem(final IqPacket packet) {
- final Element pubsub = packet.findChild("pubsub",
- "http://jabber.org/protocol/pubsub");
- if (pubsub == null) {
- return null;
- }
- final Element items = pubsub.findChild("items");
- if (items == null) {
- return null;
- }
- return items.findChild("item");
- }
+ public Element getItem(final IqPacket packet) {
+ final Element pubsub = packet.findChild("pubsub",
+ "http://jabber.org/protocol/pubsub");
+ if (pubsub == null) {
+ return null;
+ }
+ final Element items = pubsub.findChild("items");
+ if (items == null) {
+ return null;
+ }
+ return items.findChild("item");
+ }
- @NonNull
- public Set<Integer> deviceIds(final Element item) {
- Set<Integer> deviceIds = new HashSet<>();
- if (item != null) {
- final Element list = item.findChild("list");
- if (list != null) {
- for (Element device : list.getChildren()) {
- if (!device.getName().equals("device")) {
- continue;
- }
- try {
- Integer id = Integer.valueOf(device.getAttribute("id"));
- deviceIds.add(id);
- } catch (NumberFormatException e) {
- Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Encountered invalid <device> node in PEP ("+e.getMessage()+"):" + device.toString()+ ", skipping...");
- continue;
- }
- }
- }
- }
- return deviceIds;
- }
+ @NonNull
+ public Set<Integer> deviceIds(final Element item) {
+ Set<Integer> deviceIds = new HashSet<>();
+ if (item != null) {
+ final Element list = item.findChild("list");
+ if (list != null) {
+ for (Element device : list.getChildren()) {
+ if (!device.getName().equals("device")) {
+ continue;
+ }
+ try {
+ Integer id = Integer.valueOf(device.getAttribute("id"));
+ deviceIds.add(id);
+ } catch (NumberFormatException e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Encountered invalid <device> node in PEP (" + e.getMessage() + "):" + device.toString() + ", skipping...");
+ continue;
+ }
+ }
+ }
+ }
+ return deviceIds;
+ }
- public Integer signedPreKeyId(final Element bundle) {
- final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
- if(signedPreKeyPublic == null) {
- return null;
- }
+ public Integer signedPreKeyId(final Element bundle) {
+ final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
+ if (signedPreKeyPublic == null) {
+ return null;
+ }
try {
return Integer.valueOf(signedPreKeyPublic.getAttribute("signedPreKeyId"));
} catch (NumberFormatException e) {
return null;
}
- }
+ }
- public ECPublicKey signedPreKeyPublic(final Element bundle) {
- ECPublicKey publicKey = null;
- final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
- if(signedPreKeyPublic == null) {
- return null;
- }
- try {
- publicKey = Curve.decodePoint(Base64.decode(signedPreKeyPublic.getContent(),Base64.DEFAULT), 0);
- } catch (Throwable e) {
- Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Invalid signedPreKeyPublic in PEP: " + e.getMessage());
- }
- return publicKey;
- }
+ public ECPublicKey signedPreKeyPublic(final Element bundle) {
+ ECPublicKey publicKey = null;
+ final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
+ if (signedPreKeyPublic == null) {
+ return null;
+ }
+ try {
+ publicKey = Curve.decodePoint(Base64.decode(signedPreKeyPublic.getContent(), Base64.DEFAULT), 0);
+ } catch (Throwable e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid signedPreKeyPublic in PEP: " + e.getMessage());
+ }
+ return publicKey;
+ }
- public byte[] signedPreKeySignature(final Element bundle) {
- final Element signedPreKeySignature = bundle.findChild("signedPreKeySignature");
- if(signedPreKeySignature == null) {
- return null;
- }
- try {
- return Base64.decode(signedPreKeySignature.getContent(), Base64.DEFAULT);
- } catch (Throwable e) {
- Log.e(Config.LOGTAG,AxolotlService.LOGPREFIX+" : Invalid base64 in signedPreKeySignature");
- return null;
- }
- }
+ public byte[] signedPreKeySignature(final Element bundle) {
+ final Element signedPreKeySignature = bundle.findChild("signedPreKeySignature");
+ if (signedPreKeySignature == null) {
+ return null;
+ }
+ try {
+ return Base64.decode(signedPreKeySignature.getContent(), Base64.DEFAULT);
+ } catch (Throwable e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : Invalid base64 in signedPreKeySignature");
+ return null;
+ }
+ }
- public IdentityKey identityKey(final Element bundle) {
- IdentityKey identityKey = null;
- final Element identityKeyElement = bundle.findChild("identityKey");
- if(identityKeyElement == null) {
- return null;
- }
- try {
- identityKey = new IdentityKey(Base64.decode(identityKeyElement.getContent(), Base64.DEFAULT), 0);
- } catch (Throwable e) {
- Log.e(Config.LOGTAG,AxolotlService.LOGPREFIX+" : "+"Invalid identityKey in PEP: "+e.getMessage());
- }
- return identityKey;
- }
+ public IdentityKey identityKey(final Element bundle) {
+ IdentityKey identityKey = null;
+ final Element identityKeyElement = bundle.findChild("identityKey");
+ if (identityKeyElement == null) {
+ return null;
+ }
+ try {
+ identityKey = new IdentityKey(Base64.decode(identityKeyElement.getContent(), Base64.DEFAULT), 0);
+ } catch (Throwable e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid identityKey in PEP: " + e.getMessage());
+ }
+ return identityKey;
+ }
- public Map<Integer, ECPublicKey> preKeyPublics(final IqPacket packet) {
- Map<Integer, ECPublicKey> preKeyRecords = new HashMap<>();
- Element item = getItem(packet);
- if (item == null) {
- Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Couldn't find <item> in bundle IQ packet: " + packet);
- return null;
- }
- final Element bundleElement = item.findChild("bundle");
- if(bundleElement == null) {
- return null;
- }
- final Element prekeysElement = bundleElement.findChild("prekeys");
- if(prekeysElement == null) {
- Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Couldn't find <prekeys> in bundle IQ packet: " + packet);
- return null;
- }
- for(Element preKeyPublicElement : prekeysElement.getChildren()) {
- if(!preKeyPublicElement.getName().equals("preKeyPublic")){
- Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Encountered unexpected tag in prekeys list: " + preKeyPublicElement);
- continue;
- }
- Integer preKeyId = null;
- try {
- preKeyId = Integer.valueOf(preKeyPublicElement.getAttribute("preKeyId"));
- final ECPublicKey preKeyPublic = Curve.decodePoint(Base64.decode(preKeyPublicElement.getContent(), Base64.DEFAULT), 0);
- preKeyRecords.put(preKeyId, preKeyPublic);
- } catch (NumberFormatException e) {
- Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"could not parse preKeyId from preKey "+preKeyPublicElement.toString());
- } catch (Throwable e) {
- Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Invalid preKeyPublic (ID="+preKeyId+") in PEP: "+ e.getMessage()+", skipping...");
- }
- }
- return preKeyRecords;
- }
+ public Map<Integer, ECPublicKey> preKeyPublics(final IqPacket packet) {
+ Map<Integer, ECPublicKey> preKeyRecords = new HashMap<>();
+ Element item = getItem(packet);
+ if (item == null) {
+ Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Couldn't find <item> in bundle IQ packet: " + packet);
+ return null;
+ }
+ final Element bundleElement = item.findChild("bundle");
+ if (bundleElement == null) {
+ return null;
+ }
+ final Element prekeysElement = bundleElement.findChild("prekeys");
+ if (prekeysElement == null) {
+ Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Couldn't find <prekeys> in bundle IQ packet: " + packet);
+ return null;
+ }
+ for (Element preKeyPublicElement : prekeysElement.getChildren()) {
+ if (!preKeyPublicElement.getName().equals("preKeyPublic")) {
+ Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Encountered unexpected tag in prekeys list: " + preKeyPublicElement);
+ continue;
+ }
+ Integer preKeyId = null;
+ try {
+ preKeyId = Integer.valueOf(preKeyPublicElement.getAttribute("preKeyId"));
+ final ECPublicKey preKeyPublic = Curve.decodePoint(Base64.decode(preKeyPublicElement.getContent(), Base64.DEFAULT), 0);
+ preKeyRecords.put(preKeyId, preKeyPublic);
+ } catch (NumberFormatException e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "could not parse preKeyId from preKey " + preKeyPublicElement.toString());
+ } catch (Throwable e) {
+ Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid preKeyPublic (ID=" + preKeyId + ") in PEP: " + e.getMessage() + ", skipping...");
+ }
+ }
+ return preKeyRecords;
+ }
- public Pair<X509Certificate[],byte[]> verification(final IqPacket packet) {
- Element item = getItem(packet);
- Element verification = item != null ? item.findChild("verification",AxolotlService.PEP_PREFIX) : null;
- Element chain = verification != null ? verification.findChild("chain") : null;
- Element signature = verification != null ? verification.findChild("signature") : null;
- if (chain != null && signature != null) {
- List<Element> certElements = chain.getChildren();
- X509Certificate[] certificates = new X509Certificate[certElements.size()];
- try {
- CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
- int i = 0;
- for(Element cert : certElements) {
- certificates[i] = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(Base64.decode(cert.getContent(),Base64.DEFAULT)));
- ++i;
- }
- return new Pair<>(certificates,Base64.decode(signature.getContent(),Base64.DEFAULT));
- } catch (CertificateException e) {
- return null;
- }
- } else {
- return null;
- }
- }
+ public Pair<X509Certificate[], byte[]> verification(final IqPacket packet) {
+ Element item = getItem(packet);
+ Element verification = item != null ? item.findChild("verification", AxolotlService.PEP_PREFIX) : null;
+ Element chain = verification != null ? verification.findChild("chain") : null;
+ Element signature = verification != null ? verification.findChild("signature") : null;
+ if (chain != null && signature != null) {
+ List<Element> certElements = chain.getChildren();
+ X509Certificate[] certificates = new X509Certificate[certElements.size()];
+ try {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ int i = 0;
+ for (Element cert : certElements) {
+ certificates[i] = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(Base64.decode(cert.getContent(), Base64.DEFAULT)));
+ ++i;
+ }
+ return new Pair<>(certificates, Base64.decode(signature.getContent(), Base64.DEFAULT));
+ } catch (CertificateException e) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
- public PreKeyBundle bundle(final IqPacket bundle) {
- Element bundleItem = getItem(bundle);
- if(bundleItem == null) {
- return null;
- }
- final Element bundleElement = bundleItem.findChild("bundle");
- if(bundleElement == null) {
- return null;
- }
- ECPublicKey signedPreKeyPublic = signedPreKeyPublic(bundleElement);
- Integer signedPreKeyId = signedPreKeyId(bundleElement);
- byte[] signedPreKeySignature = signedPreKeySignature(bundleElement);
- IdentityKey identityKey = identityKey(bundleElement);
- if(signedPreKeyId == null || signedPreKeyPublic == null || identityKey == null) {
- return null;
- }
+ public PreKeyBundle bundle(final IqPacket bundle) {
+ Element bundleItem = getItem(bundle);
+ if (bundleItem == null) {
+ return null;
+ }
+ final Element bundleElement = bundleItem.findChild("bundle");
+ if (bundleElement == null) {
+ return null;
+ }
+ ECPublicKey signedPreKeyPublic = signedPreKeyPublic(bundleElement);
+ Integer signedPreKeyId = signedPreKeyId(bundleElement);
+ byte[] signedPreKeySignature = signedPreKeySignature(bundleElement);
+ IdentityKey identityKey = identityKey(bundleElement);
+ if (signedPreKeyId == null || signedPreKeyPublic == null || identityKey == null) {
+ return null;
+ }
- return new PreKeyBundle(0, 0, 0, null,
- signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey);
- }
+ return new PreKeyBundle(0, 0, 0, null,
+ signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey);
+ }
- public List<PreKeyBundle> preKeys(final IqPacket preKeys) {
- List<PreKeyBundle> bundles = new ArrayList<>();
- Map<Integer, ECPublicKey> preKeyPublics = preKeyPublics(preKeys);
- if ( preKeyPublics != null) {
- for (Integer preKeyId : preKeyPublics.keySet()) {
- ECPublicKey preKeyPublic = preKeyPublics.get(preKeyId);
- bundles.add(new PreKeyBundle(0, 0, preKeyId, preKeyPublic,
- 0, null, null, null));
- }
- }
+ public List<PreKeyBundle> preKeys(final IqPacket preKeys) {
+ List<PreKeyBundle> bundles = new ArrayList<>();
+ Map<Integer, ECPublicKey> preKeyPublics = preKeyPublics(preKeys);
+ if (preKeyPublics != null) {
+ for (Integer preKeyId : preKeyPublics.keySet()) {
+ ECPublicKey preKeyPublic = preKeyPublics.get(preKeyId);
+ bundles.add(new PreKeyBundle(0, 0, preKeyId, preKeyPublic,
+ 0, null, null, null));
+ }
+ }
- return bundles;
- }
+ return bundles;
+ }
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- final boolean isGet = packet.getType() == IqPacket.TYPE.GET;
- if (packet.getType() == IqPacket.TYPE.ERROR || packet.getType() == IqPacket.TYPE.TIMEOUT) {
- return;
- } else if (packet.hasChild("query", Xmlns.ROSTER) && packet.fromServer(account)) {
- final Element query = packet.findChild("query");
- // If this is in response to a query for the whole roster:
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.getRoster().markAllAsNotInRoster();
- }
- this.rosterItems(account, query);
- } else if ((packet.hasChild("block", Xmlns.BLOCKING) || packet.hasChild("blocklist", Xmlns.BLOCKING)) &&
- packet.fromServer(account)) {
- // Block list or block push.
- Log.d(Config.LOGTAG, "Received blocklist update from server");
- final Element blocklist = packet.findChild("blocklist", Xmlns.BLOCKING);
- final Element block = packet.findChild("block", Xmlns.BLOCKING);
- final Collection<Element> items = blocklist != null ? blocklist.getChildren() :
- (block != null ? block.getChildren() : null);
- // If this is a response to a blocklist query, clear the block list and replace with the new one.
- // Otherwise, just update the existing blocklist.
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.clearBlocklist();
- account.getXmppConnection().getFeatures().setBlockListRequested(true);
- }
- if (items != null) {
- final Collection<Jid> jids = new ArrayList<>(items.size());
- // Create a collection of Jids from the packet
- for (final Element item : items) {
- if (item.getName().equals("item")) {
- final Jid jid = item.getAttributeAsJid("jid");
- if (jid != null) {
- jids.add(jid);
- }
- }
- }
- account.getBlocklist().addAll(jids);
- }
- // Update the UI
- mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
- if (packet.getType() == IqPacket.TYPE.SET) {
- final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
- mXmppConnectionService.sendIqPacket(account, response, null);
- }
- } else if (packet.hasChild("unblock", Xmlns.BLOCKING) &&
- packet.fromServer(account) && packet.getType() == IqPacket.TYPE.SET) {
- Log.d(Config.LOGTAG, "Received unblock update from server");
- final Collection<Element> items = packet.findChild("unblock", Xmlns.BLOCKING).getChildren();
- if (items.size() == 0) {
- // No children to unblock == unblock all
- account.getBlocklist().clear();
- } else {
- final Collection<Jid> jids = new ArrayList<>(items.size());
- for (final Element item : items) {
- if (item.getName().equals("item")) {
- final Jid jid = item.getAttributeAsJid("jid");
- if (jid != null) {
- jids.add(jid);
- }
- }
- }
- account.getBlocklist().removeAll(jids);
- }
- mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
- final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
- mXmppConnectionService.sendIqPacket(account, response, null);
- } else if (packet.hasChild("open", "http://jabber.org/protocol/ibb")
- || packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
- mXmppConnectionService.getJingleConnectionManager()
- .deliverIbbPacket(account, packet);
- } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) {
- final IqPacket response = mXmppConnectionService.getIqGenerator().discoResponse(packet);
- mXmppConnectionService.sendIqPacket(account, response, null);
- } else if (packet.hasChild("query","jabber:iq:version") && isGet) {
- final IqPacket response = mXmppConnectionService.getIqGenerator().versionResponse(packet);
- mXmppConnectionService.sendIqPacket(account,response,null);
- } else if (packet.hasChild("ping", "urn:xmpp:ping") && isGet) {
- final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
- mXmppConnectionService.sendIqPacket(account, response, null);
- } else if (packet.hasChild("time","urn:xmpp:time") && isGet) {
- final IqPacket response;
- if (mXmppConnectionService.useTorToConnect()) {
- response = packet.generateResponse(IqPacket.TYPE.ERROR);
- final Element error = response.addChild("error");
- error.setAttribute("type","cancel");
- error.addChild("not-allowed","urn:ietf:params:xml:ns:xmpp-stanzas");
- } else {
- response = mXmppConnectionService.getIqGenerator().entityTimeResponse(packet);
- }
- mXmppConnectionService.sendIqPacket(account,response, null);
- } else {
- if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
- final IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
- final Element error = response.addChild("error");
- error.setAttribute("type", "cancel");
- error.addChild("feature-not-implemented","urn:ietf:params:xml:ns:xmpp-stanzas");
- account.getXmppConnection().sendIqPacket(response, null);
- }
- }
- }
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ final boolean isGet = packet.getType() == IqPacket.TYPE.GET;
+ if (packet.getType() == IqPacket.TYPE.ERROR || packet.getType() == IqPacket.TYPE.TIMEOUT) {
+ return;
+ } else if (packet.hasChild("query", Xmlns.ROSTER) && packet.fromServer(account)) {
+ final Element query = packet.findChild("query");
+ // If this is in response to a query for the whole roster:
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.getRoster().markAllAsNotInRoster();
+ }
+ this.rosterItems(account, query);
+ } else if ((packet.hasChild("block", Xmlns.BLOCKING) || packet.hasChild("blocklist", Xmlns.BLOCKING)) &&
+ packet.fromServer(account)) {
+ // Block list or block push.
+ Log.d(Config.LOGTAG, "Received blocklist update from server");
+ final Element blocklist = packet.findChild("blocklist", Xmlns.BLOCKING);
+ final Element block = packet.findChild("block", Xmlns.BLOCKING);
+ final Collection<Element> items = blocklist != null ? blocklist.getChildren() :
+ (block != null ? block.getChildren() : null);
+ // If this is a response to a blocklist query, clear the block list and replace with the new one.
+ // Otherwise, just update the existing blocklist.
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.clearBlocklist();
+ account.getXmppConnection().getFeatures().setBlockListRequested(true);
+ }
+ if (items != null) {
+ final Collection<Jid> jids = new ArrayList<>(items.size());
+ // Create a collection of Jids from the packet
+ for (final Element item : items) {
+ if (item.getName().equals("item")) {
+ final Jid jid = item.getAttributeAsJid("jid");
+ if (jid != null) {
+ jids.add(jid);
+ }
+ }
+ }
+ account.getBlocklist().addAll(jids);
+ }
+ // Update the UI
+ mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
+ if (packet.getType() == IqPacket.TYPE.SET) {
+ final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ }
+ } else if (packet.hasChild("unblock", Xmlns.BLOCKING) &&
+ packet.fromServer(account) && packet.getType() == IqPacket.TYPE.SET) {
+ Log.d(Config.LOGTAG, "Received unblock update from server");
+ final Collection<Element> items = packet.findChild("unblock", Xmlns.BLOCKING).getChildren();
+ if (items.size() == 0) {
+ // No children to unblock == unblock all
+ account.getBlocklist().clear();
+ } else {
+ final Collection<Jid> jids = new ArrayList<>(items.size());
+ for (final Element item : items) {
+ if (item.getName().equals("item")) {
+ final Jid jid = item.getAttributeAsJid("jid");
+ if (jid != null) {
+ jids.add(jid);
+ }
+ }
+ }
+ account.getBlocklist().removeAll(jids);
+ }
+ mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
+ final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else if (packet.hasChild("open", "http://jabber.org/protocol/ibb")
+ || packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
+ mXmppConnectionService.getJingleConnectionManager()
+ .deliverIbbPacket(account, packet);
+ } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) {
+ final IqPacket response = mXmppConnectionService.getIqGenerator().discoResponse(packet);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else if (packet.hasChild("query", "jabber:iq:version") && isGet) {
+ final IqPacket response = mXmppConnectionService.getIqGenerator().versionResponse(packet);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else if (packet.hasChild("ping", "urn:xmpp:ping") && isGet) {
+ final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else if (packet.hasChild("time", "urn:xmpp:time") && isGet) {
+ final IqPacket response;
+ if (mXmppConnectionService.useTorToConnect()) {
+ response = packet.generateResponse(IqPacket.TYPE.ERROR);
+ final Element error = response.addChild("error");
+ error.setAttribute("type", "cancel");
+ error.addChild("not-allowed", "urn:ietf:params:xml:ns:xmpp-stanzas");
+ } else {
+ response = mXmppConnectionService.getIqGenerator().entityTimeResponse(packet);
+ }
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ } else {
+ if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
+ final IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
+ final Element error = response.addChild("error");
+ error.setAttribute("type", "cancel");
+ error.addChild("feature-not-implemented", "urn:ietf:params:xml:ns:xmpp-stanzas");
+ account.getXmppConnection().sendIqPacket(response, null);
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/parser/MessageParser.java b/src/main/java/de/pixart/messenger/parser/MessageParser.java
index d506c3fa2..531e29629 100644
--- a/src/main/java/de/pixart/messenger/parser/MessageParser.java
+++ b/src/main/java/de/pixart/messenger/parser/MessageParser.java
@@ -41,396 +41,397 @@ import de.pixart.messenger.xmpp.stanzas.MessagePacket;
public class MessageParser extends AbstractParser implements OnMessagePacketReceived {
- private static final List<String> CLIENTS_SENDING_HTML_IN_OTR = Arrays.asList("Pidgin","Adium","Trillian");
-
- public MessageParser(XmppConnectionService service) {
- super(service);
- }
-
- private boolean extractChatState(Conversation conversation, final MessagePacket packet) {
- ChatState state = ChatState.parse(packet);
- if (state != null && conversation != null) {
- final Account account = conversation.getAccount();
- Jid from = packet.getFrom();
- if (from.toBareJid().equals(account.getJid().toBareJid())) {
- conversation.setOutgoingChatState(state);
- if (state == ChatState.ACTIVE || state == ChatState.COMPOSING) {
- mXmppConnectionService.markRead(conversation);
- activateGracePeriod(account);
- }
- return false;
- } else {
- return conversation.setIncomingChatState(state);
- }
- }
- return false;
- }
-
- private Message parseOtrChat(String body, Jid from, String id, Conversation conversation) {
- String presence;
- if (from.isBareJid()) {
- presence = "";
- } else {
- presence = from.getResourcepart();
- }
- if (body.matches("^\\?OTRv\\d{1,2}\\?.*")) {
- conversation.endOtrIfNeeded();
- }
- if (!conversation.hasValidOtrSession()) {
- conversation.startOtrSession(presence,false);
- } else {
- String foreignPresence = conversation.getOtrSession().getSessionID().getUserID();
- if (!foreignPresence.equals(presence)) {
- conversation.endOtrIfNeeded();
- conversation.startOtrSession(presence, false);
- }
- }
- try {
- conversation.setLastReceivedOtrMessageId(id);
- Session otrSession = conversation.getOtrSession();
- body = otrSession.transformReceiving(body);
- SessionStatus status = otrSession.getSessionStatus();
- if (body == null && status == SessionStatus.ENCRYPTED) {
- mXmppConnectionService.onOtrSessionEstablished(conversation);
- return null;
- } else if (body == null && status == SessionStatus.FINISHED) {
- conversation.resetOtrSession();
- mXmppConnectionService.updateConversationUi();
- return null;
- } else if (body == null || (body.isEmpty())) {
- return null;
- }
- if (body.startsWith(CryptoHelper.FILETRANSFER)) {
- String key = body.substring(CryptoHelper.FILETRANSFER.length());
- conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
- return null;
- }
- if (clientMightSendHtml(conversation.getAccount(), from)) {
- Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": received OTR message from bad behaving client. escaping HTML…");
- body = Html.fromHtml(body).toString();
- }
-
- final OtrService otrService = conversation.getAccount().getOtrService();
- Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, Message.STATUS_RECEIVED);
- finishedMessage.setFingerprint(otrService.getFingerprint(otrSession.getRemotePublicKey()));
- conversation.setLastReceivedOtrMessageId(null);
-
- return finishedMessage;
- } catch (Exception e) {
- conversation.resetOtrSession();
- return null;
- }
- }
-
- private static boolean clientMightSendHtml(Account account, Jid from) {
- String resource = from.getResourcepart();
- if (resource == null) {
- return false;
- }
- Presence presence = account.getRoster().getContact(from).getPresences().getPresences().get(resource);
- ServiceDiscoveryResult disco = presence == null ? null : presence.getServiceDiscoveryResult();
- if (disco == null) {
- return false;
- }
- return hasIdentityKnowForSendingHtml(disco.getIdentities());
- }
-
- private static boolean hasIdentityKnowForSendingHtml(List<ServiceDiscoveryResult.Identity> identities) {
- for(ServiceDiscoveryResult.Identity identity : identities) {
- if (identity.getName() != null) {
- if (CLIENTS_SENDING_HTML_IN_OTR.contains(identity.getName())) {
- return true;
- }
- }
- }
- return false;
- }
-
- private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status) {
- AxolotlService service = conversation.getAccount().getAxolotlService();
- XmppAxolotlMessage xmppAxolotlMessage;
- try {
- xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid());
- } catch (Exception e) {
- Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": invalid omemo message received "+e.getMessage());
- return null;
- }
- XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceivingPayloadMessage(xmppAxolotlMessage);
- if(plaintextMessage != null) {
- Message finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status);
- finishedMessage.setFingerprint(plaintextMessage.getFingerprint());
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(finishedMessage.getConversation().getAccount())+" Received Message with session fingerprint: "+plaintextMessage.getFingerprint());
- return finishedMessage;
- } else {
- return null;
- }
- }
-
- private class Invite {
- Jid jid;
- String password;
- Invite(Jid jid, String password) {
- this.jid = jid;
- this.password = password;
- }
-
- public boolean execute(Account account) {
- if (jid != null) {
- Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true);
- if (!conversation.getMucOptions().online()) {
- conversation.getMucOptions().setPassword(password);
- mXmppConnectionService.databaseBackend.updateConversation(conversation);
- mXmppConnectionService.joinMuc(conversation);
- mXmppConnectionService.updateConversationUi();
- }
- return true;
- }
- return false;
- }
- }
-
- private Invite extractInvite(Element message) {
- Element x = message.findChild("x", "http://jabber.org/protocol/muc#user");
- if (x != null) {
- Element invite = x.findChild("invite");
- if (invite != null) {
- Element pw = x.findChild("password");
- return new Invite(message.getAttributeAsJid("from"), pw != null ? pw.getContent(): null);
- }
- } else {
- x = message.findChild("x","jabber:x:conference");
- if (x != null) {
- return new Invite(x.getAttributeAsJid("jid"),x.getAttribute("password"));
- }
- }
- return null;
- }
-
- private static String extractStanzaId(Element packet, Jid by) {
- for(Element child : packet.getChildren()) {
- if (child.getName().equals("stanza-id")
- && Xmlns.STANZA_IDS.equals(child.getNamespace())
- && by.equals(child.getAttributeAsJid("by"))) {
- return child.getAttribute("id");
- }
- }
- return null;
- }
-
- private void parseEvent(final Element event, final Jid from, final Account account) {
- Element items = event.findChild("items");
- String node = items == null ? null : items.getAttribute("node");
- if ("urn:xmpp:avatar:metadata".equals(node)) {
- Avatar avatar = Avatar.parseMetadata(items);
- if (avatar != null) {
- avatar.owner = from.toBareJid();
- if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
- if (account.getJid().toBareJid().equals(from)) {
- if (account.setAvatar(avatar.getFilename())) {
- mXmppConnectionService.databaseBackend.updateAccount(account);
- }
- mXmppConnectionService.getAvatarService().clear(account);
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateAccountUi();
- } else {
- Contact contact = account.getRoster().getContact(from);
- contact.setAvatar(avatar);
- mXmppConnectionService.getAvatarService().clear(contact);
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateRosterUi();
- }
- } else if (mXmppConnectionService.isDataSaverDisabled()) {
- mXmppConnectionService.fetchAvatar(account, avatar);
- }
- }
- } else if ("http://jabber.org/protocol/nick".equals(node)) {
- Element i = items.findChild("item");
- Element nick = i == null ? null : i.findChild("nick", "http://jabber.org/protocol/nick");
- if (nick != null && nick.getContent() != null) {
- Contact contact = account.getRoster().getContact(from);
- contact.setPresenceName(nick.getContent());
- mXmppConnectionService.getAvatarService().clear(account);
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateAccountUi();
- }
- } else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Received PEP device list update from "+ from + ", processing...");
- Element item = items.findChild("item");
- Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
- AxolotlService axolotlService = account.getAxolotlService();
- axolotlService.registerDevices(from, deviceIds);
- mXmppConnectionService.updateAccountUi();
- }
- }
-
- private boolean handleErrorMessage(Account account, MessagePacket packet) {
- if (packet.getType() == MessagePacket.TYPE_ERROR) {
- Jid from = packet.getFrom();
- if (from != null) {
- Message message = mXmppConnectionService.markMessage(account,
- from.toBareJid(),
- packet.getId(),
+ private static final List<String> CLIENTS_SENDING_HTML_IN_OTR = Arrays.asList("Pidgin", "Adium", "Trillian");
+
+ public MessageParser(XmppConnectionService service) {
+ super(service);
+ }
+
+ private boolean extractChatState(Conversation conversation, final MessagePacket packet) {
+ ChatState state = ChatState.parse(packet);
+ if (state != null && conversation != null) {
+ final Account account = conversation.getAccount();
+ Jid from = packet.getFrom();
+ if (from.toBareJid().equals(account.getJid().toBareJid())) {
+ conversation.setOutgoingChatState(state);
+ if (state == ChatState.ACTIVE || state == ChatState.COMPOSING) {
+ mXmppConnectionService.markRead(conversation);
+ activateGracePeriod(account);
+ }
+ return false;
+ } else {
+ return conversation.setIncomingChatState(state);
+ }
+ }
+ return false;
+ }
+
+ private Message parseOtrChat(String body, Jid from, String id, Conversation conversation) {
+ String presence;
+ if (from.isBareJid()) {
+ presence = "";
+ } else {
+ presence = from.getResourcepart();
+ }
+ if (body.matches("^\\?OTRv\\d{1,2}\\?.*")) {
+ conversation.endOtrIfNeeded();
+ }
+ if (!conversation.hasValidOtrSession()) {
+ conversation.startOtrSession(presence, false);
+ } else {
+ String foreignPresence = conversation.getOtrSession().getSessionID().getUserID();
+ if (!foreignPresence.equals(presence)) {
+ conversation.endOtrIfNeeded();
+ conversation.startOtrSession(presence, false);
+ }
+ }
+ try {
+ conversation.setLastReceivedOtrMessageId(id);
+ Session otrSession = conversation.getOtrSession();
+ body = otrSession.transformReceiving(body);
+ SessionStatus status = otrSession.getSessionStatus();
+ if (body == null && status == SessionStatus.ENCRYPTED) {
+ mXmppConnectionService.onOtrSessionEstablished(conversation);
+ return null;
+ } else if (body == null && status == SessionStatus.FINISHED) {
+ conversation.resetOtrSession();
+ mXmppConnectionService.updateConversationUi();
+ return null;
+ } else if (body == null || (body.isEmpty())) {
+ return null;
+ }
+ if (body.startsWith(CryptoHelper.FILETRANSFER)) {
+ String key = body.substring(CryptoHelper.FILETRANSFER.length());
+ conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
+ return null;
+ }
+ if (clientMightSendHtml(conversation.getAccount(), from)) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": received OTR message from bad behaving client. escaping HTML…");
+ body = Html.fromHtml(body).toString();
+ }
+
+ final OtrService otrService = conversation.getAccount().getOtrService();
+ Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, Message.STATUS_RECEIVED);
+ finishedMessage.setFingerprint(otrService.getFingerprint(otrSession.getRemotePublicKey()));
+ conversation.setLastReceivedOtrMessageId(null);
+
+ return finishedMessage;
+ } catch (Exception e) {
+ conversation.resetOtrSession();
+ return null;
+ }
+ }
+
+ private static boolean clientMightSendHtml(Account account, Jid from) {
+ String resource = from.getResourcepart();
+ if (resource == null) {
+ return false;
+ }
+ Presence presence = account.getRoster().getContact(from).getPresences().getPresences().get(resource);
+ ServiceDiscoveryResult disco = presence == null ? null : presence.getServiceDiscoveryResult();
+ if (disco == null) {
+ return false;
+ }
+ return hasIdentityKnowForSendingHtml(disco.getIdentities());
+ }
+
+ private static boolean hasIdentityKnowForSendingHtml(List<ServiceDiscoveryResult.Identity> identities) {
+ for (ServiceDiscoveryResult.Identity identity : identities) {
+ if (identity.getName() != null) {
+ if (CLIENTS_SENDING_HTML_IN_OTR.contains(identity.getName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status) {
+ AxolotlService service = conversation.getAccount().getAxolotlService();
+ XmppAxolotlMessage xmppAxolotlMessage;
+ try {
+ xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid());
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": invalid omemo message received " + e.getMessage());
+ return null;
+ }
+ XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceivingPayloadMessage(xmppAxolotlMessage);
+ if (plaintextMessage != null) {
+ Message finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status);
+ finishedMessage.setFingerprint(plaintextMessage.getFingerprint());
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(finishedMessage.getConversation().getAccount()) + " Received Message with session fingerprint: " + plaintextMessage.getFingerprint());
+ return finishedMessage;
+ } else {
+ return null;
+ }
+ }
+
+ private class Invite {
+ Jid jid;
+ String password;
+
+ Invite(Jid jid, String password) {
+ this.jid = jid;
+ this.password = password;
+ }
+
+ public boolean execute(Account account) {
+ if (jid != null) {
+ Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true);
+ if (!conversation.getMucOptions().online()) {
+ conversation.getMucOptions().setPassword(password);
+ mXmppConnectionService.databaseBackend.updateConversation(conversation);
+ mXmppConnectionService.joinMuc(conversation);
+ mXmppConnectionService.updateConversationUi();
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private Invite extractInvite(Element message) {
+ Element x = message.findChild("x", "http://jabber.org/protocol/muc#user");
+ if (x != null) {
+ Element invite = x.findChild("invite");
+ if (invite != null) {
+ Element pw = x.findChild("password");
+ return new Invite(message.getAttributeAsJid("from"), pw != null ? pw.getContent() : null);
+ }
+ } else {
+ x = message.findChild("x", "jabber:x:conference");
+ if (x != null) {
+ return new Invite(x.getAttributeAsJid("jid"), x.getAttribute("password"));
+ }
+ }
+ return null;
+ }
+
+ private static String extractStanzaId(Element packet, Jid by) {
+ for (Element child : packet.getChildren()) {
+ if (child.getName().equals("stanza-id")
+ && Xmlns.STANZA_IDS.equals(child.getNamespace())
+ && by.equals(child.getAttributeAsJid("by"))) {
+ return child.getAttribute("id");
+ }
+ }
+ return null;
+ }
+
+ private void parseEvent(final Element event, final Jid from, final Account account) {
+ Element items = event.findChild("items");
+ String node = items == null ? null : items.getAttribute("node");
+ if ("urn:xmpp:avatar:metadata".equals(node)) {
+ Avatar avatar = Avatar.parseMetadata(items);
+ if (avatar != null) {
+ avatar.owner = from.toBareJid();
+ if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
+ if (account.getJid().toBareJid().equals(from)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ mXmppConnectionService.databaseBackend.updateAccount(account);
+ }
+ mXmppConnectionService.getAvatarService().clear(account);
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateAccountUi();
+ } else {
+ Contact contact = account.getRoster().getContact(from);
+ contact.setAvatar(avatar);
+ mXmppConnectionService.getAvatarService().clear(contact);
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateRosterUi();
+ }
+ } else if (mXmppConnectionService.isDataSaverDisabled()) {
+ mXmppConnectionService.fetchAvatar(account, avatar);
+ }
+ }
+ } else if ("http://jabber.org/protocol/nick".equals(node)) {
+ Element i = items.findChild("item");
+ Element nick = i == null ? null : i.findChild("nick", "http://jabber.org/protocol/nick");
+ if (nick != null && nick.getContent() != null) {
+ Contact contact = account.getRoster().getContact(from);
+ contact.setPresenceName(nick.getContent());
+ mXmppConnectionService.getAvatarService().clear(account);
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateAccountUi();
+ }
+ } else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received PEP device list update from " + from + ", processing...");
+ Element item = items.findChild("item");
+ Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
+ AxolotlService axolotlService = account.getAxolotlService();
+ axolotlService.registerDevices(from, deviceIds);
+ mXmppConnectionService.updateAccountUi();
+ }
+ }
+
+ private boolean handleErrorMessage(Account account, MessagePacket packet) {
+ if (packet.getType() == MessagePacket.TYPE_ERROR) {
+ Jid from = packet.getFrom();
+ if (from != null) {
+ Message message = mXmppConnectionService.markMessage(account,
+ from.toBareJid(),
+ packet.getId(),
Message.STATUS_SEND_FAILED,
extractErrorMessage(packet));
if (message != null) {
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
message.getConversation().endOtrIfNeeded();
}
- }
- }
- return true;
- }
- return false;
- }
-
- @Override
- public void onMessagePacketReceived(Account account, MessagePacket original) {
- if (handleErrorMessage(account, original)) {
- return;
- }
- final MessagePacket packet;
- Long timestamp = null;
- final boolean isForwarded;
- boolean isCarbon = false;
- String serverMsgId = null;
- final Element fin = original.findChild("fin", "urn:xmpp:mam:0");
- if (fin != null) {
- mXmppConnectionService.getMessageArchiveService().processFin(fin,original.getFrom());
- return;
- }
- final Element result = original.findChild("result","urn:xmpp:mam:0");
- final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid"));
- if (query != null && query.validFrom(original.getFrom())) {
- Pair<MessagePacket, Long> f = original.getForwardedMessagePacket("result", "urn:xmpp:mam:0");
- if (f == null) {
- return;
- }
- timestamp = f.second;
- packet = f.first;
- isForwarded = true;
- serverMsgId = result.getAttribute("id");
- query.incrementMessageCount();
- } else if (query != null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender");
- return;
- } else if (original.fromServer(account)) {
- Pair<MessagePacket, Long> f;
- f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2");
- f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f;
- packet = f != null ? f.first : original;
- if (handleErrorMessage(account, packet)) {
- return;
- }
- timestamp = f != null ? f.second : null;
- isCarbon = f != null;
- isForwarded = isCarbon;
- } else {
- packet = original;
- isForwarded = false;
- }
-
- if (timestamp == null) {
- timestamp = AbstractParser.parseTimestamp(original,AbstractParser.parseTimestamp(packet));
- }
- final String body = packet.getBody();
- final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user");
- final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted");
- final Element replaceElement = packet.findChild("replace", "urn:xmpp:message-correct:0");
- final Element oob = packet.findChild("x", "jabber:x:oob");
- final boolean isOob = oob!= null && body != null && body.equals(oob.findChildContent("url"));
- final String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id");
- final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX);
- int status;
- final Jid counterpart;
- final Jid to = packet.getTo();
- final Jid from = packet.getFrom();
- final String remoteMsgId = packet.getId();
- boolean notify = false;
-
- if (from == null) {
- Log.d(Config.LOGTAG,"no from in: "+packet.toString());
- return;
- }
-
- boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT;
- boolean isProperlyAddressed = (to != null ) && (!to.isBareJid() || account.countPresences() == 0);
- boolean isMucStatusMessage = from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status");
- if (packet.fromAccount(account)) {
- status = Message.STATUS_SEND;
- counterpart = to != null ? to : account.getJid();
- } else {
- status = Message.STATUS_RECEIVED;
- counterpart = from;
- }
-
- Invite invite = extractInvite(packet);
- if (invite != null && invite.execute(account)) {
- return;
- }
-
- if (!isTypeGroupChat
- && query == null
- && extractChatState(mXmppConnectionService.find(account, counterpart.toBareJid()), packet)) {
- mXmppConnectionService.updateConversationUi();
- }
-
- if ((body != null || pgpEncrypted != null || axolotlEncrypted != null) && !isMucStatusMessage) {
- Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat, query);
- final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI;
- if (isTypeGroupChat) {
- if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) {
- status = Message.STATUS_SEND_RECEIVED;
- isCarbon = true; //not really carbon but received from another resource
- if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status)) {
- return;
- } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) {
- Message message = conversation.findSentMessageWithBody(packet.getBody());
- if (message != null) {
- mXmppConnectionService.markMessage(message, status);
- return;
- }
- }
- } else {
- status = Message.STATUS_RECEIVED;
- }
- }
- final Message message;
- if (body != null && body.startsWith("?OTR") && Config.supportOtr()) {
- if (!isForwarded && !isTypeGroupChat && isProperlyAddressed && !conversationMultiMode) {
- message = parseOtrChat(body, from, remoteMsgId, conversation);
- if (message == null) {
- return;
- }
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": ignoring OTR message from "+from+" isForwarded="+Boolean.toString(isForwarded)+", isProperlyAddressed="+Boolean.valueOf(isProperlyAddressed));
- message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
- }
- } else if (pgpEncrypted != null && Config.supportOpenPgp()) {
- message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
- } else if (axolotlEncrypted != null && Config.supportOmemo()) {
- Jid origin;
- if (conversationMultiMode) {
- final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
- origin = getTrueCounterpart(query != null ? mucUserElement : null, fallback);
- if (origin == null) {
- Log.d(Config.LOGTAG,"axolotl message in non anonymous conference received");
- return;
- }
- } else {
- origin = from;
- }
- message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status);
- if (message == null) {
- return;
- }
- if (conversationMultiMode) {
- message.setTrueCounterpart(origin);
- }
- } else {
- message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
- }
-
- if (serverMsgId == null) {
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onMessagePacketReceived(Account account, MessagePacket original) {
+ if (handleErrorMessage(account, original)) {
+ return;
+ }
+ final MessagePacket packet;
+ Long timestamp = null;
+ final boolean isForwarded;
+ boolean isCarbon = false;
+ String serverMsgId = null;
+ final Element fin = original.findChild("fin", "urn:xmpp:mam:0");
+ if (fin != null) {
+ mXmppConnectionService.getMessageArchiveService().processFin(fin, original.getFrom());
+ return;
+ }
+ final Element result = original.findChild("result", "urn:xmpp:mam:0");
+ final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid"));
+ if (query != null && query.validFrom(original.getFrom())) {
+ Pair<MessagePacket, Long> f = original.getForwardedMessagePacket("result", "urn:xmpp:mam:0");
+ if (f == null) {
+ return;
+ }
+ timestamp = f.second;
+ packet = f.first;
+ isForwarded = true;
+ serverMsgId = result.getAttribute("id");
+ query.incrementMessageCount();
+ } else if (query != null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": received mam result from invalid sender");
+ return;
+ } else if (original.fromServer(account)) {
+ Pair<MessagePacket, Long> f;
+ f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2");
+ f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f;
+ packet = f != null ? f.first : original;
+ if (handleErrorMessage(account, packet)) {
+ return;
+ }
+ timestamp = f != null ? f.second : null;
+ isCarbon = f != null;
+ isForwarded = isCarbon;
+ } else {
+ packet = original;
+ isForwarded = false;
+ }
+
+ if (timestamp == null) {
+ timestamp = AbstractParser.parseTimestamp(original, AbstractParser.parseTimestamp(packet));
+ }
+ final String body = packet.getBody();
+ final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user");
+ final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted");
+ final Element replaceElement = packet.findChild("replace", "urn:xmpp:message-correct:0");
+ final Element oob = packet.findChild("x", "jabber:x:oob");
+ final boolean isOob = oob != null && body != null && body.equals(oob.findChildContent("url"));
+ final String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id");
+ final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX);
+ int status;
+ final Jid counterpart;
+ final Jid to = packet.getTo();
+ final Jid from = packet.getFrom();
+ final String remoteMsgId = packet.getId();
+ boolean notify = false;
+
+ if (from == null) {
+ Log.d(Config.LOGTAG, "no from in: " + packet.toString());
+ return;
+ }
+
+ boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT;
+ boolean isProperlyAddressed = (to != null) && (!to.isBareJid() || account.countPresences() == 0);
+ boolean isMucStatusMessage = from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status");
+ if (packet.fromAccount(account)) {
+ status = Message.STATUS_SEND;
+ counterpart = to != null ? to : account.getJid();
+ } else {
+ status = Message.STATUS_RECEIVED;
+ counterpart = from;
+ }
+
+ Invite invite = extractInvite(packet);
+ if (invite != null && invite.execute(account)) {
+ return;
+ }
+
+ if (!isTypeGroupChat
+ && query == null
+ && extractChatState(mXmppConnectionService.find(account, counterpart.toBareJid()), packet)) {
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ if ((body != null || pgpEncrypted != null || axolotlEncrypted != null) && !isMucStatusMessage) {
+ Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat, query);
+ final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI;
+ if (isTypeGroupChat) {
+ if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) {
+ status = Message.STATUS_SEND_RECEIVED;
+ isCarbon = true; //not really carbon but received from another resource
+ if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status)) {
+ return;
+ } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) {
+ Message message = conversation.findSentMessageWithBody(packet.getBody());
+ if (message != null) {
+ mXmppConnectionService.markMessage(message, status);
+ return;
+ }
+ }
+ } else {
+ status = Message.STATUS_RECEIVED;
+ }
+ }
+ final Message message;
+ if (body != null && body.startsWith("?OTR") && Config.supportOtr()) {
+ if (!isForwarded && !isTypeGroupChat && isProperlyAddressed && !conversationMultiMode) {
+ message = parseOtrChat(body, from, remoteMsgId, conversation);
+ if (message == null) {
+ return;
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ignoring OTR message from " + from + " isForwarded=" + Boolean.toString(isForwarded) + ", isProperlyAddressed=" + Boolean.valueOf(isProperlyAddressed));
+ message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
+ }
+ } else if (pgpEncrypted != null && Config.supportOpenPgp()) {
+ message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
+ } else if (axolotlEncrypted != null && Config.supportOmemo()) {
+ Jid origin;
+ if (conversationMultiMode) {
+ final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
+ origin = getTrueCounterpart(query != null ? mucUserElement : null, fallback);
+ if (origin == null) {
+ Log.d(Config.LOGTAG, "axolotl message in non anonymous conference received");
+ return;
+ }
+ } else {
+ origin = from;
+ }
+ message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status);
+ if (message == null) {
+ return;
+ }
+ if (conversationMultiMode) {
+ message.setTrueCounterpart(origin);
+ }
+ } else {
+ message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
+ }
+
+ if (serverMsgId == null) {
final Jid by;
final boolean safeToExtract;
if (isTypeGroupChat) {
@@ -443,154 +444,154 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (safeToExtract) {
serverMsgId = extractStanzaId(packet, by);
}
- }
-
- message.setCounterpart(counterpart);
- message.setRemoteMsgId(remoteMsgId);
- message.setServerMsgId(serverMsgId);
- message.setCarbon(isCarbon);
- message.setTime(timestamp);
- message.setOob(isOob);
- message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0");
- if (conversationMultiMode) {
- final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
- Jid trueCounterpart;
- if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
- trueCounterpart = message.getTrueCounterpart();
- } else if (Config.PARSE_REAL_JID_FROM_MUC_MAM) {
- trueCounterpart = getTrueCounterpart(query != null ? mucUserElement : null, fallback);
- } else {
- trueCounterpart = fallback;
- }
- if (trueCounterpart != null && trueCounterpart.toBareJid().equals(account.getJid().toBareJid())) {
- status = isTypeGroupChat ? Message.STATUS_SEND_RECEIVED : Message.STATUS_SEND;
- }
- message.setStatus(status);
- message.setTrueCounterpart(trueCounterpart);
- if (!isTypeGroupChat) {
- message.setType(Message.TYPE_PRIVATE);
- }
- } else {
- updateLastseen(account, from);
- }
-
- if (replacementId != null && mXmppConnectionService.allowMessageCorrection()) {
- Message replacedMessage = conversation.findMessageWithRemoteIdAndCounterpart(replacementId,
- counterpart,
- message.getStatus() == Message.STATUS_RECEIVED,
- message.isCarbon());
- if (replacedMessage != null) {
- final boolean fingerprintsMatch = replacedMessage.getFingerprint() == null
- || replacedMessage.getFingerprint().equals(message.getFingerprint());
- final boolean trueCountersMatch = replacedMessage.getTrueCounterpart() != null
- && replacedMessage.getTrueCounterpart().equals(message.getTrueCounterpart());
- if (fingerprintsMatch && (trueCountersMatch || !conversationMultiMode)) {
- Log.d(Config.LOGTAG, "replaced message '" + replacedMessage.getBody() + "' with '" + message.getBody() + "'");
- synchronized (replacedMessage) {
- final String uuid = replacedMessage.getUuid();
- replacedMessage.setUuid(UUID.randomUUID().toString());
- replacedMessage.setBody(message.getBody());
- replacedMessage.setEdited(replacedMessage.getRemoteMsgId());
- replacedMessage.setRemoteMsgId(remoteMsgId);
- replacedMessage.setEncryption(message.getEncryption());
- if (replacedMessage.getStatus() == Message.STATUS_RECEIVED) {
- replacedMessage.markUnread();
- }
- mXmppConnectionService.updateMessage(replacedMessage, uuid);
- mXmppConnectionService.getNotificationService().updateNotification(false);
- if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) {
- sendMessageReceipts(account, packet);
- }
- if (replacedMessage.getEncryption() == Message.ENCRYPTION_PGP) {
- conversation.getAccount().getPgpDecryptionService().discard(replacedMessage);
- conversation.getAccount().getPgpDecryptionService().decrypt(replacedMessage, false);
- }
- }
- return;
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received message correction but verification didn't check out");
- }
- }
- }
-
- boolean checkForDuplicates = query != null
- || (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay"))
- || message.getType() == Message.TYPE_PRIVATE;
- if (checkForDuplicates && conversation.hasDuplicateMessage(message)) {
- Log.d(Config.LOGTAG,"skipping duplicate message from "+message.getCounterpart().toString()+" "+message.getBody());
- return;
- }
-
- if (query != null && query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
- conversation.prepend(message);
- } else {
- conversation.add(message);
- }
-
- if (query == null || query.getWith() == null) { //either no mam or catchup
- if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) {
- mXmppConnectionService.markRead(conversation);
- if (query == null) {
- activateGracePeriod(account);
- }
- } else {
- message.markUnread();
- notify = true;
- }
- }
-
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- notify = conversation.getAccount().getPgpDecryptionService().decrypt(message, notify);
- }
-
- if (query == null) {
- mXmppConnectionService.updateConversationUi();
- }
+ }
+
+ message.setCounterpart(counterpart);
+ message.setRemoteMsgId(remoteMsgId);
+ message.setServerMsgId(serverMsgId);
+ message.setCarbon(isCarbon);
+ message.setTime(timestamp);
+ message.setOob(isOob);
+ message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0");
+ if (conversationMultiMode) {
+ final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
+ Jid trueCounterpart;
+ if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ trueCounterpart = message.getTrueCounterpart();
+ } else if (Config.PARSE_REAL_JID_FROM_MUC_MAM) {
+ trueCounterpart = getTrueCounterpart(query != null ? mucUserElement : null, fallback);
+ } else {
+ trueCounterpart = fallback;
+ }
+ if (trueCounterpart != null && trueCounterpart.toBareJid().equals(account.getJid().toBareJid())) {
+ status = isTypeGroupChat ? Message.STATUS_SEND_RECEIVED : Message.STATUS_SEND;
+ }
+ message.setStatus(status);
+ message.setTrueCounterpart(trueCounterpart);
+ if (!isTypeGroupChat) {
+ message.setType(Message.TYPE_PRIVATE);
+ }
+ } else {
+ updateLastseen(account, from);
+ }
+
+ if (replacementId != null && mXmppConnectionService.allowMessageCorrection()) {
+ Message replacedMessage = conversation.findMessageWithRemoteIdAndCounterpart(replacementId,
+ counterpart,
+ message.getStatus() == Message.STATUS_RECEIVED,
+ message.isCarbon());
+ if (replacedMessage != null) {
+ final boolean fingerprintsMatch = replacedMessage.getFingerprint() == null
+ || replacedMessage.getFingerprint().equals(message.getFingerprint());
+ final boolean trueCountersMatch = replacedMessage.getTrueCounterpart() != null
+ && replacedMessage.getTrueCounterpart().equals(message.getTrueCounterpart());
+ if (fingerprintsMatch && (trueCountersMatch || !conversationMultiMode)) {
+ Log.d(Config.LOGTAG, "replaced message '" + replacedMessage.getBody() + "' with '" + message.getBody() + "'");
+ synchronized (replacedMessage) {
+ final String uuid = replacedMessage.getUuid();
+ replacedMessage.setUuid(UUID.randomUUID().toString());
+ replacedMessage.setBody(message.getBody());
+ replacedMessage.setEdited(replacedMessage.getRemoteMsgId());
+ replacedMessage.setRemoteMsgId(remoteMsgId);
+ replacedMessage.setEncryption(message.getEncryption());
+ if (replacedMessage.getStatus() == Message.STATUS_RECEIVED) {
+ replacedMessage.markUnread();
+ }
+ mXmppConnectionService.updateMessage(replacedMessage, uuid);
+ mXmppConnectionService.getNotificationService().updateNotification(false);
+ if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) {
+ sendMessageReceipts(account, packet);
+ }
+ if (replacedMessage.getEncryption() == Message.ENCRYPTION_PGP) {
+ conversation.getAccount().getPgpDecryptionService().discard(replacedMessage);
+ conversation.getAccount().getPgpDecryptionService().decrypt(replacedMessage, false);
+ }
+ }
+ return;
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": received message correction but verification didn't check out");
+ }
+ }
+ }
+
+ boolean checkForDuplicates = query != null
+ || (isTypeGroupChat && packet.hasChild("delay", "urn:xmpp:delay"))
+ || message.getType() == Message.TYPE_PRIVATE;
+ if (checkForDuplicates && conversation.hasDuplicateMessage(message)) {
+ Log.d(Config.LOGTAG, "skipping duplicate message from " + message.getCounterpart().toString() + " " + message.getBody());
+ return;
+ }
+
+ if (query != null && query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
+ conversation.prepend(message);
+ } else {
+ conversation.add(message);
+ }
+
+ if (query == null || query.getWith() == null) { //either no mam or catchup
+ if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) {
+ mXmppConnectionService.markRead(conversation);
+ if (query == null) {
+ activateGracePeriod(account);
+ }
+ } else {
+ message.markUnread();
+ notify = true;
+ }
+ }
+
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ notify = conversation.getAccount().getPgpDecryptionService().decrypt(message, notify);
+ }
+
+ if (query == null) {
+ mXmppConnectionService.updateConversationUi();
+ }
if (mXmppConnectionService.confirmMessages() && message.trusted() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) {
- sendMessageReceipts(account, packet);
- }
-
- if (message.getStatus() == Message.STATUS_RECEIVED
- && conversation.getOtrSession() != null
- && !conversation.getOtrSession().getSessionID().getUserID()
- .equals(message.getCounterpart().getResourcepart())) {
- conversation.endOtrIfNeeded();
- }
-
- if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) {
- mXmppConnectionService.databaseBackend.createMessage(message);
- }
- final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager();
- if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER && manager.getAutoAcceptFileSize() > 0) {
- manager.createNewDownloadConnection(message);
- } else if (notify) {
- if (query == null) {
- mXmppConnectionService.getNotificationService().push(message);
- } else if (query.getWith() == null) { // mam catchup
- mXmppConnectionService.getNotificationService().pushFromBacklog(message);
- }
- }
- } else if (!packet.hasChild("body")){ //no body
- Conversation conversation = mXmppConnectionService.find(account, from.toBareJid());
- if (isTypeGroupChat) {
- if (packet.hasChild("subject")) {
- if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
- conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0);
- String subject = packet.findChildContent("subject");
- conversation.getMucOptions().setSubject(subject);
- final Bookmark bookmark = conversation.getBookmark();
- if (bookmark != null && bookmark.getBookmarkName() == null) {
- if (bookmark.setBookmarkName(subject)) {
- mXmppConnectionService.pushBookmarks(account);
- }
- }
- mXmppConnectionService.updateConversationUi();
- return;
- }
- }
- }
- if (conversation != null && mucUserElement != null && from.isBareJid()) {
+ sendMessageReceipts(account, packet);
+ }
+
+ if (message.getStatus() == Message.STATUS_RECEIVED
+ && conversation.getOtrSession() != null
+ && !conversation.getOtrSession().getSessionID().getUserID()
+ .equals(message.getCounterpart().getResourcepart())) {
+ conversation.endOtrIfNeeded();
+ }
+
+ if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) {
+ mXmppConnectionService.databaseBackend.createMessage(message);
+ }
+ final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager();
+ if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER && manager.getAutoAcceptFileSize() > 0) {
+ manager.createNewDownloadConnection(message);
+ } else if (notify) {
+ if (query == null) {
+ mXmppConnectionService.getNotificationService().push(message);
+ } else if (query.getWith() == null) { // mam catchup
+ mXmppConnectionService.getNotificationService().pushFromBacklog(message);
+ }
+ }
+ } else if (!packet.hasChild("body")) { //no body
+ Conversation conversation = mXmppConnectionService.find(account, from.toBareJid());
+ if (isTypeGroupChat) {
+ if (packet.hasChild("subject")) {
+ if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
+ conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0);
+ String subject = packet.findChildContent("subject");
+ conversation.getMucOptions().setSubject(subject);
+ final Bookmark bookmark = conversation.getBookmark();
+ if (bookmark != null && bookmark.getBookmarkName() == null) {
+ if (bookmark.setBookmarkName(subject)) {
+ mXmppConnectionService.pushBookmarks(account);
+ }
+ }
+ mXmppConnectionService.updateConversationUi();
+ return;
+ }
+ }
+ }
+ if (conversation != null && mucUserElement != null && from.isBareJid()) {
for (Element child : mucUserElement.getChildren()) {
if ("status".equals(child.getName())) {
try {
@@ -598,92 +599,91 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if ((code >= 170 && code <= 174) || (code >= 102 && code <= 104)) {
mXmppConnectionService.fetchConferenceConfiguration(conversation);
break;
- }
+ }
} catch (Exception e) {
//ignored
}
} else if ("item".equals(child.getName())) {
- MucOptions.User user = AbstractParser.parseItem(conversation,child);
- Log.d(Config.LOGTAG,account.getJid()+": changing affiliation for " + user.getRealJid()+" to " + user.getAffiliation() + " in " + conversation.getJid().toBareJid());
+ MucOptions.User user = AbstractParser.parseItem(conversation, child);
+ Log.d(Config.LOGTAG, account.getJid() + ": changing affiliation for " + user.getRealJid() + " to " + user.getAffiliation() + " in " + conversation.getJid().toBareJid());
if (!user.realJidMatchesAccount()) {
conversation.getMucOptions().updateUser(user);
mXmppConnectionService.getAvatarService().clear(conversation);
mXmppConnectionService.updateMucRosterUi();
mXmppConnectionService.updateConversationUi();
- }
- }
- }
- }
- }
-
-
-
- Element received = packet.findChild("received", "urn:xmpp:chat-markers:0");
- if (received == null) {
- received = packet.findChild("received", "urn:xmpp:receipts");
- }
- if (received != null && !packet.fromAccount(account)) {
- mXmppConnectionService.markMessage(account, from.toBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED);
- }
- Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0");
- if (displayed != null) {
- if (packet.fromAccount(account)) {
- Conversation conversation = mXmppConnectionService.find(account,counterpart.toBareJid());
- if (conversation != null) {
- mXmppConnectionService.markRead(conversation);
- }
- } else {
- final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("id"), Message.STATUS_SEND_DISPLAYED);
- Message message = displayedMessage == null ? null : displayedMessage.prev();
- while (message != null
- && message.getStatus() == Message.STATUS_SEND_RECEIVED
- && message.getTimeSent() < displayedMessage.getTimeSent()) {
- mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED);
- message = message.prev();
- }
- }
- }
-
- Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event");
- if (event != null) {
- parseEvent(event, from, account);
- }
-
- String nick = packet.findChildContent("nick", "http://jabber.org/protocol/nick");
- if (nick != null) {
- Contact contact = account.getRoster().getContact(from);
- contact.setPresenceName(nick);
- }
- }
-
- private static Jid getTrueCounterpart(Element mucUserElement, Jid fallback) {
- final Element item = mucUserElement == null ? null : mucUserElement.findChild("item");
- Jid result = item == null ? null : item.getAttributeAsJid("jid");
- return result != null ? result : fallback;
- }
-
- private void sendMessageReceipts(Account account, MessagePacket packet) {
- ArrayList<String> receiptsNamespaces = new ArrayList<>();
- if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
- receiptsNamespaces.add("urn:xmpp:chat-markers:0");
- }
- if (packet.hasChild("request", "urn:xmpp:receipts")) {
- receiptsNamespaces.add("urn:xmpp:receipts");
- }
- if (receiptsNamespaces.size() > 0) {
- MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account,
- packet,
- receiptsNamespaces,
- packet.getType());
- mXmppConnectionService.sendMessagePacket(account, receipt);
- }
- }
-
- private static SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss");
-
- private void activateGracePeriod(Account account) {
- long duration = mXmppConnectionService.getPreferences().getLong("race_period_length", 144) * 1000;
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": activating grace period till "+TIME_FORMAT.format(new Date(System.currentTimeMillis() + duration)));
- account.activateGracePeriod(duration);
- }
+ }
+ }
+ }
+ }
+ }
+
+
+ Element received = packet.findChild("received", "urn:xmpp:chat-markers:0");
+ if (received == null) {
+ received = packet.findChild("received", "urn:xmpp:receipts");
+ }
+ if (received != null && !packet.fromAccount(account)) {
+ mXmppConnectionService.markMessage(account, from.toBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED);
+ }
+ Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0");
+ if (displayed != null) {
+ if (packet.fromAccount(account)) {
+ Conversation conversation = mXmppConnectionService.find(account, counterpart.toBareJid());
+ if (conversation != null) {
+ mXmppConnectionService.markRead(conversation);
+ }
+ } else {
+ final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("id"), Message.STATUS_SEND_DISPLAYED);
+ Message message = displayedMessage == null ? null : displayedMessage.prev();
+ while (message != null
+ && message.getStatus() == Message.STATUS_SEND_RECEIVED
+ && message.getTimeSent() < displayedMessage.getTimeSent()) {
+ mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED);
+ message = message.prev();
+ }
+ }
+ }
+
+ Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event");
+ if (event != null) {
+ parseEvent(event, from, account);
+ }
+
+ String nick = packet.findChildContent("nick", "http://jabber.org/protocol/nick");
+ if (nick != null) {
+ Contact contact = account.getRoster().getContact(from);
+ contact.setPresenceName(nick);
+ }
+ }
+
+ private static Jid getTrueCounterpart(Element mucUserElement, Jid fallback) {
+ final Element item = mucUserElement == null ? null : mucUserElement.findChild("item");
+ Jid result = item == null ? null : item.getAttributeAsJid("jid");
+ return result != null ? result : fallback;
+ }
+
+ private void sendMessageReceipts(Account account, MessagePacket packet) {
+ ArrayList<String> receiptsNamespaces = new ArrayList<>();
+ if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
+ receiptsNamespaces.add("urn:xmpp:chat-markers:0");
+ }
+ if (packet.hasChild("request", "urn:xmpp:receipts")) {
+ receiptsNamespaces.add("urn:xmpp:receipts");
+ }
+ if (receiptsNamespaces.size() > 0) {
+ MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account,
+ packet,
+ receiptsNamespaces,
+ packet.getType());
+ mXmppConnectionService.sendMessagePacket(account, receipt);
+ }
+ }
+
+ private static SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss");
+
+ private void activateGracePeriod(Account account) {
+ long duration = mXmppConnectionService.getPreferences().getLong("race_period_length", 144) * 1000;
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": activating grace period till " + TIME_FORMAT.format(new Date(System.currentTimeMillis() + duration)));
+ account.activateGracePeriod(duration);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/parser/PresenceParser.java b/src/main/java/de/pixart/messenger/parser/PresenceParser.java
index 50f13150b..9ba45b201 100644
--- a/src/main/java/de/pixart/messenger/parser/PresenceParser.java
+++ b/src/main/java/de/pixart/messenger/parser/PresenceParser.java
@@ -24,170 +24,170 @@ import de.pixart.messenger.xmpp.pep.Avatar;
import de.pixart.messenger.xmpp.stanzas.PresencePacket;
public class PresenceParser extends AbstractParser implements
- OnPresencePacketReceived {
+ OnPresencePacketReceived {
- public PresenceParser(XmppConnectionService service) {
- super(service);
- }
+ public PresenceParser(XmppConnectionService service) {
+ super(service);
+ }
- public void parseConferencePresence(PresencePacket packet, Account account) {
- final Conversation conversation = packet.getFrom() == null ? null : mXmppConnectionService.find(account, packet.getFrom().toBareJid());
- if (conversation != null) {
- final MucOptions mucOptions = conversation.getMucOptions();
- boolean before = mucOptions.online();
- int count = mucOptions.getUserCount();
- final List<MucOptions.User> tileUserBefore = mucOptions.getUsers(5);
- processConferencePresence(packet, conversation);
- final List<MucOptions.User> tileUserAfter = mucOptions.getUsers(5);
- if (!tileUserAfter.equals(tileUserBefore)) {
- mXmppConnectionService.getAvatarService().clear(mucOptions);
- }
- if (before != mucOptions.online() || (mucOptions.online() && count != mucOptions.getUserCount())) {
- mXmppConnectionService.updateConversationUi();
- } else if (mucOptions.online()) {
- mXmppConnectionService.updateMucRosterUi();
- }
- }
- }
+ public void parseConferencePresence(PresencePacket packet, Account account) {
+ final Conversation conversation = packet.getFrom() == null ? null : mXmppConnectionService.find(account, packet.getFrom().toBareJid());
+ if (conversation != null) {
+ final MucOptions mucOptions = conversation.getMucOptions();
+ boolean before = mucOptions.online();
+ int count = mucOptions.getUserCount();
+ final List<MucOptions.User> tileUserBefore = mucOptions.getUsers(5);
+ processConferencePresence(packet, conversation);
+ final List<MucOptions.User> tileUserAfter = mucOptions.getUsers(5);
+ if (!tileUserAfter.equals(tileUserBefore)) {
+ mXmppConnectionService.getAvatarService().clear(mucOptions);
+ }
+ if (before != mucOptions.online() || (mucOptions.online() && count != mucOptions.getUserCount())) {
+ mXmppConnectionService.updateConversationUi();
+ } else if (mucOptions.online()) {
+ mXmppConnectionService.updateMucRosterUi();
+ }
+ }
+ }
private void processConferencePresence(PresencePacket packet, Conversation conversation) {
MucOptions mucOptions = conversation.getMucOptions();
- final Jid from = packet.getFrom();
- if (!from.isBareJid()) {
- final String type = packet.getAttribute("type");
- final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user");
- Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
- final List<String> codes = getStatusCodes(x);
- if (type == null) {
- if (x != null) {
- Element item = x.findChild("item");
- if (item != null && !from.isBareJid()) {
- mucOptions.setError(MucOptions.Error.NONE);
+ final Jid from = packet.getFrom();
+ if (!from.isBareJid()) {
+ final String type = packet.getAttribute("type");
+ final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user");
+ Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
+ final List<String> codes = getStatusCodes(x);
+ if (type == null) {
+ if (x != null) {
+ Element item = x.findChild("item");
+ if (item != null && !from.isBareJid()) {
+ mucOptions.setError(MucOptions.Error.NONE);
MucOptions.User user = parseItem(conversation, item, from);
- if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) {
- mucOptions.setOnline();
- mucOptions.setSelf(user);
- if (mucOptions.mNickChangingInProgress) {
- if (mucOptions.onRenameListener != null) {
- mucOptions.onRenameListener.onSuccess();
- }
- mucOptions.mNickChangingInProgress = false;
- }
- } else {
- mucOptions.updateUser(user);
- }
- if (codes.contains(MucOptions.STATUS_CODE_ROOM_CREATED) && mucOptions.autoPushConfiguration()) {
- Log.d(Config.LOGTAG,mucOptions.getAccount().getJid().toBareJid()
- +": room '"
- +mucOptions.getConversation().getJid().toBareJid()
- +"' created. pushing default configuration");
- mXmppConnectionService.pushConferenceConfiguration(mucOptions.getConversation(),
- IqGenerator.defaultRoomConfiguration(),
- null);
- }
- if (mXmppConnectionService.getPgpEngine() != null) {
- Element signed = packet.findChild("x", "jabber:x:signed");
- if (signed != null) {
- Element status = packet.findChild("status");
- String msg = status == null ? "" : status.getContent();
- long keyId = mXmppConnectionService.getPgpEngine().fetchKeyId(mucOptions.getAccount(), msg, signed.getContent());
- if (keyId != 0) {
- user.setPgpKeyId(keyId);
- }
- }
- }
- if (avatar != null) {
- avatar.owner = from;
- if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
- if (user.setAvatar(avatar)) {
- mXmppConnectionService.getAvatarService().clear(user);
- }
- } else if (mXmppConnectionService.isDataSaverDisabled()) {
- mXmppConnectionService.fetchAvatar(mucOptions.getAccount(), avatar);
- }
- }
- }
- }
- } else if (type.equals("unavailable")) {
- if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) ||
- packet.getFrom().equals(mucOptions.getConversation().getJid())) {
- if (codes.contains(MucOptions.STATUS_CODE_CHANGED_NICK)) {
- mucOptions.mNickChangingInProgress = true;
- } else if (codes.contains(MucOptions.STATUS_CODE_KICKED)) {
- mucOptions.setError(MucOptions.Error.KICKED);
- } else if (codes.contains(MucOptions.STATUS_CODE_BANNED)) {
- mucOptions.setError(MucOptions.Error.BANNED);
- } else if (codes.contains(MucOptions.STATUS_CODE_LOST_MEMBERSHIP)) {
- mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
- } else if (codes.contains(MucOptions.STATUS_CODE_AFFILIATION_CHANGE)) {
- mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
- } else if (codes.contains(MucOptions.STATUS_CODE_SHUTDOWN)) {
- mucOptions.setError(MucOptions.Error.SHUTDOWN);
- } else {
- mucOptions.setError(MucOptions.Error.UNKNOWN);
- Log.d(Config.LOGTAG, "unknown error in conference: " + packet);
- }
- } else if (!from.isBareJid()){
+ if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) {
+ mucOptions.setOnline();
+ mucOptions.setSelf(user);
+ if (mucOptions.mNickChangingInProgress) {
+ if (mucOptions.onRenameListener != null) {
+ mucOptions.onRenameListener.onSuccess();
+ }
+ mucOptions.mNickChangingInProgress = false;
+ }
+ } else {
+ mucOptions.updateUser(user);
+ }
+ if (codes.contains(MucOptions.STATUS_CODE_ROOM_CREATED) && mucOptions.autoPushConfiguration()) {
+ Log.d(Config.LOGTAG, mucOptions.getAccount().getJid().toBareJid()
+ + ": room '"
+ + mucOptions.getConversation().getJid().toBareJid()
+ + "' created. pushing default configuration");
+ mXmppConnectionService.pushConferenceConfiguration(mucOptions.getConversation(),
+ IqGenerator.defaultRoomConfiguration(),
+ null);
+ }
+ if (mXmppConnectionService.getPgpEngine() != null) {
+ Element signed = packet.findChild("x", "jabber:x:signed");
+ if (signed != null) {
+ Element status = packet.findChild("status");
+ String msg = status == null ? "" : status.getContent();
+ long keyId = mXmppConnectionService.getPgpEngine().fetchKeyId(mucOptions.getAccount(), msg, signed.getContent());
+ if (keyId != 0) {
+ user.setPgpKeyId(keyId);
+ }
+ }
+ }
+ if (avatar != null) {
+ avatar.owner = from;
+ if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
+ if (user.setAvatar(avatar)) {
+ mXmppConnectionService.getAvatarService().clear(user);
+ }
+ } else if (mXmppConnectionService.isDataSaverDisabled()) {
+ mXmppConnectionService.fetchAvatar(mucOptions.getAccount(), avatar);
+ }
+ }
+ }
+ }
+ } else if (type.equals("unavailable")) {
+ if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) ||
+ packet.getFrom().equals(mucOptions.getConversation().getJid())) {
+ if (codes.contains(MucOptions.STATUS_CODE_CHANGED_NICK)) {
+ mucOptions.mNickChangingInProgress = true;
+ } else if (codes.contains(MucOptions.STATUS_CODE_KICKED)) {
+ mucOptions.setError(MucOptions.Error.KICKED);
+ } else if (codes.contains(MucOptions.STATUS_CODE_BANNED)) {
+ mucOptions.setError(MucOptions.Error.BANNED);
+ } else if (codes.contains(MucOptions.STATUS_CODE_LOST_MEMBERSHIP)) {
+ mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
+ } else if (codes.contains(MucOptions.STATUS_CODE_AFFILIATION_CHANGE)) {
+ mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
+ } else if (codes.contains(MucOptions.STATUS_CODE_SHUTDOWN)) {
+ mucOptions.setError(MucOptions.Error.SHUTDOWN);
+ } else {
+ mucOptions.setError(MucOptions.Error.UNKNOWN);
+ Log.d(Config.LOGTAG, "unknown error in conference: " + packet);
+ }
+ } else if (!from.isBareJid()) {
Element item = x.findChild("item");
if (item != null) {
mucOptions.updateUser(parseItem(conversation, item, from));
}
- MucOptions.User user = mucOptions.deleteUser(from);
- if (user != null) {
- mXmppConnectionService.getAvatarService().clear(user);
- }
- }
- } else if (type.equals("error")) {
- Element error = packet.findChild("error");
- if (error != null && error.hasChild("conflict")) {
- if (mucOptions.online()) {
- if (mucOptions.onRenameListener != null) {
- mucOptions.onRenameListener.onFailure();
- }
- } else {
- mucOptions.setError(MucOptions.Error.NICK_IN_USE);
- }
- } else if (error != null && error.hasChild("not-authorized")) {
- mucOptions.setError(MucOptions.Error.PASSWORD_REQUIRED);
- } else if (error != null && error.hasChild("forbidden")) {
- mucOptions.setError(MucOptions.Error.BANNED);
- } else if (error != null && error.hasChild("registration-required")) {
- mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
- }
- }
- }
- }
+ MucOptions.User user = mucOptions.deleteUser(from);
+ if (user != null) {
+ mXmppConnectionService.getAvatarService().clear(user);
+ }
+ }
+ } else if (type.equals("error")) {
+ Element error = packet.findChild("error");
+ if (error != null && error.hasChild("conflict")) {
+ if (mucOptions.online()) {
+ if (mucOptions.onRenameListener != null) {
+ mucOptions.onRenameListener.onFailure();
+ }
+ } else {
+ mucOptions.setError(MucOptions.Error.NICK_IN_USE);
+ }
+ } else if (error != null && error.hasChild("not-authorized")) {
+ mucOptions.setError(MucOptions.Error.PASSWORD_REQUIRED);
+ } else if (error != null && error.hasChild("forbidden")) {
+ mucOptions.setError(MucOptions.Error.BANNED);
+ } else if (error != null && error.hasChild("registration-required")) {
+ mucOptions.setError(MucOptions.Error.MEMBERS_ONLY);
+ }
+ }
+ }
+ }
- private static List<String> getStatusCodes(Element x) {
- List<String> codes = new ArrayList<>();
- if (x != null) {
- for (Element child : x.getChildren()) {
- if (child.getName().equals("status")) {
- String code = child.getAttribute("code");
- if (code != null) {
- codes.add(code);
- }
- }
- }
- }
- return codes;
- }
+ private static List<String> getStatusCodes(Element x) {
+ List<String> codes = new ArrayList<>();
+ if (x != null) {
+ for (Element child : x.getChildren()) {
+ if (child.getName().equals("status")) {
+ String code = child.getAttribute("code");
+ if (code != null) {
+ codes.add(code);
+ }
+ }
+ }
+ }
+ return codes;
+ }
- public void parseContactPresence(final PresencePacket packet, final Account account) {
- final PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
- final Jid from = packet.getFrom();
- if (from == null || from.equals(account.getJid())) {
- return;
- }
- final String type = packet.getAttribute("type");
- final Contact contact = account.getRoster().getContact(from);
- if (type == null) {
- final String resource = from.isBareJid() ? "" : from.getResourcepart();
- contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick"));
- Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
+ public void parseContactPresence(final PresencePacket packet, final Account account) {
+ final PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
+ final Jid from = packet.getFrom();
+ if (from == null || from.equals(account.getJid())) {
+ return;
+ }
+ final String type = packet.getAttribute("type");
+ final Contact contact = account.getRoster().getContact(from);
+ if (type == null) {
+ final String resource = from.isBareJid() ? "" : from.getResourcepart();
+ contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick"));
+ Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
if (avatar != null && (!contact.isSelf() || account.getAvatar() == null)) {
avatar.owner = from.toBareJid();
- if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
+ if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
if (avatar.owner.equals(account.getJid().toBareJid())) {
account.setAvatar(avatar.getFilename());
mXmppConnectionService.databaseBackend.updateAccount(account);
@@ -197,86 +197,86 @@ public class PresenceParser extends AbstractParser implements
} else if (contact.setAvatar(avatar)) {
mXmppConnectionService.getAvatarService().clear(contact);
mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateRosterUi();
- }
- } else if (mXmppConnectionService.isDataSaverDisabled()){
- mXmppConnectionService.fetchAvatar(account, avatar);
- }
- }
- int sizeBefore = contact.getPresences().size();
+ mXmppConnectionService.updateRosterUi();
+ }
+ } else if (mXmppConnectionService.isDataSaverDisabled()) {
+ mXmppConnectionService.fetchAvatar(account, avatar);
+ }
+ }
+ int sizeBefore = contact.getPresences().size();
- final String show = packet.findChildContent("show");
- final Element caps = packet.findChild("c", "http://jabber.org/protocol/caps");
- final String message = packet.findChildContent("status");
- final Presence presence = Presence.parse(show, caps, message);
- contact.updatePresence(resource, presence);
- if (presence.hasCaps()) {
- mXmppConnectionService.fetchCaps(account, from, presence);
- }
+ final String show = packet.findChildContent("show");
+ final Element caps = packet.findChild("c", "http://jabber.org/protocol/caps");
+ final String message = packet.findChildContent("status");
+ final Presence presence = Presence.parse(show, caps, message);
+ contact.updatePresence(resource, presence);
+ if (presence.hasCaps()) {
+ mXmppConnectionService.fetchCaps(account, from, presence);
+ }
- final Element idle = packet.findChild("idle","urn:xmpp:idle:1");
- if (idle != null) {
- contact.flagInactive();
- String since = idle.getAttribute("since");
- try {
- contact.setLastseen(AbstractParser.parseTimestamp(since));
- } catch (NullPointerException | ParseException e) {
- contact.setLastseen(System.currentTimeMillis());
- }
- } else {
- contact.flagActive();
- contact.setLastseen(AbstractParser.parseTimestamp(packet));
- }
+ final Element idle = packet.findChild("idle", "urn:xmpp:idle:1");
+ if (idle != null) {
+ contact.flagInactive();
+ String since = idle.getAttribute("since");
+ try {
+ contact.setLastseen(AbstractParser.parseTimestamp(since));
+ } catch (NullPointerException | ParseException e) {
+ contact.setLastseen(System.currentTimeMillis());
+ }
+ } else {
+ contact.flagActive();
+ contact.setLastseen(AbstractParser.parseTimestamp(packet));
+ }
- PgpEngine pgp = mXmppConnectionService.getPgpEngine();
- Element x = packet.findChild("x", "jabber:x:signed");
- if (pgp != null && x != null) {
- Element status = packet.findChild("status");
- String msg = status != null ? status.getContent() : "";
- contact.setPgpKeyId(pgp.fetchKeyId(account, msg, x.getContent()));
- }
- boolean online = sizeBefore < contact.getPresences().size();
- mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, online);
- } else if (type.equals("unavailable")) {
- if (from.isBareJid()) {
- contact.clearPresences();
- } else {
- contact.removePresence(from.getResourcepart());
- }
- mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, false);
- } else if (type.equals("subscribe")) {
- if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
- mXmppConnectionService.sendPresencePacket(account,
- mPresenceGenerator.sendPresenceUpdatesTo(contact));
- } else {
- contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
- final Conversation conversation = mXmppConnectionService.findOrCreateConversation(
- account, contact.getJid().toBareJid(), false);
- final String statusMessage = packet.findChildContent("status");
- if (statusMessage != null
- && !statusMessage.isEmpty()
- && conversation.countMessages() == 0) {
- conversation.add(new Message(
- conversation,
- statusMessage,
- Message.ENCRYPTION_NONE,
- Message.STATUS_RECEIVED
- ));
- }
- }
- }
- mXmppConnectionService.updateRosterUi();
- }
+ PgpEngine pgp = mXmppConnectionService.getPgpEngine();
+ Element x = packet.findChild("x", "jabber:x:signed");
+ if (pgp != null && x != null) {
+ Element status = packet.findChild("status");
+ String msg = status != null ? status.getContent() : "";
+ contact.setPgpKeyId(pgp.fetchKeyId(account, msg, x.getContent()));
+ }
+ boolean online = sizeBefore < contact.getPresences().size();
+ mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, online);
+ } else if (type.equals("unavailable")) {
+ if (from.isBareJid()) {
+ contact.clearPresences();
+ } else {
+ contact.removePresence(from.getResourcepart());
+ }
+ mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, false);
+ } else if (type.equals("subscribe")) {
+ if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
+ mXmppConnectionService.sendPresencePacket(account,
+ mPresenceGenerator.sendPresenceUpdatesTo(contact));
+ } else {
+ contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
+ final Conversation conversation = mXmppConnectionService.findOrCreateConversation(
+ account, contact.getJid().toBareJid(), false);
+ final String statusMessage = packet.findChildContent("status");
+ if (statusMessage != null
+ && !statusMessage.isEmpty()
+ && conversation.countMessages() == 0) {
+ conversation.add(new Message(
+ conversation,
+ statusMessage,
+ Message.ENCRYPTION_NONE,
+ Message.STATUS_RECEIVED
+ ));
+ }
+ }
+ }
+ mXmppConnectionService.updateRosterUi();
+ }
- @Override
- public void onPresencePacketReceived(Account account, PresencePacket packet) {
- if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
- this.parseConferencePresence(packet, account);
- } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
- this.parseConferencePresence(packet, account);
- } else {
- this.parseContactPresence(packet, account);
- }
- }
+ @Override
+ public void onPresencePacketReceived(Account account, PresencePacket packet) {
+ if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
+ this.parseConferencePresence(packet, account);
+ } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
+ this.parseConferencePresence(packet, account);
+ } else {
+ this.parseContactPresence(packet, account);
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
index 4ce66edf8..993a42c21 100644
--- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
@@ -52,1261 +52,1261 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class DatabaseBackend extends SQLiteOpenHelper {
- private static DatabaseBackend instance = null;
-
- public static final String DATABASE_NAME = "history";
- public static final int DATABASE_VERSION = 31;
-
- private static String CREATE_CONTATCS_STATEMENT = "create table "
- + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
- + Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT,"
- + Contact.JID + " TEXT," + Contact.KEYS + " TEXT,"
- + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
- + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
- + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, "
- + Contact.GROUPS + " TEXT, FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
- + Account.TABLENAME + "(" + Account.UUID
- + ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
- + Contact.JID + ") ON CONFLICT REPLACE);";
-
- private static String CREATE_DISCOVERY_RESULTS_STATEMENT = "create table "
- + ServiceDiscoveryResult.TABLENAME + "("
- + ServiceDiscoveryResult.HASH + " TEXT, "
- + ServiceDiscoveryResult.VER + " TEXT, "
- + ServiceDiscoveryResult.RESULT + " TEXT, "
- + "UNIQUE(" + ServiceDiscoveryResult.HASH + ", "
- + ServiceDiscoveryResult.VER + ") ON CONFLICT REPLACE);";
-
- private static String CREATE_PRESENCE_TEMPLATES_STATEMENT = "CREATE TABLE "
- + PresenceTemplate.TABELNAME + "("
- + PresenceTemplate.UUID + " TEXT, "
- + PresenceTemplate.LAST_USED + " NUMBER,"
- + PresenceTemplate.MESSAGE + " TEXT,"
- + PresenceTemplate.STATUS + " TEXT,"
- + "UNIQUE("+PresenceTemplate.MESSAGE + "," +PresenceTemplate.STATUS+") ON CONFLICT REPLACE);";
-
- private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.PREKEY_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.ID + " INTEGER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.ID
- + ") ON CONFLICT REPLACE"
- + ");";
-
- private static String CREATE_SIGNED_PREKEYS_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.ID + " INTEGER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.ID
- + ") ON CONFLICT REPLACE" +
- ");";
-
- private static String CREATE_SESSIONS_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.SESSION_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.NAME + " TEXT, "
- + SQLiteAxolotlStore.DEVICE_ID + " INTEGER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.NAME + ", "
- + SQLiteAxolotlStore.DEVICE_ID
- + ") ON CONFLICT REPLACE"
- + ");";
-
- private static String CREATE_IDENTITIES_STATEMENT = "CREATE TABLE "
- + SQLiteAxolotlStore.IDENTITIES_TABLENAME + "("
- + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
- + SQLiteAxolotlStore.NAME + " TEXT, "
- + SQLiteAxolotlStore.OWN + " INTEGER, "
- + SQLiteAxolotlStore.FINGERPRINT + " TEXT, "
- + SQLiteAxolotlStore.CERTIFICATE + " BLOB, "
- + SQLiteAxolotlStore.TRUST + " TEXT, "
- + SQLiteAxolotlStore.ACTIVE + " NUMBER, "
- + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
- + SQLiteAxolotlStore.ACCOUNT
- + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
- + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
- + SQLiteAxolotlStore.NAME + ", "
- + SQLiteAxolotlStore.FINGERPRINT
- + ") ON CONFLICT IGNORE"
- + ");";
-
- private static String START_TIMES_TABLE = "start_times";
- private static String CREATE_START_TIMES_TABLE = "create table "+START_TIMES_TABLE+" (timestamp NUMBER);";
-
- private DatabaseBackend(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("PRAGMA foreign_keys=ON;");
- db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID+ " TEXT PRIMARY KEY,"
- + Account.USERNAME + " TEXT,"
- + Account.SERVER + " TEXT,"
- + Account.PASSWORD + " TEXT,"
- + Account.DISPLAY_NAME + " TEXT, "
- + Account.STATUS + " TEXT,"
- + Account.STATUS_MESSAGE + " TEXT,"
- + Account.ROSTERVERSION + " TEXT,"
- + Account.OPTIONS + " NUMBER, "
- + Account.AVATAR + " TEXT, "
- + Account.KEYS + " TEXT, "
- + Account.HOSTNAME + " TEXT, "
- + Account.PORT + " NUMBER DEFAULT 5222)");
- db.execSQL("create table " + Conversation.TABLENAME + " ("
- + Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
- + " TEXT, " + Conversation.CONTACT + " TEXT, "
- + Conversation.ACCOUNT + " TEXT, " + Conversation.CONTACTJID
- + " TEXT, " + Conversation.CREATED + " NUMBER, "
- + Conversation.STATUS + " NUMBER, " + Conversation.MODE
- + " NUMBER, " + Conversation.ATTRIBUTES + " TEXT, FOREIGN KEY("
- + Conversation.ACCOUNT + ") REFERENCES " + Account.TABLENAME
- + "(" + Account.UUID + ") ON DELETE CASCADE);");
- db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
- + " TEXT PRIMARY KEY, " + Message.CONVERSATION + " TEXT, "
- + Message.TIME_SENT + " NUMBER, " + Message.COUNTERPART
- + " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
- + Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
- + Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
- + Message.RELATIVE_FILE_PATH + " TEXT, "
- + Message.SERVER_MSG_ID + " TEXT, "
- + Message.FINGERPRINT + " TEXT, "
- + Message.CARBON + " INTEGER, "
- + Message.EDITED + " TEXT, "
- + Message.READ + " NUMBER DEFAULT 1, "
- + Message.OOB + " INTEGER, "
- + Message.ERROR_MESSAGE + " TEXT,"
- + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
- + Message.CONVERSATION + ") REFERENCES "
- + Conversation.TABLENAME + "(" + Conversation.UUID
- + ") ON DELETE CASCADE);");
-
- db.execSQL(CREATE_CONTATCS_STATEMENT);
- db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
- db.execSQL(CREATE_SESSIONS_STATEMENT);
- db.execSQL(CREATE_PREKEYS_STATEMENT);
- db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
- db.execSQL(CREATE_IDENTITIES_STATEMENT);
- db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
- db.execSQL(CREATE_START_TIMES_TABLE);
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion < 2 && newVersion >= 2) {
- db.execSQL("update " + Account.TABLENAME + " set "
- + Account.OPTIONS + " = " + Account.OPTIONS + " | 8");
- }
- if (oldVersion < 3 && newVersion >= 3) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.TYPE + " NUMBER");
- }
- if (oldVersion < 5 && newVersion >= 5) {
- db.execSQL("DROP TABLE " + Contact.TABLENAME);
- db.execSQL(CREATE_CONTATCS_STATEMENT);
- db.execSQL("UPDATE " + Account.TABLENAME + " SET "
- + Account.ROSTERVERSION + " = NULL");
- }
- if (oldVersion < 6 && newVersion >= 6) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.TRUE_COUNTERPART + " TEXT");
- }
- if (oldVersion < 7 && newVersion >= 7) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.REMOTE_MSG_ID + " TEXT");
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.AVATAR + " TEXT");
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "
- + Account.AVATAR + " TEXT");
- }
- if (oldVersion < 8 && newVersion >= 8) {
- db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN "
- + Conversation.ATTRIBUTES + " TEXT");
- }
- if (oldVersion < 9 && newVersion >= 9) {
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.LAST_TIME + " NUMBER");
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.LAST_PRESENCE + " TEXT");
- }
- if (oldVersion < 10 && newVersion >= 10) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.RELATIVE_FILE_PATH + " TEXT");
- }
- if (oldVersion < 11 && newVersion >= 11) {
- db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
- + Contact.GROUPS + " TEXT");
- db.execSQL("delete from " + Contact.TABLENAME);
- db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
- }
- if (oldVersion < 12 && newVersion >= 12) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.SERVER_MSG_ID + " TEXT");
- }
- if (oldVersion < 13 && newVersion >= 13) {
- db.execSQL("delete from " + Contact.TABLENAME);
- db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
- }
- if (oldVersion < 14 && newVersion >= 14) {
- canonicalizeJids(db);
- }
- if (oldVersion < 15 && newVersion >= 15) {
- recreateAxolotlDb(db);
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.FINGERPRINT + " TEXT");
- } else if (oldVersion < 22 && newVersion >= 22) {
- db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE);
- }
- if (oldVersion < 16 && newVersion >= 16) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
- + Message.CARBON + " INTEGER");
- }
- if (oldVersion < 19 && newVersion >= 19) {
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.DISPLAY_NAME + " TEXT");
- }
- if (oldVersion < 20 && newVersion >= 20) {
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.HOSTNAME + " TEXT");
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PORT + " NUMBER DEFAULT 5222");
- }
- if (oldVersion < 26 && newVersion >= 26) {
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS + " TEXT");
- db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT");
- }
- /* Any migrations that alter the Account table need to happen BEFORE this migration, as it
+ private static DatabaseBackend instance = null;
+
+ public static final String DATABASE_NAME = "history";
+ public static final int DATABASE_VERSION = 31;
+
+ private static String CREATE_CONTATCS_STATEMENT = "create table "
+ + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
+ + Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT,"
+ + Contact.JID + " TEXT," + Contact.KEYS + " TEXT,"
+ + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
+ + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
+ + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, "
+ + Contact.GROUPS + " TEXT, FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
+ + Account.TABLENAME + "(" + Account.UUID
+ + ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
+ + Contact.JID + ") ON CONFLICT REPLACE);";
+
+ private static String CREATE_DISCOVERY_RESULTS_STATEMENT = "create table "
+ + ServiceDiscoveryResult.TABLENAME + "("
+ + ServiceDiscoveryResult.HASH + " TEXT, "
+ + ServiceDiscoveryResult.VER + " TEXT, "
+ + ServiceDiscoveryResult.RESULT + " TEXT, "
+ + "UNIQUE(" + ServiceDiscoveryResult.HASH + ", "
+ + ServiceDiscoveryResult.VER + ") ON CONFLICT REPLACE);";
+
+ private static String CREATE_PRESENCE_TEMPLATES_STATEMENT = "CREATE TABLE "
+ + PresenceTemplate.TABELNAME + "("
+ + PresenceTemplate.UUID + " TEXT, "
+ + PresenceTemplate.LAST_USED + " NUMBER,"
+ + PresenceTemplate.MESSAGE + " TEXT,"
+ + PresenceTemplate.STATUS + " TEXT,"
+ + "UNIQUE(" + PresenceTemplate.MESSAGE + "," + PresenceTemplate.STATUS + ") ON CONFLICT REPLACE);";
+
+ private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE "
+ + SQLiteAxolotlStore.PREKEY_TABLENAME + "("
+ + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
+ + SQLiteAxolotlStore.ID + " INTEGER, "
+ + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
+ + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
+ + SQLiteAxolotlStore.ID
+ + ") ON CONFLICT REPLACE"
+ + ");";
+
+ private static String CREATE_SIGNED_PREKEYS_STATEMENT = "CREATE TABLE "
+ + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME + "("
+ + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
+ + SQLiteAxolotlStore.ID + " INTEGER, "
+ + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
+ + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
+ + SQLiteAxolotlStore.ID
+ + ") ON CONFLICT REPLACE" +
+ ");";
+
+ private static String CREATE_SESSIONS_STATEMENT = "CREATE TABLE "
+ + SQLiteAxolotlStore.SESSION_TABLENAME + "("
+ + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
+ + SQLiteAxolotlStore.NAME + " TEXT, "
+ + SQLiteAxolotlStore.DEVICE_ID + " INTEGER, "
+ + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
+ + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
+ + SQLiteAxolotlStore.NAME + ", "
+ + SQLiteAxolotlStore.DEVICE_ID
+ + ") ON CONFLICT REPLACE"
+ + ");";
+
+ private static String CREATE_IDENTITIES_STATEMENT = "CREATE TABLE "
+ + SQLiteAxolotlStore.IDENTITIES_TABLENAME + "("
+ + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
+ + SQLiteAxolotlStore.NAME + " TEXT, "
+ + SQLiteAxolotlStore.OWN + " INTEGER, "
+ + SQLiteAxolotlStore.FINGERPRINT + " TEXT, "
+ + SQLiteAxolotlStore.CERTIFICATE + " BLOB, "
+ + SQLiteAxolotlStore.TRUST + " TEXT, "
+ + SQLiteAxolotlStore.ACTIVE + " NUMBER, "
+ + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
+ + SQLiteAxolotlStore.ACCOUNT
+ + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
+ + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
+ + SQLiteAxolotlStore.NAME + ", "
+ + SQLiteAxolotlStore.FINGERPRINT
+ + ") ON CONFLICT IGNORE"
+ + ");";
+
+ private static String START_TIMES_TABLE = "start_times";
+ private static String CREATE_START_TIMES_TABLE = "create table " + START_TIMES_TABLE + " (timestamp NUMBER);";
+
+ private DatabaseBackend(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("PRAGMA foreign_keys=ON;");
+ db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID + " TEXT PRIMARY KEY,"
+ + Account.USERNAME + " TEXT,"
+ + Account.SERVER + " TEXT,"
+ + Account.PASSWORD + " TEXT,"
+ + Account.DISPLAY_NAME + " TEXT, "
+ + Account.STATUS + " TEXT,"
+ + Account.STATUS_MESSAGE + " TEXT,"
+ + Account.ROSTERVERSION + " TEXT,"
+ + Account.OPTIONS + " NUMBER, "
+ + Account.AVATAR + " TEXT, "
+ + Account.KEYS + " TEXT, "
+ + Account.HOSTNAME + " TEXT, "
+ + Account.PORT + " NUMBER DEFAULT 5222)");
+ db.execSQL("create table " + Conversation.TABLENAME + " ("
+ + Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
+ + " TEXT, " + Conversation.CONTACT + " TEXT, "
+ + Conversation.ACCOUNT + " TEXT, " + Conversation.CONTACTJID
+ + " TEXT, " + Conversation.CREATED + " NUMBER, "
+ + Conversation.STATUS + " NUMBER, " + Conversation.MODE
+ + " NUMBER, " + Conversation.ATTRIBUTES + " TEXT, FOREIGN KEY("
+ + Conversation.ACCOUNT + ") REFERENCES " + Account.TABLENAME
+ + "(" + Account.UUID + ") ON DELETE CASCADE);");
+ db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
+ + " TEXT PRIMARY KEY, " + Message.CONVERSATION + " TEXT, "
+ + Message.TIME_SENT + " NUMBER, " + Message.COUNTERPART
+ + " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
+ + Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
+ + Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
+ + Message.RELATIVE_FILE_PATH + " TEXT, "
+ + Message.SERVER_MSG_ID + " TEXT, "
+ + Message.FINGERPRINT + " TEXT, "
+ + Message.CARBON + " INTEGER, "
+ + Message.EDITED + " TEXT, "
+ + Message.READ + " NUMBER DEFAULT 1, "
+ + Message.OOB + " INTEGER, "
+ + Message.ERROR_MESSAGE + " TEXT,"
+ + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
+ + Message.CONVERSATION + ") REFERENCES "
+ + Conversation.TABLENAME + "(" + Conversation.UUID
+ + ") ON DELETE CASCADE);");
+
+ db.execSQL(CREATE_CONTATCS_STATEMENT);
+ db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
+ db.execSQL(CREATE_SESSIONS_STATEMENT);
+ db.execSQL(CREATE_PREKEYS_STATEMENT);
+ db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
+ db.execSQL(CREATE_IDENTITIES_STATEMENT);
+ db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
+ db.execSQL(CREATE_START_TIMES_TABLE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion < 2 && newVersion >= 2) {
+ db.execSQL("update " + Account.TABLENAME + " set "
+ + Account.OPTIONS + " = " + Account.OPTIONS + " | 8");
+ }
+ if (oldVersion < 3 && newVersion >= 3) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.TYPE + " NUMBER");
+ }
+ if (oldVersion < 5 && newVersion >= 5) {
+ db.execSQL("DROP TABLE " + Contact.TABLENAME);
+ db.execSQL(CREATE_CONTATCS_STATEMENT);
+ db.execSQL("UPDATE " + Account.TABLENAME + " SET "
+ + Account.ROSTERVERSION + " = NULL");
+ }
+ if (oldVersion < 6 && newVersion >= 6) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.TRUE_COUNTERPART + " TEXT");
+ }
+ if (oldVersion < 7 && newVersion >= 7) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.REMOTE_MSG_ID + " TEXT");
+ db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ + Contact.AVATAR + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "
+ + Account.AVATAR + " TEXT");
+ }
+ if (oldVersion < 8 && newVersion >= 8) {
+ db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN "
+ + Conversation.ATTRIBUTES + " TEXT");
+ }
+ if (oldVersion < 9 && newVersion >= 9) {
+ db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ + Contact.LAST_TIME + " NUMBER");
+ db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ + Contact.LAST_PRESENCE + " TEXT");
+ }
+ if (oldVersion < 10 && newVersion >= 10) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.RELATIVE_FILE_PATH + " TEXT");
+ }
+ if (oldVersion < 11 && newVersion >= 11) {
+ db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ + Contact.GROUPS + " TEXT");
+ db.execSQL("delete from " + Contact.TABLENAME);
+ db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
+ }
+ if (oldVersion < 12 && newVersion >= 12) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.SERVER_MSG_ID + " TEXT");
+ }
+ if (oldVersion < 13 && newVersion >= 13) {
+ db.execSQL("delete from " + Contact.TABLENAME);
+ db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
+ }
+ if (oldVersion < 14 && newVersion >= 14) {
+ canonicalizeJids(db);
+ }
+ if (oldVersion < 15 && newVersion >= 15) {
+ recreateAxolotlDb(db);
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.FINGERPRINT + " TEXT");
+ } else if (oldVersion < 22 && newVersion >= 22) {
+ db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE);
+ }
+ if (oldVersion < 16 && newVersion >= 16) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.CARBON + " INTEGER");
+ }
+ if (oldVersion < 19 && newVersion >= 19) {
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.DISPLAY_NAME + " TEXT");
+ }
+ if (oldVersion < 20 && newVersion >= 20) {
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.HOSTNAME + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PORT + " NUMBER DEFAULT 5222");
+ }
+ if (oldVersion < 26 && newVersion >= 26) {
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT");
+ }
+ /* Any migrations that alter the Account table need to happen BEFORE this migration, as it
* depends on account de-serialization.
*/
- if (oldVersion < 17 && newVersion >= 17) {
- List<Account> accounts = getAccounts(db);
- for (Account account : accounts) {
- String ownDeviceIdString = account.getKey(SQLiteAxolotlStore.JSONKEY_REGISTRATION_ID);
- if (ownDeviceIdString == null) {
- continue;
- }
- int ownDeviceId = Integer.valueOf(ownDeviceIdString);
- AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), ownDeviceId);
- deleteSession(db, account, ownAddress);
- IdentityKeyPair identityKeyPair = loadOwnIdentityKeyPair(db, account);
- if (identityKeyPair != null) {
- String[] selectionArgs = {
- account.getUuid(),
- identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", "")
- };
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.TRUSTED, 2);
- db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
- selectionArgs);
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not load own identity key pair");
- }
- }
- }
- if (oldVersion < 18 && newVersion >= 18) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.READ + " NUMBER DEFAULT 1");
- }
-
- if (oldVersion < 21 && newVersion >= 21) {
- List<Account> accounts = getAccounts(db);
- for (Account account : accounts) {
- account.unsetPgpSignature();
- db.update(Account.TABLENAME, account.getContentValues(), Account.UUID
- + "=?", new String[]{account.getUuid()});
- }
- }
-
- if (oldVersion < 23 && newVersion >= 23) {
- db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
- }
-
- if (oldVersion < 24 && newVersion >= 24) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.EDITED + " TEXT");
- }
-
- if (oldVersion < 25 && newVersion >= 25) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OOB + " INTEGER");
- }
-
- if (oldVersion < 26 && newVersion >= 26) {
- db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
- }
-
- if (oldVersion < 27 && newVersion >= 27) {
- db.execSQL("DELETE FROM "+ServiceDiscoveryResult.TABLENAME);
- }
-
- if (oldVersion < 28 && newVersion >= 28) {
- canonicalizeJids(db);
- }
-
- if (oldVersion < 29 && newVersion >= 29) {
- db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.ERROR_MESSAGE + " TEXT");
- }
-
- if (oldVersion < 30 && newVersion >= 30) {
- db.execSQL(CREATE_START_TIMES_TABLE);
- }
- if (oldVersion < 31 && newVersion >= 31) {
- db.execSQL("ALTER TABLE "+ SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN "+SQLiteAxolotlStore.TRUST + " TEXT");
- db.execSQL("ALTER TABLE "+ SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN "+SQLiteAxolotlStore.ACTIVE + " NUMBER");
- HashMap<Integer,ContentValues> migration = new HashMap<>();
- migration.put(0,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNDECIDED,true));
- migration.put(1,createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, true));
- migration.put(2,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, true));
- migration.put(3,createFingerprintStatusContentValues(FingerprintStatus.Trust.COMPROMISED, false));
- migration.put(4,createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, false));
- migration.put(5,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNDECIDED, false));
- migration.put(6,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, false));
- migration.put(7,createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, true));
- migration.put(8,createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, false));
- for(Map.Entry<Integer,ContentValues> entry : migration.entrySet()) {
- String whereClause = SQLiteAxolotlStore.TRUSTED+"=?";
- String[] where = {String.valueOf(entry.getKey())};
- db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME,entry.getValue(),whereClause,where);
- }
- }
- }
-
- private static ContentValues createFingerprintStatusContentValues(FingerprintStatus.Trust trust, boolean active) {
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.TRUST,trust.toString());
- values.put(SQLiteAxolotlStore.ACTIVE,active ? 1 : 0);
- return values;
- }
-
- private void canonicalizeJids(SQLiteDatabase db) {
- // migrate db to new, canonicalized JID domainpart representation
-
- // Conversation table
- Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME, new String[0]);
- while (cursor.moveToNext()) {
- String newJid;
- try {
- newJid = Jid.fromString(
- cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
- ).toPreppedString();
- } catch (InvalidJidException ignored) {
- Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID "
- + cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
- + ": " + ignored + ". Skipping...");
- continue;
- }
-
- String updateArgs[] = {
- newJid,
- cursor.getString(cursor.getColumnIndex(Conversation.UUID)),
- };
- db.execSQL("update " + Conversation.TABLENAME
- + " set " + Conversation.CONTACTJID + " = ? "
- + " where " + Conversation.UUID + " = ?", updateArgs);
- }
- cursor.close();
-
- // Contact table
- cursor = db.rawQuery("select * from " + Contact.TABLENAME, new String[0]);
- while (cursor.moveToNext()) {
- String newJid;
- try {
- newJid = Jid.fromString(
- cursor.getString(cursor.getColumnIndex(Contact.JID))
- ).toPreppedString();
- } catch (InvalidJidException ignored) {
- Log.e(Config.LOGTAG, "Failed to migrate Contact JID "
- + cursor.getString(cursor.getColumnIndex(Contact.JID))
- + ": " + ignored + ". Skipping...");
- continue;
- }
-
- String updateArgs[] = {
- newJid,
- cursor.getString(cursor.getColumnIndex(Contact.ACCOUNT)),
- cursor.getString(cursor.getColumnIndex(Contact.JID)),
- };
- db.execSQL("update " + Contact.TABLENAME
- + " set " + Contact.JID + " = ? "
- + " where " + Contact.ACCOUNT + " = ? "
- + " AND " + Contact.JID + " = ?", updateArgs);
- }
- cursor.close();
-
- // Account table
- cursor = db.rawQuery("select * from " + Account.TABLENAME, new String[0]);
- while (cursor.moveToNext()) {
- String newServer;
- try {
- newServer = Jid.fromParts(
- cursor.getString(cursor.getColumnIndex(Account.USERNAME)),
- cursor.getString(cursor.getColumnIndex(Account.SERVER)),
- "mobile"
- ).getDomainpart();
- } catch (InvalidJidException ignored) {
- Log.e(Config.LOGTAG, "Failed to migrate Account SERVER "
- + cursor.getString(cursor.getColumnIndex(Account.SERVER))
- + ": " + ignored + ". Skipping...");
- continue;
- }
-
- String updateArgs[] = {
- newServer,
- cursor.getString(cursor.getColumnIndex(Account.UUID)),
- };
- db.execSQL("update " + Account.TABLENAME
- + " set " + Account.SERVER + " = ? "
- + " where " + Account.UUID + " = ?", updateArgs);
- }
- cursor.close();
- }
-
- public static synchronized DatabaseBackend getInstance(Context context) {
- if (instance == null) {
- instance = new DatabaseBackend(context);
- }
- return instance;
- }
-
- public void createConversation(Conversation conversation) {
- SQLiteDatabase db = this.getWritableDatabase();
- db.insert(Conversation.TABLENAME, null, conversation.getContentValues());
- }
-
- public void createMessage(Message message) {
- SQLiteDatabase db = this.getWritableDatabase();
- db.insert(Message.TABLENAME, null, message.getContentValues());
- }
-
- public void createAccount(Account account) {
- SQLiteDatabase db = this.getWritableDatabase();
- db.insert(Account.TABLENAME, null, account.getContentValues());
- }
-
- public void insertDiscoveryResult(ServiceDiscoveryResult result) {
- SQLiteDatabase db = this.getWritableDatabase();
- db.insert(ServiceDiscoveryResult.TABLENAME, null, result.getContentValues());
- }
-
- public ServiceDiscoveryResult findDiscoveryResult(final String hash, final String ver) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] selectionArgs = {hash, ver};
- Cursor cursor = db.query(ServiceDiscoveryResult.TABLENAME, null,
- ServiceDiscoveryResult.HASH + "=? AND " + ServiceDiscoveryResult.VER + "=?",
- selectionArgs, null, null, null);
- if (cursor.getCount() == 0) {
- cursor.close();
- return null;
- }
- cursor.moveToFirst();
-
- ServiceDiscoveryResult result = null;
- try {
- result = new ServiceDiscoveryResult(cursor);
- } catch (JSONException e) { /* result is still null */ }
-
- cursor.close();
- return result;
- }
-
- public void insertPresenceTemplate(PresenceTemplate template) {
- SQLiteDatabase db = this.getWritableDatabase();
- db.insert(PresenceTemplate.TABELNAME, null, template.getContentValues());
- }
-
- public List<PresenceTemplate> getPresenceTemplates() {
- ArrayList<PresenceTemplate> templates = new ArrayList<>();
- SQLiteDatabase db = this.getReadableDatabase();
- Cursor cursor = db.query(PresenceTemplate.TABELNAME,null,null,null,null,null,PresenceTemplate.LAST_USED+" desc");
- while (cursor.moveToNext()) {
- templates.add(PresenceTemplate.fromCursor(cursor));
- }
- cursor.close();
- return templates;
- }
-
- public void deletePresenceTemplate(PresenceTemplate template) {
- Log.d(Config.LOGTAG,"deleting presence template with uuid "+template.getUuid());
- SQLiteDatabase db = this.getWritableDatabase();
- String where = PresenceTemplate.UUID+"=?";
- String[] whereArgs = {template.getUuid()};
- db.delete(PresenceTemplate.TABELNAME,where,whereArgs);
- }
-
- public CopyOnWriteArrayList<Conversation> getConversations(int status) {
- CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<>();
- SQLiteDatabase db = this.getReadableDatabase();
- String[] selectionArgs = {Integer.toString(status)};
- Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME
- + " where " + Conversation.STATUS + " = ? order by "
- + Conversation.CREATED + " desc", selectionArgs);
- while (cursor.moveToNext()) {
- list.add(Conversation.fromCursor(cursor));
- }
- cursor.close();
- return list;
- }
-
- public ArrayList<Message> getMessages(Conversation conversations, int limit) {
- return getMessages(conversations, limit, -1);
- }
-
- public ArrayList<Message> getMessages(Conversation conversation, int limit,
- long timestamp) {
- ArrayList<Message> list = new ArrayList<>();
- SQLiteDatabase db = this.getReadableDatabase();
- Cursor cursor;
- if (timestamp == -1) {
- String[] selectionArgs = {conversation.getUuid()};
- cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
- + "=?", selectionArgs, null, null, Message.TIME_SENT
- + " DESC", String.valueOf(limit));
- } else {
- String[] selectionArgs = {conversation.getUuid(),
- Long.toString(timestamp)};
- cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
- + "=? and " + Message.TIME_SENT + "<?", selectionArgs,
- null, null, Message.TIME_SENT + " DESC",
- String.valueOf(limit));
- }
- if (cursor.getCount() > 0) {
- cursor.moveToLast();
- do {
- Message message = Message.fromCursor(cursor);
- message.setConversation(conversation);
- list.add(message);
- } while (cursor.moveToPrevious());
- }
- cursor.close();
- return list;
- }
-
- public Iterable<Message> getMessagesIterable(final Conversation conversation) {
- return new Iterable<Message>() {
- @Override
- public Iterator<Message> iterator() {
- class MessageIterator implements Iterator<Message> {
- SQLiteDatabase db = getReadableDatabase();
- String[] selectionArgs = {conversation.getUuid()};
- Cursor cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
- + "=?", selectionArgs, null, null, Message.TIME_SENT
- + " ASC", null);
-
- public MessageIterator() {
- cursor.moveToFirst();
- }
-
- @Override
- public boolean hasNext() {
- return !cursor.isAfterLast();
- }
-
- @Override
- public Message next() {
- Message message = Message.fromCursor(cursor);
- cursor.moveToNext();
- return message;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
- return new MessageIterator();
- }
- };
- }
-
- public Conversation findConversation(final Account account, final Jid contactJid) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] selectionArgs = {account.getUuid(),
- contactJid.toBareJid().toPreppedString() + "/%",
- contactJid.toBareJid().toPreppedString()
- };
- Cursor cursor = db.query(Conversation.TABLENAME, null,
- Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID
- + " like ? OR " + Conversation.CONTACTJID + "=?)", selectionArgs, null, null, null);
- if (cursor.getCount() == 0) {
- cursor.close();
- return null;
- }
- cursor.moveToFirst();
- Conversation conversation = Conversation.fromCursor(cursor);
- cursor.close();
- return conversation;
- }
-
- public void updateConversation(final Conversation conversation) {
- final SQLiteDatabase db = this.getWritableDatabase();
- final String[] args = {conversation.getUuid()};
- db.update(Conversation.TABLENAME, conversation.getContentValues(),
- Conversation.UUID + "=?", args);
- }
-
- public List<Account> getAccounts() {
- SQLiteDatabase db = this.getReadableDatabase();
- return getAccounts(db);
- }
-
- private List<Account> getAccounts(SQLiteDatabase db) {
- List<Account> list = new ArrayList<>();
- Cursor cursor = db.query(Account.TABLENAME, null, null, null, null,
- null, null);
- while (cursor.moveToNext()) {
- list.add(Account.fromCursor(cursor));
- }
- cursor.close();
- return list;
- }
-
- public boolean updateAccount(Account account) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {account.getUuid()};
- final int rows = db.update(Account.TABLENAME, account.getContentValues(), Account.UUID + "=?", args);
- return rows == 1;
- }
-
- public boolean deleteAccount(Account account) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {account.getUuid()};
- final int rows = db.delete(Account.TABLENAME, Account.UUID + "=?", args);
- return rows == 1;
- }
-
- public boolean hasEnabledAccounts() {
- SQLiteDatabase db = this.getReadableDatabase();
- Cursor cursor = db.rawQuery("select count(" + Account.UUID + ") from "
- + Account.TABLENAME + " where not options & (1 <<1)", null);
- try {
- cursor.moveToFirst();
- int count = cursor.getInt(0);
- return (count > 0);
- } catch (SQLiteCantOpenDatabaseException e) {
- return true; // better safe than sorry
- } catch (RuntimeException e) {
- return true; // better safe than sorry
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- @Override
- public SQLiteDatabase getWritableDatabase() {
- SQLiteDatabase db = super.getWritableDatabase();
- db.execSQL("PRAGMA foreign_keys=ON;");
- return db;
- }
-
- public void updateMessage(Message message) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {message.getUuid()};
- db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
- + "=?", args);
- }
-
- public void updateMessage(Message message, String uuid) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {uuid};
- db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
- + "=?", args);
- }
-
- public void readRoster(Roster roster) {
- SQLiteDatabase db = this.getReadableDatabase();
- Cursor cursor;
- String args[] = {roster.getAccount().getUuid()};
- cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?", args, null, null, null);
- while (cursor.moveToNext()) {
- roster.initContact(Contact.fromCursor(cursor));
- }
- cursor.close();
- }
-
- public void writeRoster(final Roster roster) {
- final Account account = roster.getAccount();
- final SQLiteDatabase db = this.getWritableDatabase();
- db.beginTransaction();
- for (Contact contact : roster.getContacts()) {
- if (contact.getOption(Contact.Options.IN_ROSTER)) {
- db.insert(Contact.TABLENAME, null, contact.getContentValues());
- } else {
- String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
- String[] whereArgs = {account.getUuid(), contact.getJid().toPreppedString()};
- db.delete(Contact.TABLENAME, where, whereArgs);
- }
- }
- db.setTransactionSuccessful();
- db.endTransaction();
- account.setRosterVersion(roster.getVersion());
- updateAccount(account);
- }
-
- public void deleteMessagesInConversation(Conversation conversation) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {conversation.getUuid()};
- db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
- }
-
- public Pair<Long, String> getLastMessageReceived(Account account) {
- Cursor cursor = null;
- try {
- SQLiteDatabase db = this.getReadableDatabase();
- String sql = "select messages.timeSent,messages.serverMsgId from accounts join conversations on accounts.uuid=conversations.accountUuid join messages on conversations.uuid=messages.conversationUuid where accounts.uuid=? and (messages.status=0 or messages.carbon=1 or messages.serverMsgId not null) order by messages.timesent desc limit 1";
- String[] args = {account.getUuid()};
- cursor = db.rawQuery(sql, args);
- if (cursor.getCount() == 0) {
- return null;
- } else {
- cursor.moveToFirst();
- return new Pair<>(cursor.getLong(0), cursor.getString(1));
- }
- } catch (Exception e) {
- return null;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- public Pair<Long, String> getLastClearDate(Account account) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] columns = {Conversation.ATTRIBUTES};
- String selection = Conversation.ACCOUNT + "=?";
- String[] args = {account.getUuid()};
- Cursor cursor = db.query(Conversation.TABLENAME, columns, selection, args, null, null, null);
- long maxClearDate = 0;
- while (cursor.moveToNext()) {
- try {
- final JSONObject jsonObject = new JSONObject(cursor.getString(0));
- maxClearDate = Math.max(maxClearDate, jsonObject.getLong(Conversation.ATTRIBUTE_LAST_CLEAR_HISTORY));
- } catch (Exception e) {
- //ignored
- }
- }
- cursor.close();
- return new Pair<>(maxClearDate, null);
- }
-
- private Cursor getCursorForSession(Account account, AxolotlAddress contact) {
- final SQLiteDatabase db = this.getReadableDatabase();
- String[] selectionArgs = {account.getUuid(),
- contact.getName(),
- Integer.toString(contact.getDeviceId())};
- return db.query(SQLiteAxolotlStore.SESSION_TABLENAME,
- null,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.NAME + " = ? AND "
- + SQLiteAxolotlStore.DEVICE_ID + " = ? ",
- selectionArgs,
- null, null, null);
- }
-
- public SessionRecord loadSession(Account account, AxolotlAddress contact) {
- SessionRecord session = null;
- Cursor cursor = getCursorForSession(account, contact);
- if (cursor.getCount() != 0) {
- cursor.moveToFirst();
- try {
- session = new SessionRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
- } catch (IOException e) {
- cursor.close();
- throw new AssertionError(e);
- }
- }
- cursor.close();
- return session;
- }
-
- public List<Integer> getSubDeviceSessions(Account account, AxolotlAddress contact) {
- final SQLiteDatabase db = this.getReadableDatabase();
- return getSubDeviceSessions(db, account, contact);
- }
-
- private List<Integer> getSubDeviceSessions(SQLiteDatabase db, Account account, AxolotlAddress contact) {
- List<Integer> devices = new ArrayList<>();
- String[] columns = {SQLiteAxolotlStore.DEVICE_ID};
- String[] selectionArgs = {account.getUuid(),
- contact.getName()};
- Cursor cursor = db.query(SQLiteAxolotlStore.SESSION_TABLENAME,
- columns,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.NAME + " = ?",
- selectionArgs,
- null, null, null);
-
- while (cursor.moveToNext()) {
- devices.add(cursor.getInt(
- cursor.getColumnIndex(SQLiteAxolotlStore.DEVICE_ID)));
- }
-
- cursor.close();
- return devices;
- }
-
- public boolean containsSession(Account account, AxolotlAddress contact) {
- Cursor cursor = getCursorForSession(account, contact);
- int count = cursor.getCount();
- cursor.close();
- return count != 0;
- }
-
- public void storeSession(Account account, AxolotlAddress contact, SessionRecord session) {
- SQLiteDatabase db = this.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.NAME, contact.getName());
- values.put(SQLiteAxolotlStore.DEVICE_ID, contact.getDeviceId());
- values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(session.serialize(), Base64.DEFAULT));
- values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
- db.insert(SQLiteAxolotlStore.SESSION_TABLENAME, null, values);
- }
-
- public void deleteSession(Account account, AxolotlAddress contact) {
- SQLiteDatabase db = this.getWritableDatabase();
- deleteSession(db, account, contact);
- }
-
- private void deleteSession(SQLiteDatabase db, Account account, AxolotlAddress contact) {
- String[] args = {account.getUuid(),
- contact.getName(),
- Integer.toString(contact.getDeviceId())};
- db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.NAME + " = ? AND "
- + SQLiteAxolotlStore.DEVICE_ID + " = ? ",
- args);
- }
-
- public void deleteAllSessions(Account account, AxolotlAddress contact) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {account.getUuid(), contact.getName()};
- db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + "=? AND "
- + SQLiteAxolotlStore.NAME + " = ?",
- args);
- }
-
- private Cursor getCursorForPreKey(Account account, int preKeyId) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] columns = {SQLiteAxolotlStore.KEY};
- String[] selectionArgs = {account.getUuid(), Integer.toString(preKeyId)};
- Cursor cursor = db.query(SQLiteAxolotlStore.PREKEY_TABLENAME,
- columns,
- SQLiteAxolotlStore.ACCOUNT + "=? AND "
- + SQLiteAxolotlStore.ID + "=?",
- selectionArgs,
- null, null, null);
-
- return cursor;
- }
-
- public PreKeyRecord loadPreKey(Account account, int preKeyId) {
- PreKeyRecord record = null;
- Cursor cursor = getCursorForPreKey(account, preKeyId);
- if (cursor.getCount() != 0) {
- cursor.moveToFirst();
- try {
- record = new PreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
- } catch (IOException e) {
- throw new AssertionError(e);
- }
- }
- cursor.close();
- return record;
- }
-
- public boolean containsPreKey(Account account, int preKeyId) {
- Cursor cursor = getCursorForPreKey(account, preKeyId);
- int count = cursor.getCount();
- cursor.close();
- return count != 0;
- }
-
- public void storePreKey(Account account, PreKeyRecord record) {
- SQLiteDatabase db = this.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.ID, record.getId());
- values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT));
- values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
- db.insert(SQLiteAxolotlStore.PREKEY_TABLENAME, null, values);
- }
-
- public void deletePreKey(Account account, int preKeyId) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {account.getUuid(), Integer.toString(preKeyId)};
- db.delete(SQLiteAxolotlStore.PREKEY_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + "=? AND "
- + SQLiteAxolotlStore.ID + "=?",
- args);
- }
-
- private Cursor getCursorForSignedPreKey(Account account, int signedPreKeyId) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] columns = {SQLiteAxolotlStore.KEY};
- String[] selectionArgs = {account.getUuid(), Integer.toString(signedPreKeyId)};
- Cursor cursor = db.query(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
- columns,
- SQLiteAxolotlStore.ACCOUNT + "=? AND " + SQLiteAxolotlStore.ID + "=?",
- selectionArgs,
- null, null, null);
-
- return cursor;
- }
-
- public SignedPreKeyRecord loadSignedPreKey(Account account, int signedPreKeyId) {
- SignedPreKeyRecord record = null;
- Cursor cursor = getCursorForSignedPreKey(account, signedPreKeyId);
- if (cursor.getCount() != 0) {
- cursor.moveToFirst();
- try {
- record = new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
- } catch (IOException e) {
- throw new AssertionError(e);
- }
- }
- cursor.close();
- return record;
- }
-
- public List<SignedPreKeyRecord> loadSignedPreKeys(Account account) {
- List<SignedPreKeyRecord> prekeys = new ArrayList<>();
- SQLiteDatabase db = this.getReadableDatabase();
- String[] columns = {SQLiteAxolotlStore.KEY};
- String[] selectionArgs = {account.getUuid()};
- Cursor cursor = db.query(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
- columns,
- SQLiteAxolotlStore.ACCOUNT + "=?",
- selectionArgs,
- null, null, null);
-
- while (cursor.moveToNext()) {
- try {
- prekeys.add(new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT)));
- } catch (IOException ignored) {
- }
- }
- cursor.close();
- return prekeys;
- }
-
- public boolean containsSignedPreKey(Account account, int signedPreKeyId) {
- Cursor cursor = getCursorForPreKey(account, signedPreKeyId);
- int count = cursor.getCount();
- cursor.close();
- return count != 0;
- }
-
- public void storeSignedPreKey(Account account, SignedPreKeyRecord record) {
- SQLiteDatabase db = this.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.ID, record.getId());
- values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT));
- values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
- db.insert(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, null, values);
- }
-
- public void deleteSignedPreKey(Account account, int signedPreKeyId) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {account.getUuid(), Integer.toString(signedPreKeyId)};
- db.delete(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + "=? AND "
- + SQLiteAxolotlStore.ID + "=?",
- args);
- }
-
- private Cursor getIdentityKeyCursor(Account account, String name, boolean own) {
- final SQLiteDatabase db = this.getReadableDatabase();
- return getIdentityKeyCursor(db, account, name, own);
- }
-
- private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, boolean own) {
- return getIdentityKeyCursor(db, account, name, own, null);
- }
-
- private Cursor getIdentityKeyCursor(Account account, String fingerprint) {
- final SQLiteDatabase db = this.getReadableDatabase();
- return getIdentityKeyCursor(db, account, fingerprint);
- }
-
- private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String fingerprint) {
- return getIdentityKeyCursor(db, account, null, null, fingerprint);
- }
-
- private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, Boolean own, String fingerprint) {
- String[] columns = {SQLiteAxolotlStore.TRUST,
- SQLiteAxolotlStore.ACTIVE,
- SQLiteAxolotlStore.KEY};
- ArrayList<String> selectionArgs = new ArrayList<>(4);
- selectionArgs.add(account.getUuid());
- String selectionString = SQLiteAxolotlStore.ACCOUNT + " = ?";
- if (name != null) {
- selectionArgs.add(name);
- selectionString += " AND " + SQLiteAxolotlStore.NAME + " = ?";
- }
- if (fingerprint != null) {
- selectionArgs.add(fingerprint);
- selectionString += " AND " + SQLiteAxolotlStore.FINGERPRINT + " = ?";
- }
- if (own != null) {
- selectionArgs.add(own ? "1" : "0");
- selectionString += " AND " + SQLiteAxolotlStore.OWN + " = ?";
- }
- Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME,
- columns,
- selectionString,
- selectionArgs.toArray(new String[selectionArgs.size()]),
- null, null, null);
-
- return cursor;
- }
-
- public IdentityKeyPair loadOwnIdentityKeyPair(Account account) {
- SQLiteDatabase db = getReadableDatabase();
- return loadOwnIdentityKeyPair(db, account);
- }
-
- private IdentityKeyPair loadOwnIdentityKeyPair(SQLiteDatabase db, Account account) {
- String name = account.getJid().toBareJid().toPreppedString();
- IdentityKeyPair identityKeyPair = null;
- Cursor cursor = getIdentityKeyCursor(db, account, name, true);
- if (cursor.getCount() != 0) {
- cursor.moveToFirst();
- try {
- identityKeyPair = new IdentityKeyPair(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
- } catch (InvalidKeyException e) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
- }
- }
- cursor.close();
-
- return identityKeyPair;
- }
-
- public Set<IdentityKey> loadIdentityKeys(Account account, String name) {
- return loadIdentityKeys(account, name, null);
- }
-
- public Set<IdentityKey> loadIdentityKeys(Account account, String name, FingerprintStatus status) {
- Set<IdentityKey> identityKeys = new HashSet<>();
- Cursor cursor = getIdentityKeyCursor(account, name, false);
-
- while (cursor.moveToNext()) {
- if (status != null && !FingerprintStatus.fromCursor(cursor).equals(status)) {
- continue;
- }
- try {
- String key = cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY));
- if (key != null) {
- identityKeys.add(new IdentityKey(Base64.decode(key, Base64.DEFAULT), 0));
- } else {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Missing key (possibly preverified) in database for account" + account.getJid().toBareJid() + ", address: " + name);
- }
- } catch (InvalidKeyException e) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
- }
- }
- cursor.close();
-
- return identityKeys;
- }
-
- public long numTrustedKeys(Account account, String name) {
- SQLiteDatabase db = getReadableDatabase();
- String[] args = {
- account.getUuid(),
- name,
- FingerprintStatus.Trust.TRUSTED.toString(),
- FingerprintStatus.Trust.VERIFIED.toString(),
- FingerprintStatus.Trust.VERIFIED_X509.toString()
- };
- return DatabaseUtils.queryNumEntries(db, SQLiteAxolotlStore.IDENTITIES_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ?"
- + " AND " + SQLiteAxolotlStore.NAME + " = ?"
- + " AND (" + SQLiteAxolotlStore.TRUST + " = ? OR " + SQLiteAxolotlStore.TRUST + " = ? OR " +SQLiteAxolotlStore.TRUST +" = ?)"
- + " AND " +SQLiteAxolotlStore.ACTIVE + " > 0",
- args
- );
- }
-
- private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized, FingerprintStatus status) {
- SQLiteDatabase db = this.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
- values.put(SQLiteAxolotlStore.NAME, name);
- values.put(SQLiteAxolotlStore.OWN, own ? 1 : 0);
- values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
- values.put(SQLiteAxolotlStore.KEY, base64Serialized);
- values.putAll(status.toContentValues());
- String where = SQLiteAxolotlStore.ACCOUNT+"=? AND "+SQLiteAxolotlStore.NAME+"=? AND "+SQLiteAxolotlStore.FINGERPRINT+" =?";
- String[] whereArgs = {account.getUuid(),name,fingerprint};
- int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME,values,where,whereArgs);
- if (rows == 0) {
- db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
- }
- }
-
- public void storePreVerification(Account account, String name, String fingerprint, FingerprintStatus status) {
- SQLiteDatabase db = this.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
- values.put(SQLiteAxolotlStore.NAME, name);
- values.put(SQLiteAxolotlStore.OWN, 0);
- values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
- values.putAll(status.toContentValues());
- db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
- }
-
- public FingerprintStatus getFingerprintStatus(Account account, String fingerprint) {
- Cursor cursor = getIdentityKeyCursor(account, fingerprint);
- final FingerprintStatus status;
- if (cursor.getCount() > 0) {
- cursor.moveToFirst();
- status = FingerprintStatus.fromCursor(cursor);
- } else {
- status = null;
- }
- cursor.close();
- return status;
- }
-
- public boolean setIdentityKeyTrust(Account account, String fingerprint, FingerprintStatus fingerprintStatus) {
- SQLiteDatabase db = this.getWritableDatabase();
- return setIdentityKeyTrust(db, account, fingerprint, fingerprintStatus);
- }
-
- private boolean setIdentityKeyTrust(SQLiteDatabase db, Account account, String fingerprint, FingerprintStatus status) {
- String[] selectionArgs = {
- account.getUuid(),
- fingerprint
- };
- int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, status.toContentValues(),
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
- selectionArgs);
- return rows == 1;
- }
-
- public boolean setIdentityKeyCertificate(Account account, String fingerprint, X509Certificate x509Certificate) {
- SQLiteDatabase db = this.getWritableDatabase();
- String[] selectionArgs = {
- account.getUuid(),
- fingerprint
- };
- try {
- ContentValues values = new ContentValues();
- values.put(SQLiteAxolotlStore.CERTIFICATE, x509Certificate.getEncoded());
- return db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
- SQLiteAxolotlStore.ACCOUNT + " = ? AND "
- + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
- selectionArgs) == 1;
- } catch (CertificateEncodingException e) {
- Log.d(Config.LOGTAG, "could not encode certificate");
- return false;
- }
- }
-
- public X509Certificate getIdentityKeyCertifcate(Account account, String fingerprint) {
- SQLiteDatabase db = this.getReadableDatabase();
- String[] selectionArgs = {
- account.getUuid(),
- fingerprint
- };
- String[] colums = {SQLiteAxolotlStore.CERTIFICATE};
- String selection = SQLiteAxolotlStore.ACCOUNT + " = ? AND " + SQLiteAxolotlStore.FINGERPRINT + " = ? ";
- Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME, colums, selection, selectionArgs, null, null, null);
- if (cursor.getCount() < 1) {
- return null;
- } else {
- cursor.moveToFirst();
- byte[] certificate = cursor.getBlob(cursor.getColumnIndex(SQLiteAxolotlStore.CERTIFICATE));
- cursor.close();
- if (certificate == null || certificate.length == 0) {
- return null;
- }
- try {
- CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
- return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certificate));
- } catch (CertificateException e) {
- Log.d(Config.LOGTAG,"certificate exception "+e.getMessage());
- return null;
- }
- }
- }
-
- public void storeIdentityKey(Account account, String name, IdentityKey identityKey, FingerprintStatus status) {
- storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT), status);
- }
-
- public void storeOwnIdentityKeyPair(Account account, IdentityKeyPair identityKeyPair) {
- storeIdentityKey(account, account.getJid().toBareJid().toPreppedString(), true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), FingerprintStatus.createActiveVerified(false));
- }
-
- public void recreateAxolotlDb(SQLiteDatabase db) {
- Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + ">>> (RE)CREATING AXOLOTL DATABASE <<<");
- db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SESSION_TABLENAME);
- db.execSQL(CREATE_SESSIONS_STATEMENT);
- db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.PREKEY_TABLENAME);
- db.execSQL(CREATE_PREKEYS_STATEMENT);
- db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME);
- db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
- db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.IDENTITIES_TABLENAME);
- db.execSQL(CREATE_IDENTITIES_STATEMENT);
- }
-
- public void wipeAxolotlDb(Account account) {
- String accountName = account.getUuid();
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<");
- SQLiteDatabase db = this.getWritableDatabase();
- String[] deleteArgs = {
- accountName
- };
- db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ?",
- deleteArgs);
- db.delete(SQLiteAxolotlStore.PREKEY_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ?",
- deleteArgs);
- db.delete(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ?",
- deleteArgs);
- db.delete(SQLiteAxolotlStore.IDENTITIES_TABLENAME,
- SQLiteAxolotlStore.ACCOUNT + " = ?",
- deleteArgs);
- }
-
- public boolean startTimeCountExceedsThreshold() {
- SQLiteDatabase db = this.getWritableDatabase();
- long cleanBeforeTimestamp = System.currentTimeMillis() - Config.FREQUENT_RESTARTS_DETECTION_WINDOW;
- db.execSQL("delete from "+START_TIMES_TABLE+" where timestamp < "+cleanBeforeTimestamp);
- ContentValues values = new ContentValues();
- values.put("timestamp",System.currentTimeMillis());
- db.insert(START_TIMES_TABLE,null,values);
- String[] columns = new String[]{"count(timestamp)"};
- Cursor cursor = db.query(START_TIMES_TABLE,columns,null,null,null,null,null);
- int count;
- if (cursor.moveToFirst()) {
- count = cursor.getInt(0);
- } else {
- count = 0;
- }
- cursor.close();
- Log.d(Config.LOGTAG,"start time counter reached "+count);
- return count >= Config.FREQUENT_RESTARTS_THRESHOLD;
- }
-
- public void clearStartTimeCounter() {
- Log.d(Config.LOGTAG,"resetting start time counter");
- SQLiteDatabase db = this.getWritableDatabase();
- db.execSQL("delete from " + START_TIMES_TABLE);
- }
+ if (oldVersion < 17 && newVersion >= 17) {
+ List<Account> accounts = getAccounts(db);
+ for (Account account : accounts) {
+ String ownDeviceIdString = account.getKey(SQLiteAxolotlStore.JSONKEY_REGISTRATION_ID);
+ if (ownDeviceIdString == null) {
+ continue;
+ }
+ int ownDeviceId = Integer.valueOf(ownDeviceIdString);
+ AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), ownDeviceId);
+ deleteSession(db, account, ownAddress);
+ IdentityKeyPair identityKeyPair = loadOwnIdentityKeyPair(db, account);
+ if (identityKeyPair != null) {
+ String[] selectionArgs = {
+ account.getUuid(),
+ identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", "")
+ };
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.TRUSTED, 2);
+ db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
+ selectionArgs);
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not load own identity key pair");
+ }
+ }
+ }
+ if (oldVersion < 18 && newVersion >= 18) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.READ + " NUMBER DEFAULT 1");
+ }
+
+ if (oldVersion < 21 && newVersion >= 21) {
+ List<Account> accounts = getAccounts(db);
+ for (Account account : accounts) {
+ account.unsetPgpSignature();
+ db.update(Account.TABLENAME, account.getContentValues(), Account.UUID
+ + "=?", new String[]{account.getUuid()});
+ }
+ }
+
+ if (oldVersion < 23 && newVersion >= 23) {
+ db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
+ }
+
+ if (oldVersion < 24 && newVersion >= 24) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.EDITED + " TEXT");
+ }
+
+ if (oldVersion < 25 && newVersion >= 25) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OOB + " INTEGER");
+ }
+
+ if (oldVersion < 26 && newVersion >= 26) {
+ db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
+ }
+
+ if (oldVersion < 27 && newVersion >= 27) {
+ db.execSQL("DELETE FROM " + ServiceDiscoveryResult.TABLENAME);
+ }
+
+ if (oldVersion < 28 && newVersion >= 28) {
+ canonicalizeJids(db);
+ }
+
+ if (oldVersion < 29 && newVersion >= 29) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.ERROR_MESSAGE + " TEXT");
+ }
+
+ if (oldVersion < 30 && newVersion >= 30) {
+ db.execSQL(CREATE_START_TIMES_TABLE);
+ }
+ if (oldVersion < 31 && newVersion >= 31) {
+ db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.TRUST + " TEXT");
+ db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.ACTIVE + " NUMBER");
+ HashMap<Integer, ContentValues> migration = new HashMap<>();
+ migration.put(0, createFingerprintStatusContentValues(FingerprintStatus.Trust.UNDECIDED, true));
+ migration.put(1, createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, true));
+ migration.put(2, createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, true));
+ migration.put(3, createFingerprintStatusContentValues(FingerprintStatus.Trust.COMPROMISED, false));
+ migration.put(4, createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, false));
+ migration.put(5, createFingerprintStatusContentValues(FingerprintStatus.Trust.UNDECIDED, false));
+ migration.put(6, createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, false));
+ migration.put(7, createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, true));
+ migration.put(8, createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, false));
+ for (Map.Entry<Integer, ContentValues> entry : migration.entrySet()) {
+ String whereClause = SQLiteAxolotlStore.TRUSTED + "=?";
+ String[] where = {String.valueOf(entry.getKey())};
+ db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, entry.getValue(), whereClause, where);
+ }
+ }
+ }
+
+ private static ContentValues createFingerprintStatusContentValues(FingerprintStatus.Trust trust, boolean active) {
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.TRUST, trust.toString());
+ values.put(SQLiteAxolotlStore.ACTIVE, active ? 1 : 0);
+ return values;
+ }
+
+ private void canonicalizeJids(SQLiteDatabase db) {
+ // migrate db to new, canonicalized JID domainpart representation
+
+ // Conversation table
+ Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME, new String[0]);
+ while (cursor.moveToNext()) {
+ String newJid;
+ try {
+ newJid = Jid.fromString(
+ cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
+ ).toPreppedString();
+ } catch (InvalidJidException ignored) {
+ Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID "
+ + cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
+ + ": " + ignored + ". Skipping...");
+ continue;
+ }
+
+ String updateArgs[] = {
+ newJid,
+ cursor.getString(cursor.getColumnIndex(Conversation.UUID)),
+ };
+ db.execSQL("update " + Conversation.TABLENAME
+ + " set " + Conversation.CONTACTJID + " = ? "
+ + " where " + Conversation.UUID + " = ?", updateArgs);
+ }
+ cursor.close();
+
+ // Contact table
+ cursor = db.rawQuery("select * from " + Contact.TABLENAME, new String[0]);
+ while (cursor.moveToNext()) {
+ String newJid;
+ try {
+ newJid = Jid.fromString(
+ cursor.getString(cursor.getColumnIndex(Contact.JID))
+ ).toPreppedString();
+ } catch (InvalidJidException ignored) {
+ Log.e(Config.LOGTAG, "Failed to migrate Contact JID "
+ + cursor.getString(cursor.getColumnIndex(Contact.JID))
+ + ": " + ignored + ". Skipping...");
+ continue;
+ }
+
+ String updateArgs[] = {
+ newJid,
+ cursor.getString(cursor.getColumnIndex(Contact.ACCOUNT)),
+ cursor.getString(cursor.getColumnIndex(Contact.JID)),
+ };
+ db.execSQL("update " + Contact.TABLENAME
+ + " set " + Contact.JID + " = ? "
+ + " where " + Contact.ACCOUNT + " = ? "
+ + " AND " + Contact.JID + " = ?", updateArgs);
+ }
+ cursor.close();
+
+ // Account table
+ cursor = db.rawQuery("select * from " + Account.TABLENAME, new String[0]);
+ while (cursor.moveToNext()) {
+ String newServer;
+ try {
+ newServer = Jid.fromParts(
+ cursor.getString(cursor.getColumnIndex(Account.USERNAME)),
+ cursor.getString(cursor.getColumnIndex(Account.SERVER)),
+ "mobile"
+ ).getDomainpart();
+ } catch (InvalidJidException ignored) {
+ Log.e(Config.LOGTAG, "Failed to migrate Account SERVER "
+ + cursor.getString(cursor.getColumnIndex(Account.SERVER))
+ + ": " + ignored + ". Skipping...");
+ continue;
+ }
+
+ String updateArgs[] = {
+ newServer,
+ cursor.getString(cursor.getColumnIndex(Account.UUID)),
+ };
+ db.execSQL("update " + Account.TABLENAME
+ + " set " + Account.SERVER + " = ? "
+ + " where " + Account.UUID + " = ?", updateArgs);
+ }
+ cursor.close();
+ }
+
+ public static synchronized DatabaseBackend getInstance(Context context) {
+ if (instance == null) {
+ instance = new DatabaseBackend(context);
+ }
+ return instance;
+ }
+
+ public void createConversation(Conversation conversation) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(Conversation.TABLENAME, null, conversation.getContentValues());
+ }
+
+ public void createMessage(Message message) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(Message.TABLENAME, null, message.getContentValues());
+ }
+
+ public void createAccount(Account account) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(Account.TABLENAME, null, account.getContentValues());
+ }
+
+ public void insertDiscoveryResult(ServiceDiscoveryResult result) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(ServiceDiscoveryResult.TABLENAME, null, result.getContentValues());
+ }
+
+ public ServiceDiscoveryResult findDiscoveryResult(final String hash, final String ver) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] selectionArgs = {hash, ver};
+ Cursor cursor = db.query(ServiceDiscoveryResult.TABLENAME, null,
+ ServiceDiscoveryResult.HASH + "=? AND " + ServiceDiscoveryResult.VER + "=?",
+ selectionArgs, null, null, null);
+ if (cursor.getCount() == 0) {
+ cursor.close();
+ return null;
+ }
+ cursor.moveToFirst();
+
+ ServiceDiscoveryResult result = null;
+ try {
+ result = new ServiceDiscoveryResult(cursor);
+ } catch (JSONException e) { /* result is still null */ }
+
+ cursor.close();
+ return result;
+ }
+
+ public void insertPresenceTemplate(PresenceTemplate template) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(PresenceTemplate.TABELNAME, null, template.getContentValues());
+ }
+
+ public List<PresenceTemplate> getPresenceTemplates() {
+ ArrayList<PresenceTemplate> templates = new ArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor = db.query(PresenceTemplate.TABELNAME, null, null, null, null, null, PresenceTemplate.LAST_USED + " desc");
+ while (cursor.moveToNext()) {
+ templates.add(PresenceTemplate.fromCursor(cursor));
+ }
+ cursor.close();
+ return templates;
+ }
+
+ public void deletePresenceTemplate(PresenceTemplate template) {
+ Log.d(Config.LOGTAG, "deleting presence template with uuid " + template.getUuid());
+ SQLiteDatabase db = this.getWritableDatabase();
+ String where = PresenceTemplate.UUID + "=?";
+ String[] whereArgs = {template.getUuid()};
+ db.delete(PresenceTemplate.TABELNAME, where, whereArgs);
+ }
+
+ public CopyOnWriteArrayList<Conversation> getConversations(int status) {
+ CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] selectionArgs = {Integer.toString(status)};
+ Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME
+ + " where " + Conversation.STATUS + " = ? order by "
+ + Conversation.CREATED + " desc", selectionArgs);
+ while (cursor.moveToNext()) {
+ list.add(Conversation.fromCursor(cursor));
+ }
+ cursor.close();
+ return list;
+ }
+
+ public ArrayList<Message> getMessages(Conversation conversations, int limit) {
+ return getMessages(conversations, limit, -1);
+ }
+
+ public ArrayList<Message> getMessages(Conversation conversation, int limit,
+ long timestamp) {
+ ArrayList<Message> list = new ArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor;
+ if (timestamp == -1) {
+ String[] selectionArgs = {conversation.getUuid()};
+ cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
+ + "=?", selectionArgs, null, null, Message.TIME_SENT
+ + " DESC", String.valueOf(limit));
+ } else {
+ String[] selectionArgs = {conversation.getUuid(),
+ Long.toString(timestamp)};
+ cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
+ + "=? and " + Message.TIME_SENT + "<?", selectionArgs,
+ null, null, Message.TIME_SENT + " DESC",
+ String.valueOf(limit));
+ }
+ if (cursor.getCount() > 0) {
+ cursor.moveToLast();
+ do {
+ Message message = Message.fromCursor(cursor);
+ message.setConversation(conversation);
+ list.add(message);
+ } while (cursor.moveToPrevious());
+ }
+ cursor.close();
+ return list;
+ }
+
+ public Iterable<Message> getMessagesIterable(final Conversation conversation) {
+ return new Iterable<Message>() {
+ @Override
+ public Iterator<Message> iterator() {
+ class MessageIterator implements Iterator<Message> {
+ SQLiteDatabase db = getReadableDatabase();
+ String[] selectionArgs = {conversation.getUuid()};
+ Cursor cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
+ + "=?", selectionArgs, null, null, Message.TIME_SENT
+ + " ASC", null);
+
+ public MessageIterator() {
+ cursor.moveToFirst();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return !cursor.isAfterLast();
+ }
+
+ @Override
+ public Message next() {
+ Message message = Message.fromCursor(cursor);
+ cursor.moveToNext();
+ return message;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+ return new MessageIterator();
+ }
+ };
+ }
+
+ public Conversation findConversation(final Account account, final Jid contactJid) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] selectionArgs = {account.getUuid(),
+ contactJid.toBareJid().toPreppedString() + "/%",
+ contactJid.toBareJid().toPreppedString()
+ };
+ Cursor cursor = db.query(Conversation.TABLENAME, null,
+ Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID
+ + " like ? OR " + Conversation.CONTACTJID + "=?)", selectionArgs, null, null, null);
+ if (cursor.getCount() == 0) {
+ cursor.close();
+ return null;
+ }
+ cursor.moveToFirst();
+ Conversation conversation = Conversation.fromCursor(cursor);
+ cursor.close();
+ return conversation;
+ }
+
+ public void updateConversation(final Conversation conversation) {
+ final SQLiteDatabase db = this.getWritableDatabase();
+ final String[] args = {conversation.getUuid()};
+ db.update(Conversation.TABLENAME, conversation.getContentValues(),
+ Conversation.UUID + "=?", args);
+ }
+
+ public List<Account> getAccounts() {
+ SQLiteDatabase db = this.getReadableDatabase();
+ return getAccounts(db);
+ }
+
+ private List<Account> getAccounts(SQLiteDatabase db) {
+ List<Account> list = new ArrayList<>();
+ Cursor cursor = db.query(Account.TABLENAME, null, null, null, null,
+ null, null);
+ while (cursor.moveToNext()) {
+ list.add(Account.fromCursor(cursor));
+ }
+ cursor.close();
+ return list;
+ }
+
+ public boolean updateAccount(Account account) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {account.getUuid()};
+ final int rows = db.update(Account.TABLENAME, account.getContentValues(), Account.UUID + "=?", args);
+ return rows == 1;
+ }
+
+ public boolean deleteAccount(Account account) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {account.getUuid()};
+ final int rows = db.delete(Account.TABLENAME, Account.UUID + "=?", args);
+ return rows == 1;
+ }
+
+ public boolean hasEnabledAccounts() {
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor = db.rawQuery("select count(" + Account.UUID + ") from "
+ + Account.TABLENAME + " where not options & (1 <<1)", null);
+ try {
+ cursor.moveToFirst();
+ int count = cursor.getInt(0);
+ return (count > 0);
+ } catch (SQLiteCantOpenDatabaseException e) {
+ return true; // better safe than sorry
+ } catch (RuntimeException e) {
+ return true; // better safe than sorry
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ @Override
+ public SQLiteDatabase getWritableDatabase() {
+ SQLiteDatabase db = super.getWritableDatabase();
+ db.execSQL("PRAGMA foreign_keys=ON;");
+ return db;
+ }
+
+ public void updateMessage(Message message) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {message.getUuid()};
+ db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
+ + "=?", args);
+ }
+
+ public void updateMessage(Message message, String uuid) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {uuid};
+ db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
+ + "=?", args);
+ }
+
+ public void readRoster(Roster roster) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor;
+ String args[] = {roster.getAccount().getUuid()};
+ cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?", args, null, null, null);
+ while (cursor.moveToNext()) {
+ roster.initContact(Contact.fromCursor(cursor));
+ }
+ cursor.close();
+ }
+
+ public void writeRoster(final Roster roster) {
+ final Account account = roster.getAccount();
+ final SQLiteDatabase db = this.getWritableDatabase();
+ db.beginTransaction();
+ for (Contact contact : roster.getContacts()) {
+ if (contact.getOption(Contact.Options.IN_ROSTER)) {
+ db.insert(Contact.TABLENAME, null, contact.getContentValues());
+ } else {
+ String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
+ String[] whereArgs = {account.getUuid(), contact.getJid().toPreppedString()};
+ db.delete(Contact.TABLENAME, where, whereArgs);
+ }
+ }
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ account.setRosterVersion(roster.getVersion());
+ updateAccount(account);
+ }
+
+ public void deleteMessagesInConversation(Conversation conversation) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {conversation.getUuid()};
+ db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
+ }
+
+ public Pair<Long, String> getLastMessageReceived(Account account) {
+ Cursor cursor = null;
+ try {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String sql = "select messages.timeSent,messages.serverMsgId from accounts join conversations on accounts.uuid=conversations.accountUuid join messages on conversations.uuid=messages.conversationUuid where accounts.uuid=? and (messages.status=0 or messages.carbon=1 or messages.serverMsgId not null) order by messages.timesent desc limit 1";
+ String[] args = {account.getUuid()};
+ cursor = db.rawQuery(sql, args);
+ if (cursor.getCount() == 0) {
+ return null;
+ } else {
+ cursor.moveToFirst();
+ return new Pair<>(cursor.getLong(0), cursor.getString(1));
+ }
+ } catch (Exception e) {
+ return null;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ public Pair<Long, String> getLastClearDate(Account account) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] columns = {Conversation.ATTRIBUTES};
+ String selection = Conversation.ACCOUNT + "=?";
+ String[] args = {account.getUuid()};
+ Cursor cursor = db.query(Conversation.TABLENAME, columns, selection, args, null, null, null);
+ long maxClearDate = 0;
+ while (cursor.moveToNext()) {
+ try {
+ final JSONObject jsonObject = new JSONObject(cursor.getString(0));
+ maxClearDate = Math.max(maxClearDate, jsonObject.getLong(Conversation.ATTRIBUTE_LAST_CLEAR_HISTORY));
+ } catch (Exception e) {
+ //ignored
+ }
+ }
+ cursor.close();
+ return new Pair<>(maxClearDate, null);
+ }
+
+ private Cursor getCursorForSession(Account account, AxolotlAddress contact) {
+ final SQLiteDatabase db = this.getReadableDatabase();
+ String[] selectionArgs = {account.getUuid(),
+ contact.getName(),
+ Integer.toString(contact.getDeviceId())};
+ return db.query(SQLiteAxolotlStore.SESSION_TABLENAME,
+ null,
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.NAME + " = ? AND "
+ + SQLiteAxolotlStore.DEVICE_ID + " = ? ",
+ selectionArgs,
+ null, null, null);
+ }
+
+ public SessionRecord loadSession(Account account, AxolotlAddress contact) {
+ SessionRecord session = null;
+ Cursor cursor = getCursorForSession(account, contact);
+ if (cursor.getCount() != 0) {
+ cursor.moveToFirst();
+ try {
+ session = new SessionRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
+ } catch (IOException e) {
+ cursor.close();
+ throw new AssertionError(e);
+ }
+ }
+ cursor.close();
+ return session;
+ }
+
+ public List<Integer> getSubDeviceSessions(Account account, AxolotlAddress contact) {
+ final SQLiteDatabase db = this.getReadableDatabase();
+ return getSubDeviceSessions(db, account, contact);
+ }
+
+ private List<Integer> getSubDeviceSessions(SQLiteDatabase db, Account account, AxolotlAddress contact) {
+ List<Integer> devices = new ArrayList<>();
+ String[] columns = {SQLiteAxolotlStore.DEVICE_ID};
+ String[] selectionArgs = {account.getUuid(),
+ contact.getName()};
+ Cursor cursor = db.query(SQLiteAxolotlStore.SESSION_TABLENAME,
+ columns,
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.NAME + " = ?",
+ selectionArgs,
+ null, null, null);
+
+ while (cursor.moveToNext()) {
+ devices.add(cursor.getInt(
+ cursor.getColumnIndex(SQLiteAxolotlStore.DEVICE_ID)));
+ }
+
+ cursor.close();
+ return devices;
+ }
+
+ public boolean containsSession(Account account, AxolotlAddress contact) {
+ Cursor cursor = getCursorForSession(account, contact);
+ int count = cursor.getCount();
+ cursor.close();
+ return count != 0;
+ }
+
+ public void storeSession(Account account, AxolotlAddress contact, SessionRecord session) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.NAME, contact.getName());
+ values.put(SQLiteAxolotlStore.DEVICE_ID, contact.getDeviceId());
+ values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(session.serialize(), Base64.DEFAULT));
+ values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
+ db.insert(SQLiteAxolotlStore.SESSION_TABLENAME, null, values);
+ }
+
+ public void deleteSession(Account account, AxolotlAddress contact) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ deleteSession(db, account, contact);
+ }
+
+ private void deleteSession(SQLiteDatabase db, Account account, AxolotlAddress contact) {
+ String[] args = {account.getUuid(),
+ contact.getName(),
+ Integer.toString(contact.getDeviceId())};
+ db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.NAME + " = ? AND "
+ + SQLiteAxolotlStore.DEVICE_ID + " = ? ",
+ args);
+ }
+
+ public void deleteAllSessions(Account account, AxolotlAddress contact) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {account.getUuid(), contact.getName()};
+ db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + "=? AND "
+ + SQLiteAxolotlStore.NAME + " = ?",
+ args);
+ }
+
+ private Cursor getCursorForPreKey(Account account, int preKeyId) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] columns = {SQLiteAxolotlStore.KEY};
+ String[] selectionArgs = {account.getUuid(), Integer.toString(preKeyId)};
+ Cursor cursor = db.query(SQLiteAxolotlStore.PREKEY_TABLENAME,
+ columns,
+ SQLiteAxolotlStore.ACCOUNT + "=? AND "
+ + SQLiteAxolotlStore.ID + "=?",
+ selectionArgs,
+ null, null, null);
+
+ return cursor;
+ }
+
+ public PreKeyRecord loadPreKey(Account account, int preKeyId) {
+ PreKeyRecord record = null;
+ Cursor cursor = getCursorForPreKey(account, preKeyId);
+ if (cursor.getCount() != 0) {
+ cursor.moveToFirst();
+ try {
+ record = new PreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+ cursor.close();
+ return record;
+ }
+
+ public boolean containsPreKey(Account account, int preKeyId) {
+ Cursor cursor = getCursorForPreKey(account, preKeyId);
+ int count = cursor.getCount();
+ cursor.close();
+ return count != 0;
+ }
+
+ public void storePreKey(Account account, PreKeyRecord record) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.ID, record.getId());
+ values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT));
+ values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
+ db.insert(SQLiteAxolotlStore.PREKEY_TABLENAME, null, values);
+ }
+
+ public void deletePreKey(Account account, int preKeyId) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {account.getUuid(), Integer.toString(preKeyId)};
+ db.delete(SQLiteAxolotlStore.PREKEY_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + "=? AND "
+ + SQLiteAxolotlStore.ID + "=?",
+ args);
+ }
+
+ private Cursor getCursorForSignedPreKey(Account account, int signedPreKeyId) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] columns = {SQLiteAxolotlStore.KEY};
+ String[] selectionArgs = {account.getUuid(), Integer.toString(signedPreKeyId)};
+ Cursor cursor = db.query(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
+ columns,
+ SQLiteAxolotlStore.ACCOUNT + "=? AND " + SQLiteAxolotlStore.ID + "=?",
+ selectionArgs,
+ null, null, null);
+
+ return cursor;
+ }
+
+ public SignedPreKeyRecord loadSignedPreKey(Account account, int signedPreKeyId) {
+ SignedPreKeyRecord record = null;
+ Cursor cursor = getCursorForSignedPreKey(account, signedPreKeyId);
+ if (cursor.getCount() != 0) {
+ cursor.moveToFirst();
+ try {
+ record = new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+ cursor.close();
+ return record;
+ }
+
+ public List<SignedPreKeyRecord> loadSignedPreKeys(Account account) {
+ List<SignedPreKeyRecord> prekeys = new ArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] columns = {SQLiteAxolotlStore.KEY};
+ String[] selectionArgs = {account.getUuid()};
+ Cursor cursor = db.query(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
+ columns,
+ SQLiteAxolotlStore.ACCOUNT + "=?",
+ selectionArgs,
+ null, null, null);
+
+ while (cursor.moveToNext()) {
+ try {
+ prekeys.add(new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT)));
+ } catch (IOException ignored) {
+ }
+ }
+ cursor.close();
+ return prekeys;
+ }
+
+ public boolean containsSignedPreKey(Account account, int signedPreKeyId) {
+ Cursor cursor = getCursorForPreKey(account, signedPreKeyId);
+ int count = cursor.getCount();
+ cursor.close();
+ return count != 0;
+ }
+
+ public void storeSignedPreKey(Account account, SignedPreKeyRecord record) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.ID, record.getId());
+ values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT));
+ values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
+ db.insert(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, null, values);
+ }
+
+ public void deleteSignedPreKey(Account account, int signedPreKeyId) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {account.getUuid(), Integer.toString(signedPreKeyId)};
+ db.delete(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + "=? AND "
+ + SQLiteAxolotlStore.ID + "=?",
+ args);
+ }
+
+ private Cursor getIdentityKeyCursor(Account account, String name, boolean own) {
+ final SQLiteDatabase db = this.getReadableDatabase();
+ return getIdentityKeyCursor(db, account, name, own);
+ }
+
+ private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, boolean own) {
+ return getIdentityKeyCursor(db, account, name, own, null);
+ }
+
+ private Cursor getIdentityKeyCursor(Account account, String fingerprint) {
+ final SQLiteDatabase db = this.getReadableDatabase();
+ return getIdentityKeyCursor(db, account, fingerprint);
+ }
+
+ private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String fingerprint) {
+ return getIdentityKeyCursor(db, account, null, null, fingerprint);
+ }
+
+ private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, Boolean own, String fingerprint) {
+ String[] columns = {SQLiteAxolotlStore.TRUST,
+ SQLiteAxolotlStore.ACTIVE,
+ SQLiteAxolotlStore.KEY};
+ ArrayList<String> selectionArgs = new ArrayList<>(4);
+ selectionArgs.add(account.getUuid());
+ String selectionString = SQLiteAxolotlStore.ACCOUNT + " = ?";
+ if (name != null) {
+ selectionArgs.add(name);
+ selectionString += " AND " + SQLiteAxolotlStore.NAME + " = ?";
+ }
+ if (fingerprint != null) {
+ selectionArgs.add(fingerprint);
+ selectionString += " AND " + SQLiteAxolotlStore.FINGERPRINT + " = ?";
+ }
+ if (own != null) {
+ selectionArgs.add(own ? "1" : "0");
+ selectionString += " AND " + SQLiteAxolotlStore.OWN + " = ?";
+ }
+ Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME,
+ columns,
+ selectionString,
+ selectionArgs.toArray(new String[selectionArgs.size()]),
+ null, null, null);
+
+ return cursor;
+ }
+
+ public IdentityKeyPair loadOwnIdentityKeyPair(Account account) {
+ SQLiteDatabase db = getReadableDatabase();
+ return loadOwnIdentityKeyPair(db, account);
+ }
+
+ private IdentityKeyPair loadOwnIdentityKeyPair(SQLiteDatabase db, Account account) {
+ String name = account.getJid().toBareJid().toPreppedString();
+ IdentityKeyPair identityKeyPair = null;
+ Cursor cursor = getIdentityKeyCursor(db, account, name, true);
+ if (cursor.getCount() != 0) {
+ cursor.moveToFirst();
+ try {
+ identityKeyPair = new IdentityKeyPair(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
+ } catch (InvalidKeyException e) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
+ }
+ }
+ cursor.close();
+
+ return identityKeyPair;
+ }
+
+ public Set<IdentityKey> loadIdentityKeys(Account account, String name) {
+ return loadIdentityKeys(account, name, null);
+ }
+
+ public Set<IdentityKey> loadIdentityKeys(Account account, String name, FingerprintStatus status) {
+ Set<IdentityKey> identityKeys = new HashSet<>();
+ Cursor cursor = getIdentityKeyCursor(account, name, false);
+
+ while (cursor.moveToNext()) {
+ if (status != null && !FingerprintStatus.fromCursor(cursor).equals(status)) {
+ continue;
+ }
+ try {
+ String key = cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY));
+ if (key != null) {
+ identityKeys.add(new IdentityKey(Base64.decode(key, Base64.DEFAULT), 0));
+ } else {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Missing key (possibly preverified) in database for account" + account.getJid().toBareJid() + ", address: " + name);
+ }
+ } catch (InvalidKeyException e) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
+ }
+ }
+ cursor.close();
+
+ return identityKeys;
+ }
+
+ public long numTrustedKeys(Account account, String name) {
+ SQLiteDatabase db = getReadableDatabase();
+ String[] args = {
+ account.getUuid(),
+ name,
+ FingerprintStatus.Trust.TRUSTED.toString(),
+ FingerprintStatus.Trust.VERIFIED.toString(),
+ FingerprintStatus.Trust.VERIFIED_X509.toString()
+ };
+ return DatabaseUtils.queryNumEntries(db, SQLiteAxolotlStore.IDENTITIES_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ?"
+ + " AND " + SQLiteAxolotlStore.NAME + " = ?"
+ + " AND (" + SQLiteAxolotlStore.TRUST + " = ? OR " + SQLiteAxolotlStore.TRUST + " = ? OR " + SQLiteAxolotlStore.TRUST + " = ?)"
+ + " AND " + SQLiteAxolotlStore.ACTIVE + " > 0",
+ args
+ );
+ }
+
+ private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized, FingerprintStatus status) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
+ values.put(SQLiteAxolotlStore.NAME, name);
+ values.put(SQLiteAxolotlStore.OWN, own ? 1 : 0);
+ values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
+ values.put(SQLiteAxolotlStore.KEY, base64Serialized);
+ values.putAll(status.toContentValues());
+ String where = SQLiteAxolotlStore.ACCOUNT + "=? AND " + SQLiteAxolotlStore.NAME + "=? AND " + SQLiteAxolotlStore.FINGERPRINT + " =?";
+ String[] whereArgs = {account.getUuid(), name, fingerprint};
+ int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values, where, whereArgs);
+ if (rows == 0) {
+ db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
+ }
+ }
+
+ public void storePreVerification(Account account, String name, String fingerprint, FingerprintStatus status) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
+ values.put(SQLiteAxolotlStore.NAME, name);
+ values.put(SQLiteAxolotlStore.OWN, 0);
+ values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
+ values.putAll(status.toContentValues());
+ db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
+ }
+
+ public FingerprintStatus getFingerprintStatus(Account account, String fingerprint) {
+ Cursor cursor = getIdentityKeyCursor(account, fingerprint);
+ final FingerprintStatus status;
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ status = FingerprintStatus.fromCursor(cursor);
+ } else {
+ status = null;
+ }
+ cursor.close();
+ return status;
+ }
+
+ public boolean setIdentityKeyTrust(Account account, String fingerprint, FingerprintStatus fingerprintStatus) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ return setIdentityKeyTrust(db, account, fingerprint, fingerprintStatus);
+ }
+
+ private boolean setIdentityKeyTrust(SQLiteDatabase db, Account account, String fingerprint, FingerprintStatus status) {
+ String[] selectionArgs = {
+ account.getUuid(),
+ fingerprint
+ };
+ int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, status.toContentValues(),
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
+ selectionArgs);
+ return rows == 1;
+ }
+
+ public boolean setIdentityKeyCertificate(Account account, String fingerprint, X509Certificate x509Certificate) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] selectionArgs = {
+ account.getUuid(),
+ fingerprint
+ };
+ try {
+ ContentValues values = new ContentValues();
+ values.put(SQLiteAxolotlStore.CERTIFICATE, x509Certificate.getEncoded());
+ return db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
+ SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
+ selectionArgs) == 1;
+ } catch (CertificateEncodingException e) {
+ Log.d(Config.LOGTAG, "could not encode certificate");
+ return false;
+ }
+ }
+
+ public X509Certificate getIdentityKeyCertifcate(Account account, String fingerprint) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ String[] selectionArgs = {
+ account.getUuid(),
+ fingerprint
+ };
+ String[] colums = {SQLiteAxolotlStore.CERTIFICATE};
+ String selection = SQLiteAxolotlStore.ACCOUNT + " = ? AND " + SQLiteAxolotlStore.FINGERPRINT + " = ? ";
+ Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME, colums, selection, selectionArgs, null, null, null);
+ if (cursor.getCount() < 1) {
+ return null;
+ } else {
+ cursor.moveToFirst();
+ byte[] certificate = cursor.getBlob(cursor.getColumnIndex(SQLiteAxolotlStore.CERTIFICATE));
+ cursor.close();
+ if (certificate == null || certificate.length == 0) {
+ return null;
+ }
+ try {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certificate));
+ } catch (CertificateException e) {
+ Log.d(Config.LOGTAG, "certificate exception " + e.getMessage());
+ return null;
+ }
+ }
+ }
+
+ public void storeIdentityKey(Account account, String name, IdentityKey identityKey, FingerprintStatus status) {
+ storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT), status);
+ }
+
+ public void storeOwnIdentityKeyPair(Account account, IdentityKeyPair identityKeyPair) {
+ storeIdentityKey(account, account.getJid().toBareJid().toPreppedString(), true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), FingerprintStatus.createActiveVerified(false));
+ }
+
+ public void recreateAxolotlDb(SQLiteDatabase db) {
+ Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + ">>> (RE)CREATING AXOLOTL DATABASE <<<");
+ db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SESSION_TABLENAME);
+ db.execSQL(CREATE_SESSIONS_STATEMENT);
+ db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.PREKEY_TABLENAME);
+ db.execSQL(CREATE_PREKEYS_STATEMENT);
+ db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME);
+ db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
+ db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.IDENTITIES_TABLENAME);
+ db.execSQL(CREATE_IDENTITIES_STATEMENT);
+ }
+
+ public void wipeAxolotlDb(Account account) {
+ String accountName = account.getUuid();
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<");
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] deleteArgs = {
+ accountName
+ };
+ db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ?",
+ deleteArgs);
+ db.delete(SQLiteAxolotlStore.PREKEY_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ?",
+ deleteArgs);
+ db.delete(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ?",
+ deleteArgs);
+ db.delete(SQLiteAxolotlStore.IDENTITIES_TABLENAME,
+ SQLiteAxolotlStore.ACCOUNT + " = ?",
+ deleteArgs);
+ }
+
+ public boolean startTimeCountExceedsThreshold() {
+ SQLiteDatabase db = this.getWritableDatabase();
+ long cleanBeforeTimestamp = System.currentTimeMillis() - Config.FREQUENT_RESTARTS_DETECTION_WINDOW;
+ db.execSQL("delete from " + START_TIMES_TABLE + " where timestamp < " + cleanBeforeTimestamp);
+ ContentValues values = new ContentValues();
+ values.put("timestamp", System.currentTimeMillis());
+ db.insert(START_TIMES_TABLE, null, values);
+ String[] columns = new String[]{"count(timestamp)"};
+ Cursor cursor = db.query(START_TIMES_TABLE, columns, null, null, null, null, null);
+ int count;
+ if (cursor.moveToFirst()) {
+ count = cursor.getInt(0);
+ } else {
+ count = 0;
+ }
+ cursor.close();
+ Log.d(Config.LOGTAG, "start time counter reached " + count);
+ return count >= Config.FREQUENT_RESTARTS_THRESHOLD;
+ }
+
+ public void clearStartTimeCounter() {
+ Log.d(Config.LOGTAG, "resetting start time counter");
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.execSQL("delete from " + START_TIMES_TABLE);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/persistance/FileBackend.java b/src/main/java/de/pixart/messenger/persistance/FileBackend.java
index 9babae1de..893834ad7 100644
--- a/src/main/java/de/pixart/messenger/persistance/FileBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/FileBackend.java
@@ -58,918 +58,918 @@ import de.pixart.messenger.utils.FileWriterException;
import de.pixart.messenger.xmpp.pep.Avatar;
public class FileBackend {
- private static final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
-
- public static final String CONVERSATIONS_FILE_PROVIDER = "de.pixart.messenger.files";
-
- private XmppConnectionService mXmppConnectionService;
-
- public FileBackend(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
-
- private void createNoMedia() {
- final File nomedia_files = new File(getConversationsFileDirectory()+".nomedia");
- final File nomedia_audios = new File(getConversationsAudioDirectory()+".nomedia");
- if (!nomedia_files.exists()) {
- try {
- nomedia_files.createNewFile();
- } catch (Exception e) {
- Log.d(Config.LOGTAG, "could not create nomedia file for files directory");
- }
- }
- if (!nomedia_audios.exists()) {
- try {
- nomedia_audios.createNewFile();
- } catch (Exception e) {
- Log.d(Config.LOGTAG, "could not create nomedia file for audio directory");
- }
- }
- }
-
- public void updateMediaScanner(File file) {
- if (file.getAbsolutePath().startsWith(getConversationsImageDirectory())
- || file.getAbsolutePath().startsWith(getConversationsVideoDirectory())) {
- Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
- intent.setData(Uri.fromFile(file));
- mXmppConnectionService.sendBroadcast(intent);
- } else {
- createNoMedia();
- }
- }
-
- public boolean deleteFile(Message message) {
- File file = getFile(message);
- if (file.delete()) {
- updateMediaScanner(file);
- return true;
- } else {
- return false;
- }
- }
-
- public DownloadableFile getFile(Message message) {
- return getFile(message, true);
- }
-
- public DownloadableFile getFile(Message message, boolean decrypted) {
- final boolean encrypted = !decrypted
- && (message.getEncryption() == Message.ENCRYPTION_PGP
- || message.getEncryption() == Message.ENCRYPTION_DECRYPTED);
- final DownloadableFile file;
- String path = message.getRelativeFilePath();
- if (path == null) {
- String filename = fileDateFormat.format(new Date(message.getTimeSent()))+"_"+message.getUuid().substring(0,4);
- path = filename;
- }
- if (path.startsWith("/")) {
- file = new DownloadableFile(path);
- } else {
- String mime = message.getMimeType();
- if (mime != null && mime.startsWith("image")) {
- file = new DownloadableFile(getConversationsImageDirectory() + path);
- } else if (mime != null && mime.startsWith("video")) {
- file = new DownloadableFile(getConversationsVideoDirectory() + path);
- } else if (mime != null && mime.startsWith("audio")) {
- file = new DownloadableFile(getConversationsAudioDirectory() + path);
- } else {
- file = new DownloadableFile(getConversationsFileDirectory() + path);
- }
- }
- if (encrypted) {
- return new DownloadableFile(getConversationsFileDirectory() + file.getName() + ".pgp");
- } else {
- return file;
- }
- }
-
- private static long getFileSize(Context context, Uri uri) {
- Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
- if (cursor != null && cursor.moveToFirst()) {
- return cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
- } else {
- return -1;
- }
- }
-
- public static boolean allFilesUnderSize(Context context, List<Uri> uris, long max) {
- if (max <= 0) {
- Log.d(Config.LOGTAG,"server did not report max file size for http upload");
- return true; //exception to be compatible with HTTP Upload < v0.2
- }
- for(Uri uri : uris) {
- if (FileBackend.getFileSize(context, uri) > max) {
- Log.d(Config.LOGTAG,"not all files are under "+max+" bytes. suggesting falling back to jingle");
- return false;
- }
- }
- return true;
- }
-
- public static String getConversationsFileDirectory() {
- return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pix-Art Messenger/files/";
- }
-
- public static String getConversationsImageDirectory() {
- return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pix-Art Messenger/images/";
- }
-
- public static String getConversationsVideoDirectory() {
- return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pix-Art Messenger/videos/";
- }
-
- public static String getConversationsAudioDirectory() {
- return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pix-Art Messenger/audios/";
- }
-
- public static String getConversationsDirectory() {
- return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pix-Art Messenger/";
- }
-
- public Bitmap resize(Bitmap originalBitmap, int size) {
- int w = originalBitmap.getWidth();
- int h = originalBitmap.getHeight();
- if (Math.max(w, h) > size) {
- int scalledW;
- int scalledH;
- if (w <= h) {
- scalledW = (int) (w / ((double) h / size));
- scalledH = size;
- } else {
- scalledW = size;
- scalledH = (int) (h / ((double) w / size));
- }
- Bitmap result = Bitmap.createScaledBitmap(originalBitmap, scalledW, scalledH, true);
- if (originalBitmap != null && !originalBitmap.isRecycled()) {
- originalBitmap.recycle();
- }
- return result;
- } else {
- return originalBitmap;
- }
- }
-
- public static Bitmap rotate(Bitmap bitmap, int degree) {
- if (degree == 0) {
- return bitmap;
- }
- int w = bitmap.getWidth();
- int h = bitmap.getHeight();
- Matrix mtx = new Matrix();
- mtx.postRotate(degree);
- Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
- if (bitmap != null && !bitmap.isRecycled()) {
- bitmap.recycle();
- }
- return result;
- }
-
- public boolean useImageAsIs(Uri uri) {
- String path = getOriginalPath(uri);
- if (path == null) {
- return false;
- }
- File file = new File(path);
- long size = file.length();
- if (size == 0 || size >= Config.IMAGE_MAX_SIZE ) {
- return false;
- }
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- try {
- BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(uri), null, options);
- if (options == null || options.outMimeType == null || options.outHeight <= 0 || options.outWidth <= 0) {
- return false;
- }
- return (options.outWidth <= Config.IMAGE_SIZE && options.outHeight <= Config.IMAGE_SIZE && options.outMimeType.contains(Config.IMAGE_FORMAT.name().toLowerCase()));
- } catch (FileNotFoundException e) {
- return false;
- }
- }
-
- public String getOriginalPath(Uri uri) {
- return FileUtils.getPath(mXmppConnectionService,uri);
- }
-
- public void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException {
- Log.d(Config.LOGTAG,"copy file ("+uri.toString()+") to private storage "+file.getAbsolutePath());
- file.getParentFile().mkdirs();
- OutputStream os = null;
- InputStream is = null;
- try {
- file.createNewFile();
- os = new FileOutputStream(file);
- is = mXmppConnectionService.getContentResolver().openInputStream(uri);
- byte[] buffer = new byte[1024];
- int length;
- while ((length = is.read(buffer)) > 0) {
- try {
- os.write(buffer, 0, length);
- } catch (IOException e) {
- throw new FileWriterException();
- }
- }
- try {
- os.flush();
- } catch (IOException e) {
- throw new FileWriterException();
- }
- } catch(FileNotFoundException e) {
- throw new FileCopyException(R.string.error_file_not_found);
- } catch(FileWriterException e) {
- throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
- } catch (IOException e) {
- e.printStackTrace();
- throw new FileCopyException(R.string.error_io_exception);
- } finally {
- close(os);
- close(is);
- }
- }
-
- public void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
- String mime = mXmppConnectionService.getContentResolver().getType(uri);
- Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage (mime="+mime+")");
- String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
- if (extension == null) {
- extension = getExtensionFromUri(uri);
- }
- String filename = fileDateFormat.format(new Date(message.getTimeSent()))+"_"+message.getUuid().substring(0,4);
- message.setRelativeFilePath(filename + "." + extension);
- copyFileToPrivateStorage(mXmppConnectionService.getFileBackend().getFile(message), uri);
- }
-
- private String getExtensionFromUri(Uri uri) {
- String[] projection = {MediaStore.MediaColumns.DATA};
- String filename = null;
- Cursor cursor = mXmppConnectionService.getContentResolver().query(uri, projection, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- filename = cursor.getString(0);
- }
- } catch (Exception e) {
- filename = null;
- } finally {
- cursor.close();
- }
- }
- int pos = filename == null ? -1 : filename.lastIndexOf('.');
- return pos > 0 ? filename.substring(pos+1) : null;
- }
-
- private void copyImageToPrivateStorage(File file, Uri image, int sampleSize) throws FileCopyException {
- file.getParentFile().mkdirs();
- InputStream is = null;
- OutputStream os = null;
- try {
- if (!file.exists() && !file.createNewFile()) {
- throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
- }
- is = mXmppConnectionService.getContentResolver().openInputStream(image);
- if (is == null) {
- throw new FileCopyException(R.string.error_not_an_image_file);
- }
- Bitmap originalBitmap;
- BitmapFactory.Options options = new BitmapFactory.Options();
- int inSampleSize = (int) Math.pow(2, sampleSize);
- Log.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize);
- options.inSampleSize = inSampleSize;
- originalBitmap = BitmapFactory.decodeStream(is, null, options);
- is.close();
- if (originalBitmap == null) {
- throw new FileCopyException(R.string.error_not_an_image_file);
- }
- Bitmap scaledBitmap = resize(originalBitmap, Config.IMAGE_SIZE);
- int rotation = getRotation(image);
- scaledBitmap = rotate(scaledBitmap, rotation);
- boolean targetSizeReached = false;
- int quality = Config.IMAGE_QUALITY;
- while(!targetSizeReached) {
- os = new FileOutputStream(file);
- boolean success = scaledBitmap.compress(Config.IMAGE_FORMAT, quality, os);
- if (!success) {
- throw new FileCopyException(R.string.error_compressing_image);
- }
- os.flush();
- targetSizeReached = file.length() <= Config.IMAGE_MAX_SIZE || quality <= 50;
- quality -= 5;
- }
- scaledBitmap.recycle();
- } catch (FileNotFoundException e) {
- throw new FileCopyException(R.string.error_file_not_found);
- } catch (IOException e) {
- e.printStackTrace();
- throw new FileCopyException(R.string.error_io_exception);
- } catch (SecurityException e) {
- throw new FileCopyException(R.string.error_security_exception_during_image_copy);
- } catch (OutOfMemoryError e) {
- ++sampleSize;
- if (sampleSize <= 3) {
- copyImageToPrivateStorage(file, image, sampleSize);
- } else {
- throw new FileCopyException(R.string.error_out_of_memory);
- }
- } finally {
- close(os);
- close(is);
- }
- }
-
- public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException {
- Log.d(Config.LOGTAG,"copy image ("+image.toString()+") to private storage "+file.getAbsolutePath());
- copyImageToPrivateStorage(file, image, 0);
- }
-
- public void copyImageToPrivateStorage(Message message, Uri image) throws FileCopyException {
- String filename = fileDateFormat.format(new Date(message.getTimeSent()))+"_"+message.getUuid().substring(0,4);
- switch(Config.IMAGE_FORMAT) {
- case JPEG:
- message.setRelativeFilePath(filename+".jpg");
- break;
- case PNG:
- message.setRelativeFilePath(filename+".png");
- break;
- case WEBP:
- message.setRelativeFilePath(filename+".webp");
- break;
- }
- copyImageToPrivateStorage(getFile(message), image);
- updateFileParams(message);
- }
-
- private int getRotation(File file) {
- return getRotation(Uri.parse("file://"+file.getAbsolutePath()));
- }
-
- private int getRotation(Uri image) {
- InputStream is = null;
- try {
- is = mXmppConnectionService.getContentResolver().openInputStream(image);
- return ExifHelper.getOrientation(is);
- } catch (FileNotFoundException e) {
- return 0;
- } finally {
- close(is);
- }
- }
-
- public Bitmap getThumbnail(Message message, int size, boolean cacheOnly) throws FileNotFoundException {
- final String uuid = message.getUuid();
- final LruCache<String,Bitmap> cache = mXmppConnectionService.getBitmapCache();
- Bitmap thumbnail = cache.get(uuid);
- if ((thumbnail == null) && (!cacheOnly)) {
- synchronized (cache) {
- thumbnail = cache.get(uuid);
- if (thumbnail != null) {
- return thumbnail;
- }
- DownloadableFile file = getFile(message);
- if (file.getMimeType().startsWith("video/")) {
- thumbnail = getVideoPreview(file, size);
- } else {
- Bitmap fullsize = getFullsizeImagePreview(file, size);
- if (fullsize == null) {
- throw new FileNotFoundException();
- }
- thumbnail = resize(fullsize, size);
- thumbnail = rotate(thumbnail, getRotation(file));
- }
- this.mXmppConnectionService.getBitmapCache().put(uuid, thumbnail);
- }
- }
- return thumbnail;
- }
-
- private Bitmap getFullsizeImagePreview(File file, int size) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = calcSampleSize(file, size);
- try {
- return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
- } catch (OutOfMemoryError e) {
- options.inSampleSize *= 2;
- return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
- }
- }
-
- private Bitmap getVideoPreview(File file, int size) {
- MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
- Bitmap frame;
- try {
- metadataRetriever.setDataSource(file.getAbsolutePath());
- frame = metadataRetriever.getFrameAtTime(0);
- metadataRetriever.release();
- frame = resize(frame, size);
- } catch(IllegalArgumentException | NullPointerException e) {
- frame = Bitmap.createBitmap(size,size, Bitmap.Config.ARGB_8888);
- frame.eraseColor(0xff000000);
- }
- Canvas canvas = new Canvas(frame);
- Bitmap play = BitmapFactory.decodeResource(mXmppConnectionService.getResources(), R.drawable.play_video);
- float x = (frame.getWidth() - play.getWidth()) / 2.0f;
- float y = (frame.getHeight() - play.getHeight()) / 2.0f;
- canvas.drawBitmap(play,x,y,null);
- return frame;
- }
-
- private static String getTakePhotoPath() {
- return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)+"/Camera/";
- }
-
- public Uri getTakePhotoUri() {
- File file = new File(getTakePhotoPath()+"IMG_" + fileDateFormat.format(new Date()) + ".jpg");
- file.getParentFile().mkdirs();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- return FileProvider.getUriForFile(mXmppConnectionService, CONVERSATIONS_FILE_PROVIDER, file);
- } else {
- return Uri.fromFile(file);
- }
- }
-
- public static Uri getIndexableTakePhotoUri(Uri original) {
- if ("file".equals(original.getScheme())) {
- return original;
- } else {
- List<String> segments = original.getPathSegments();
- return Uri.parse("file://"+getTakePhotoPath()+segments.get(segments.size() - 1));
- }
- }
-
- public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) {
- try {
- Avatar avatar = new Avatar();
- Bitmap bm = cropCenterSquare(image, size);
- if (bm == null) {
- return null;
- }
- ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
- Base64OutputStream mBase64OutputStream = new Base64OutputStream(
- mByteArrayOutputStream, Base64.DEFAULT);
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- DigestOutputStream mDigestOutputStream = new DigestOutputStream(
- mBase64OutputStream, digest);
- if (!bm.compress(format, 75, mDigestOutputStream)) {
- return null;
- }
- mDigestOutputStream.flush();
- mDigestOutputStream.close();
- avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
- avatar.image = new String(mByteArrayOutputStream.toByteArray());
- return avatar;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } catch (IOException e) {
- return null;
- }
- }
-
- public Avatar getStoredPepAvatar(String hash) {
- if (hash == null) {
- return null;
- }
- Avatar avatar = new Avatar();
- File file = new File(getAvatarPath(hash));
- FileInputStream is = null;
- try {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(file.getAbsolutePath(), options);
- is = new FileInputStream(file);
- ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
- Base64OutputStream mBase64OutputStream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- DigestOutputStream os = new DigestOutputStream(mBase64OutputStream, digest);
- byte[] buffer = new byte[4096];
- int length;
- while ((length = is.read(buffer)) > 0) {
- os.write(buffer, 0, length);
- }
- os.flush();
- os.close();
- avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
- avatar.image = new String(mByteArrayOutputStream.toByteArray());
- avatar.height = options.outHeight;
- avatar.width = options.outWidth;
- return avatar;
- } catch (IOException e) {
- return null;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } finally {
- close(is);
- }
- }
-
- public boolean isAvatarCached(Avatar avatar) {
- File file = new File(getAvatarPath(avatar.getFilename()));
- return file.exists();
- }
-
- public boolean save(Avatar avatar) {
- File file;
- if (isAvatarCached(avatar)) {
- file = new File(getAvatarPath(avatar.getFilename()));
- } else {
- String filename = getAvatarPath(avatar.getFilename());
- file = new File(filename + ".tmp");
- file.getParentFile().mkdirs();
- OutputStream os = null;
- try {
- file.createNewFile();
- os = new FileOutputStream(file);
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- digest.reset();
- DigestOutputStream mDigestOutputStream = new DigestOutputStream(os, digest);
- mDigestOutputStream.write(avatar.getImageAsBytes());
- mDigestOutputStream.flush();
- mDigestOutputStream.close();
- String sha1sum = CryptoHelper.bytesToHex(digest.digest());
- if (sha1sum.equals(avatar.sha1sum)) {
- file.renameTo(new File(filename));
- } else {
- Log.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner);
- file.delete();
- return false;
- }
- } catch (IllegalArgumentException | IOException | NoSuchAlgorithmException e) {
- return false;
- } finally {
- close(os);
- }
- }
- avatar.size = file.length();
- return true;
- }
-
- public String getAvatarPath(String avatar) {
- return mXmppConnectionService.getFilesDir().getAbsolutePath()+ "/avatars/" + avatar;
- }
-
- public Uri getAvatarUri(String avatar) {
- return Uri.parse("file:" + getAvatarPath(avatar));
- }
-
- public Bitmap cropCenterSquare(Uri image, int size) {
- if (image == null) {
- return null;
- }
- InputStream is = null;
- try {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = calcSampleSize(image, size);
- is = mXmppConnectionService.getContentResolver().openInputStream(image);
- if (is == null) {
- return null;
- }
- Bitmap input = BitmapFactory.decodeStream(is, null, options);
- if (input == null) {
- return null;
- } else {
- input = rotate(input, getRotation(image));
- return cropCenterSquare(input, size);
- }
- } catch (SecurityException e) {
- return null; // happens for example on Android 6.0 if contacts permissions get revoked
- } catch (FileNotFoundException e) {
- return null;
- } finally {
- close(is);
- }
- }
-
- public Bitmap cropCenter(Uri image, int newHeight, int newWidth) {
- if (image == null) {
- return null;
- }
- InputStream is = null;
- try {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = calcSampleSize(image, Math.max(newHeight, newWidth));
- is = mXmppConnectionService.getContentResolver().openInputStream(image);
- if (is == null) {
- return null;
- }
- Bitmap source = BitmapFactory.decodeStream(is, null, options);
- if (source == null) {
- return null;
- }
- int sourceWidth = source.getWidth();
- int sourceHeight = source.getHeight();
- float xScale = (float) newWidth / sourceWidth;
- float yScale = (float) newHeight / sourceHeight;
- float scale = Math.max(xScale, yScale);
- float scaledWidth = scale * sourceWidth;
- float scaledHeight = scale * sourceHeight;
- float left = (newWidth - scaledWidth) / 2;
- float top = (newHeight - scaledHeight) / 2;
-
- RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
- Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(dest);
- canvas.drawBitmap(source, null, targetRect, null);
- if (source != null && !source.isRecycled()) {
- source.recycle();
- }
- return dest;
- } catch (SecurityException e) {
- return null; //android 6.0 with revoked permissions for example
- } catch (FileNotFoundException e) {
- return null;
- } finally {
- close(is);
- }
- }
-
- public Bitmap cropCenterSquare(Bitmap input, int size) {
- int w = input.getWidth();
- int h = input.getHeight();
-
- float scale = Math.max((float) size / h, (float) size / w);
-
- float outWidth = scale * w;
- float outHeight = scale * h;
- float left = (size - outWidth) / 2;
- float top = (size - outHeight) / 2;
- RectF target = new RectF(left, top, left + outWidth, top + outHeight);
-
- Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(output);
- canvas.drawBitmap(input, null, target, null);
- if (input != null && !input.isRecycled()) {
- input.recycle();
- }
- return output;
- }
-
- private int calcSampleSize(Uri image, int size) throws FileNotFoundException, SecurityException {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(image), null, options);
- return calcSampleSize(options, size);
- }
-
- private static int calcSampleSize(File image, int size) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(image.getAbsolutePath(), options);
- return calcSampleSize(options, size);
- }
-
- public static int calcSampleSize(BitmapFactory.Options options, int size) {
- int height = options.outHeight;
- int width = options.outWidth;
- int inSampleSize = 1;
-
- if (height > size || width > size) {
- int halfHeight = height / 2;
- int halfWidth = width / 2;
-
- while ((halfHeight / inSampleSize) > size
- && (halfWidth / inSampleSize) > size) {
- inSampleSize *= 2;
- }
- }
- return inSampleSize;
- }
-
- public Uri getJingleFileUri(Message message) {
- File file = getFile(message);
- return Uri.parse("file://" + file.getAbsolutePath());
- }
-
- public void updateFileParams(Message message) {
- updateFileParams(message,null);
- }
-
- public void updateFileParams(Message message, URL url) {
- DownloadableFile file = getFile(message);
- final String mime = file.getMimeType();
- boolean image = message.getType() == Message.TYPE_IMAGE || (mime != null && mime.startsWith("image/"));
- boolean video = mime != null && mime.startsWith("video/");
- if (image || video) {
- try {
- Dimensions dimensions = image ? getImageDimensions(file) : getVideoDimensions(file);
- if (url == null) {
- message.setBody(Long.toString(file.getSize()) + '|' + dimensions.width + '|' + dimensions.height);
- } else {
- message.setBody(url.toString() + "|" + Long.toString(file.getSize()) + '|' + dimensions.width + '|' + dimensions.height);
- }
- return;
- } catch (NotAVideoFile notAVideoFile) {
- Log.d(Config.LOGTAG,"file with mime type "+file.getMimeType()+" was not a video file");
- //fall threw
- }
- }
- if (url != null) {
- message.setBody(url.toString()+"|"+Long.toString(file.getSize()));
- } else {
- message.setBody(Long.toString(file.getSize()));
- }
-
- }
-
- private Dimensions getImageDimensions(File file) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(file.getAbsolutePath(), options);
- int rotation = getRotation(file);
- boolean rotated = rotation == 90 || rotation == 270;
- int imageHeight = rotated ? options.outWidth : options.outHeight;
- int imageWidth = rotated ? options.outHeight : options.outWidth;
- return new Dimensions(imageHeight, imageWidth);
- }
-
- private Dimensions getVideoDimensions(File file) throws NotAVideoFile {
- MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
- try {
- metadataRetriever.setDataSource(file.getAbsolutePath());
- } catch (Exception e) {
- throw new NotAVideoFile();
- }
- String hasVideo = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
- if (hasVideo == null) {
- throw new NotAVideoFile();
- }
- int rotation = extractRotationFromMediaRetriever(metadataRetriever);
- boolean rotated = rotation == 90 || rotation == 270;
- int height;
- try {
- String h = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
- height = Integer.parseInt(h);
- } catch (Exception e) {
- height = -1;
- }
- int width;
- try {
- String w = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
- width = Integer.parseInt(w);
- } catch (Exception e) {
- width = -1;
- }
- metadataRetriever.release();
- Log.d(Config.LOGTAG,"extracted video dims "+width+"x"+height);
- return rotated ? new Dimensions(width, height) : new Dimensions(height, width);
- }
-
- private int extractRotationFromMediaRetriever(MediaMetadataRetriever metadataRetriever) {
- int rotation;
- if (Build.VERSION.SDK_INT >= 17) {
- String r = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
- try {
- rotation = Integer.parseInt(r);
- } catch (Exception e) {
- rotation = 0;
- }
- } else {
- rotation = 0;
- }
- return rotation;
- }
-
- private class Dimensions {
- public final int width;
- public final int height;
-
- public Dimensions(int height, int width) {
- this.width = width;
- this.height = height;
- }
- }
-
- private class NotAVideoFile extends Exception {
-
- }
-
- public class FileCopyException extends Exception {
- private static final long serialVersionUID = -1010013599132881427L;
- private int resId;
-
- public FileCopyException(int resId) {
- this.resId = resId;
- }
-
- public int getResId() {
- return resId;
- }
- }
-
- public Bitmap getAvatar(String avatar, int size) {
- if (avatar == null) {
- return null;
- }
- Bitmap bm = cropCenter(getAvatarUri(avatar), size, size);
- if (bm == null) {
- return null;
- }
- return bm;
- }
-
- public boolean isFileAvailable(Message message) {
- return getFile(message).exists();
- }
-
- public static void close(Closeable stream) {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- }
- }
- }
-
- public static void close(Socket socket) {
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException e) {
- }
- }
- }
-
-
- public static boolean weOwnFile(Context context, Uri uri) {
- if (uri == null || !ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
- return false;
- } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- return fileIsInFilesDir(context, uri);
- } else {
- return weOwnFileLollipop(uri);
- }
- }
-
-
- /**
- * This is more than hacky but probably way better than doing nothing
- * Further 'optimizations' might contain to get the parents of CacheDir and NoBackupDir
- * and check against those as well
- */
- private static boolean fileIsInFilesDir(Context context, Uri uri) {
- try {
- final String haystack = context.getFilesDir().getParentFile().getCanonicalPath();
- final String needle = new File(uri.getPath()).getCanonicalPath();
- return needle.startsWith(haystack);
- } catch (IOException e) {
- return false;
- }
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- private static boolean weOwnFileLollipop(Uri uri) {
- try {
- File file = new File(uri.getPath());
- FileDescriptor fd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY).getFileDescriptor();
- StructStat st = Os.fstat(fd);
- return st.st_uid == android.os.Process.myUid();
- } catch (FileNotFoundException e) {
- return false;
- } catch (Exception e) {
- return true;
- }
- }
-
- public static Bitmap rotateBitmap(File file, Bitmap bitmap, int orientation) {
-
- if (orientation == 1) {
- return bitmap;
- }
-
- Matrix matrix = new Matrix();
- switch (orientation) {
- case 2:
- matrix.setScale(-1, 1);
- break;
- case 3:
- matrix.setRotate(180);
- break;
- case 4:
- matrix.setRotate(180);
- matrix.postScale(-1, 1);
- break;
- case 5:
- matrix.setRotate(90);
- matrix.postScale(-1, 1);
- break;
- case 6:
- matrix.setRotate(90);
- break;
- case 7:
- matrix.setRotate(-90);
- matrix.postScale(-1, 1);
- break;
- case 8:
- matrix.setRotate(-90);
- break;
- default:
- return bitmap;
- }
-
- try {
- Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
- bitmap.recycle();
- return oriented;
- } catch (OutOfMemoryError e) {
- e.printStackTrace();
- return bitmap;
- }
- }
+ private static final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+
+ public static final String CONVERSATIONS_FILE_PROVIDER = "de.pixart.messenger.files";
+
+ private XmppConnectionService mXmppConnectionService;
+
+ public FileBackend(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ private void createNoMedia() {
+ final File nomedia_files = new File(getConversationsFileDirectory() + ".nomedia");
+ final File nomedia_audios = new File(getConversationsAudioDirectory() + ".nomedia");
+ if (!nomedia_files.exists()) {
+ try {
+ nomedia_files.createNewFile();
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, "could not create nomedia file for files directory");
+ }
+ }
+ if (!nomedia_audios.exists()) {
+ try {
+ nomedia_audios.createNewFile();
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, "could not create nomedia file for audio directory");
+ }
+ }
+ }
+
+ public void updateMediaScanner(File file) {
+ if (file.getAbsolutePath().startsWith(getConversationsImageDirectory())
+ || file.getAbsolutePath().startsWith(getConversationsVideoDirectory())) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ intent.setData(Uri.fromFile(file));
+ mXmppConnectionService.sendBroadcast(intent);
+ } else {
+ createNoMedia();
+ }
+ }
+
+ public boolean deleteFile(Message message) {
+ File file = getFile(message);
+ if (file.delete()) {
+ updateMediaScanner(file);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public DownloadableFile getFile(Message message) {
+ return getFile(message, true);
+ }
+
+ public DownloadableFile getFile(Message message, boolean decrypted) {
+ final boolean encrypted = !decrypted
+ && (message.getEncryption() == Message.ENCRYPTION_PGP
+ || message.getEncryption() == Message.ENCRYPTION_DECRYPTED);
+ final DownloadableFile file;
+ String path = message.getRelativeFilePath();
+ if (path == null) {
+ String filename = fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4);
+ path = filename;
+ }
+ if (path.startsWith("/")) {
+ file = new DownloadableFile(path);
+ } else {
+ String mime = message.getMimeType();
+ if (mime != null && mime.startsWith("image")) {
+ file = new DownloadableFile(getConversationsImageDirectory() + path);
+ } else if (mime != null && mime.startsWith("video")) {
+ file = new DownloadableFile(getConversationsVideoDirectory() + path);
+ } else if (mime != null && mime.startsWith("audio")) {
+ file = new DownloadableFile(getConversationsAudioDirectory() + path);
+ } else {
+ file = new DownloadableFile(getConversationsFileDirectory() + path);
+ }
+ }
+ if (encrypted) {
+ return new DownloadableFile(getConversationsFileDirectory() + file.getName() + ".pgp");
+ } else {
+ return file;
+ }
+ }
+
+ private static long getFileSize(Context context, Uri uri) {
+ Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ return cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
+ } else {
+ return -1;
+ }
+ }
+
+ public static boolean allFilesUnderSize(Context context, List<Uri> uris, long max) {
+ if (max <= 0) {
+ Log.d(Config.LOGTAG, "server did not report max file size for http upload");
+ return true; //exception to be compatible with HTTP Upload < v0.2
+ }
+ for (Uri uri : uris) {
+ if (FileBackend.getFileSize(context, uri) > max) {
+ Log.d(Config.LOGTAG, "not all files are under " + max + " bytes. suggesting falling back to jingle");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static String getConversationsFileDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pix-Art Messenger/files/";
+ }
+
+ public static String getConversationsImageDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pix-Art Messenger/images/";
+ }
+
+ public static String getConversationsVideoDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pix-Art Messenger/videos/";
+ }
+
+ public static String getConversationsAudioDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pix-Art Messenger/audios/";
+ }
+
+ public static String getConversationsDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pix-Art Messenger/";
+ }
+
+ public Bitmap resize(Bitmap originalBitmap, int size) {
+ int w = originalBitmap.getWidth();
+ int h = originalBitmap.getHeight();
+ if (Math.max(w, h) > size) {
+ int scalledW;
+ int scalledH;
+ if (w <= h) {
+ scalledW = (int) (w / ((double) h / size));
+ scalledH = size;
+ } else {
+ scalledW = size;
+ scalledH = (int) (h / ((double) w / size));
+ }
+ Bitmap result = Bitmap.createScaledBitmap(originalBitmap, scalledW, scalledH, true);
+ if (originalBitmap != null && !originalBitmap.isRecycled()) {
+ originalBitmap.recycle();
+ }
+ return result;
+ } else {
+ return originalBitmap;
+ }
+ }
+
+ public static Bitmap rotate(Bitmap bitmap, int degree) {
+ if (degree == 0) {
+ return bitmap;
+ }
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+ Matrix mtx = new Matrix();
+ mtx.postRotate(degree);
+ Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
+ if (bitmap != null && !bitmap.isRecycled()) {
+ bitmap.recycle();
+ }
+ return result;
+ }
+
+ public boolean useImageAsIs(Uri uri) {
+ String path = getOriginalPath(uri);
+ if (path == null) {
+ return false;
+ }
+ File file = new File(path);
+ long size = file.length();
+ if (size == 0 || size >= Config.IMAGE_MAX_SIZE) {
+ return false;
+ }
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ try {
+ BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(uri), null, options);
+ if (options == null || options.outMimeType == null || options.outHeight <= 0 || options.outWidth <= 0) {
+ return false;
+ }
+ return (options.outWidth <= Config.IMAGE_SIZE && options.outHeight <= Config.IMAGE_SIZE && options.outMimeType.contains(Config.IMAGE_FORMAT.name().toLowerCase()));
+ } catch (FileNotFoundException e) {
+ return false;
+ }
+ }
+
+ public String getOriginalPath(Uri uri) {
+ return FileUtils.getPath(mXmppConnectionService, uri);
+ }
+
+ public void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException {
+ Log.d(Config.LOGTAG, "copy file (" + uri.toString() + ") to private storage " + file.getAbsolutePath());
+ file.getParentFile().mkdirs();
+ OutputStream os = null;
+ InputStream is = null;
+ try {
+ file.createNewFile();
+ os = new FileOutputStream(file);
+ is = mXmppConnectionService.getContentResolver().openInputStream(uri);
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = is.read(buffer)) > 0) {
+ try {
+ os.write(buffer, 0, length);
+ } catch (IOException e) {
+ throw new FileWriterException();
+ }
+ }
+ try {
+ os.flush();
+ } catch (IOException e) {
+ throw new FileWriterException();
+ }
+ } catch (FileNotFoundException e) {
+ throw new FileCopyException(R.string.error_file_not_found);
+ } catch (FileWriterException e) {
+ throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new FileCopyException(R.string.error_io_exception);
+ } finally {
+ close(os);
+ close(is);
+ }
+ }
+
+ public void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
+ String mime = mXmppConnectionService.getContentResolver().getType(uri);
+ Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage (mime=" + mime + ")");
+ String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
+ if (extension == null) {
+ extension = getExtensionFromUri(uri);
+ }
+ String filename = fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4);
+ message.setRelativeFilePath(filename + "." + extension);
+ copyFileToPrivateStorage(mXmppConnectionService.getFileBackend().getFile(message), uri);
+ }
+
+ private String getExtensionFromUri(Uri uri) {
+ String[] projection = {MediaStore.MediaColumns.DATA};
+ String filename = null;
+ Cursor cursor = mXmppConnectionService.getContentResolver().query(uri, projection, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ filename = cursor.getString(0);
+ }
+ } catch (Exception e) {
+ filename = null;
+ } finally {
+ cursor.close();
+ }
+ }
+ int pos = filename == null ? -1 : filename.lastIndexOf('.');
+ return pos > 0 ? filename.substring(pos + 1) : null;
+ }
+
+ private void copyImageToPrivateStorage(File file, Uri image, int sampleSize) throws FileCopyException {
+ file.getParentFile().mkdirs();
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ if (!file.exists() && !file.createNewFile()) {
+ throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
+ }
+ is = mXmppConnectionService.getContentResolver().openInputStream(image);
+ if (is == null) {
+ throw new FileCopyException(R.string.error_not_an_image_file);
+ }
+ Bitmap originalBitmap;
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ int inSampleSize = (int) Math.pow(2, sampleSize);
+ Log.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize);
+ options.inSampleSize = inSampleSize;
+ originalBitmap = BitmapFactory.decodeStream(is, null, options);
+ is.close();
+ if (originalBitmap == null) {
+ throw new FileCopyException(R.string.error_not_an_image_file);
+ }
+ Bitmap scaledBitmap = resize(originalBitmap, Config.IMAGE_SIZE);
+ int rotation = getRotation(image);
+ scaledBitmap = rotate(scaledBitmap, rotation);
+ boolean targetSizeReached = false;
+ int quality = Config.IMAGE_QUALITY;
+ while (!targetSizeReached) {
+ os = new FileOutputStream(file);
+ boolean success = scaledBitmap.compress(Config.IMAGE_FORMAT, quality, os);
+ if (!success) {
+ throw new FileCopyException(R.string.error_compressing_image);
+ }
+ os.flush();
+ targetSizeReached = file.length() <= Config.IMAGE_MAX_SIZE || quality <= 50;
+ quality -= 5;
+ }
+ scaledBitmap.recycle();
+ } catch (FileNotFoundException e) {
+ throw new FileCopyException(R.string.error_file_not_found);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new FileCopyException(R.string.error_io_exception);
+ } catch (SecurityException e) {
+ throw new FileCopyException(R.string.error_security_exception_during_image_copy);
+ } catch (OutOfMemoryError e) {
+ ++sampleSize;
+ if (sampleSize <= 3) {
+ copyImageToPrivateStorage(file, image, sampleSize);
+ } else {
+ throw new FileCopyException(R.string.error_out_of_memory);
+ }
+ } finally {
+ close(os);
+ close(is);
+ }
+ }
+
+ public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException {
+ Log.d(Config.LOGTAG, "copy image (" + image.toString() + ") to private storage " + file.getAbsolutePath());
+ copyImageToPrivateStorage(file, image, 0);
+ }
+
+ public void copyImageToPrivateStorage(Message message, Uri image) throws FileCopyException {
+ String filename = fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4);
+ switch (Config.IMAGE_FORMAT) {
+ case JPEG:
+ message.setRelativeFilePath(filename + ".jpg");
+ break;
+ case PNG:
+ message.setRelativeFilePath(filename + ".png");
+ break;
+ case WEBP:
+ message.setRelativeFilePath(filename + ".webp");
+ break;
+ }
+ copyImageToPrivateStorage(getFile(message), image);
+ updateFileParams(message);
+ }
+
+ private int getRotation(File file) {
+ return getRotation(Uri.parse("file://" + file.getAbsolutePath()));
+ }
+
+ private int getRotation(Uri image) {
+ InputStream is = null;
+ try {
+ is = mXmppConnectionService.getContentResolver().openInputStream(image);
+ return ExifHelper.getOrientation(is);
+ } catch (FileNotFoundException e) {
+ return 0;
+ } finally {
+ close(is);
+ }
+ }
+
+ public Bitmap getThumbnail(Message message, int size, boolean cacheOnly) throws FileNotFoundException {
+ final String uuid = message.getUuid();
+ final LruCache<String, Bitmap> cache = mXmppConnectionService.getBitmapCache();
+ Bitmap thumbnail = cache.get(uuid);
+ if ((thumbnail == null) && (!cacheOnly)) {
+ synchronized (cache) {
+ thumbnail = cache.get(uuid);
+ if (thumbnail != null) {
+ return thumbnail;
+ }
+ DownloadableFile file = getFile(message);
+ if (file.getMimeType().startsWith("video/")) {
+ thumbnail = getVideoPreview(file, size);
+ } else {
+ Bitmap fullsize = getFullsizeImagePreview(file, size);
+ if (fullsize == null) {
+ throw new FileNotFoundException();
+ }
+ thumbnail = resize(fullsize, size);
+ thumbnail = rotate(thumbnail, getRotation(file));
+ }
+ this.mXmppConnectionService.getBitmapCache().put(uuid, thumbnail);
+ }
+ }
+ return thumbnail;
+ }
+
+ private Bitmap getFullsizeImagePreview(File file, int size) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(file, size);
+ try {
+ return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ } catch (OutOfMemoryError e) {
+ options.inSampleSize *= 2;
+ return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ }
+ }
+
+ private Bitmap getVideoPreview(File file, int size) {
+ MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
+ Bitmap frame;
+ try {
+ metadataRetriever.setDataSource(file.getAbsolutePath());
+ frame = metadataRetriever.getFrameAtTime(0);
+ metadataRetriever.release();
+ frame = resize(frame, size);
+ } catch (IllegalArgumentException | NullPointerException e) {
+ frame = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ frame.eraseColor(0xff000000);
+ }
+ Canvas canvas = new Canvas(frame);
+ Bitmap play = BitmapFactory.decodeResource(mXmppConnectionService.getResources(), R.drawable.play_video);
+ float x = (frame.getWidth() - play.getWidth()) / 2.0f;
+ float y = (frame.getHeight() - play.getHeight()) / 2.0f;
+ canvas.drawBitmap(play, x, y, null);
+ return frame;
+ }
+
+ private static String getTakePhotoPath() {
+ return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/";
+ }
+
+ public Uri getTakePhotoUri() {
+ File file = new File(getTakePhotoPath() + "IMG_" + fileDateFormat.format(new Date()) + ".jpg");
+ file.getParentFile().mkdirs();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return FileProvider.getUriForFile(mXmppConnectionService, CONVERSATIONS_FILE_PROVIDER, file);
+ } else {
+ return Uri.fromFile(file);
+ }
+ }
+
+ public static Uri getIndexableTakePhotoUri(Uri original) {
+ if ("file".equals(original.getScheme())) {
+ return original;
+ } else {
+ List<String> segments = original.getPathSegments();
+ return Uri.parse("file://" + getTakePhotoPath() + segments.get(segments.size() - 1));
+ }
+ }
+
+ public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) {
+ try {
+ Avatar avatar = new Avatar();
+ Bitmap bm = cropCenterSquare(image, size);
+ if (bm == null) {
+ return null;
+ }
+ ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
+ Base64OutputStream mBase64OutputStream = new Base64OutputStream(
+ mByteArrayOutputStream, Base64.DEFAULT);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ DigestOutputStream mDigestOutputStream = new DigestOutputStream(
+ mBase64OutputStream, digest);
+ if (!bm.compress(format, 75, mDigestOutputStream)) {
+ return null;
+ }
+ mDigestOutputStream.flush();
+ mDigestOutputStream.close();
+ avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ avatar.image = new String(mByteArrayOutputStream.toByteArray());
+ return avatar;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public Avatar getStoredPepAvatar(String hash) {
+ if (hash == null) {
+ return null;
+ }
+ Avatar avatar = new Avatar();
+ File file = new File(getAvatarPath(hash));
+ FileInputStream is = null;
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ is = new FileInputStream(file);
+ ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
+ Base64OutputStream mBase64OutputStream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ DigestOutputStream os = new DigestOutputStream(mBase64OutputStream, digest);
+ byte[] buffer = new byte[4096];
+ int length;
+ while ((length = is.read(buffer)) > 0) {
+ os.write(buffer, 0, length);
+ }
+ os.flush();
+ os.close();
+ avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ avatar.image = new String(mByteArrayOutputStream.toByteArray());
+ avatar.height = options.outHeight;
+ avatar.width = options.outWidth;
+ return avatar;
+ } catch (IOException e) {
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } finally {
+ close(is);
+ }
+ }
+
+ public boolean isAvatarCached(Avatar avatar) {
+ File file = new File(getAvatarPath(avatar.getFilename()));
+ return file.exists();
+ }
+
+ public boolean save(Avatar avatar) {
+ File file;
+ if (isAvatarCached(avatar)) {
+ file = new File(getAvatarPath(avatar.getFilename()));
+ } else {
+ String filename = getAvatarPath(avatar.getFilename());
+ file = new File(filename + ".tmp");
+ file.getParentFile().mkdirs();
+ OutputStream os = null;
+ try {
+ file.createNewFile();
+ os = new FileOutputStream(file);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ DigestOutputStream mDigestOutputStream = new DigestOutputStream(os, digest);
+ mDigestOutputStream.write(avatar.getImageAsBytes());
+ mDigestOutputStream.flush();
+ mDigestOutputStream.close();
+ String sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ if (sha1sum.equals(avatar.sha1sum)) {
+ file.renameTo(new File(filename));
+ } else {
+ Log.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner);
+ file.delete();
+ return false;
+ }
+ } catch (IllegalArgumentException | IOException | NoSuchAlgorithmException e) {
+ return false;
+ } finally {
+ close(os);
+ }
+ }
+ avatar.size = file.length();
+ return true;
+ }
+
+ public String getAvatarPath(String avatar) {
+ return mXmppConnectionService.getFilesDir().getAbsolutePath() + "/avatars/" + avatar;
+ }
+
+ public Uri getAvatarUri(String avatar) {
+ return Uri.parse("file:" + getAvatarPath(avatar));
+ }
+
+ public Bitmap cropCenterSquare(Uri image, int size) {
+ if (image == null) {
+ return null;
+ }
+ InputStream is = null;
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(image, size);
+ is = mXmppConnectionService.getContentResolver().openInputStream(image);
+ if (is == null) {
+ return null;
+ }
+ Bitmap input = BitmapFactory.decodeStream(is, null, options);
+ if (input == null) {
+ return null;
+ } else {
+ input = rotate(input, getRotation(image));
+ return cropCenterSquare(input, size);
+ }
+ } catch (SecurityException e) {
+ return null; // happens for example on Android 6.0 if contacts permissions get revoked
+ } catch (FileNotFoundException e) {
+ return null;
+ } finally {
+ close(is);
+ }
+ }
+
+ public Bitmap cropCenter(Uri image, int newHeight, int newWidth) {
+ if (image == null) {
+ return null;
+ }
+ InputStream is = null;
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(image, Math.max(newHeight, newWidth));
+ is = mXmppConnectionService.getContentResolver().openInputStream(image);
+ if (is == null) {
+ return null;
+ }
+ Bitmap source = BitmapFactory.decodeStream(is, null, options);
+ if (source == null) {
+ return null;
+ }
+ int sourceWidth = source.getWidth();
+ int sourceHeight = source.getHeight();
+ float xScale = (float) newWidth / sourceWidth;
+ float yScale = (float) newHeight / sourceHeight;
+ float scale = Math.max(xScale, yScale);
+ float scaledWidth = scale * sourceWidth;
+ float scaledHeight = scale * sourceHeight;
+ float left = (newWidth - scaledWidth) / 2;
+ float top = (newHeight - scaledHeight) / 2;
+
+ RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
+ Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(dest);
+ canvas.drawBitmap(source, null, targetRect, null);
+ if (source != null && !source.isRecycled()) {
+ source.recycle();
+ }
+ return dest;
+ } catch (SecurityException e) {
+ return null; //android 6.0 with revoked permissions for example
+ } catch (FileNotFoundException e) {
+ return null;
+ } finally {
+ close(is);
+ }
+ }
+
+ public Bitmap cropCenterSquare(Bitmap input, int size) {
+ int w = input.getWidth();
+ int h = input.getHeight();
+
+ float scale = Math.max((float) size / h, (float) size / w);
+
+ float outWidth = scale * w;
+ float outHeight = scale * h;
+ float left = (size - outWidth) / 2;
+ float top = (size - outHeight) / 2;
+ RectF target = new RectF(left, top, left + outWidth, top + outHeight);
+
+ Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(output);
+ canvas.drawBitmap(input, null, target, null);
+ if (input != null && !input.isRecycled()) {
+ input.recycle();
+ }
+ return output;
+ }
+
+ private int calcSampleSize(Uri image, int size) throws FileNotFoundException, SecurityException {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(image), null, options);
+ return calcSampleSize(options, size);
+ }
+
+ private static int calcSampleSize(File image, int size) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(image.getAbsolutePath(), options);
+ return calcSampleSize(options, size);
+ }
+
+ public static int calcSampleSize(BitmapFactory.Options options, int size) {
+ int height = options.outHeight;
+ int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > size || width > size) {
+ int halfHeight = height / 2;
+ int halfWidth = width / 2;
+
+ while ((halfHeight / inSampleSize) > size
+ && (halfWidth / inSampleSize) > size) {
+ inSampleSize *= 2;
+ }
+ }
+ return inSampleSize;
+ }
+
+ public Uri getJingleFileUri(Message message) {
+ File file = getFile(message);
+ return Uri.parse("file://" + file.getAbsolutePath());
+ }
+
+ public void updateFileParams(Message message) {
+ updateFileParams(message, null);
+ }
+
+ public void updateFileParams(Message message, URL url) {
+ DownloadableFile file = getFile(message);
+ final String mime = file.getMimeType();
+ boolean image = message.getType() == Message.TYPE_IMAGE || (mime != null && mime.startsWith("image/"));
+ boolean video = mime != null && mime.startsWith("video/");
+ if (image || video) {
+ try {
+ Dimensions dimensions = image ? getImageDimensions(file) : getVideoDimensions(file);
+ if (url == null) {
+ message.setBody(Long.toString(file.getSize()) + '|' + dimensions.width + '|' + dimensions.height);
+ } else {
+ message.setBody(url.toString() + "|" + Long.toString(file.getSize()) + '|' + dimensions.width + '|' + dimensions.height);
+ }
+ return;
+ } catch (NotAVideoFile notAVideoFile) {
+ Log.d(Config.LOGTAG, "file with mime type " + file.getMimeType() + " was not a video file");
+ //fall threw
+ }
+ }
+ if (url != null) {
+ message.setBody(url.toString() + "|" + Long.toString(file.getSize()));
+ } else {
+ message.setBody(Long.toString(file.getSize()));
+ }
+
+ }
+
+ private Dimensions getImageDimensions(File file) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ int rotation = getRotation(file);
+ boolean rotated = rotation == 90 || rotation == 270;
+ int imageHeight = rotated ? options.outWidth : options.outHeight;
+ int imageWidth = rotated ? options.outHeight : options.outWidth;
+ return new Dimensions(imageHeight, imageWidth);
+ }
+
+ private Dimensions getVideoDimensions(File file) throws NotAVideoFile {
+ MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
+ try {
+ metadataRetriever.setDataSource(file.getAbsolutePath());
+ } catch (Exception e) {
+ throw new NotAVideoFile();
+ }
+ String hasVideo = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
+ if (hasVideo == null) {
+ throw new NotAVideoFile();
+ }
+ int rotation = extractRotationFromMediaRetriever(metadataRetriever);
+ boolean rotated = rotation == 90 || rotation == 270;
+ int height;
+ try {
+ String h = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+ height = Integer.parseInt(h);
+ } catch (Exception e) {
+ height = -1;
+ }
+ int width;
+ try {
+ String w = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
+ width = Integer.parseInt(w);
+ } catch (Exception e) {
+ width = -1;
+ }
+ metadataRetriever.release();
+ Log.d(Config.LOGTAG, "extracted video dims " + width + "x" + height);
+ return rotated ? new Dimensions(width, height) : new Dimensions(height, width);
+ }
+
+ private int extractRotationFromMediaRetriever(MediaMetadataRetriever metadataRetriever) {
+ int rotation;
+ if (Build.VERSION.SDK_INT >= 17) {
+ String r = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+ try {
+ rotation = Integer.parseInt(r);
+ } catch (Exception e) {
+ rotation = 0;
+ }
+ } else {
+ rotation = 0;
+ }
+ return rotation;
+ }
+
+ private class Dimensions {
+ public final int width;
+ public final int height;
+
+ public Dimensions(int height, int width) {
+ this.width = width;
+ this.height = height;
+ }
+ }
+
+ private class NotAVideoFile extends Exception {
+
+ }
+
+ public class FileCopyException extends Exception {
+ private static final long serialVersionUID = -1010013599132881427L;
+ private int resId;
+
+ public FileCopyException(int resId) {
+ this.resId = resId;
+ }
+
+ public int getResId() {
+ return resId;
+ }
+ }
+
+ public Bitmap getAvatar(String avatar, int size) {
+ if (avatar == null) {
+ return null;
+ }
+ Bitmap bm = cropCenter(getAvatarUri(avatar), size, size);
+ if (bm == null) {
+ return null;
+ }
+ return bm;
+ }
+
+ public boolean isFileAvailable(Message message) {
+ return getFile(message).exists();
+ }
+
+ public static void close(Closeable stream) {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ public static void close(Socket socket) {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+
+ public static boolean weOwnFile(Context context, Uri uri) {
+ if (uri == null || !ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
+ return false;
+ } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ return fileIsInFilesDir(context, uri);
+ } else {
+ return weOwnFileLollipop(uri);
+ }
+ }
+
+
+ /**
+ * This is more than hacky but probably way better than doing nothing
+ * Further 'optimizations' might contain to get the parents of CacheDir and NoBackupDir
+ * and check against those as well
+ */
+ private static boolean fileIsInFilesDir(Context context, Uri uri) {
+ try {
+ final String haystack = context.getFilesDir().getParentFile().getCanonicalPath();
+ final String needle = new File(uri.getPath()).getCanonicalPath();
+ return needle.startsWith(haystack);
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private static boolean weOwnFileLollipop(Uri uri) {
+ try {
+ File file = new File(uri.getPath());
+ FileDescriptor fd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY).getFileDescriptor();
+ StructStat st = Os.fstat(fd);
+ return st.st_uid == android.os.Process.myUid();
+ } catch (FileNotFoundException e) {
+ return false;
+ } catch (Exception e) {
+ return true;
+ }
+ }
+
+ public static Bitmap rotateBitmap(File file, Bitmap bitmap, int orientation) {
+
+ if (orientation == 1) {
+ return bitmap;
+ }
+
+ Matrix matrix = new Matrix();
+ switch (orientation) {
+ case 2:
+ matrix.setScale(-1, 1);
+ break;
+ case 3:
+ matrix.setRotate(180);
+ break;
+ case 4:
+ matrix.setRotate(180);
+ matrix.postScale(-1, 1);
+ break;
+ case 5:
+ matrix.setRotate(90);
+ matrix.postScale(-1, 1);
+ break;
+ case 6:
+ matrix.setRotate(90);
+ break;
+ case 7:
+ matrix.setRotate(-90);
+ matrix.postScale(-1, 1);
+ break;
+ case 8:
+ matrix.setRotate(-90);
+ break;
+ default:
+ return bitmap;
+ }
+
+ try {
+ Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+ bitmap.recycle();
+ return oriented;
+ } catch (OutOfMemoryError e) {
+ e.printStackTrace();
+ return bitmap;
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/persistance/OnPhoneContactsMerged.java b/src/main/java/de/pixart/messenger/persistance/OnPhoneContactsMerged.java
index 85e852b5d..d5253f227 100644
--- a/src/main/java/de/pixart/messenger/persistance/OnPhoneContactsMerged.java
+++ b/src/main/java/de/pixart/messenger/persistance/OnPhoneContactsMerged.java
@@ -1,5 +1,5 @@
package de.pixart.messenger.persistance;
public interface OnPhoneContactsMerged {
- public void phoneContactsMerged();
+ public void phoneContactsMerged();
}
diff --git a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
index 6b0bfdc35..fe101edc6 100644
--- a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
+++ b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
@@ -34,17 +34,17 @@ import de.pixart.messenger.Config;
import de.pixart.messenger.entities.DownloadableFile;
public class AbstractConnectionManager {
- protected XmppConnectionService mXmppConnectionService;
+ protected XmppConnectionService mXmppConnectionService;
- public AbstractConnectionManager(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
+ public AbstractConnectionManager(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
- public XmppConnectionService getXmppConnectionService() {
- return this.mXmppConnectionService;
- }
+ public XmppConnectionService getXmppConnectionService() {
+ return this.mXmppConnectionService;
+ }
- public long getAutoAcceptFileSize() {
+ public long getAutoAcceptFileSize() {
String config = "0";
if (mXmppConnectionService.isWIFI()) {
config = this.mXmppConnectionService.getPreferences().getString(
@@ -57,96 +57,96 @@ public class AbstractConnectionManager {
"auto_accept_file_size_roaming", "1");
}
try {
- return Long.parseLong(config);
- } catch (NumberFormatException e) {
- return 1048576;
- }
- }
+ return Long.parseLong(config);
+ } catch (NumberFormatException e) {
+ return 1048576;
+ }
+ }
- public boolean hasStoragePermission() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- return mXmppConnectionService.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
- } else {
- return true;
- }
- }
+ public boolean hasStoragePermission() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ return mXmppConnectionService.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
+ } else {
+ return true;
+ }
+ }
- public static Pair<InputStream,Integer> createInputStream(DownloadableFile file, boolean gcm) throws FileNotFoundException {
- FileInputStream is;
- int size;
- is = new FileInputStream(file);
- size = (int) file.getSize();
- if (file.getKey() == null) {
- return new Pair<InputStream,Integer>(is,size);
- }
- try {
- if (gcm) {
- AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
- cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
- InputStream cis = new org.bouncycastle.crypto.io.CipherInputStream(is, cipher);
- return new Pair<>(cis, cipher.getOutputSize(size));
- } else {
- IvParameterSpec ips = new IvParameterSpec(file.getIv());
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
- Log.d(Config.LOGTAG, "opening encrypted input stream");
- final int s = Config.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE ? size : (size / 16 + 1) * 16;
- return new Pair<InputStream,Integer>(new CipherInputStream(is, cipher),s);
- }
- } catch (InvalidKeyException e) {
- return null;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } catch (NoSuchPaddingException e) {
- return null;
- } catch (InvalidAlgorithmParameterException e) {
- return null;
- }
- }
+ public static Pair<InputStream, Integer> createInputStream(DownloadableFile file, boolean gcm) throws FileNotFoundException {
+ FileInputStream is;
+ int size;
+ is = new FileInputStream(file);
+ size = (int) file.getSize();
+ if (file.getKey() == null) {
+ return new Pair<InputStream, Integer>(is, size);
+ }
+ try {
+ if (gcm) {
+ AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
+ cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
+ InputStream cis = new org.bouncycastle.crypto.io.CipherInputStream(is, cipher);
+ return new Pair<>(cis, cipher.getOutputSize(size));
+ } else {
+ IvParameterSpec ips = new IvParameterSpec(file.getIv());
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
+ Log.d(Config.LOGTAG, "opening encrypted input stream");
+ final int s = Config.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE ? size : (size / 16 + 1) * 16;
+ return new Pair<InputStream, Integer>(new CipherInputStream(is, cipher), s);
+ }
+ } catch (InvalidKeyException e) {
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (NoSuchPaddingException e) {
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ return null;
+ }
+ }
- public static OutputStream createAppendedOutputStream(DownloadableFile file) {
- return createOutputStream(file, false, true);
- }
+ public static OutputStream createAppendedOutputStream(DownloadableFile file) {
+ return createOutputStream(file, false, true);
+ }
- public static OutputStream createOutputStream(DownloadableFile file, boolean gcm) {
- return createOutputStream(file, gcm, false);
- }
+ public static OutputStream createOutputStream(DownloadableFile file, boolean gcm) {
+ return createOutputStream(file, gcm, false);
+ }
- private static OutputStream createOutputStream(DownloadableFile file, boolean gcm, boolean append) {
- FileOutputStream os;
- try {
- os = new FileOutputStream(file, append);
- if (file.getKey() == null) {
- return os;
- }
- } catch (FileNotFoundException e) {
- return null;
- }
- try {
- if (gcm) {
- AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
- cipher.init(false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
- return new org.bouncycastle.crypto.io.CipherOutputStream(os, cipher);
- } else {
- IvParameterSpec ips = new IvParameterSpec(file.getIv());
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
- Log.d(Config.LOGTAG, "opening encrypted output stream");
- return new CipherOutputStream(os, cipher);
- }
- } catch (InvalidKeyException e) {
- return null;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } catch (NoSuchPaddingException e) {
- return null;
- } catch (InvalidAlgorithmParameterException e) {
- return null;
- }
- }
+ private static OutputStream createOutputStream(DownloadableFile file, boolean gcm, boolean append) {
+ FileOutputStream os;
+ try {
+ os = new FileOutputStream(file, append);
+ if (file.getKey() == null) {
+ return os;
+ }
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ try {
+ if (gcm) {
+ AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
+ cipher.init(false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
+ return new org.bouncycastle.crypto.io.CipherOutputStream(os, cipher);
+ } else {
+ IvParameterSpec ips = new IvParameterSpec(file.getIv());
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
+ Log.d(Config.LOGTAG, "opening encrypted output stream");
+ return new CipherOutputStream(os, cipher);
+ }
+ } catch (InvalidKeyException e) {
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (NoSuchPaddingException e) {
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ return null;
+ }
+ }
- public PowerManager.WakeLock createWakeLock(String name) {
- PowerManager powerManager = (PowerManager) mXmppConnectionService.getSystemService(Context.POWER_SERVICE);
- return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,name);
- }
+ public PowerManager.WakeLock createWakeLock(String name) {
+ PowerManager powerManager = (PowerManager) mXmppConnectionService.getSystemService(Context.POWER_SERVICE);
+ return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/AlarmReceiver.java b/src/main/java/de/pixart/messenger/services/AlarmReceiver.java
index 0fd3ba049..e20b567dd 100644
--- a/src/main/java/de/pixart/messenger/services/AlarmReceiver.java
+++ b/src/main/java/de/pixart/messenger/services/AlarmReceiver.java
@@ -7,7 +7,7 @@ import android.util.Log;
import de.pixart.messenger.Config;
-public class AlarmReceiver extends BroadcastReceiver{
+public class AlarmReceiver extends BroadcastReceiver {
public static final int SCHEDULE_ALARM_REQUEST_CODE = 523976483;
@Override
diff --git a/src/main/java/de/pixart/messenger/services/AvatarService.java b/src/main/java/de/pixart/messenger/services/AvatarService.java
index 943516271..4e7c69b22 100644
--- a/src/main/java/de/pixart/messenger/services/AvatarService.java
+++ b/src/main/java/de/pixart/messenger/services/AvatarService.java
@@ -26,406 +26,406 @@ import de.pixart.messenger.xmpp.XmppConnection;
public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
- private static final int FG_COLOR = 0xFFFAFAFA;
- private static final int TRANSPARENT = 0x00000000;
- private static final int PLACEHOLDER_COLOR = 0xFF202020;
-
- private static final String PREFIX_CONTACT = "contact";
- private static final String PREFIX_CONVERSATION = "conversation";
- private static final String PREFIX_ACCOUNT = "account";
- private static final String PREFIX_GENERIC = "generic";
-
- final private ArrayList<Integer> sizes = new ArrayList<>();
-
- protected XmppConnectionService mXmppConnectionService = null;
-
- public AvatarService(XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
-
- private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
- final String KEY = key(contact, size);
- Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
- if (avatar != null || cachedOnly) {
- return avatar;
- }
- if (avatar == null && contact.getAvatar() != null) {
- avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatar(), size);
- }
- if (avatar == null && contact.getProfilePhoto() != null) {
- avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size);
- }
- if (avatar == null) {
+ private static final int FG_COLOR = 0xFFFAFAFA;
+ private static final int TRANSPARENT = 0x00000000;
+ private static final int PLACEHOLDER_COLOR = 0xFF202020;
+
+ private static final String PREFIX_CONTACT = "contact";
+ private static final String PREFIX_CONVERSATION = "conversation";
+ private static final String PREFIX_ACCOUNT = "account";
+ private static final String PREFIX_GENERIC = "generic";
+
+ final private ArrayList<Integer> sizes = new ArrayList<>();
+
+ protected XmppConnectionService mXmppConnectionService = null;
+
+ public AvatarService(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
+ final String KEY = key(contact, size);
+ Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
+ if (avatar != null || cachedOnly) {
+ return avatar;
+ }
+ if (avatar == null && contact.getAvatar() != null) {
+ avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatar(), size);
+ }
+ if (avatar == null && contact.getProfilePhoto() != null) {
+ avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size);
+ }
+ if (avatar == null) {
avatar = get(contact.getDisplayName(), size, cachedOnly);
- }
- this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
- return avatar;
- }
-
- public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) {
- Contact c = user.getContact();
- if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null || user.getAvatar() == null)) {
- return get(c, size, cachedOnly);
- } else {
- return getImpl(user, size, cachedOnly);
- }
- }
-
- private Bitmap getImpl(final MucOptions.User user, final int size, boolean cachedOnly) {
- final String KEY = key(user, size);
- Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
- if (avatar != null || cachedOnly) {
- return avatar;
- }
- if (user.getAvatar() != null) {
- avatar = mXmppConnectionService.getFileBackend().getAvatar(user.getAvatar(), size);
- }
- if (avatar == null) {
- Contact contact = user.getContact();
- if (contact != null) {
- avatar = get(contact, size, cachedOnly);
- } else {
- avatar = get(user.getName(), size, cachedOnly);
- }
- }
- this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
- return avatar;
- }
-
- public void clear(Contact contact) {
- synchronized (this.sizes) {
- for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(
- key(contact, size));
- }
- }
- for(Conversation conversation : mXmppConnectionService.findAllConferencesWith(contact)) {
- clear(conversation);
- }
- }
-
- private String key(Contact contact, int size) {
- synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
- }
- return PREFIX_CONTACT + "_" + contact.getAccount().getJid().toBareJid() + "_"
- + contact.getJid() + "_" + String.valueOf(size);
- }
-
- private String key(MucOptions.User user, int size) {
- synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
- }
- return PREFIX_CONTACT + "_" + user.getAccount().getJid().toBareJid() + "_"
- + user.getFullJid() + "_" + String.valueOf(size);
- }
-
- public Bitmap get(ListItem item, int size) {
- return get(item,size,false);
- }
-
- public Bitmap get(ListItem item, int size, boolean cachedOnly) {
- if (item instanceof Contact) {
- return get((Contact) item, size,cachedOnly);
- } else if (item instanceof Bookmark) {
- Bookmark bookmark = (Bookmark) item;
- if (bookmark.getConversation() != null) {
- return get(bookmark.getConversation(), size, cachedOnly);
- } else {
- return get(bookmark.getDisplayName(), size, cachedOnly);
- }
- } else {
- return get(item.getDisplayName(), size, cachedOnly);
- }
- }
-
- public Bitmap get(Conversation conversation, int size) {
- return get(conversation,size,false);
- }
-
- public Bitmap get(Conversation conversation, int size, boolean cachedOnly) {
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- return get(conversation.getContact(), size, cachedOnly);
- } else {
- return get(conversation.getMucOptions(), size, cachedOnly);
- }
- }
-
- public void clear(Conversation conversation) {
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- clear(conversation.getContact());
- } else {
- clear(conversation.getMucOptions());
- }
- }
-
- private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) {
- final String KEY = key(mucOptions, size);
- Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
- if (bitmap != null || cachedOnly) {
- return bitmap;
- }
- final List<MucOptions.User> users = mucOptions.getUsers();
- int count = users.size();
- bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- bitmap.eraseColor(TRANSPARENT);
-
- if (count == 0) {
- String name = mucOptions.getConversation().getName();
- drawTile(canvas, name, 0, 0, size, size);
- } else if (count == 1) {
- drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
- drawTile(canvas, mucOptions.getConversation().getAccount(), size / 2 + 1, 0, size, size);
- } else if (count == 2) {
- drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
- drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size);
- } else if (count == 3) {
- drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
- drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size / 2 - 1);
- drawTile(canvas, users.get(2), size / 2 + 1, size / 2 + 1, size,
- size);
- } else if (count == 4) {
- drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
- drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
- drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
- drawTile(canvas, users.get(3), size / 2 + 1, size / 2 + 1, size,
- size);
- } else {
- drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
- drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
- drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
- drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1,
- size, size);
- }
- this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
- return bitmap;
- }
-
- public void clear(MucOptions options) {
- synchronized (this.sizes) {
- for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(key(options, size));
- }
- }
- }
-
- private String key(MucOptions options, int size) {
- synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
- }
- return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid()
- + "_" + String.valueOf(size);
- }
-
- public Bitmap get(Account account, int size) {
- return get(account, size, false);
- }
-
- public Bitmap get(Account account, int size, boolean cachedOnly) {
- final String KEY = key(account, size);
- Bitmap avatar = mXmppConnectionService.getBitmapCache().get(KEY);
- if (avatar != null || cachedOnly) {
- return avatar;
- }
- avatar = mXmppConnectionService.getFileBackend().getAvatar(account.getAvatar(), size);
- if (avatar == null) {
- avatar = get(account.getJid().toBareJid().toString(), size,false);
- }
- mXmppConnectionService.getBitmapCache().put(KEY, avatar);
- return avatar;
- }
-
- public Bitmap get(Message message, int size, boolean cachedOnly) {
- final Conversation conversation = message.getConversation();
- if (message.getStatus() == Message.STATUS_RECEIVED) {
- Contact c = message.getContact();
- if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) {
- return get(c, size, cachedOnly);
- } else if (message.getConversation().getMode() == Conversation.MODE_MULTI){
- MucOptions.User user = conversation.getMucOptions().findUserByFullJid(message.getCounterpart());
- if (user != null) {
- return getImpl(user,size,cachedOnly);
- }
- }
- return get(UIHelper.getMessageDisplayName(message), size, cachedOnly);
- } else {
- return get(conversation.getAccount(), size, cachedOnly);
- }
- }
-
- public void clear(Account account) {
- synchronized (this.sizes) {
- for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(key(account, size));
- }
- }
- }
-
- public void clear(MucOptions.User user) {
- synchronized (this.sizes) {
- for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(key(user, size));
- }
- }
- }
-
- private String key(Account account, int size) {
- synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
- }
- return PREFIX_ACCOUNT + "_" + account.getUuid() + "_"
- + String.valueOf(size);
- }
-
- public Bitmap get(String name, int size) {
- return get(name,size,false);
- }
-
- public Bitmap get(final String name, final int size, boolean cachedOnly) {
- final String KEY = key(name, size);
- Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(KEY);
- if (bitmap != null || cachedOnly) {
- return bitmap;
- }
- bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- final String trimmedName = name == null ? "" : name.trim();
- drawTile(canvas, trimmedName, 0, 0, size, size);
- mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
- return bitmap;
- }
-
- private String key(String name, int size) {
- synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
- }
- return PREFIX_GENERIC + "_" + name + "_" + String.valueOf(size);
- }
-
- private boolean drawTile(Canvas canvas, String letter, int tileColor,
- int left, int top, int right, int bottom) {
- letter = letter.toUpperCase(Locale.getDefault());
- Paint tilePaint = new Paint(), textPaint = new Paint();
- tilePaint.setColor(tileColor);
- textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
- textPaint.setColor(FG_COLOR);
- textPaint.setTypeface(Typeface.create("sans-serif-light",
- Typeface.NORMAL));
- textPaint.setTextSize((float) ((right - left) * 0.8));
- Rect rect = new Rect();
-
- canvas.drawRect(new Rect(left, top, right, bottom), tilePaint);
- textPaint.getTextBounds(letter, 0, 1, rect);
- float width = textPaint.measureText(letter);
- canvas.drawText(letter, (right + left) / 2 - width / 2, (top + bottom)
- / 2 + rect.height() / 2, textPaint);
- return true;
- }
-
- private boolean drawTile(Canvas canvas, MucOptions.User user, int left,
- int top, int right, int bottom) {
- Contact contact = user.getContact();
- if (contact != null) {
- Uri uri = null;
- if (contact.getAvatar() != null) {
- uri = mXmppConnectionService.getFileBackend().getAvatarUri(
- contact.getAvatar());
- } else if (contact.getProfilePhoto() != null) {
- uri = Uri.parse(contact.getProfilePhoto());
- }
- if (drawTile(canvas, uri, left, top, right, bottom)) {
- return true;
- }
- } else if (user.getAvatar() != null) {
- Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
- if (drawTile(canvas, uri, left, top, right, bottom)) {
- return true;
- }
- } else if (user.getAvatar() != null) {
- Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
- if (drawTile(canvas, uri, left, top, right, bottom)) {
- return true;
- }
- } else if (user.getAvatar() != null) {
- Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
- if (drawTile(canvas, uri, left, top, right, bottom)) {
- return true;
- }
- }
- String name = contact != null ? contact.getDisplayName() : user.getName();
- drawTile(canvas, name, left, top, right, bottom);
- return true;
- }
-
- private boolean drawTile(Canvas canvas, Account account, int left, int top, int right, int bottom) {
- String avatar = account.getAvatar();
- if (avatar != null) {
- Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(avatar);
- if (uri != null) {
- if (drawTile(canvas, uri, left, top, right, bottom)) {
- return true;
- }
- }
- }
- return drawTile(canvas, account.getJid().toBareJid().toString(), left, top, right, bottom);
- }
-
- private boolean drawTile(Canvas canvas, String name, int left, int top, int right, int bottom) {
- if (name != null) {
- final String letter = getFirstLetter(name);
- final int color = UIHelper.getColorForName(name);
- drawTile(canvas, letter, color, left, top, right, bottom);
- return true;
- }
- return false;
- }
-
- private static String getFirstLetter(String name) {
- for(Character c : name.toCharArray()) {
- if (Character.isLetterOrDigit(c)) {
- return c.toString();
- }
- }
- return "X";
- }
-
- private boolean drawTile(Canvas canvas, Uri uri, int left, int top, int right, int bottom) {
- if (uri != null) {
- Bitmap bitmap = mXmppConnectionService.getFileBackend()
- .cropCenter(uri, bottom - top, right - left);
- if (bitmap != null) {
- drawTile(canvas, bitmap, left, top, right, bottom);
- return true;
- }
- }
- return false;
- }
-
- private boolean drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop, int dstright, int dstbottom) {
- Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom);
- canvas.drawBitmap(bm, null, dst, null);
- return true;
- }
-
- @Override
- public void onAdvancedStreamFeaturesAvailable(Account account) {
- XmppConnection.Features features = account.getXmppConnection().getFeatures();
- if (features.pep() && !features.pepPersistent()) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": has pep but is not persistent");
- if (account.getAvatar() != null) {
- mXmppConnectionService.republishAvatarIfNeeded(account);
- }
- }
- }
+ }
+ this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ return avatar;
+ }
+
+ public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) {
+ Contact c = user.getContact();
+ if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null || user.getAvatar() == null)) {
+ return get(c, size, cachedOnly);
+ } else {
+ return getImpl(user, size, cachedOnly);
+ }
+ }
+
+ private Bitmap getImpl(final MucOptions.User user, final int size, boolean cachedOnly) {
+ final String KEY = key(user, size);
+ Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
+ if (avatar != null || cachedOnly) {
+ return avatar;
+ }
+ if (user.getAvatar() != null) {
+ avatar = mXmppConnectionService.getFileBackend().getAvatar(user.getAvatar(), size);
+ }
+ if (avatar == null) {
+ Contact contact = user.getContact();
+ if (contact != null) {
+ avatar = get(contact, size, cachedOnly);
+ } else {
+ avatar = get(user.getName(), size, cachedOnly);
+ }
+ }
+ this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ return avatar;
+ }
+
+ public void clear(Contact contact) {
+ synchronized (this.sizes) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(
+ key(contact, size));
+ }
+ }
+ for (Conversation conversation : mXmppConnectionService.findAllConferencesWith(contact)) {
+ clear(conversation);
+ }
+ }
+
+ private String key(Contact contact, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_CONTACT + "_" + contact.getAccount().getJid().toBareJid() + "_"
+ + contact.getJid() + "_" + String.valueOf(size);
+ }
+
+ private String key(MucOptions.User user, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_CONTACT + "_" + user.getAccount().getJid().toBareJid() + "_"
+ + user.getFullJid() + "_" + String.valueOf(size);
+ }
+
+ public Bitmap get(ListItem item, int size) {
+ return get(item, size, false);
+ }
+
+ public Bitmap get(ListItem item, int size, boolean cachedOnly) {
+ if (item instanceof Contact) {
+ return get((Contact) item, size, cachedOnly);
+ } else if (item instanceof Bookmark) {
+ Bookmark bookmark = (Bookmark) item;
+ if (bookmark.getConversation() != null) {
+ return get(bookmark.getConversation(), size, cachedOnly);
+ } else {
+ return get(bookmark.getDisplayName(), size, cachedOnly);
+ }
+ } else {
+ return get(item.getDisplayName(), size, cachedOnly);
+ }
+ }
+
+ public Bitmap get(Conversation conversation, int size) {
+ return get(conversation, size, false);
+ }
+
+ public Bitmap get(Conversation conversation, int size, boolean cachedOnly) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ return get(conversation.getContact(), size, cachedOnly);
+ } else {
+ return get(conversation.getMucOptions(), size, cachedOnly);
+ }
+ }
+
+ public void clear(Conversation conversation) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ clear(conversation.getContact());
+ } else {
+ clear(conversation.getMucOptions());
+ }
+ }
+
+ private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) {
+ final String KEY = key(mucOptions, size);
+ Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
+ if (bitmap != null || cachedOnly) {
+ return bitmap;
+ }
+ final List<MucOptions.User> users = mucOptions.getUsers();
+ int count = users.size();
+ bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ bitmap.eraseColor(TRANSPARENT);
+
+ if (count == 0) {
+ String name = mucOptions.getConversation().getName();
+ drawTile(canvas, name, 0, 0, size, size);
+ } else if (count == 1) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+ drawTile(canvas, mucOptions.getConversation().getAccount(), size / 2 + 1, 0, size, size);
+ } else if (count == 2) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+ drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size);
+ } else if (count == 3) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+ drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size / 2 - 1);
+ drawTile(canvas, users.get(2), size / 2 + 1, size / 2 + 1, size,
+ size);
+ } else if (count == 4) {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
+ drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
+ drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
+ drawTile(canvas, users.get(3), size / 2 + 1, size / 2 + 1, size,
+ size);
+ } else {
+ drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
+ drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
+ drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
+ drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1,
+ size, size);
+ }
+ this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+ return bitmap;
+ }
+
+ public void clear(MucOptions options) {
+ synchronized (this.sizes) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(key(options, size));
+ }
+ }
+ }
+
+ private String key(MucOptions options, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid()
+ + "_" + String.valueOf(size);
+ }
+
+ public Bitmap get(Account account, int size) {
+ return get(account, size, false);
+ }
+
+ public Bitmap get(Account account, int size, boolean cachedOnly) {
+ final String KEY = key(account, size);
+ Bitmap avatar = mXmppConnectionService.getBitmapCache().get(KEY);
+ if (avatar != null || cachedOnly) {
+ return avatar;
+ }
+ avatar = mXmppConnectionService.getFileBackend().getAvatar(account.getAvatar(), size);
+ if (avatar == null) {
+ avatar = get(account.getJid().toBareJid().toString(), size, false);
+ }
+ mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+ return avatar;
+ }
+
+ public Bitmap get(Message message, int size, boolean cachedOnly) {
+ final Conversation conversation = message.getConversation();
+ if (message.getStatus() == Message.STATUS_RECEIVED) {
+ Contact c = message.getContact();
+ if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null)) {
+ return get(c, size, cachedOnly);
+ } else if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ MucOptions.User user = conversation.getMucOptions().findUserByFullJid(message.getCounterpart());
+ if (user != null) {
+ return getImpl(user, size, cachedOnly);
+ }
+ }
+ return get(UIHelper.getMessageDisplayName(message), size, cachedOnly);
+ } else {
+ return get(conversation.getAccount(), size, cachedOnly);
+ }
+ }
+
+ public void clear(Account account) {
+ synchronized (this.sizes) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(key(account, size));
+ }
+ }
+ }
+
+ public void clear(MucOptions.User user) {
+ synchronized (this.sizes) {
+ for (Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(key(user, size));
+ }
+ }
+ }
+
+ private String key(Account account, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_ACCOUNT + "_" + account.getUuid() + "_"
+ + String.valueOf(size);
+ }
+
+ public Bitmap get(String name, int size) {
+ return get(name, size, false);
+ }
+
+ public Bitmap get(final String name, final int size, boolean cachedOnly) {
+ final String KEY = key(name, size);
+ Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(KEY);
+ if (bitmap != null || cachedOnly) {
+ return bitmap;
+ }
+ bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ final String trimmedName = name == null ? "" : name.trim();
+ drawTile(canvas, trimmedName, 0, 0, size, size);
+ mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+ return bitmap;
+ }
+
+ private String key(String name, int size) {
+ synchronized (this.sizes) {
+ if (!this.sizes.contains(size)) {
+ this.sizes.add(size);
+ }
+ }
+ return PREFIX_GENERIC + "_" + name + "_" + String.valueOf(size);
+ }
+
+ private boolean drawTile(Canvas canvas, String letter, int tileColor,
+ int left, int top, int right, int bottom) {
+ letter = letter.toUpperCase(Locale.getDefault());
+ Paint tilePaint = new Paint(), textPaint = new Paint();
+ tilePaint.setColor(tileColor);
+ textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ textPaint.setColor(FG_COLOR);
+ textPaint.setTypeface(Typeface.create("sans-serif-light",
+ Typeface.NORMAL));
+ textPaint.setTextSize((float) ((right - left) * 0.8));
+ Rect rect = new Rect();
+
+ canvas.drawRect(new Rect(left, top, right, bottom), tilePaint);
+ textPaint.getTextBounds(letter, 0, 1, rect);
+ float width = textPaint.measureText(letter);
+ canvas.drawText(letter, (right + left) / 2 - width / 2, (top + bottom)
+ / 2 + rect.height() / 2, textPaint);
+ return true;
+ }
+
+ private boolean drawTile(Canvas canvas, MucOptions.User user, int left,
+ int top, int right, int bottom) {
+ Contact contact = user.getContact();
+ if (contact != null) {
+ Uri uri = null;
+ if (contact.getAvatar() != null) {
+ uri = mXmppConnectionService.getFileBackend().getAvatarUri(
+ contact.getAvatar());
+ } else if (contact.getProfilePhoto() != null) {
+ uri = Uri.parse(contact.getProfilePhoto());
+ }
+ if (drawTile(canvas, uri, left, top, right, bottom)) {
+ return true;
+ }
+ } else if (user.getAvatar() != null) {
+ Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
+ if (drawTile(canvas, uri, left, top, right, bottom)) {
+ return true;
+ }
+ } else if (user.getAvatar() != null) {
+ Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
+ if (drawTile(canvas, uri, left, top, right, bottom)) {
+ return true;
+ }
+ } else if (user.getAvatar() != null) {
+ Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
+ if (drawTile(canvas, uri, left, top, right, bottom)) {
+ return true;
+ }
+ }
+ String name = contact != null ? contact.getDisplayName() : user.getName();
+ drawTile(canvas, name, left, top, right, bottom);
+ return true;
+ }
+
+ private boolean drawTile(Canvas canvas, Account account, int left, int top, int right, int bottom) {
+ String avatar = account.getAvatar();
+ if (avatar != null) {
+ Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(avatar);
+ if (uri != null) {
+ if (drawTile(canvas, uri, left, top, right, bottom)) {
+ return true;
+ }
+ }
+ }
+ return drawTile(canvas, account.getJid().toBareJid().toString(), left, top, right, bottom);
+ }
+
+ private boolean drawTile(Canvas canvas, String name, int left, int top, int right, int bottom) {
+ if (name != null) {
+ final String letter = getFirstLetter(name);
+ final int color = UIHelper.getColorForName(name);
+ drawTile(canvas, letter, color, left, top, right, bottom);
+ return true;
+ }
+ return false;
+ }
+
+ private static String getFirstLetter(String name) {
+ for (Character c : name.toCharArray()) {
+ if (Character.isLetterOrDigit(c)) {
+ return c.toString();
+ }
+ }
+ return "X";
+ }
+
+ private boolean drawTile(Canvas canvas, Uri uri, int left, int top, int right, int bottom) {
+ if (uri != null) {
+ Bitmap bitmap = mXmppConnectionService.getFileBackend()
+ .cropCenter(uri, bottom - top, right - left);
+ if (bitmap != null) {
+ drawTile(canvas, bitmap, left, top, right, bottom);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop, int dstright, int dstbottom) {
+ Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom);
+ canvas.drawBitmap(bm, null, dst, null);
+ return true;
+ }
+
+ @Override
+ public void onAdvancedStreamFeaturesAvailable(Account account) {
+ XmppConnection.Features features = account.getXmppConnection().getFeatures();
+ if (features.pep() && !features.pepPersistent()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": has pep but is not persistent");
+ if (account.getAvatar() != null) {
+ mXmppConnectionService.republishAvatarIfNeeded(account);
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/CheckAppVersionService.java b/src/main/java/de/pixart/messenger/services/CheckAppVersionService.java
index 72bc3508a..e3c4353ed 100644
--- a/src/main/java/de/pixart/messenger/services/CheckAppVersionService.java
+++ b/src/main/java/de/pixart/messenger/services/CheckAppVersionService.java
@@ -19,7 +19,7 @@ public class CheckAppVersionService extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
- doPost(request,response);
+ doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
diff --git a/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java b/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java
index 8fcedc095..53a7b6625 100644
--- a/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java
+++ b/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java
@@ -22,62 +22,62 @@ import de.pixart.messenger.ui.ShareWithActivity;
@TargetApi(Build.VERSION_CODES.M)
public class ContactChooserTargetService extends ChooserTargetService implements ServiceConnection {
- private final Object lock = new Object();
+ private final Object lock = new Object();
- private XmppConnectionService mXmppConnectionService;
+ private XmppConnectionService mXmppConnectionService;
- private final int MAX_TARGETS = 5;
+ private final int MAX_TARGETS = 5;
- @Override
- public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
- Intent intent = new Intent(this, XmppConnectionService.class);
- intent.setAction("contact_chooser");
- startService(intent);
- bindService(intent, this, Context.BIND_AUTO_CREATE);
- ArrayList<ChooserTarget> chooserTargets = new ArrayList<>();
- try {
- waitForService();
- final ArrayList<Conversation> conversations = new ArrayList<>();
- if (!mXmppConnectionService.areMessagesInitialized()) {
- return chooserTargets;
- }
- mXmppConnectionService.populateWithOrderedConversations(conversations, false);
- final ComponentName componentName = new ComponentName(this, ShareWithActivity.class);
- final int pixel = (int) (48 * getResources().getDisplayMetrics().density);
- for(int i = 0; i < Math.min(conversations.size(),MAX_TARGETS); ++i) {
- final Conversation conversation = conversations.get(i);
- final String name = conversation.getName();
- final Icon icon = Icon.createWithBitmap(mXmppConnectionService.getAvatarService().get(conversation, pixel));
- final float score = 1 - (1.0f / MAX_TARGETS) * i;
- final Bundle extras = new Bundle();
- extras.putString("uuid", conversation.getUuid());
- chooserTargets.add(new ChooserTarget(name, icon, score, componentName, extras));
- }
- } catch (InterruptedException e) {
- }
- unbindService(this);
- return chooserTargets;
- }
+ @Override
+ public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
+ Intent intent = new Intent(this, XmppConnectionService.class);
+ intent.setAction("contact_chooser");
+ startService(intent);
+ bindService(intent, this, Context.BIND_AUTO_CREATE);
+ ArrayList<ChooserTarget> chooserTargets = new ArrayList<>();
+ try {
+ waitForService();
+ final ArrayList<Conversation> conversations = new ArrayList<>();
+ if (!mXmppConnectionService.areMessagesInitialized()) {
+ return chooserTargets;
+ }
+ mXmppConnectionService.populateWithOrderedConversations(conversations, false);
+ final ComponentName componentName = new ComponentName(this, ShareWithActivity.class);
+ final int pixel = (int) (48 * getResources().getDisplayMetrics().density);
+ for (int i = 0; i < Math.min(conversations.size(), MAX_TARGETS); ++i) {
+ final Conversation conversation = conversations.get(i);
+ final String name = conversation.getName();
+ final Icon icon = Icon.createWithBitmap(mXmppConnectionService.getAvatarService().get(conversation, pixel));
+ final float score = 1 - (1.0f / MAX_TARGETS) * i;
+ final Bundle extras = new Bundle();
+ extras.putString("uuid", conversation.getUuid());
+ chooserTargets.add(new ChooserTarget(name, icon, score, componentName, extras));
+ }
+ } catch (InterruptedException e) {
+ }
+ unbindService(this);
+ return chooserTargets;
+ }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- XmppConnectionService.XmppConnectionBinder binder = (XmppConnectionService.XmppConnectionBinder) service;
- mXmppConnectionService = binder.getService();
- synchronized (this.lock) {
- lock.notifyAll();
- }
- }
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ XmppConnectionService.XmppConnectionBinder binder = (XmppConnectionService.XmppConnectionBinder) service;
+ mXmppConnectionService = binder.getService();
+ synchronized (this.lock) {
+ lock.notifyAll();
+ }
+ }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mXmppConnectionService = null;
- }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mXmppConnectionService = null;
+ }
- private void waitForService() throws InterruptedException {
- if (mXmppConnectionService == null) {
- synchronized (this.lock) {
- lock.wait();
- }
- }
- }
+ private void waitForService() throws InterruptedException {
+ if (mXmppConnectionService == null) {
+ synchronized (this.lock) {
+ lock.wait();
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/EventReceiver.java b/src/main/java/de/pixart/messenger/services/EventReceiver.java
index 63cefe738..cfbbeed48 100644
--- a/src/main/java/de/pixart/messenger/services/EventReceiver.java
+++ b/src/main/java/de/pixart/messenger/services/EventReceiver.java
@@ -7,19 +7,19 @@ import android.content.Intent;
import de.pixart.messenger.persistance.DatabaseBackend;
public class EventReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Intent mIntentForService = new Intent(context,
- XmppConnectionService.class);
- if (intent.getAction() != null) {
- mIntentForService.setAction(intent.getAction());
- } else {
- mIntentForService.setAction("other");
- }
- final String action = intent.getAction();
- if (action.equals("ui") || DatabaseBackend.getInstance(context).hasEnabledAccounts()) {
- context.startService(mIntentForService);
- }
- }
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Intent mIntentForService = new Intent(context,
+ XmppConnectionService.class);
+ if (intent.getAction() != null) {
+ mIntentForService.setAction(intent.getAction());
+ } else {
+ mIntentForService.setAction("other");
+ }
+ final String action = intent.getAction();
+ if (action.equals("ui") || DatabaseBackend.getInstance(context).hasEnabledAccounts()) {
+ context.startService(mIntentForService);
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/ExportLogsService.java b/src/main/java/de/pixart/messenger/services/ExportLogsService.java
index 8dcabcaf7..9008985c6 100644
--- a/src/main/java/de/pixart/messenger/services/ExportLogsService.java
+++ b/src/main/java/de/pixart/messenger/services/ExportLogsService.java
@@ -37,170 +37,170 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class ExportLogsService extends Service {
- private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
- private static final String DIRECTORY_STRING_FORMAT = FileBackend.getConversationsDirectory() + "/chats/%s";
- private static final String MESSAGE_STRING_FORMAT = "(%s) %s: %s\n";
- private static final int NOTIFICATION_ID = 1;
- private static AtomicBoolean running = new AtomicBoolean(false);
- private DatabaseBackend mDatabaseBackend;
- private List<Account> mAccounts;
- boolean ReadableLogsEnabled = false;
-
- @Override
- public void onCreate() {
- mDatabaseBackend = DatabaseBackend.getInstance(getBaseContext());
- mAccounts = mDatabaseBackend.getAccounts();
- final SharedPreferences ReadableLogs = PreferenceManager.getDefaultSharedPreferences(this);
- ReadableLogsEnabled = ReadableLogs.getBoolean("export_plain_text_logs", false);
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (running.compareAndSet(false, true)) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- export();
- stopForeground(true);
- running.set(false);
- stopSelf();
- }
- }).start();
- }
- return START_NOT_STICKY;
- }
-
- private void export() {
- List<Conversation> conversations = mDatabaseBackend.getConversations(Conversation.STATUS_AVAILABLE);
- conversations.addAll(mDatabaseBackend.getConversations(Conversation.STATUS_ARCHIVED));
- NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext());
- mBuilder.setContentTitle(getString(R.string.app_name))
- .setContentText(getString(R.string.notification_export_logs_title))
- .setSmallIcon(R.drawable.ic_import_export_white_24dp)
- .setProgress(0, 0, true);
- startForeground(NOTIFICATION_ID, mBuilder.build());
+ private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ private static final String DIRECTORY_STRING_FORMAT = FileBackend.getConversationsDirectory() + "/chats/%s";
+ private static final String MESSAGE_STRING_FORMAT = "(%s) %s: %s\n";
+ private static final int NOTIFICATION_ID = 1;
+ private static AtomicBoolean running = new AtomicBoolean(false);
+ private DatabaseBackend mDatabaseBackend;
+ private List<Account> mAccounts;
+ boolean ReadableLogsEnabled = false;
+
+ @Override
+ public void onCreate() {
+ mDatabaseBackend = DatabaseBackend.getInstance(getBaseContext());
+ mAccounts = mDatabaseBackend.getAccounts();
+ final SharedPreferences ReadableLogs = PreferenceManager.getDefaultSharedPreferences(this);
+ ReadableLogsEnabled = ReadableLogs.getBoolean("export_plain_text_logs", false);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (running.compareAndSet(false, true)) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ export();
+ stopForeground(true);
+ running.set(false);
+ stopSelf();
+ }
+ }).start();
+ }
+ return START_NOT_STICKY;
+ }
+
+ private void export() {
+ List<Conversation> conversations = mDatabaseBackend.getConversations(Conversation.STATUS_AVAILABLE);
+ conversations.addAll(mDatabaseBackend.getConversations(Conversation.STATUS_ARCHIVED));
+ NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext());
+ mBuilder.setContentTitle(getString(R.string.app_name))
+ .setContentText(getString(R.string.notification_export_logs_title))
+ .setSmallIcon(R.drawable.ic_import_export_white_24dp)
+ .setProgress(0, 0, true);
+ startForeground(NOTIFICATION_ID, mBuilder.build());
mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
- if (ReadableLogsEnabled) {
- for (Conversation conversation : conversations) {
- writeToFile(conversation);
- }
- }
- if (mAccounts.size() == 1) {
- try {
- ExportDatabase();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- private void writeToFile(Conversation conversation) {
- Jid accountJid = resolveAccountUuid(conversation.getAccountUuid());
- Jid contactJid = conversation.getJid();
-
- File dir = new File(String.format(DIRECTORY_STRING_FORMAT,accountJid.toBareJid().toString()));
- dir.mkdirs();
-
- BufferedWriter bw = null;
- try {
- for (Message message : mDatabaseBackend.getMessagesIterable(conversation)) {
- if (message.getType() == Message.TYPE_TEXT || message.hasFileOnRemoteHost()) {
- String date = simpleDateFormat.format(new Date(message.getTimeSent()));
- if (bw == null) {
- bw = new BufferedWriter(new FileWriter(
- new File(dir, contactJid.toBareJid().toString() + ".txt")));
- }
- String jid = null;
- switch (message.getStatus()) {
- case Message.STATUS_RECEIVED:
- jid = getMessageCounterpart(message);
- break;
- case Message.STATUS_SEND:
- case Message.STATUS_SEND_RECEIVED:
- case Message.STATUS_SEND_DISPLAYED:
- jid = accountJid.toBareJid().toString();
- break;
- }
- if (jid != null) {
- String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody();
- bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid,
- body.replace("\\\n", "\\ \n").replace("\n", "\\ \n")));
- }
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (bw != null) {
- bw.close();
- }
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- }
- }
-
- private Jid resolveAccountUuid(String accountUuid) {
- for (Account account : mAccounts) {
- if (account.getUuid().equals(accountUuid)) {
- return account.getJid();
- }
- }
- return null;
- }
-
- private String getMessageCounterpart(Message message) {
- String trueCounterpart = (String) message.getContentValues().get(Message.TRUE_COUNTERPART);
- if (trueCounterpart != null) {
- return trueCounterpart;
- } else {
- return message.getCounterpart().toString();
- }
- }
-
- public void ExportDatabase() throws IOException {
- Account mAccount = mAccounts.get(0);
- // Get hold of the db:
- FileInputStream InputFile = new FileInputStream(this.getDatabasePath(DatabaseBackend.DATABASE_NAME));
- // Set the output folder on the SDcard
- File directory = new File(FileBackend.getConversationsDirectory() + "/database/");
- // Create the folder if it doesn't exist:
- if (!directory.exists()) {
- directory.mkdirs();
- }
- //Delete old database export file
- File temp_db_file = new File(directory + "/database.bak");
- if (temp_db_file.exists()) {
+ if (ReadableLogsEnabled) {
+ for (Conversation conversation : conversations) {
+ writeToFile(conversation);
+ }
+ }
+ if (mAccounts.size() == 1) {
+ try {
+ ExportDatabase();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void writeToFile(Conversation conversation) {
+ Jid accountJid = resolveAccountUuid(conversation.getAccountUuid());
+ Jid contactJid = conversation.getJid();
+
+ File dir = new File(String.format(DIRECTORY_STRING_FORMAT, accountJid.toBareJid().toString()));
+ dir.mkdirs();
+
+ BufferedWriter bw = null;
+ try {
+ for (Message message : mDatabaseBackend.getMessagesIterable(conversation)) {
+ if (message.getType() == Message.TYPE_TEXT || message.hasFileOnRemoteHost()) {
+ String date = simpleDateFormat.format(new Date(message.getTimeSent()));
+ if (bw == null) {
+ bw = new BufferedWriter(new FileWriter(
+ new File(dir, contactJid.toBareJid().toString() + ".txt")));
+ }
+ String jid = null;
+ switch (message.getStatus()) {
+ case Message.STATUS_RECEIVED:
+ jid = getMessageCounterpart(message);
+ break;
+ case Message.STATUS_SEND:
+ case Message.STATUS_SEND_RECEIVED:
+ case Message.STATUS_SEND_DISPLAYED:
+ jid = accountJid.toBareJid().toString();
+ break;
+ }
+ if (jid != null) {
+ String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody();
+ bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid,
+ body.replace("\\\n", "\\ \n").replace("\n", "\\ \n")));
+ }
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (bw != null) {
+ bw.close();
+ }
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+
+ private Jid resolveAccountUuid(String accountUuid) {
+ for (Account account : mAccounts) {
+ if (account.getUuid().equals(accountUuid)) {
+ return account.getJid();
+ }
+ }
+ return null;
+ }
+
+ private String getMessageCounterpart(Message message) {
+ String trueCounterpart = (String) message.getContentValues().get(Message.TRUE_COUNTERPART);
+ if (trueCounterpart != null) {
+ return trueCounterpart;
+ } else {
+ return message.getCounterpart().toString();
+ }
+ }
+
+ public void ExportDatabase() throws IOException {
+ Account mAccount = mAccounts.get(0);
+ // Get hold of the db:
+ FileInputStream InputFile = new FileInputStream(this.getDatabasePath(DatabaseBackend.DATABASE_NAME));
+ // Set the output folder on the SDcard
+ File directory = new File(FileBackend.getConversationsDirectory() + "/database/");
+ // Create the folder if it doesn't exist:
+ if (!directory.exists()) {
+ directory.mkdirs();
+ }
+ //Delete old database export file
+ File temp_db_file = new File(directory + "/database.bak");
+ if (temp_db_file.exists()) {
Log.d(Config.LOGTAG, "Delete temp database backup file from " + temp_db_file.toString());
- temp_db_file.delete();
- }
- // Set the output file stream up:
- FileOutputStream OutputFile = new FileOutputStream(directory.getPath() + "/database.db.crypt");
-
- String EncryptionKey = mAccount.getPassword(); //get account password
-
- // encrypt database from the input file to the output file
- try {
- EncryptDecryptFile.encrypt(InputFile, OutputFile, EncryptionKey);
- } catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG,"Database exporter: encryption failed with " + e);
- e.printStackTrace();
- } catch (NoSuchPaddingException e) {
- Log.d(Config.LOGTAG,"Database exporter: encryption failed with " + e);
- e.printStackTrace();
- } catch (InvalidKeyException e) {
- Log.d(Config.LOGTAG,"Database exporter: encryption failed (invalid key) with " + e);
- e.printStackTrace();
- } catch (IOException e) {
- Log.d(Config.LOGTAG,"Database exporter: encryption failed (IO) with " + e);
- e.printStackTrace();
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
+ temp_db_file.delete();
+ }
+ // Set the output file stream up:
+ FileOutputStream OutputFile = new FileOutputStream(directory.getPath() + "/database.db.crypt");
+
+ String EncryptionKey = mAccount.getPassword(); //get account password
+
+ // encrypt database from the input file to the output file
+ try {
+ EncryptDecryptFile.encrypt(InputFile, OutputFile, EncryptionKey);
+ } catch (NoSuchAlgorithmException e) {
+ Log.d(Config.LOGTAG, "Database exporter: encryption failed with " + e);
+ e.printStackTrace();
+ } catch (NoSuchPaddingException e) {
+ Log.d(Config.LOGTAG, "Database exporter: encryption failed with " + e);
+ e.printStackTrace();
+ } catch (InvalidKeyException e) {
+ Log.d(Config.LOGTAG, "Database exporter: encryption failed (invalid key) with " + e);
+ e.printStackTrace();
+ } catch (IOException e) {
+ Log.d(Config.LOGTAG, "Database exporter: encryption failed (IO) with " + e);
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/MessageArchiveService.java b/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
index 4748dc86d..702e0d093 100644
--- a/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
+++ b/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
@@ -22,29 +22,29 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
- private final XmppConnectionService mXmppConnectionService;
-
- private final HashSet<Query> queries = new HashSet<>();
- private final ArrayList<Query> pendingQueries = new ArrayList<>();
-
- public enum PagingOrder {
- NORMAL,
- REVERSE
- }
-
- public MessageArchiveService(final XmppConnectionService service) {
- this.mXmppConnectionService = service;
- }
-
- private void catchup(final Account account) {
- synchronized (this.queries) {
- for(Iterator<Query> iterator = this.queries.iterator(); iterator.hasNext();) {
- Query query = iterator.next();
- if (query.getAccount() == account) {
- iterator.remove();
- }
- }
- }
+ private final XmppConnectionService mXmppConnectionService;
+
+ private final HashSet<Query> queries = new HashSet<>();
+ private final ArrayList<Query> pendingQueries = new ArrayList<>();
+
+ public enum PagingOrder {
+ NORMAL,
+ REVERSE
+ }
+
+ public MessageArchiveService(final XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ private void catchup(final Account account) {
+ synchronized (this.queries) {
+ for (Iterator<Query> iterator = this.queries.iterator(); iterator.hasNext(); ) {
+ Query query = iterator.next();
+ if (query.getAccount() == account) {
+ iterator.remove();
+ }
+ }
+ }
final Pair<Long, String> lastMessageReceived = mXmppConnectionService.databaseBackend.getLastMessageReceived(account);
final Pair<Long, String> lastClearDate = mXmppConnectionService.databaseBackend.getLastClearDate(account);
long startCatchup;
@@ -57,363 +57,363 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
reference = null;
}
long endCatchup = account.getXmppConnection().getLastSessionEstablished();
- final Query query;
- if (startCatchup == 0) {
- return;
- } else if (endCatchup - startCatchup >= Config.MAM_MAX_CATCHUP) {
- startCatchup = endCatchup - Config.MAM_MAX_CATCHUP;
- List<Conversation> conversations = mXmppConnectionService.getConversations();
- for (Conversation conversation : conversations) {
- if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) {
- this.query(conversation,startCatchup);
- }
- }
- query = new Query(account, startCatchup, endCatchup);
- } else {
- query = new Query(account, startCatchup, endCatchup);
- query.reference = reference;
- }
- this.queries.add(query);
- this.execute(query);
- }
-
- public void catchupMUC(final Conversation conversation) {
- if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
- query(conversation,
- 0,
- System.currentTimeMillis());
- } else {
- query(conversation,
- conversation.getLastMessageTransmitted(),
- System.currentTimeMillis());
- }
- }
-
- public Query query(final Conversation conversation) {
- if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
- return query(conversation,
- 0,
- System.currentTimeMillis());
- } else {
- return query(conversation,
- conversation.getLastMessageTransmitted(),
- conversation.getAccount().getXmppConnection().getLastSessionEstablished());
- }
- }
-
- public Query query(final Conversation conversation, long end) {
- return this.query(conversation,conversation.getLastMessageTransmitted(),end);
- }
-
- public Query query(Conversation conversation, long start, long end) {
- synchronized (this.queries) {
- if (start > end) {
- return null;
- }
- final Query query = new Query(conversation, start, end,PagingOrder.REVERSE);
- query.reference = conversation.getFirstMamReference();
- this.queries.add(query);
- this.execute(query);
- return query;
- }
- }
-
- public void executePendingQueries(final Account account) {
- List<Query> pending = new ArrayList<>();
- synchronized(this.pendingQueries) {
- for(Iterator<Query> iterator = this.pendingQueries.iterator(); iterator.hasNext();) {
- Query query = iterator.next();
- if (query.getAccount() == account) {
- pending.add(query);
- iterator.remove();
- }
- }
- }
- for(Query query : pending) {
- this.execute(query);
- }
- }
-
- private void execute(final Query query) {
- final Account account= query.getAccount();
- if (account.getStatus() == Account.State.ONLINE) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": running mam query " + query.toString());
- IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
- this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
- synchronized (MessageArchiveService.this.queries) {
- MessageArchiveService.this.queries.remove(query);
- if (query.hasCallback()) {
- query.callback(false);
- }
- }
- } else if (packet.getType() != IqPacket.TYPE.RESULT) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString());
- finalizeQuery(query, true);
- }
- }
- });
- } else {
- synchronized (this.pendingQueries) {
- this.pendingQueries.add(query);
- }
- }
- }
-
- private void finalizeQuery(Query query, boolean done) {
- synchronized (this.queries) {
- this.queries.remove(query);
- }
- final Conversation conversation = query.getConversation();
- if (conversation != null) {
- conversation.sort();
- conversation.setHasMessagesLeftOnServer(!done);
- } else {
- for(Conversation tmp : this.mXmppConnectionService.getConversations()) {
- if (tmp.getAccount() == query.getAccount()) {
- tmp.sort();
- }
- }
- }
- if (query.hasCallback()) {
- query.callback(done);
- } else {
- this.mXmppConnectionService.updateConversationUi();
- }
- }
-
- public boolean queryInProgress(Conversation conversation, XmppConnectionService.OnMoreMessagesLoaded callback) {
- synchronized (this.queries) {
- for(Query query : queries) {
- if (query.conversation == conversation) {
- if (!query.hasCallback() && callback != null) {
- query.setCallback(callback);
- }
- return true;
- }
- }
- return false;
- }
- }
-
- public boolean queryInProgress(Conversation conversation) {
- return queryInProgress(conversation, null);
- }
-
- public void processFin(Element fin, Jid from) {
- if (fin == null) {
- return;
- }
- Query query = findQuery(fin.getAttribute("queryid"));
- if (query == null || !query.validFrom(from)) {
- return;
- }
- boolean complete = fin.getAttributeAsBoolean("complete");
- Element set = fin.findChild("set","http://jabber.org/protocol/rsm");
- Element last = set == null ? null : set.findChild("last");
- Element first = set == null ? null : set.findChild("first");
- Element relevant = query.getPagingOrder() == PagingOrder.NORMAL ? last : first;
- boolean abort = (query.getStart() == 0 && query.getTotalCount() >= Config.PAGE_SIZE) || query.getTotalCount() >= Config.MAM_MAX_MESSAGES;
- if (query.getConversation() != null) {
- query.getConversation().setFirstMamReference(first == null ? null : first.getContent());
- }
- if (complete || relevant == null || abort) {
- final boolean done = (complete || query.getMessageCount() == 0) && query.getStart() == 0;
- this.finalizeQuery(query, done);
- Log.d(Config.LOGTAG,query.getAccount().getJid().toBareJid()+": finished mam after "+query.getTotalCount()+" messages. messages left="+Boolean.toString(!done));
- if (query.getWith() == null && query.getMessageCount() > 0) {
- mXmppConnectionService.getNotificationService().finishBacklog(true);
- }
- } else {
- final Query nextQuery;
- if (query.getPagingOrder() == PagingOrder.NORMAL) {
- nextQuery = query.next(last == null ? null : last.getContent());
- } else {
- nextQuery = query.prev(first == null ? null : first.getContent());
- }
- this.execute(nextQuery);
- this.finalizeQuery(query, false);
- synchronized (this.queries) {
- this.queries.add(nextQuery);
- }
- }
- }
-
- public Query findQuery(String id) {
- if (id == null) {
- return null;
- }
- synchronized (this.queries) {
- for(Query query : this.queries) {
- if (query.getQueryId().equals(id)) {
- return query;
- }
- }
- return null;
- }
- }
-
- @Override
- public void onAdvancedStreamFeaturesAvailable(Account account) {
- if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
- this.catchup(account);
- }
- }
-
- public class Query {
- private int totalCount = 0;
- private int messageCount = 0;
- private long start;
- private long end;
- private String queryId;
- private String reference = null;
- private Account account;
- private Conversation conversation;
- private PagingOrder pagingOrder = PagingOrder.NORMAL;
- private XmppConnectionService.OnMoreMessagesLoaded callback = null;
-
-
- public Query(Conversation conversation, long start, long end) {
- this(conversation.getAccount(), start, end);
- this.conversation = conversation;
- }
-
- public Query(Conversation conversation, long start, long end, PagingOrder order) {
- this(conversation,start,end);
- this.pagingOrder = order;
- }
-
- public Query(Account account, long start, long end) {
- this.account = account;
- this.start = start;
- this.end = end;
- this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
- }
-
- private Query page(String reference) {
- Query query = new Query(this.account,this.start,this.end);
- query.reference = reference;
- query.conversation = conversation;
- query.totalCount = totalCount;
- query.callback = callback;
- return query;
- }
-
- public Query next(String reference) {
- Query query = page(reference);
- query.pagingOrder = PagingOrder.NORMAL;
- return query;
- }
-
- public Query prev(String reference) {
- Query query = page(reference);
- query.pagingOrder = PagingOrder.REVERSE;
- return query;
- }
-
- public String getReference() {
- return reference;
- }
-
- public PagingOrder getPagingOrder() {
- return this.pagingOrder;
- }
-
- public String getQueryId() {
- return queryId;
- }
-
- public Jid getWith() {
- return conversation == null ? null : conversation.getJid().toBareJid();
- }
-
- public boolean muc() {
- return conversation != null && conversation.getMode() == Conversation.MODE_MULTI;
- }
-
- public long getStart() {
- return start;
- }
-
- public void setCallback(XmppConnectionService.OnMoreMessagesLoaded callback) {
- this.callback = callback;
- }
-
- public void callback(boolean done) {
- if (this.callback != null) {
- this.callback.onMoreMessagesLoaded(messageCount,conversation);
- if (done) {
- this.callback.informUser(R.string.no_more_history_on_server);
- }
- }
- }
-
- public long getEnd() {
- return end;
- }
-
- public Conversation getConversation() {
- return conversation;
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public void incrementMessageCount() {
- this.messageCount++;
- this.totalCount++;
- }
-
- public int getTotalCount() {
- return this.totalCount;
- }
-
- public int getMessageCount() {
- return this.messageCount;
- }
-
- public boolean validFrom(Jid from) {
- if (muc()) {
- return getWith().equals(from);
- } else {
- return (from == null) || account.getJid().toBareJid().equals(from.toBareJid());
- }
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- if (this.muc()) {
- builder.append("to=");
- builder.append(this.getWith().toString());
- } else {
- builder.append("with=");
- if (this.getWith() == null) {
- builder.append("*");
- } else {
- builder.append(getWith().toString());
- }
- }
- builder.append(", start=");
- builder.append(AbstractGenerator.getTimestamp(this.start));
- builder.append(", end=");
- builder.append(AbstractGenerator.getTimestamp(this.end));
- if (this.reference!=null) {
- if (this.pagingOrder == PagingOrder.NORMAL) {
- builder.append(", after=");
- } else {
- builder.append(", before=");
- }
- builder.append(this.reference);
- }
- return builder.toString();
- }
-
- public boolean hasCallback() {
- return this.callback != null;
- }
- }
+ final Query query;
+ if (startCatchup == 0) {
+ return;
+ } else if (endCatchup - startCatchup >= Config.MAM_MAX_CATCHUP) {
+ startCatchup = endCatchup - Config.MAM_MAX_CATCHUP;
+ List<Conversation> conversations = mXmppConnectionService.getConversations();
+ for (Conversation conversation : conversations) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) {
+ this.query(conversation, startCatchup);
+ }
+ }
+ query = new Query(account, startCatchup, endCatchup);
+ } else {
+ query = new Query(account, startCatchup, endCatchup);
+ query.reference = reference;
+ }
+ this.queries.add(query);
+ this.execute(query);
+ }
+
+ public void catchupMUC(final Conversation conversation) {
+ if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
+ query(conversation,
+ 0,
+ System.currentTimeMillis());
+ } else {
+ query(conversation,
+ conversation.getLastMessageTransmitted(),
+ System.currentTimeMillis());
+ }
+ }
+
+ public Query query(final Conversation conversation) {
+ if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
+ return query(conversation,
+ 0,
+ System.currentTimeMillis());
+ } else {
+ return query(conversation,
+ conversation.getLastMessageTransmitted(),
+ conversation.getAccount().getXmppConnection().getLastSessionEstablished());
+ }
+ }
+
+ public Query query(final Conversation conversation, long end) {
+ return this.query(conversation, conversation.getLastMessageTransmitted(), end);
+ }
+
+ public Query query(Conversation conversation, long start, long end) {
+ synchronized (this.queries) {
+ if (start > end) {
+ return null;
+ }
+ final Query query = new Query(conversation, start, end, PagingOrder.REVERSE);
+ query.reference = conversation.getFirstMamReference();
+ this.queries.add(query);
+ this.execute(query);
+ return query;
+ }
+ }
+
+ public void executePendingQueries(final Account account) {
+ List<Query> pending = new ArrayList<>();
+ synchronized (this.pendingQueries) {
+ for (Iterator<Query> iterator = this.pendingQueries.iterator(); iterator.hasNext(); ) {
+ Query query = iterator.next();
+ if (query.getAccount() == account) {
+ pending.add(query);
+ iterator.remove();
+ }
+ }
+ }
+ for (Query query : pending) {
+ this.execute(query);
+ }
+ }
+
+ private void execute(final Query query) {
+ final Account account = query.getAccount();
+ if (account.getStatus() == Account.State.ONLINE) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": running mam query " + query.toString());
+ IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
+ this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
+ synchronized (MessageArchiveService.this.queries) {
+ MessageArchiveService.this.queries.remove(query);
+ if (query.hasCallback()) {
+ query.callback(false);
+ }
+ }
+ } else if (packet.getType() != IqPacket.TYPE.RESULT) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString());
+ finalizeQuery(query, true);
+ }
+ }
+ });
+ } else {
+ synchronized (this.pendingQueries) {
+ this.pendingQueries.add(query);
+ }
+ }
+ }
+
+ private void finalizeQuery(Query query, boolean done) {
+ synchronized (this.queries) {
+ this.queries.remove(query);
+ }
+ final Conversation conversation = query.getConversation();
+ if (conversation != null) {
+ conversation.sort();
+ conversation.setHasMessagesLeftOnServer(!done);
+ } else {
+ for (Conversation tmp : this.mXmppConnectionService.getConversations()) {
+ if (tmp.getAccount() == query.getAccount()) {
+ tmp.sort();
+ }
+ }
+ }
+ if (query.hasCallback()) {
+ query.callback(done);
+ } else {
+ this.mXmppConnectionService.updateConversationUi();
+ }
+ }
+
+ public boolean queryInProgress(Conversation conversation, XmppConnectionService.OnMoreMessagesLoaded callback) {
+ synchronized (this.queries) {
+ for (Query query : queries) {
+ if (query.conversation == conversation) {
+ if (!query.hasCallback() && callback != null) {
+ query.setCallback(callback);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ public boolean queryInProgress(Conversation conversation) {
+ return queryInProgress(conversation, null);
+ }
+
+ public void processFin(Element fin, Jid from) {
+ if (fin == null) {
+ return;
+ }
+ Query query = findQuery(fin.getAttribute("queryid"));
+ if (query == null || !query.validFrom(from)) {
+ return;
+ }
+ boolean complete = fin.getAttributeAsBoolean("complete");
+ Element set = fin.findChild("set", "http://jabber.org/protocol/rsm");
+ Element last = set == null ? null : set.findChild("last");
+ Element first = set == null ? null : set.findChild("first");
+ Element relevant = query.getPagingOrder() == PagingOrder.NORMAL ? last : first;
+ boolean abort = (query.getStart() == 0 && query.getTotalCount() >= Config.PAGE_SIZE) || query.getTotalCount() >= Config.MAM_MAX_MESSAGES;
+ if (query.getConversation() != null) {
+ query.getConversation().setFirstMamReference(first == null ? null : first.getContent());
+ }
+ if (complete || relevant == null || abort) {
+ final boolean done = (complete || query.getMessageCount() == 0) && query.getStart() == 0;
+ this.finalizeQuery(query, done);
+ Log.d(Config.LOGTAG, query.getAccount().getJid().toBareJid() + ": finished mam after " + query.getTotalCount() + " messages. messages left=" + Boolean.toString(!done));
+ if (query.getWith() == null && query.getMessageCount() > 0) {
+ mXmppConnectionService.getNotificationService().finishBacklog(true);
+ }
+ } else {
+ final Query nextQuery;
+ if (query.getPagingOrder() == PagingOrder.NORMAL) {
+ nextQuery = query.next(last == null ? null : last.getContent());
+ } else {
+ nextQuery = query.prev(first == null ? null : first.getContent());
+ }
+ this.execute(nextQuery);
+ this.finalizeQuery(query, false);
+ synchronized (this.queries) {
+ this.queries.add(nextQuery);
+ }
+ }
+ }
+
+ public Query findQuery(String id) {
+ if (id == null) {
+ return null;
+ }
+ synchronized (this.queries) {
+ for (Query query : this.queries) {
+ if (query.getQueryId().equals(id)) {
+ return query;
+ }
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public void onAdvancedStreamFeaturesAvailable(Account account) {
+ if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
+ this.catchup(account);
+ }
+ }
+
+ public class Query {
+ private int totalCount = 0;
+ private int messageCount = 0;
+ private long start;
+ private long end;
+ private String queryId;
+ private String reference = null;
+ private Account account;
+ private Conversation conversation;
+ private PagingOrder pagingOrder = PagingOrder.NORMAL;
+ private XmppConnectionService.OnMoreMessagesLoaded callback = null;
+
+
+ public Query(Conversation conversation, long start, long end) {
+ this(conversation.getAccount(), start, end);
+ this.conversation = conversation;
+ }
+
+ public Query(Conversation conversation, long start, long end, PagingOrder order) {
+ this(conversation, start, end);
+ this.pagingOrder = order;
+ }
+
+ public Query(Account account, long start, long end) {
+ this.account = account;
+ this.start = start;
+ this.end = end;
+ this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
+ }
+
+ private Query page(String reference) {
+ Query query = new Query(this.account, this.start, this.end);
+ query.reference = reference;
+ query.conversation = conversation;
+ query.totalCount = totalCount;
+ query.callback = callback;
+ return query;
+ }
+
+ public Query next(String reference) {
+ Query query = page(reference);
+ query.pagingOrder = PagingOrder.NORMAL;
+ return query;
+ }
+
+ public Query prev(String reference) {
+ Query query = page(reference);
+ query.pagingOrder = PagingOrder.REVERSE;
+ return query;
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ public PagingOrder getPagingOrder() {
+ return this.pagingOrder;
+ }
+
+ public String getQueryId() {
+ return queryId;
+ }
+
+ public Jid getWith() {
+ return conversation == null ? null : conversation.getJid().toBareJid();
+ }
+
+ public boolean muc() {
+ return conversation != null && conversation.getMode() == Conversation.MODE_MULTI;
+ }
+
+ public long getStart() {
+ return start;
+ }
+
+ public void setCallback(XmppConnectionService.OnMoreMessagesLoaded callback) {
+ this.callback = callback;
+ }
+
+ public void callback(boolean done) {
+ if (this.callback != null) {
+ this.callback.onMoreMessagesLoaded(messageCount, conversation);
+ if (done) {
+ this.callback.informUser(R.string.no_more_history_on_server);
+ }
+ }
+ }
+
+ public long getEnd() {
+ return end;
+ }
+
+ public Conversation getConversation() {
+ return conversation;
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public void incrementMessageCount() {
+ this.messageCount++;
+ this.totalCount++;
+ }
+
+ public int getTotalCount() {
+ return this.totalCount;
+ }
+
+ public int getMessageCount() {
+ return this.messageCount;
+ }
+
+ public boolean validFrom(Jid from) {
+ if (muc()) {
+ return getWith().equals(from);
+ } else {
+ return (from == null) || account.getJid().toBareJid().equals(from.toBareJid());
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ if (this.muc()) {
+ builder.append("to=");
+ builder.append(this.getWith().toString());
+ } else {
+ builder.append("with=");
+ if (this.getWith() == null) {
+ builder.append("*");
+ } else {
+ builder.append(getWith().toString());
+ }
+ }
+ builder.append(", start=");
+ builder.append(AbstractGenerator.getTimestamp(this.start));
+ builder.append(", end=");
+ builder.append(AbstractGenerator.getTimestamp(this.end));
+ if (this.reference != null) {
+ if (this.pagingOrder == PagingOrder.NORMAL) {
+ builder.append(", after=");
+ } else {
+ builder.append(", before=");
+ }
+ builder.append(this.reference);
+ }
+ return builder.toString();
+ }
+
+ public boolean hasCallback() {
+ return this.callback != null;
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/services/NotificationService.java b/src/main/java/de/pixart/messenger/services/NotificationService.java
index a774f7641..8aa8541e9 100644
--- a/src/main/java/de/pixart/messenger/services/NotificationService.java
+++ b/src/main/java/de/pixart/messenger/services/NotificationService.java
@@ -383,7 +383,7 @@ public class NotificationService {
builder.setStyle(messagingStyle);
} else {
builder.setStyle(new NotificationCompat.BigTextStyle().bigText(getMergedBodies(messages)));
- builder.setContentText(UIHelper.getMessagePreview(mXmppConnectionService, messages.get((messages.size()-1))).first);
+ builder.setContentText(UIHelper.getMessagePreview(mXmppConnectionService, messages.get((messages.size() - 1))).first);
}
}
@@ -485,8 +485,8 @@ public class NotificationService {
final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
intent.setAction(XmppConnectionService.ACTION_REPLY_TO_CONVERSATION);
intent.putExtra("uuid", conversation.getUuid());
- intent.putExtra("dismiss_notification",dismissAfterReply);
- int id = conversation.getUuid().hashCode() % (dismissAfterReply ? 402359 : 426583);
+ intent.putExtra("dismiss_notification", dismissAfterReply);
+ int id = conversation.getUuid().hashCode() % (dismissAfterReply ? 402359 : 426583);
return PendingIntent.getService(mXmppConnectionService, id, intent, 0);
}
@@ -635,7 +635,7 @@ public class NotificationService {
mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp);
mBuilder.setContentIntent(PendingIntent.getActivity(mXmppConnectionService,
145,
- new Intent(mXmppConnectionService,ManageAccountActivity.class),
+ new Intent(mXmppConnectionService, ManageAccountActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT));
notificationManager.notify(ERROR_NOTIFICATION_ID, mBuilder.build());
}
diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
index e41943597..96b98fbb3 100644
--- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
+++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
@@ -141,3786 +141,3787 @@ import me.leolin.shortcutbadger.ShortcutBadger;
public class XmppConnectionService extends Service {
- public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations";
- public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
- public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground";
- public static final String ACTION_TRY_AGAIN = "try_again";
- public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = "dismiss_error";
- public static final String ACTION_IDLE_PING = "idle_ping";
- private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
- public static final String ACTION_GCM_TOKEN_REFRESH = "gcm_token_refresh";
- public static final String ACTION_GCM_MESSAGE_RECEIVED = "gcm_message_received";
- private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor();
- private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor();
- private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true);
- private final IBinder mBinder = new XmppConnectionBinder();
- private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
- private final IqGenerator mIqGenerator = new IqGenerator(this);
- private final List<String> mInProgressAvatarFetches = new ArrayList<>();
- private final HashSet<Jid> mLowPingTimeoutMode = new HashSet<>();
- private WakeLock wakeLock;
- private long mLastActivity = 0;
- public static VideoCompressor CompressVideo;
- private NotificationManager mNotifyManager;
-
- public DatabaseBackend databaseBackend;
- private ContentObserver contactObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- Intent intent = new Intent(getApplicationContext(),
- XmppConnectionService.class);
- intent.setAction(ACTION_MERGE_PHONE_CONTACTS);
- startService(intent);
- }
- };
- private FileBackend fileBackend = new FileBackend(this);
- private MemorizingTrustManager mMemorizingTrustManager;
- private NotificationService mNotificationService = new NotificationService(
- this);
- private OnMessagePacketReceived mMessageParser = new MessageParser(this);
- private OnPresencePacketReceived mPresenceParser = new PresenceParser(this);
- private IqParser mIqParser = new IqParser(this);
- private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() != IqPacket.TYPE.RESULT) {
- Element error = packet.findChild("error");
- String text = error != null ? error.findChildContent("text") : null;
- if (text != null) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": received iq error - " + text);
- }
- }
- }
- };
- 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 = new OnContactStatusChanged() {
-
- @Override
- public void onContactStatusChanged(Contact contact, boolean online) {
- Conversation conversation = find(getConversations(), contact);
- if (conversation != null) {
- if (online) {
- conversation.endOtrIfNeeded();
- if (contact.getPresences().size() == 1) {
- sendUnsentMessages(conversation);
- }
- } else {
- //check if the resource we are haveing a conversation with is still online
- if (conversation.hasValidOtrSession()) {
- String otrResource = conversation.getOtrSession().getSessionID().getUserID();
- if (!(Arrays.asList(contact.getPresences().toResourceArray()).contains(otrResource))) {
- conversation.endOtrIfNeeded();
- }
- }
- }
- }
- }
- };
- private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
- this);
- private AvatarService mAvatarService = new AvatarService(this);
- private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
- private PushManagementService mPushManagementService = new PushManagementService(this);
- private OnConversationUpdate mOnConversationUpdate = null;
-
-
- private final ConversationsFileObserver fileObserver = new ConversationsFileObserver(
- Environment.getExternalStorageDirectory().getAbsolutePath()
- ) {
- @Override
- public void onEvent(int event, String path) {
- markFileDeleted(path);
- }
- };
- private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
-
- @Override
- public void onJinglePacketReceived(Account account, JinglePacket packet) {
- mJingleConnectionManager.deliverPacket(account, packet);
- }
- };
- private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
-
- @Override
- public void onMessageAcknowledged(Account account, String uuid) {
- for (final Conversation conversation : getConversations()) {
- if (conversation.getAccount() == account) {
- Message message = conversation.findUnsentMessageWithUuid(uuid);
- if (message != null) {
- markMessage(message, Message.STATUS_SEND);
- }
- }
- }
- }
- };
- private int convChangedListenerCount = 0;
- private OnShowErrorToast mOnShowErrorToast = null;
- private int showErrorToastListenerCount = 0;
- private int unreadCount = -1;
- private OnAccountUpdate mOnAccountUpdate = null;
- private OnCaptchaRequested mOnCaptchaRequested = null;
- private int accountChangedListenerCount = 0;
- private int captchaRequestedListenerCount = 0;
- private OnRosterUpdate mOnRosterUpdate = null;
- private OnUpdateBlocklist mOnUpdateBlocklist = null;
- private int updateBlocklistListenerCount = 0;
- private int rosterChangedListenerCount = 0;
- private OnMucRosterUpdate mOnMucRosterUpdate = null;
- private int mucRosterChangedListenerCount = 0;
- private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
- private int keyStatusUpdatedListenerCount = 0;
- private SecureRandom mRandom;
- private LruCache<Pair<String,String>,ServiceDiscoveryResult> discoCache = new LruCache<>(20);
- private final OnBindListener mOnBindListener = new OnBindListener() {
-
- @Override
- public void onBind(final Account account) {
- synchronized (mInProgressAvatarFetches) {
- for (Iterator<String> iterator = mInProgressAvatarFetches.iterator(); iterator.hasNext(); ) {
- final String KEY = iterator.next();
- if (KEY.startsWith(account.getJid().toBareJid() + "_")) {
- iterator.remove();
- }
- }
- }
- account.getRoster().clearPresences();
- mJingleConnectionManager.cancelInTransmission();
- fetchRosterFromServer(account);
- fetchBookmarks(account);
- sendPresence(account);
- if (mPushManagementService.available(account)) {
- mPushManagementService.registerPushTokenOnServer(account);
- }
- connectMultiModeConversations(account);
- syncDirtyContacts(account);
- }
- };
- private OnStatusChanged statusListener = new OnStatusChanged() {
-
- @Override
- public void onStatusChanged(final Account account) {
- XmppConnection connection = account.getXmppConnection();
- if (mOnAccountUpdate != null) {
- mOnAccountUpdate.onAccountUpdate();
- }
- if (account.getStatus() == Account.State.ONLINE) {
- synchronized (mLowPingTimeoutMode) {
- if (mLowPingTimeoutMode.remove(account.getJid().toBareJid())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": leaving low ping timeout mode");
- }
- }
- if (account.setShowErrorNotification(true)) {
- databaseBackend.updateAccount(account);
- }
- mMessageArchiveService.executePendingQueries(account);
- if (connection != null && connection.getFeatures().csi()) {
- if (checkListeners()) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//inactive");
- connection.sendInactive();
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//active");
- connection.sendActive();
- }
- }
- List<Conversation> conversations = getConversations();
- for (Conversation conversation : conversations) {
- if (conversation.getAccount() == account
- && !account.pendingConferenceJoins.contains(conversation)) {
- if (!conversation.startOtrIfNeeded()) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": couldn't start OTR with "+conversation.getContact().getJid()+" when needed");
- }
- sendUnsentMessages(conversation);
- }
- }
- for (Conversation conversation : account.pendingConferenceLeaves) {
- leaveMuc(conversation);
- }
- account.pendingConferenceLeaves.clear();
- for (Conversation conversation : account.pendingConferenceJoins) {
- joinMuc(conversation);
- }
- account.pendingConferenceJoins.clear();
- scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
- } else if (account.getStatus() == Account.State.OFFLINE || account.getStatus() == Account.State.DISABLED) {
- resetSendingToWaiting(account);
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- synchronized (mLowPingTimeoutMode) {
- if (mLowPingTimeoutMode.contains(account.getJid().toBareJid())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": went into offline state during low ping mode. reconnecting now");
- reconnectAccount(account, true, false);
- } else {
- int timeToReconnect = mRandom.nextInt(20) + 10;
- scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode());
- }
- }
- }
- } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) {
- databaseBackend.updateAccount(account);
- reconnectAccount(account, true, false);
- } else if ((account.getStatus() != Account.State.CONNECTING)
- && (account.getStatus() != Account.State.NO_INTERNET)) {
- resetSendingToWaiting(account);
- if (connection != null) {
- int next = connection.getTimeToNextAttempt();
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": error connecting account. try again in "
- + next + "s for the "
- + (connection.getAttempt() + 1) + " time");
- scheduleWakeUpCall(next, account.getUuid().hashCode());
- }
- }
- getNotificationService().updateErrorNotification();
- }
- };
- private OpenPgpServiceConnection pgpServiceConnection;
- private PgpEngine mPgpEngine = null;
- private PowerManager pm;
- private LruCache<String, Bitmap> mBitmapCache;
- private EventReceiver mEventReceiver = new EventReceiver();
-
- private boolean mRestoredFromDatabase = false;
-
- private static String generateFetchKey(Account account, final Avatar avatar) {
- return account.getJid().toBareJid() + "_" + avatar.owner + "_" + avatar.sha1sum;
- }
-
- public boolean areMessagesInitialized() {
- return this.mRestoredFromDatabase;
- }
-
- public PgpEngine getPgpEngine() {
- if (!Config.supportOpenPgp()) {
- return null;
- } else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
- if (this.mPgpEngine == null) {
- this.mPgpEngine = new PgpEngine(new OpenPgpApi(
- getApplicationContext(),
- pgpServiceConnection.getService()), this);
- }
- return mPgpEngine;
- } else {
- return null;
- }
-
- }
-
- public OpenPgpApi getOpenPgpApi() {
- if (!Config.supportOpenPgp()) {
- return null;
- } else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
- return new OpenPgpApi(this, pgpServiceConnection.getService());
- } else {
- return null;
- }
- }
-
- public FileBackend getFileBackend() {
- return this.fileBackend;
- }
-
- public AvatarService getAvatarService() {
- return this.mAvatarService;
- }
-
- public void attachLocationToConversation(final Conversation conversation,
- final Uri uri,
- final UiCallback<Message> callback) {
- int encryption = conversation.getNextEncryption();
- if (encryption == Message.ENCRYPTION_PGP) {
- encryption = Message.ENCRYPTION_DECRYPTED;
- }
- Message message = new Message(conversation, uri.toString(), encryption);
- if (conversation.getNextCounterpart() != null) {
- message.setCounterpart(conversation.getNextCounterpart());
- }
- if (encryption == Message.ENCRYPTION_DECRYPTED) {
- getPgpEngine().encrypt(message, callback);
- } else {
- callback.success(message);
- }
- }
-
- public void attachFileToConversation(final Conversation conversation,
- final Uri uri,
- final UiCallback<Message> callback) {
- if (FileBackend.weOwnFile(this, uri)) {
- Log.d(Config.LOGTAG,"trying to attach file that belonged to us");
- callback.error(R.string.security_error_invalid_file_access, null);
- return;
- }
- final Message message;
- if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
- message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
- } else {
- message = new Message(conversation, "", conversation.getNextEncryption());
- }
- message.setCounterpart(conversation.getNextCounterpart());
- message.setType(Message.TYPE_FILE);
- final String path = getFileBackend().getOriginalPath(uri);
- Log.d(Config.LOGTAG,"File path = " + path);
- mFileAddingExecutor.execute(new Runnable() {
- @Override
- public void run() {
- if (path != null) {
- message.setRelativeFilePath(path);
- getFileBackend().updateFileParams(message);
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- getPgpEngine().encrypt(message, callback);
- } else {
- callback.success(message);
- }
- } else {
- try {
- getFileBackend().copyFileToPrivateStorage(message, uri);
- getFileBackend().updateFileParams(message);
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- final PgpEngine pgpEngine = getPgpEngine();
- if (pgpEngine != null) {
- pgpEngine.encrypt(message, callback);
- } else if (callback != null) {
- callback.error(R.string.unable_to_connect_to_keychain, null);
- }
- } else {
- callback.success(message);
- }
- } catch (FileBackend.FileCopyException e) {
- callback.error(e.getResId(), message);
- }
- }
- }
- });
- }
-
- public void attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
- if (FileBackend.weOwnFile(this, uri)) {
- Log.d(Config.LOGTAG,"trying to attach file that belonged to us");
- callback.error(R.string.security_error_invalid_file_access, null);
- return;
- }
- final String compressPictures = getCompressPicturesPreference();
- if ("never".equals(compressPictures)
- || ("auto".equals(compressPictures) && getFileBackend().useImageAsIs(uri))) {
- Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+ ": not compressing picture. sending as file");
- attachFileToConversation(conversation, uri, callback);
- return;
- }
- final Message message;
- if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
- message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
- } else {
- message = new Message(conversation, "", conversation.getNextEncryption());
- }
- message.setCounterpart(conversation.getNextCounterpart());
- message.setType(Message.TYPE_IMAGE);
- mFileAddingExecutor.execute(new Runnable() {
-
- @Override
- public void run() {
- try {
- getFileBackend().copyImageToPrivateStorage(message, uri);
- if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
- final PgpEngine pgpEngine = getPgpEngine();
- if (pgpEngine != null) {
- pgpEngine.encrypt(message, callback);
- } else if (callback != null){
- callback.error(R.string.unable_to_connect_to_keychain, null);
- }
- } else {
- callback.success(message);
- }
- } catch (final FileBackend.FileCopyException e) {
- callback.error(e.getResId(), message);
- }
- }
- });
- }
-
- public void attachVideoToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
- File f = new File(FileUtils.getPath(this, uri));
- long filesize = f.length();
- String path = f.toString();
- final String compressVideos = getCompressVideoPreference();
- boolean sendVideoAsIs = false;
- final Integer NOTIFICATION_ID = (int) (new Date().getTime()/1000);
- mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext());
- mBuilder.setContentTitle(getString(R.string.app_name))
- .setContentText(getString(R.string.compressing_video))
- .setSmallIcon(R.drawable.ic_play_box_outline_white_24dp)
- .setOngoing(true)
- .setProgress(0, 0, true);
- mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
- if (FileBackend.weOwnFile(this, uri)) {
- Log.d(Config.LOGTAG,"trying to attach video that belonged to us");
- mNotifyManager.cancel(NOTIFICATION_ID);
- callback.error(R.string.security_error_invalid_file_access, null);
- return;
- }
- Log.d(Config.LOGTAG,"Video file (size) :" + f.toString() + "("+filesize/1024/1024+"MB)");
- if (filesize == 0) {
- Log.d(Config.LOGTAG,"Error with file, size = 0");
- mNotifyManager.cancel(NOTIFICATION_ID);
- callback.error(R.string.error_file_corrupt, null);
- return;
- }
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
- File compressed_file = new File(FileBackend.getConversationsVideoDirectory() + "/"
- + dateFormat.format(new Date())
- + "_komp.mp4");
- final String compressed_path = compressed_file.toString();
- final Uri compressed_uri = Uri.fromFile(compressed_file);
- if ("never".equals(compressVideos)) {
- sendVideoAsIs = true;
- } else if ("always".equals(compressVideos) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- sendVideoAsIs = false;
- } else if ("auto".equals(compressVideos) && filesize > Config.VIDEO_MAX_SIZE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- sendVideoAsIs = false;
- } else {
- sendVideoAsIs = true;
- }
- if (!sendVideoAsIs){
- CompressVideo = new VideoCompressor(path, compressed_path, new Interface() {
- @Override
- public void videocompressed(boolean result) {
- if (result) {
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending compressed video.");
- mNotifyManager.cancel(NOTIFICATION_ID);
- attachFileToConversation(conversation, compressed_uri, callback);
- }
- }
- });
- CompressVideo.execute();
- } else {
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": not compressing video. sending as file");
- mNotifyManager.cancel(NOTIFICATION_ID);
- attachFileToConversation(conversation, uri, callback);
- }
- }
-
- public interface Interface {
- void videocompressed(boolean result);
- }
-
- class VideoCompressor extends AsyncTask<String, Void, Boolean> {
- private String originalpath;
- private String compressedpath;
- private Interface mListener;
-
- VideoCompressor(String path, String compressed_path, Interface mListener) {
- originalpath = path;
- compressedpath = compressed_path;
- this.mListener = mListener;
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- Log.d(Config.LOGTAG,"Start video compression");
- wakeLock.acquire();
- }
-
- @Override
- protected Boolean doInBackground(String... params) {
- return MediaController.getInstance().convertVideo(originalpath, compressedpath);
- }
-
- @Override
- protected void onPostExecute(Boolean compressed) {
- super.onPostExecute(compressed);
- wakeLock.release();
- File video = new File(compressedpath);
- if (mListener != null) {
- if (video.exists() && video.length() > 0) {
- mListener.videocompressed(compressed);
- Log.d(Config.LOGTAG, "Compression successfully!");
- } else {
- mListener.videocompressed(false);
- Log.d(Config.LOGTAG, "Compression failed!");
- }
- }
- }
- }
-
- public Conversation find(Bookmark bookmark) {
- return find(bookmark.getAccount(), bookmark.getJid());
- }
-
- public Conversation find(final Account account, final Jid jid) {
- return find(getConversations(), account, jid);
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- final String action = intent == null ? null : intent.getAction();
- String pushedAccountHash = null;
- boolean interactive = false;
- if (action != null) {
- final Conversation c = findConversationByUuid(intent.getStringExtra("uuid"));
- switch (action) {
- case ConnectivityManager.CONNECTIVITY_ACTION:
- if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) {
- resetAllAttemptCounts(true);
- }
- break;
- case ACTION_MERGE_PHONE_CONTACTS:
- if (mRestoredFromDatabase) {
- loadPhoneContacts();
- }
- return START_STICKY;
- case Intent.ACTION_SHUTDOWN:
- logoutAndSave(true);
- return START_NOT_STICKY;
- case ACTION_CLEAR_NOTIFICATION:
- if (c != null) {
- mNotificationService.clear(c);
- } else {
- mNotificationService.clear();
- }
- break;
- case ACTION_DISMISS_ERROR_NOTIFICATIONS:
- dismissErrorNotifications();
- break;
- case ACTION_TRY_AGAIN:
- resetAllAttemptCounts(false);
- interactive = true;
- break;
- case ACTION_REPLY_TO_CONVERSATION:
- Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
- if (remoteInput != null && c != null) {
- final CharSequence body = remoteInput.getCharSequence("text_reply");
- if (body != null && body.length() > 0) {
- directReply(c, body.toString(),intent.getBooleanExtra("dismiss_notification",false));
- }
- }
- break;
- case AudioManager.RINGER_MODE_CHANGED_ACTION:
- if (xaOnSilentMode()) {
- refreshAllPresences();
- }
- break;
- case Intent.ACTION_SCREEN_ON:
- deactivateGracePeriod();
- case Intent.ACTION_SCREEN_OFF:
- if (awayWhenScreenOff()) {
- refreshAllPresences();
- }
- break;
- case ACTION_GCM_TOKEN_REFRESH:
- refreshAllGcmTokens();
- break;
- case ACTION_IDLE_PING:
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- scheduleNextIdlePing();
- }
- break;
- case ACTION_GCM_MESSAGE_RECEIVED:
- Log.d(Config.LOGTAG,"gcm push message arrived in service. extras="+intent.getExtras());
- pushedAccountHash = intent.getStringExtra("account");
- break;
- }
- }
- this.wakeLock.acquire();
-
- boolean pingNow = false;
- HashSet<Account> pingCandidates = new HashSet<>();
-
- for (Account account : accounts) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- if (!hasInternetConnection()) {
- account.setStatus(Account.State.NO_INTERNET);
- if (statusListener != null) {
- statusListener.onStatusChanged(account);
- }
- } else {
- if (account.getStatus() == Account.State.NO_INTERNET) {
- account.setStatus(Account.State.OFFLINE);
- if (statusListener != null) {
- statusListener.onStatusChanged(account);
- }
- }
- if (account.getStatus() == Account.State.ONLINE) {
- synchronized (mLowPingTimeoutMode) {
- long lastReceived = account.getXmppConnection().getLastPacketReceived();
- long lastSent = account.getXmppConnection().getLastPingSent();
- long pingInterval = "ui".equals(action) ? Config.PING_MIN_INTERVAL * 1000 : Config.PING_MAX_INTERVAL * 1000;
- long msToNextPing = (Math.max(lastReceived, lastSent) + pingInterval) - SystemClock.elapsedRealtime();
- int pingTimeout = mLowPingTimeoutMode.contains(account.getJid().toBareJid()) ? Config.LOW_PING_TIMEOUT * 1000 : Config.PING_TIMEOUT * 1000;
- long pingTimeoutIn = (lastSent + pingTimeout) - SystemClock.elapsedRealtime();
- if (lastSent > lastReceived) {
- if (pingTimeoutIn < 0) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout");
- this.reconnectAccount(account, true, interactive);
- } else {
- int secs = (int) (pingTimeoutIn / 1000);
- this.scheduleWakeUpCall(secs, account.getUuid().hashCode());
- }
- } else {
- pingCandidates.add(account);
- if (CryptoHelper.getAccountFingerprint(account).equals(pushedAccountHash)) {
- pingNow = true;
- if (mLowPingTimeoutMode.add(account.getJid().toBareJid())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": entering low ping timeout mode");
- }
- } else if (msToNextPing <= 0) {
- pingNow = true;
- } else {
- this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode());
- if (mLowPingTimeoutMode.remove(account.getJid().toBareJid())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": leaving low ping timeout mode");
- }
- }
- }
- }
- } else if (account.getStatus() == Account.State.OFFLINE) {
- reconnectAccount(account, true, interactive);
- } else if (account.getStatus() == Account.State.CONNECTING) {
- long secondsSinceLastConnect = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastConnect()) / 1000;
- long secondsSinceLastDisco = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastDiscoStarted()) / 1000;
- long discoTimeout = Config.CONNECT_DISCO_TIMEOUT - secondsSinceLastDisco;
- long timeout = Config.CONNECT_TIMEOUT - secondsSinceLastConnect;
- if (timeout < 0) {
- Log.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting");
- account.getXmppConnection().resetAttemptCount();
- reconnectAccount(account, true, interactive);
- } else if (discoTimeout < 0) {
- account.getXmppConnection().sendDiscoTimeout();
- scheduleWakeUpCall((int) Math.min(timeout,discoTimeout), account.getUuid().hashCode());
- } else {
- scheduleWakeUpCall((int) Math.min(timeout,discoTimeout), account.getUuid().hashCode());
- }
- } else {
- if (account.getXmppConnection().getTimeToNextAttempt() <= 0) {
- reconnectAccount(account, true, interactive);
- }
- }
- }
- if (mOnAccountUpdate != null) {
- mOnAccountUpdate.onAccountUpdate();
- }
- }
- }
- if (pingNow) {
- for (Account account : pingCandidates) {
- synchronized (mLowPingTimeoutMode) {
- final boolean lowTimeout = mLowPingTimeoutMode.contains(account.getJid().toBareJid());
- account.getXmppConnection().sendPing();
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + " send ping (action=" + action + ",lowTimeout=" + Boolean.toString(lowTimeout) + ")");
- scheduleWakeUpCall(lowTimeout ? Config.LOW_PING_TIMEOUT : Config.PING_TIMEOUT, account.getUuid().hashCode());
- }
- }
- }
- if (wakeLock.isHeld()) {
- try {
- wakeLock.release();
- } catch (final RuntimeException ignored) {
- }
- }
- return START_STICKY;
- }
-
- public boolean isDataSaverDisabled() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
- return !connectivityManager.isActiveNetworkMetered()
- || connectivityManager.getRestrictBackgroundStatus() == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
- } else {
- return true;
- }
- }
-
- private void directReply(Conversation conversation, String body, final boolean dismissAfterReply) {
- Message message = new Message(conversation,body,conversation.getNextEncryption());
- message.markUnread();
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- getPgpEngine().encrypt(message, new UiCallback<Message>() {
- @Override
- public void success(Message message) {
- message.setEncryption(Message.ENCRYPTION_DECRYPTED);
- sendMessage(message);
- if (dismissAfterReply) {
- markRead(message.getConversation(),true);
- } else {
- mNotificationService.pushFromDirectReply(message);
- }
- }
-
- @Override
- public void error(int errorCode, Message object) {
-
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
-
- }
- });
- } else {
- sendMessage(message);
- if (dismissAfterReply) {
- markRead(conversation,true);
- } else {
- mNotificationService.pushFromDirectReply(message);
- }
- }
- }
-
- private boolean xaOnSilentMode() {
- return getPreferences().getBoolean("xa_on_silent_mode", false);
- }
-
- private boolean manuallyChangePresence() {
- return getPreferences().getBoolean("manually_change_presence", false);
- }
-
- private boolean treatVibrateAsSilent() {
- return getPreferences().getBoolean("treat_vibrate_as_silent", false);
- }
-
- private boolean awayWhenScreenOff() {
- return getPreferences().getBoolean("away_when_screen_off", false);
- }
-
- private String getCompressPicturesPreference() {
- return getPreferences().getString("picture_compression", "auto");
- }
-
- private String getCompressVideoPreference() {
- return getPreferences().getString("video_compression", "auto");
- }
-
- private Presence.Status getTargetPresence() {
- if (xaOnSilentMode() && isPhoneSilenced()) {
- return Presence.Status.XA;
- } else if (awayWhenScreenOff() && !isInteractive()) {
- return Presence.Status.AWAY;
- } else {
- return Presence.Status.ONLINE;
- }
- }
-
- @SuppressLint("NewApi")
- @SuppressWarnings("deprecation")
- public boolean isInteractive() {
- final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
-
- final boolean isScreenOn;
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- isScreenOn = pm.isScreenOn();
- } else {
- isScreenOn = pm.isInteractive();
- }
- return isScreenOn;
- }
-
- private boolean isPhoneSilenced() {
- AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- try {
- if (treatVibrateAsSilent()) {
- return audioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
- } else {
- return audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT;
- }
- } catch (Throwable throwable) {
- Log.d(Config.LOGTAG,"platform bug in isPhoneSilenced ("+ throwable.getMessage()+")");
- return false;
- }
- }
-
- private void resetAllAttemptCounts(boolean reallyAll) {
- Log.d(Config.LOGTAG, "resetting all attempt counts");
- for (Account account : accounts) {
- if (account.hasErrorStatus() || reallyAll) {
- final XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- connection.resetAttemptCount();
- }
- }
- if (account.setShowErrorNotification(true)) {
- databaseBackend.updateAccount(account);
- }
- }
- mNotificationService.updateErrorNotification();
- }
-
- private void dismissErrorNotifications() {
- for (final Account account : this.accounts) {
- if (account.hasErrorStatus()) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": dismissing error notification");
- if (account.setShowErrorNotification(false)) {
- databaseBackend.updateAccount(account);
- }
- }
- }
- }
-
- public boolean hasInternetConnection() {
- ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
- return activeNetwork != null && activeNetwork.isConnected();
- }
-
- public boolean isWIFI() {
- ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
- if (activeNetwork != null) { // connected to the internet
- if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
- return true;
- }
- }
- return false;
- }
-
- public boolean isMobile() {
- ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
- if (activeNetwork != null) { // connected to the internet
- if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
- if (!activeNetwork.isRoaming()) {
- return true;
- }
- }
- }
- return false;
- }
-
- public boolean isMobileRoaming() {
- ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
- if (activeNetwork != null) { // connected to the internet
- if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
- if (activeNetwork.isRoaming()) {
- return true;
- }
- }
- }
- return false;
- }
-
- @SuppressLint("TrulyRandom")
- @Override
- public void onCreate() {
- ExceptionHelper.init(getApplicationContext());
- PRNGFixes.apply();
- this.mRandom = new SecureRandom();
- updateMemorizingTrustmanager();
- final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
- final int cacheSize = maxMemory / 8;
- this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) {
- @Override
- protected int sizeOf(final String key, final Bitmap bitmap) {
- return bitmap.getByteCount() / 1024;
- }
- };
-
- this.databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
- this.accounts = databaseBackend.getAccounts();
-
- if (databaseBackend.startTimeCountExceedsThreshold()) {
- Log.d(Config.LOGTAG,"number of restarts exceeds threshold.");
- }
-
- restoreFromDatabase();
-
- getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
- new Thread(new Runnable() {
- @Override
- public void run() {
- fileObserver.startWatching();
- }
- }).start();
- if (Config.supportOpenPgp()) {
- this.pgpServiceConnection = new OpenPgpServiceConnection(getApplicationContext(), "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() {
- @Override
- public void onBound(IOpenPgpService2 service) {
- for (Account account : accounts) {
- final PgpDecryptionService pgp = account.getPgpDecryptionService();
- if(pgp != null) {
- pgp.continueDecryption(true);
- }
- }
- }
-
- @Override
- public void onError(Exception e) {
- }
- });
- this.pgpServiceConnection.bindToService();
- }
-
- this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
- this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService");
- toggleForegroundService();
- updateUnreadCountBadge();
- toggleScreenEventReceiver();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- scheduleNextIdlePing();
- }
- //start export log service every day at given time
- ScheduleAutomaticExport();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- registerReceiver(this.mEventReceiver,new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
- }
- }
-
- @Override
- public void onTrimMemory(int level) {
- super.onTrimMemory(level);
- if (level >= TRIM_MEMORY_COMPLETE) {
- Log.d(Config.LOGTAG, "clear cache due to low memory");
- getBitmapCache().evictAll();
- }
- }
-
- @Override
- public void onDestroy() {
- try {
- unregisterReceiver(this.mEventReceiver);
- } catch (IllegalArgumentException e) {
- //ignored
- }
- fileObserver.stopWatching();
- super.onDestroy();
- // cancel scheduled exporter
- CancelAutomaticExport();
- }
-
- public void toggleScreenEventReceiver() {
- if (awayWhenScreenOff() && !manuallyChangePresence()) {
- final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- registerReceiver(this.mEventReceiver, filter);
- } else {
- try {
- unregisterReceiver(this.mEventReceiver);
- } catch (IllegalArgumentException e) {
- //ignored
- }
- }
- }
-
- public void toggleForegroundService() {
- if (Config.USE_ALWAYS_FOREGROUND) {
- startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification());
- } else {
- stopForeground(true);
- }
- }
-
- @Override
- public void onTaskRemoved(final Intent rootIntent) {
- super.onTaskRemoved(rootIntent);
- }
-
- private void logoutAndSave(boolean stop) {
- int activeAccounts = 0;
- databaseBackend.clearStartTimeCounter(); // regular swipes don't count towards restart counter
- for (final Account account : accounts) {
- if (account.getStatus() != Account.State.DISABLED) {
- activeAccounts++;
- }
- databaseBackend.writeRoster(account.getRoster());
- if (account.getXmppConnection() != null) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- disconnect(account, false);
- }
- }).start();
- }
- }
- if (stop || activeAccounts == 0) {
- Log.d(Config.LOGTAG, "good bye");
- stopSelf();
- }
- }
-
- public void scheduleWakeUpCall(int seconds, int requestCode) {
- final long timeToWake = SystemClock.elapsedRealtime() + (seconds < 0 ? 1 : seconds + 1) * 1000;
- AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(this, EventReceiver.class);
- intent.setAction("ping");
- PendingIntent alarmIntent = PendingIntent.getBroadcast(this, requestCode, intent, 0);
- alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToWake, alarmIntent);
- }
-
- @TargetApi(Build.VERSION_CODES.M)
- private void scheduleNextIdlePing() {
- Log.d(Config.LOGTAG,"schedule next idle ping");
- AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(this, EventReceiver.class);
- intent.setAction(ACTION_IDLE_PING);
- alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime()+(Config.IDLE_PING_INTERVAL * 1000),
- PendingIntent.getBroadcast(this,0,intent,0)
- );
- }
-
- public XmppConnection createConnection(final Account account) {
- final SharedPreferences sharedPref = getPreferences();
- String resource;
- try {
- resource = sharedPref.getString("resource", getString(R.string.default_resource)).toLowerCase(Locale.ENGLISH);
- if (resource.trim().isEmpty()) {
- throw new Exception();
- }
- } catch (Exception e) {
- resource = "Pix-Art Messenger";
- }
- account.setResource(resource);
- final XmppConnection connection = new XmppConnection(account, this);
- connection.setOnMessagePacketReceivedListener(this.mMessageParser);
- connection.setOnStatusChangedListener(this.statusListener);
- connection.setOnPresencePacketReceivedListener(this.mPresenceParser);
- connection.setOnUnregisteredIqPacketReceivedListener(this.mIqParser);
- connection.setOnJinglePacketReceivedListener(this.jingleListener);
- connection.setOnBindListener(this.mOnBindListener);
- connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
- connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
- connection.addOnAdvancedStreamFeaturesAvailableListener(this.mAvatarService);
- AxolotlService axolotlService = account.getAxolotlService();
- if (axolotlService != null) {
- connection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
- }
- return connection;
- }
-
- public void sendChatState(Conversation conversation) {
- if (sendChatStates()) {
- MessagePacket packet = mMessageGenerator.generateChatState(conversation);
- sendMessagePacket(conversation.getAccount(), packet);
- }
- }
-
- private void sendFileMessage(final Message message, final boolean delay) {
- Log.d(Config.LOGTAG, "send file message");
- final Account account = message.getConversation().getAccount();
- if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize())) {
- mHttpConnectionManager.createNewUploadConnection(message, delay);
- } else {
- mJingleConnectionManager.createNewConnection(message);
- }
- }
-
- public void sendMessage(final Message message) {
- sendMessage(message, false, false);
- }
-
- private void sendMessage(final Message message, final boolean resend, final boolean delay) {
- final Account account = message.getConversation().getAccount();
- if (account.setShowErrorNotification(true)) {
- databaseBackend.updateAccount(account);
- mNotificationService.updateErrorNotification();
- }
- final Conversation conversation = message.getConversation();
- account.deactivateGracePeriod();
- MessagePacket packet = null;
- final boolean addToConversation = (conversation.getMode() != Conversation.MODE_MULTI
- || account.getServerIdentity() != XmppConnection.Identity.SLACK)
- && !message.edited();
- boolean saveInDb = addToConversation;
- message.setStatus(Message.STATUS_WAITING);
-
- if (!resend && message.getEncryption() != Message.ENCRYPTION_OTR) {
- message.getConversation().endOtrIfNeeded();
- message.getConversation().findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR,
- new Conversation.OnMessageFound() {
- @Override
- public void onMessageFound(Message message) {
- markMessage(message, Message.STATUS_SEND_FAILED);
- }
- });
- }
-
- if (account.isOnlineAndConnected()) {
- switch (message.getEncryption()) {
- case Message.ENCRYPTION_NONE:
- if (message.needsUploading()) {
- if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize())
- || message.fixCounterpart()) {
- this.sendFileMessage(message, delay);
- } else {
- break;
- }
- } else {
- packet = mMessageGenerator.generateChat(message);
- }
- break;
- case Message.ENCRYPTION_PGP:
- case Message.ENCRYPTION_DECRYPTED:
- if (message.needsUploading()) {
- if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize())
- || message.fixCounterpart()) {
- this.sendFileMessage(message, delay);
- } else {
- break;
- }
- } else {
- packet = mMessageGenerator.generatePgpChat(message);
- }
- break;
- case Message.ENCRYPTION_OTR:
- SessionImpl otrSession = conversation.getOtrSession();
- if (otrSession != null && otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
- try {
- message.setCounterpart(Jid.fromSessionID(otrSession.getSessionID()));
- } catch (InvalidJidException e) {
- break;
- }
- if (message.needsUploading()) {
- mJingleConnectionManager.createNewConnection(message);
- } else {
- packet = mMessageGenerator.generateOtrChat(message);
- }
- } else if (otrSession == null) {
- if (message.fixCounterpart()) {
- conversation.startOtrSession(message.getCounterpart().getResourcepart(), true);
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not fix counterpart for OTR message to contact "+message.getContact().getJid());
- break;
- }
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+" OTR session with "+message.getContact()+" is in wrong state: "+otrSession.getSessionStatus().toString());
- }
- break;
- case Message.ENCRYPTION_AXOLOTL:
- message.setFingerprint(account.getAxolotlService().getOwnFingerprint());
- if (message.needsUploading()) {
- if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize())
- || message.fixCounterpart()) {
- this.sendFileMessage(message, delay);
- } else {
- break;
- }
- } else {
- XmppAxolotlMessage axolotlMessage = account.getAxolotlService().fetchAxolotlMessageFromCache(message);
- if (axolotlMessage == null) {
- account.getAxolotlService().preparePayloadMessage(message, delay);
- } else {
- packet = mMessageGenerator.generateAxolotlChat(message, axolotlMessage);
- }
- }
- break;
-
- }
- if (packet != null) {
- if (account.getXmppConnection().getFeatures().sm() || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
- message.setStatus(Message.STATUS_UNSEND);
- } else {
- message.setStatus(Message.STATUS_SEND);
- }
- }
- } else {
- switch (message.getEncryption()) {
- case Message.ENCRYPTION_DECRYPTED:
- if (!message.needsUploading()) {
- String pgpBody = message.getEncryptedBody();
- String decryptedBody = message.getBody();
- message.setBody(pgpBody);
- message.setEncryption(Message.ENCRYPTION_PGP);
- if (message.edited()) {
- message.setBody(decryptedBody);
- message.setEncryption(Message.ENCRYPTION_DECRYPTED);
- databaseBackend.updateMessage(message, message.getEditedId());
- updateConversationUi();
- return;
- } else {
- databaseBackend.createMessage(message);
- saveInDb = false;
- message.setBody(decryptedBody);
- message.setEncryption(Message.ENCRYPTION_DECRYPTED);
- }
- }
- break;
- case Message.ENCRYPTION_OTR:
- if (!conversation.hasValidOtrSession() && message.getCounterpart() != null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": create otr session without starting for "+message.getContact().getJid());
- conversation.startOtrSession(message.getCounterpart().getResourcepart(), false);
- }
- break;
- case Message.ENCRYPTION_AXOLOTL:
- message.setFingerprint(account.getAxolotlService().getOwnFingerprint());
- break;
- }
- }
-
- if (resend) {
- if (packet != null && addToConversation) {
- if (account.getXmppConnection().getFeatures().sm() || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
- markMessage(message, Message.STATUS_UNSEND);
- } else {
- markMessage(message, Message.STATUS_SEND);
- }
- }
- } else {
- if (addToConversation) {
- conversation.add(message);
- }
- if (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages()) {
- if (saveInDb) {
- databaseBackend.createMessage(message);
- } else if (message.edited()) {
- databaseBackend.updateMessage(message, message.getEditedId());
- }
- }
- updateConversationUi();
- }
- if (packet != null) {
- if (delay) {
- mMessageGenerator.addDelay(packet, message.getTimeSent());
- }
- if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
- if (this.sendChatStates()) {
- packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
- }
- }
- sendMessagePacket(account, packet);
- }
- }
-
- private void sendUnsentMessages(final Conversation conversation) {
- conversation.findWaitingMessages(new Conversation.OnMessageFound() {
-
- @Override
- public void onMessageFound(Message message) {
- resendMessage(message, true);
- }
- });
- }
-
- public void resendMessage(final Message message, final boolean delay) {
- sendMessage(message, true, delay);
- }
-
- public void fetchRosterFromServer(final Account account) {
- final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET);
- if (!"".equals(account.getRosterVersion())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": fetching roster version " + account.getRosterVersion());
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster");
- }
- iqPacket.query(Xmlns.ROSTER).setAttribute("ver", account.getRosterVersion());
- sendIqPacket(account, iqPacket, mIqParser);
- }
-
- 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() {
-
- @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) {
- conversation.setBookmark(bookmark);
- } else if (bookmark.autojoin() && bookmark.getJid() != null && autojoin) {
- conversation = findOrCreateConversation(
- account, bookmark.getJid(), true);
- conversation.setBookmark(bookmark);
- joinMuc(conversation);
- }
- }
- }
- }
- account.setBookmarks(new ArrayList<>(bookmarks.values()));
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fetch bookmarks");
- }
- }
- };
- sendIqPacket(account, iqPacket, callback);
- }
-
- public void pushBookmarks(Account account) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()+": pushing bookmarks");
- IqPacket iqPacket = new IqPacket(IqPacket.TYPE.SET);
- Element query = iqPacket.query("jabber:iq:private");
- Element storage = query.addChild("storage", "storage:bookmarks");
- for (Bookmark bookmark : account.getBookmarks()) {
- storage.addChild(bookmark);
- }
- sendIqPacket(account, iqPacket, mDefaultIqHandler);
- }
-
- private void restoreFromDatabase() {
- synchronized (this.conversations) {
- final Map<String, Account> accountLookupTable = new Hashtable<>();
- for (Account account : this.accounts) {
- accountLookupTable.put(account.getUuid(), account);
- }
- this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE));
- for (Conversation conversation : this.conversations) {
- Account account = accountLookupTable.get(conversation.getAccountUuid());
- conversation.setAccount(account);
- }
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- Log.d(Config.LOGTAG, "restoring roster");
- for (Account account : accounts) {
- databaseBackend.readRoster(account.getRoster());
- account.initAccountServices(XmppConnectionService.this); //roster needs to be loaded at this stage
- }
- getBitmapCache().evictAll();
- loadPhoneContacts();
- Log.d(Config.LOGTAG, "restoring messages");
- for (Conversation conversation : conversations) {
- conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
- checkDeletedFiles(conversation);
- conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
-
- @Override
- public void onMessageFound(Message message) {
- markMessage(message, Message.STATUS_WAITING);
- }
- });
- conversation.findUnreadMessages(new Conversation.OnMessageFound() {
- @Override
- public void onMessageFound(Message message) {
- mNotificationService.pushFromBacklog(message);
- }
- });
- }
- mNotificationService.finishBacklog(false);
- mRestoredFromDatabase = true;
- Log.d(Config.LOGTAG, "restored all messages");
- updateConversationUi();
- }
- };
- mDatabaseExecutor.execute(runnable);
- }
- }
-
- public void loadPhoneContacts() {
- mContactMergerExecutor.execute(new Runnable() {
- @Override
- public void run() {
- PhoneHelper.loadPhoneContacts(XmppConnectionService.this, new OnPhoneContactsLoadedListener() {
- @Override
- public void onPhoneContactsLoaded(List<Bundle> phoneContacts) {
- Log.d(Config.LOGTAG, "start merging phone contacts with roster");
- for (Account account : accounts) {
- List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts();
- for (Bundle phoneContact : phoneContacts) {
- Jid jid;
- try {
- jid = Jid.fromString(phoneContact.getString("jid"));
- } catch (final InvalidJidException e) {
- continue;
- }
- final Contact contact = account.getRoster().getContact(jid);
- String systemAccount = phoneContact.getInt("phoneid")
- + "#"
- + phoneContact.getString("lookup");
- contact.setSystemAccount(systemAccount);
- if (contact.setPhotoUri(phoneContact.getString("photouri"))) {
- getAvatarService().clear(contact);
- }
- contact.setSystemName(phoneContact.getString("displayname"));
- withSystemAccounts.remove(contact);
- }
- for (Contact contact : withSystemAccounts) {
- contact.setSystemAccount(null);
- contact.setSystemName(null);
- if (contact.setPhotoUri(null)) {
- getAvatarService().clear(contact);
- }
- }
- }
- Log.d(Config.LOGTAG, "finished merging phone contacts");
- updateAccountUi();
- }
- });
- }
- });
- }
-
- public List<Conversation> getConversations() {
- return this.conversations;
- }
-
- private void checkDeletedFiles(Conversation conversation) {
- conversation.findMessagesWithFiles(new Conversation.OnMessageFound() {
-
- @Override
- public void onMessageFound(Message message) {
- if (!getFileBackend().isFileAvailable(message)) {
- message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- final int s = message.getStatus();
- if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
- markMessage(message, Message.STATUS_SEND_FAILED);
- }
- }
- }
- });
- }
-
- private void markFileDeleted(final String path) {
- Log.d(Config.LOGTAG,"deleted file "+path);
- for (Conversation conversation : getConversations()) {
- conversation.findMessagesWithFiles(new Conversation.OnMessageFound() {
- @Override
- public void onMessageFound(Message message) {
- DownloadableFile file = fileBackend.getFile(message);
- if (file.getAbsolutePath().equals(path)) {
- if (!file.exists()) {
- message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- final int s = message.getStatus();
- if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
- markMessage(message, Message.STATUS_SEND_FAILED);
- } else {
- updateConversationUi();
- }
- } else {
- Log.d(Config.LOGTAG,"found matching message for file "+path+" but file still exists");
- }
- }
- }
- });
- }
- }
-
- public void populateWithOrderedConversations(final List<Conversation> list) {
- populateWithOrderedConversations(list, true);
- }
-
- public void populateWithOrderedConversations(final List<Conversation> list, boolean includeNoFileUpload) {
- list.clear();
- if (includeNoFileUpload) {
- list.addAll(getConversations());
- } else {
- for (Conversation conversation : getConversations()) {
- if (conversation.getMode() == Conversation.MODE_SINGLE
- || conversation.getAccount().httpUploadAvailable()) {
- list.add(conversation);
- }
- }
- }
- try {
- Collections.sort(list);
- } catch (IllegalArgumentException e) {
- //ignore
- }
- }
-
- public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) {
- if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation, callback)) {
- return;
- } else if (timestamp == 0) {
- return;
- }
- Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp));
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- final Account account = conversation.getAccount();
- List<Message> messages = databaseBackend.getMessages(conversation, 50, timestamp);
- if (messages.size() > 0) {
- conversation.addAll(0, messages);
- checkDeletedFiles(conversation);
- callback.onMoreMessagesLoaded(messages.size(), conversation);
- } else if (conversation.hasMessagesLeftOnServer()
- && account.isOnlineAndConnected()
- && conversation.getLastClearHistory() == 0) {
- if ((conversation.getMode() == Conversation.MODE_SINGLE && account.getXmppConnection().getFeatures().mam())
- || (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().mamSupport())) {
- MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp);
- if (query != null) {
- query.setCallback(callback);
- }
- callback.informUser(R.string.fetching_history_from_server);
- }
- }
- }
- };
- mDatabaseExecutor.execute(runnable);
- }
-
- public List<Account> getAccounts() {
- return this.accounts;
- }
-
- public List<Conversation> findAllConferencesWith(Contact contact) {
- ArrayList<Conversation> results = new ArrayList<>();
- for(Conversation conversation : conversations) {
- if (conversation.getMode() == Conversation.MODE_MULTI
- && conversation.getMucOptions().isContactInRoom(contact)) {
- results.add(conversation);
- }
- }
- return results;
- }
-
- public Conversation find(final Iterable<Conversation> haystack, final Contact contact) {
- for (final Conversation conversation : haystack) {
- if (conversation.getContact() == contact) {
- return conversation;
- }
- }
- return null;
- }
-
- public Conversation find(final Iterable<Conversation> haystack, final Account account, final Jid jid) {
- if (jid == null) {
- return null;
- }
- for (final Conversation conversation : haystack) {
- if ((account == null || conversation.getAccount() == account)
- && (conversation.getJid().toBareJid().equals(jid.toBareJid()))) {
- return conversation;
- }
- }
- return null;
- }
-
- public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc) {
- return this.findOrCreateConversation(account, jid, muc, null);
- }
-
- public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc, final MessageArchiveService.Query query) {
- synchronized (this.conversations) {
- Conversation conversation = find(account, jid);
- if (conversation != null) {
- return conversation;
- }
- conversation = databaseBackend.findConversation(account, jid);
- if (conversation != null) {
- conversation.setStatus(Conversation.STATUS_AVAILABLE);
- conversation.setAccount(account);
- if (muc) {
- conversation.setMode(Conversation.MODE_MULTI);
- conversation.setContactJid(jid);
- } else {
- conversation.setMode(Conversation.MODE_SINGLE);
- conversation.setContactJid(jid.toBareJid());
- }
- conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
- this.databaseBackend.updateConversation(conversation);
- } else {
- String conversationName;
- Contact contact = account.getRoster().getContact(jid);
- if (contact != null) {
- conversationName = contact.getDisplayName();
- } else {
- conversationName = jid.getLocalpart();
- }
- if (muc) {
- conversation = new Conversation(conversationName, account, jid,
- Conversation.MODE_MULTI);
- } else {
- conversation = new Conversation(conversationName, account, jid.toBareJid(),
- Conversation.MODE_SINGLE);
- }
- this.databaseBackend.createConversation(conversation);
- }
- if (account.getXmppConnection() != null
- && account.getXmppConnection().getFeatures().mam()
- && !muc) {
- if (query == null) {
- this.mMessageArchiveService.query(conversation);
- } else {
- if (query.getConversation() == null) {
- this.mMessageArchiveService.query(conversation, query.getStart());
- }
- }
- }
- checkDeletedFiles(conversation);
- this.conversations.add(conversation);
- updateConversationUi();
- return conversation;
- }
- }
-
- public void archiveConversation(Conversation conversation) {
- getNotificationService().clear(conversation);
- conversation.setStatus(Conversation.STATUS_ARCHIVED);
- synchronized (this.conversations) {
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
- Bookmark bookmark = conversation.getBookmark();
- if (bookmark != null && bookmark.autojoin() && respectAutojoin()) {
- bookmark.setAutojoin(false);
- pushBookmarks(bookmark.getAccount());
- }
- }
- leaveMuc(conversation);
- } else {
- conversation.endOtrIfNeeded();
- if (conversation.getContact().getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- Log.d(Config.LOGTAG, "Canceling presence request from " + conversation.getJid().toString());
- sendPresencePacket(
- conversation.getAccount(),
- mPresenceGenerator.stopPresenceUpdatesTo(conversation.getContact())
- );
- }
- }
- updateConversation(conversation);
- this.conversations.remove(conversation);
- updateConversationUi();
- }
- }
-
- public void createAccount(final Account account) {
- account.initAccountServices(this);
- databaseBackend.createAccount(account);
- this.accounts.add(account);
- this.reconnectAccountInBackground(account);
- updateAccountUi();
- }
-
- public void createAccountFromKey(final String alias, final OnAccountCreated callback) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
- Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]);
- if (findAccountByJid(info.first) == null) {
- Account account = new Account(info.first, "");
- account.setPrivateKeyAlias(alias);
- account.setOption(Account.OPTION_DISABLED, true);
- account.setDisplayName(info.second);
- createAccount(account);
- callback.onAccountCreated(account);
- if (Config.X509_VERIFICATION) {
- try {
- getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA");
- } catch (CertificateException e) {
- callback.informUser(R.string.certificate_chain_is_not_trusted);
- }
- }
- } else {
- callback.informUser(R.string.account_already_exists);
- }
- } catch (Exception e) {
- e.printStackTrace();
- callback.informUser(R.string.unable_to_parse_certificate);
- }
- }
- }).start();
-
- }
-
- public void updateKeyInAccount(final Account account, final String alias) {
- Log.d(Config.LOGTAG, "update key in account " + alias);
- try {
- X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
- Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]);
- if (account.getJid().toBareJid().equals(info.first)) {
- account.setPrivateKeyAlias(alias);
- account.setDisplayName(info.second);
- databaseBackend.updateAccount(account);
- if (Config.X509_VERIFICATION) {
- try {
- getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA");
- } catch (CertificateException e) {
- showErrorToastInUi(R.string.certificate_chain_is_not_trusted);
- }
- account.getAxolotlService().regenerateKeys(true);
- }
- } else {
- showErrorToastInUi(R.string.jid_does_not_match_certificate);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public boolean updateAccount(final Account account) {
- if (databaseBackend.updateAccount(account)) {
- account.setShowErrorNotification(true);
- this.statusListener.onStatusChanged(account);
- databaseBackend.updateAccount(account);
- reconnectAccountInBackground(account);
- updateAccountUi();
- getNotificationService().updateErrorNotification();
- return true;
- } else {
- return false;
- }
- }
-
- public void updateAccountPasswordOnServer(final Account account, final String newPassword, final OnAccountPasswordChanged callback) {
- final IqPacket iq = getIqGenerator().generateSetPassword(account, newPassword);
- sendIqPacket(account, iq, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.setPassword(newPassword);
- account.setOption(Account.OPTION_MAGIC_CREATE, false);
- databaseBackend.updateAccount(account);
- callback.onPasswordChangeSucceeded();
- } else {
- callback.onPasswordChangeFailed();
- }
- }
- });
- }
-
- public void deleteAccount(final Account account) {
- synchronized (this.conversations) {
- for (final Conversation conversation : conversations) {
- if (conversation.getAccount() == account) {
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- leaveMuc(conversation);
- } else if (conversation.getMode() == Conversation.MODE_SINGLE) {
- conversation.endOtrIfNeeded();
- }
- conversations.remove(conversation);
- }
- }
- if (account.getXmppConnection() != null) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- disconnect(account, true);
- }
- }).start();
- }
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- if (!databaseBackend.deleteAccount(account)) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": unable to delete account");
- }
- }
- };
- mDatabaseExecutor.execute(runnable);
- this.accounts.remove(account);
- updateAccountUi();
- getNotificationService().updateErrorNotification();
- }
- }
-
- public void setOnConversationListChangedListener(OnConversationUpdate listener) {
- synchronized (this) {
- this.mLastActivity = System.currentTimeMillis();
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnConversationUpdate = listener;
- this.mNotificationService.setIsInForeground(true);
- if (this.convChangedListenerCount < 2) {
- this.convChangedListenerCount++;
- }
- }
- }
-
- public void removeOnConversationListChangedListener() {
- synchronized (this) {
- this.convChangedListenerCount--;
- if (this.convChangedListenerCount <= 0) {
- this.convChangedListenerCount = 0;
- this.mOnConversationUpdate = null;
- this.mNotificationService.setIsInForeground(false);
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnShowErrorToastListener(OnShowErrorToast onShowErrorToast) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnShowErrorToast = onShowErrorToast;
- if (this.showErrorToastListenerCount < 2) {
- this.showErrorToastListenerCount++;
- }
- }
- this.mOnShowErrorToast = onShowErrorToast;
- }
-
- public void removeOnShowErrorToastListener() {
- synchronized (this) {
- this.showErrorToastListenerCount--;
- if (this.showErrorToastListenerCount <= 0) {
- this.showErrorToastListenerCount = 0;
- this.mOnShowErrorToast = null;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnAccountListChangedListener(OnAccountUpdate listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnAccountUpdate = listener;
- if (this.accountChangedListenerCount < 2) {
- this.accountChangedListenerCount++;
- }
- }
- }
-
- public void removeOnAccountListChangedListener() {
- synchronized (this) {
- this.accountChangedListenerCount--;
- if (this.accountChangedListenerCount <= 0) {
- this.mOnAccountUpdate = null;
- this.accountChangedListenerCount = 0;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnCaptchaRequestedListener(OnCaptchaRequested listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnCaptchaRequested = listener;
- if (this.captchaRequestedListenerCount < 2) {
- this.captchaRequestedListenerCount++;
- }
- }
- }
-
- public void removeOnCaptchaRequestedListener() {
- synchronized (this) {
- this.captchaRequestedListenerCount--;
- if (this.captchaRequestedListenerCount <= 0) {
- this.mOnCaptchaRequested = null;
- this.captchaRequestedListenerCount = 0;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnRosterUpdateListener(final OnRosterUpdate listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnRosterUpdate = listener;
- if (this.rosterChangedListenerCount < 2) {
- this.rosterChangedListenerCount++;
- }
- }
- }
-
- public void removeOnRosterUpdateListener() {
- synchronized (this) {
- this.rosterChangedListenerCount--;
- if (this.rosterChangedListenerCount <= 0) {
- this.rosterChangedListenerCount = 0;
- this.mOnRosterUpdate = null;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnUpdateBlocklistListener(final OnUpdateBlocklist listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnUpdateBlocklist = listener;
- if (this.updateBlocklistListenerCount < 2) {
- this.updateBlocklistListenerCount++;
- }
- }
- }
-
- public void removeOnUpdateBlocklistListener() {
- synchronized (this) {
- this.updateBlocklistListenerCount--;
- if (this.updateBlocklistListenerCount <= 0) {
- this.updateBlocklistListenerCount = 0;
- this.mOnUpdateBlocklist = null;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnKeyStatusUpdatedListener(final OnKeyStatusUpdated listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnKeyStatusUpdated = listener;
- if (this.keyStatusUpdatedListenerCount < 2) {
- this.keyStatusUpdatedListenerCount++;
- }
- }
- }
-
- public void removeOnNewKeysAvailableListener() {
- synchronized (this) {
- this.keyStatusUpdatedListenerCount--;
- if (this.keyStatusUpdatedListenerCount <= 0) {
- this.keyStatusUpdatedListenerCount = 0;
- this.mOnKeyStatusUpdated = null;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) {
- synchronized (this) {
- if (checkListeners()) {
- switchToForeground();
- }
- this.mOnMucRosterUpdate = listener;
- if (this.mucRosterChangedListenerCount < 2) {
- this.mucRosterChangedListenerCount++;
- }
- }
- }
-
- public void removeOnMucRosterUpdateListener() {
- synchronized (this) {
- this.mucRosterChangedListenerCount--;
- if (this.mucRosterChangedListenerCount <= 0) {
- this.mucRosterChangedListenerCount = 0;
- this.mOnMucRosterUpdate = null;
- if (checkListeners()) {
- switchToBackground();
- }
- }
- }
- }
-
- public boolean checkListeners() {
- return (this.mOnAccountUpdate == null
- && this.mOnConversationUpdate == null
- && this.mOnRosterUpdate == null
- && this.mOnCaptchaRequested == null
- && this.mOnUpdateBlocklist == null
- && this.mOnShowErrorToast == null
- && this.mOnKeyStatusUpdated == null);
- }
-
- private void switchToForeground() {
- final boolean broadcastLastActivity = broadcastLastActivity();
- for (Conversation conversation : getConversations()) {
- conversation.setIncomingChatState(ChatState.ACTIVE);
- }
- for (Account account : getAccounts()) {
- if (account.getStatus() == Account.State.ONLINE) {
- account.deactivateGracePeriod();
- final XmppConnection connection = account.getXmppConnection();
- if (connection != null ) {
- if (connection.getFeatures().csi()) {
- connection.sendActive();
- }
- if (broadcastLastActivity) {
- sendPresence(account, false); //send new presence but don't include idle because we are not
- }
- }
- }
- }
- Log.d(Config.LOGTAG, "app switched into foreground");
- }
-
- private void switchToBackground() {
- final boolean broadcastLastActivity = broadcastLastActivity();
- for (Account account : getAccounts()) {
- if (account.getStatus() == Account.State.ONLINE) {
- XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- if (broadcastLastActivity) {
- sendPresence(account, broadcastLastActivity);
- }
- if (connection.getFeatures().csi()) {
- connection.sendInactive();
- }
- }
- }
- }
- this.mNotificationService.setIsInForeground(false);
- Log.d(Config.LOGTAG, "app switched into background");
- }
-
- private void connectMultiModeConversations(Account account) {
- List<Conversation> conversations = getConversations();
- for (Conversation conversation : conversations) {
- if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getAccount() == account) {
- joinMuc(conversation);
- }
- }
- }
-
- public void joinMuc(Conversation conversation) {
- joinMuc(conversation, null);
- }
-
- private void joinMuc(Conversation conversation, final OnConferenceJoined onConferenceJoined) {
- Account account = conversation.getAccount();
- account.pendingConferenceJoins.remove(conversation);
- account.pendingConferenceLeaves.remove(conversation);
- if (account.getStatus() == Account.State.ONLINE) {
- conversation.resetMucOptions();
- if (onConferenceJoined != null) {
- conversation.getMucOptions().flagNoAutoPushConfiguration();
- }
- conversation.setHasMessagesLeftOnServer(false);
- fetchConferenceConfiguration(conversation, new OnConferenceConfigurationFetched() {
-
- private void join(Conversation conversation) {
- Account account = conversation.getAccount();
- final MucOptions mucOptions = conversation.getMucOptions();
- final Jid joinJid = mucOptions.getSelf().getFullJid();
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": joining conversation " + joinJid.toString());
- PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, mucOptions.nonanonymous());
- packet.setTo(joinJid);
- Element x = packet.addChild("x", "http://jabber.org/protocol/muc");
- if (conversation.getMucOptions().getPassword() != null) {
- x.addChild("password").setContent(mucOptions.getPassword());
- }
-
- if (mucOptions.mamSupport()) {
- // Use MAM instead of the limited muc history to get history
- x.addChild("history").setAttribute("maxchars", "0");
- } else {
- // Fallback to muc history
- x.addChild("history").setAttribute("since", PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted()));
- }
- sendPresencePacket(account, packet);
- if (onConferenceJoined != null) {
- onConferenceJoined.onConferenceJoined(conversation);
- }
- if (!joinJid.equals(conversation.getJid())) {
- conversation.setContactJid(joinJid);
- databaseBackend.updateConversation(conversation);
- }
-
- if (mucOptions.mamSupport()) {
- getMessageArchiveService().catchupMUC(conversation);
- }
- if (mucOptions.membersOnly() && mucOptions.nonanonymous()) {
- fetchConferenceMembers(conversation);
- }
- sendUnsentMessages(conversation);
- }
-
- @Override
- public void onConferenceConfigurationFetched(Conversation conversation) {
- join(conversation);
- }
-
- @Override
- public void onFetchFailed(final Conversation conversation, Element error) {
- if (error != null && "remote-server-not-found".equals(error.getName())) {
- conversation.getMucOptions().setError(MucOptions.Error.SERVER_NOT_FOUND);
- } else {
- join(conversation);
- fetchConferenceConfiguration(conversation);
- }
- }
- });
- updateConversationUi();
- } else {
- account.pendingConferenceJoins.add(conversation);
- conversation.resetMucOptions();
- conversation.setHasMessagesLeftOnServer(false);
- updateConversationUi();
- }
- }
-
- private void fetchConferenceMembers(final Conversation conversation) {
- final Account account = conversation.getAccount();
- final String[] affiliations = {"member","admin","owner"};
- OnIqPacketReceived callback = new OnIqPacketReceived() {
-
- private int i = 0;
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
-
- Element query = packet.query("http://jabber.org/protocol/muc#admin");
- if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
- for(Element child : query.getChildren()) {
- if ("item".equals(child.getName())) {
- MucOptions.User user = AbstractParser.parseItem(conversation,child);
- if (!user.realJidMatchesAccount()) {
- conversation.getMucOptions().updateUser(user);
- }
- }
- }
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not request affiliation "+affiliations[i]+" in "+conversation.getJid().toBareJid());
- }
- ++i;
- if (i >= affiliations.length) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": retrieved members for "+conversation.getJid().toBareJid()+": "+conversation.getMucOptions().getMembers());
- getAvatarService().clear(conversation);
- updateMucRosterUi();
- updateConversationUi();
- }
- }
- };
- for(String affiliation : affiliations) {
- sendIqPacket(account, mIqGenerator.queryAffiliation(conversation, affiliation), callback);
- }
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetching members for "+conversation.getName());
- }
-
- public void providePasswordForMuc(Conversation conversation, String password) {
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- conversation.getMucOptions().setPassword(password);
- if (conversation.getBookmark() != null) {
- if (respectAutojoin()) {
- conversation.getBookmark().setAutojoin(true);
- }
- pushBookmarks(conversation.getAccount());
- }
- updateConversation(conversation);
- joinMuc(conversation);
- }
- }
-
- public void renameInMuc(final Conversation conversation, final String nick, final UiCallback<Conversation> callback) {
- final MucOptions options = conversation.getMucOptions();
- final Jid joinJid = options.createJoinJid(nick);
- if (options.online()) {
- Account account = conversation.getAccount();
- options.setOnRenameListener(new OnRenameListener() {
-
- @Override
- public void onSuccess() {
- conversation.setContactJid(joinJid);
- databaseBackend.updateConversation(conversation);
- Bookmark bookmark = conversation.getBookmark();
- if (bookmark != null) {
- bookmark.setNick(nick);
- pushBookmarks(bookmark.getAccount());
- }
- callback.success(conversation);
- }
-
- @Override
- public void onFailure() {
- callback.error(R.string.nick_in_use, conversation);
- }
- });
-
- PresencePacket packet = new PresencePacket();
- packet.setTo(joinJid);
- packet.setFrom(conversation.getAccount().getJid());
-
- String sig = account.getPgpSignature();
- if (sig != null) {
- packet.addChild("status").setContent("online");
- packet.addChild("x", "jabber:x:signed").setContent(sig);
- }
- sendPresencePacket(account, packet);
- } else {
- conversation.setContactJid(joinJid);
- databaseBackend.updateConversation(conversation);
- if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
- Bookmark bookmark = conversation.getBookmark();
- if (bookmark != null) {
- bookmark.setNick(nick);
- pushBookmarks(bookmark.getAccount());
- }
- joinMuc(conversation);
- }
- }
- }
-
- public void leaveMuc(Conversation conversation) {
- leaveMuc(conversation, false);
- }
-
- private void leaveMuc(Conversation conversation, boolean now) {
- Account account = conversation.getAccount();
- account.pendingConferenceJoins.remove(conversation);
- account.pendingConferenceLeaves.remove(conversation);
- if (account.getStatus() == Account.State.ONLINE || now) {
- PresencePacket packet = new PresencePacket();
- packet.setTo(conversation.getMucOptions().getSelf().getFullJid());
- packet.setFrom(conversation.getAccount().getJid());
- packet.setAttribute("type", "unavailable");
- sendPresencePacket(conversation.getAccount(), packet);
- conversation.getMucOptions().setOffline();
- conversation.deregisterWithBookmark();
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid()
- + ": leaving muc " + conversation.getJid());
- } else {
- account.pendingConferenceLeaves.add(conversation);
- }
- }
-
- private String findConferenceServer(final Account account) {
- String server;
- if (account.getXmppConnection() != null) {
- server = account.getXmppConnection().getMucServer();
- if (server != null) {
- return server;
- }
- }
- for (Account other : getAccounts()) {
- if (other != account && other.getXmppConnection() != null) {
- server = other.getXmppConnection().getMucServer();
- if (server != null) {
- return server;
- }
- }
- }
- return null;
- }
-
- public void createAdhocConference(final Account account,
- final String subject,
- final Iterable<Jid> jids,
- final UiCallback<Conversation> callback) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": creating adhoc conference with " + jids.toString());
- if (account.getStatus() == Account.State.ONLINE) {
- try {
- String server = findConferenceServer(account);
- if (server == null) {
- if (callback != null) {
- callback.error(R.string.no_conference_server_found, null);
- }
- return;
- }
- final Jid jid = Jid.fromParts(new BigInteger(64, getRNG()).toString(Character.MAX_RADIX), server, null);
- final Conversation conversation = findOrCreateConversation(account, jid, true);
- joinMuc(conversation, new OnConferenceJoined() {
- @Override
- public void onConferenceJoined(final Conversation conversation) {
- pushConferenceConfiguration(conversation, IqGenerator.defaultRoomConfiguration(), new OnConferenceOptionsPushed() {
- @Override
- public void onPushSucceeded() {
- if (subject != null && !subject.trim().isEmpty()) {
- pushSubjectToConference(conversation, subject.trim());
- }
- for (Jid invite : jids) {
- invite(conversation, invite);
- }
- if (account.countPresences() > 1) {
- directInvite(conversation, account.getJid().toBareJid());
- }
- saveConversationAsBookmark(conversation, subject);
- if (callback != null) {
- callback.success(conversation);
- }
- }
-
- @Override
- public void onPushFailed() {
- archiveConversation(conversation);
- if (callback != null) {
- callback.error(R.string.conference_creation_failed, conversation);
- }
- }
- });
- }
- });
- } catch (InvalidJidException e) {
- if (callback != null) {
- callback.error(R.string.conference_creation_failed, null);
- }
- }
- } else {
- if (callback != null) {
- callback.error(R.string.not_connected_try_again, null);
- }
- }
- }
-
- public void fetchConferenceConfiguration(final Conversation conversation) {
- fetchConferenceConfiguration(conversation, null);
- }
-
- public void fetchConferenceConfiguration(final Conversation conversation, final OnConferenceConfigurationFetched callback) {
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.setTo(conversation.getJid().toBareJid());
- request.query("http://jabber.org/protocol/disco#info");
- sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element query = packet.findChild("query","http://jabber.org/protocol/disco#info");
- if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
- ArrayList<String> features = new ArrayList<>();
- for (Element child : query.getChildren()) {
- if (child != null && child.getName().equals("feature")) {
- String var = child.getAttribute("var");
- if (var != null) {
- features.add(var);
- }
- }
- }
- Element form = query.findChild("x", "jabber:x:data");
- if (form != null) {
- conversation.getMucOptions().updateFormData(Data.parse(form));
- }
- conversation.getMucOptions().updateFeatures(features);
- if (callback != null) {
- callback.onConferenceConfigurationFetched(conversation);
- }
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetched muc configuration for "+conversation.getJid().toBareJid()+" - "+features.toString());
- updateConversationUi();
- } else if (packet.getType() == IqPacket.TYPE.ERROR) {
- if (callback != null) {
- callback.onFetchFailed(conversation, packet.getError());
- }
- }
- }
- });
- }
-
- public void pushConferenceConfiguration(final Conversation conversation, final Bundle options, final OnConferenceOptionsPushed callback) {
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.setTo(conversation.getJid().toBareJid());
- request.query("http://jabber.org/protocol/muc#owner");
- sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Data data = Data.parse(packet.query().findChild("x", "jabber:x:data"));
- for (Field field : data.getFields()) {
- if (options.containsKey(field.getFieldName())) {
- field.setValue(options.getString(field.getFieldName()));
- }
- }
- data.submit();
- IqPacket set = new IqPacket(IqPacket.TYPE.SET);
- set.setTo(conversation.getJid().toBareJid());
- set.query("http://jabber.org/protocol/muc#owner").addChild(data);
- sendIqPacket(account, set, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (callback != null) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- callback.onPushSucceeded();
- } else {
- callback.onPushFailed();
- }
- }
- }
- });
- } else {
- if (callback != null) {
- callback.onPushFailed();
- }
- }
- }
- });
- }
-
- public void pushSubjectToConference(final Conversation conference, final String subject) {
- MessagePacket packet = this.getMessageGenerator().conferenceSubject(conference, subject);
- this.sendMessagePacket(conference.getAccount(), packet);
- final MucOptions mucOptions = conference.getMucOptions();
- final MucOptions.User self = mucOptions.getSelf();
- if (!mucOptions.persistent() && self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
- Bundle options = new Bundle();
- options.putString("muc#roomconfig_persistentroom", "1");
- this.pushConferenceConfiguration(conference, options, null);
- }
- }
-
- public void changeAffiliationInConference(final Conversation conference, Jid user, final MucOptions.Affiliation affiliation, final OnAffiliationChanged callback) {
- final Jid jid = user.toBareJid();
- IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString());
- sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- conference.getMucOptions().changeAffiliation(jid, affiliation);
- getAvatarService().clear(conference);
- callback.onAffiliationChangedSuccessful(jid);
- } else {
- callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation);
- }
- }
- });
- }
-
- public void changeAffiliationsInConference(final Conversation conference, MucOptions.Affiliation before, MucOptions.Affiliation after) {
- List<Jid> jids = new ArrayList<>();
- for (MucOptions.User user : conference.getMucOptions().getUsers()) {
- if (user.getAffiliation() == before && user.getRealJid() != null) {
- jids.add(user.getRealJid());
- }
- }
- IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString());
- sendIqPacket(conference.getAccount(), request, mDefaultIqHandler);
- }
-
- public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role, final OnRoleChanged callback) {
- IqPacket request = this.mIqGenerator.changeRole(conference, nick, role.toString());
- Log.d(Config.LOGTAG, request.toString());
- sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Log.d(Config.LOGTAG, packet.toString());
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- callback.onRoleChangedSuccessful(nick);
- } else {
- callback.onRoleChangeFailed(nick, R.string.could_not_change_role);
- }
- }
- });
- }
-
- private void disconnect(Account account, boolean force) {
- if ((account.getStatus() == Account.State.ONLINE)
- || (account.getStatus() == Account.State.DISABLED)) {
- final XmppConnection connection = account.getXmppConnection();
- if (!force) {
- List<Conversation> conversations = getConversations();
- for (Conversation conversation : conversations) {
- if (conversation.getAccount() == account) {
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- leaveMuc(conversation, true);
- } else {
- if (conversation.endOtrIfNeeded()) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": ended otr session with "
- + conversation.getJid());
- }
- }
- }
- }
- sendOfflinePresence(account);
- }
- connection.disconnect(force);
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-
- public void updateMessage(Message message) {
- databaseBackend.updateMessage(message);
- updateConversationUi();
- }
-
- public void updateMessage(Message message, String uuid) {
- databaseBackend.updateMessage(message, uuid);
- updateConversationUi();
- }
-
- protected void syncDirtyContacts(Account account) {
- for (Contact contact : account.getRoster().getContacts()) {
- if (contact.getOption(Contact.Options.DIRTY_PUSH)) {
- pushContactToServer(contact);
- }
- if (contact.getOption(Contact.Options.DIRTY_DELETE)) {
- deleteContactOnServer(contact);
- }
- }
- }
-
- public void createContact(Contact contact) {
- boolean autoGrant = getPreferences().getBoolean("grant_new_contacts", true);
- if (autoGrant) {
- contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
- contact.setOption(Contact.Options.ASKING);
- }
- pushContactToServer(contact);
- }
-
- public void onOtrSessionEstablished(Conversation conversation) {
- final Account account = conversation.getAccount();
- final Session otrSession = conversation.getOtrSession();
- Log.d(Config.LOGTAG,
- account.getJid().toBareJid() + " otr session established with "
- + conversation.getJid() + "/"
- + otrSession.getSessionID().getUserID());
- conversation.findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR, new Conversation.OnMessageFound() {
-
- @Override
- public void onMessageFound(Message message) {
- SessionID id = otrSession.getSessionID();
- try {
- message.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
- } catch (InvalidJidException e) {
- return;
- }
- if (message.needsUploading()) {
- mJingleConnectionManager.createNewConnection(message);
- } else {
- MessagePacket outPacket = mMessageGenerator.generateOtrChat(message);
- if (outPacket != null) {
- mMessageGenerator.addDelay(outPacket, message.getTimeSent());
- message.setStatus(Message.STATUS_SEND);
- databaseBackend.updateMessage(message);
- sendMessagePacket(account, outPacket);
- }
- }
- updateConversationUi();
- }
- });
- }
-
- public boolean renewSymmetricKey(Conversation conversation) {
- Account account = conversation.getAccount();
- byte[] symmetricKey = new byte[32];
- this.mRandom.nextBytes(symmetricKey);
- Session otrSession = conversation.getOtrSession();
- if (otrSession != null) {
- MessagePacket packet = new MessagePacket();
- packet.setType(MessagePacket.TYPE_CHAT);
- packet.setFrom(account.getJid());
- MessageGenerator.addMessageHints(packet);
- packet.setAttribute("to", otrSession.getSessionID().getAccountID() + "/"
- + otrSession.getSessionID().getUserID());
- try {
- packet.setBody(otrSession
- .transformSending(CryptoHelper.FILETRANSFER
- + CryptoHelper.bytesToHex(symmetricKey))[0]);
- sendMessagePacket(account, packet);
- conversation.setSymmetricKey(symmetricKey);
- return true;
- } catch (OtrException e) {
- return false;
- }
- }
- return false;
- }
-
- public void pushContactToServer(final Contact contact) {
- contact.resetOption(Contact.Options.DIRTY_DELETE);
- contact.setOption(Contact.Options.DIRTY_PUSH);
- final Account account = contact.getAccount();
- if (account.getStatus() == Account.State.ONLINE) {
- final boolean ask = contact.getOption(Contact.Options.ASKING);
- final boolean sendUpdates = contact
- .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)
- && contact.getOption(Contact.Options.PREEMPTIVE_GRANT);
- final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.query(Xmlns.ROSTER).addChild(contact.asElement());
- account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
- if (sendUpdates) {
- sendPresencePacket(account,
- mPresenceGenerator.sendPresenceUpdatesTo(contact));
- }
- if (ask) {
- sendPresencePacket(account,
- mPresenceGenerator.requestPresenceUpdatesFrom(contact));
- }
- }
- }
-
- public void publishAvatar(Account account, Uri image, UiCallback<Avatar> callback) {
- final Bitmap.CompressFormat format = Config.AVATAR_FORMAT;
- final int size = Config.AVATAR_SIZE;
- final Avatar avatar = getFileBackend().getPepAvatar(image, size, format);
- if (avatar != null) {
- avatar.height = size;
- avatar.width = size;
- if (format.equals(Bitmap.CompressFormat.WEBP)) {
- avatar.type = "image/webp";
- } else if (format.equals(Bitmap.CompressFormat.JPEG)) {
- avatar.type = "image/jpeg";
- } else if (format.equals(Bitmap.CompressFormat.PNG)) {
- avatar.type = "image/png";
- }
- if (!getFileBackend().save(avatar)) {
- callback.error(R.string.error_saving_avatar, avatar);
- return;
- }
- publishAvatar(account, avatar, callback);
- } else {
- callback.error(R.string.error_publish_avatar_converting, null);
- }
- }
-
- public void publishAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
- final IqPacket packet = this.mIqGenerator.publishAvatar(avatar);
- this.sendIqPacket(account, packet, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket result) {
- if (result.getType() == IqPacket.TYPE.RESULT) {
- final IqPacket packet = XmppConnectionService.this.mIqGenerator
- .publishAvatarMetadata(avatar);
- sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket result) {
- if (result.getType() == IqPacket.TYPE.RESULT) {
- if (account.setAvatar(avatar.getFilename())) {
- getAvatarService().clear(account);
- databaseBackend.updateAccount(account);
- }
- if (callback != null) {
- callback.success(avatar);
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": published avatar");
- }
- } else {
- if (callback != null) {
- callback.error(
- R.string.error_publish_avatar_server_reject,
- avatar);
- }
- }
- }
- });
- } else {
- if (callback != null) {
- callback.error(
- R.string.error_publish_avatar_server_reject,
- avatar);
- }
- }
- }
- });
- }
-
- public void republishAvatarIfNeeded(Account account) {
- if (account.getAxolotlService().isPepBroken()) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping republication of avatar because pep is broken");
- return;
- }
- IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
- this.sendIqPacket(account, packet, new OnIqPacketReceived() {
-
- private Avatar parseAvatar(IqPacket packet) {
- Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
- if (pubsub != null) {
- Element items = pubsub.findChild("items");
- if (items != null) {
- return Avatar.parseMetadata(items);
- }
- }
- return null;
- }
-
- private boolean errorIsItemNotFound(IqPacket packet) {
- Element error = packet.findChild("error");
- return packet.getType() == IqPacket.TYPE.ERROR
- && error != null
- && error.hasChild("item-not-found");
- }
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT || errorIsItemNotFound(packet)) {
- Avatar serverAvatar = parseAvatar(packet);
- if (serverAvatar == null && account.getAvatar() != null) {
- Avatar avatar = fileBackend.getStoredPepAvatar(account.getAvatar());
- if (avatar != null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": avatar on server was null. republishing");
- publishAvatar(account, fileBackend.getStoredPepAvatar(account.getAvatar()), null);
- } else {
- Log.e(Config.LOGTAG, account.getJid().toBareJid()+": error rereading avatar");
- }
- }
- }
- }
- });
- }
-
- public void fetchAvatar(Account account, Avatar avatar) {
- fetchAvatar(account, avatar, null);
- }
-
- public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
- final String KEY = generateFetchKey(account, avatar);
- synchronized (this.mInProgressAvatarFetches) {
- if (!this.mInProgressAvatarFetches.contains(KEY)) {
- switch (avatar.origin) {
- case PEP:
- this.mInProgressAvatarFetches.add(KEY);
- fetchAvatarPep(account, avatar, callback);
- break;
- case VCARD:
- this.mInProgressAvatarFetches.add(KEY);
- fetchAvatarVcard(account, avatar, callback);
- break;
- }
- }
- }
- }
-
- private void fetchAvatarPep(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
- IqPacket packet = this.mIqGenerator.retrievePepAvatar(avatar);
- sendIqPacket(account, packet, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket result) {
- synchronized (mInProgressAvatarFetches) {
- mInProgressAvatarFetches.remove(generateFetchKey(account, avatar));
- }
- final String ERROR = account.getJid().toBareJid()
- + ": fetching avatar for " + avatar.owner + " failed ";
- if (result.getType() == IqPacket.TYPE.RESULT) {
- avatar.image = mIqParser.avatarData(result);
- if (avatar.image != null) {
- if (getFileBackend().save(avatar)) {
- if (account.getJid().toBareJid().equals(avatar.owner)) {
- if (account.setAvatar(avatar.getFilename())) {
- databaseBackend.updateAccount(account);
- }
- getAvatarService().clear(account);
- updateConversationUi();
- updateAccountUi();
- } else {
- Contact contact = account.getRoster()
- .getContact(avatar.owner);
- contact.setAvatar(avatar);
- getAvatarService().clear(contact);
- updateConversationUi();
- updateRosterUi();
- }
- if (callback != null) {
- callback.success(avatar);
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": successfully fetched pep avatar for " + avatar.owner);
- return;
- }
- } else {
-
- Log.d(Config.LOGTAG, ERROR + "(parsing error)");
- }
- } else {
- Element error = result.findChild("error");
- if (error == null) {
- Log.d(Config.LOGTAG, ERROR + "(server error)");
- } else {
- Log.d(Config.LOGTAG, ERROR + error.toString());
- }
- }
- if (callback != null) {
- callback.error(0, null);
- }
-
- }
- });
- }
-
- private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
- IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar);
- this.sendIqPacket(account, packet, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- synchronized (mInProgressAvatarFetches) {
- mInProgressAvatarFetches.remove(generateFetchKey(account, avatar));
- }
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Element vCard = packet.findChild("vCard", "vcard-temp");
- Element photo = vCard != null ? vCard.findChild("PHOTO") : null;
- String image = photo != null ? photo.findChildContent("BINVAL") : null;
- if (image != null) {
- avatar.image = image;
- if (getFileBackend().save(avatar)) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": successfully fetched vCard avatar for " + avatar.owner);
- if (avatar.owner.isBareJid()) {
- if (account.getJid().toBareJid().equals(avatar.owner) && account.getAvatar() == null) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": had no avatar. replacing with vcard");
- account.setAvatar(avatar.getFilename());
- databaseBackend.updateAccount(account);
- getAvatarService().clear(account);
- updateAccountUi();
- } else {
- Contact contact = account.getRoster().getContact(avatar.owner);
- contact.setAvatar(avatar);
- getAvatarService().clear(contact);
- updateRosterUi();
- }
- updateConversationUi();
- } else {
- Conversation conversation = find(account, avatar.owner.toBareJid());
- if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
- MucOptions.User user = conversation.getMucOptions().findUserByFullJid(avatar.owner);
- if (user != null) {
- if (user.setAvatar(avatar)) {
- getAvatarService().clear(user);
- updateConversationUi();
- updateMucRosterUi();
- }
- }
- }
- }
- }
- }
- }
- }
- });
- }
-
- public void checkForAvatar(Account account, final UiCallback<Avatar> callback) {
- IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
- this.sendIqPacket(account, packet, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Element pubsub = packet.findChild("pubsub","http://jabber.org/protocol/pubsub");
- if (pubsub != null) {
- Element items = pubsub.findChild("items");
- if (items != null) {
- Avatar avatar = Avatar.parseMetadata(items);
- if (avatar != null) {
- avatar.owner = account.getJid().toBareJid();
- if (fileBackend.isAvatarCached(avatar)) {
- if (account.setAvatar(avatar.getFilename())) {
- databaseBackend.updateAccount(account);
- }
- getAvatarService().clear(account);
- callback.success(avatar);
- } else {
- fetchAvatarPep(account, avatar, callback);
- }
- return;
- }
- }
- }
- }
- callback.error(0, null);
- }
- });
- }
-
- public void deleteContactOnServer(Contact contact) {
- contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- contact.resetOption(Contact.Options.DIRTY_PUSH);
- contact.setOption(Contact.Options.DIRTY_DELETE);
- Account account = contact.getAccount();
- if (account.getStatus() == Account.State.ONLINE) {
- IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- Element item = iq.query(Xmlns.ROSTER).addChild("item");
- item.setAttribute("jid", contact.getJid().toString());
- item.setAttribute("subscription", "remove");
- account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
- }
- }
-
- public void updateConversation(final Conversation conversation) {
- mDatabaseExecutor.execute(new Runnable() {
- @Override
- public void run() {
- databaseBackend.updateConversation(conversation);
- }
- });
- }
-
- private void reconnectAccount(final Account account, final boolean force, final boolean interactive) {
- synchronized (account) {
- XmppConnection connection = account.getXmppConnection();
- if (connection == null) {
- connection = createConnection(account);
- account.setXmppConnection(connection);
- } else {
- connection.interrupt();
- }
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- if (!force) {
- disconnect(account, false);
- }
- Thread thread = new Thread(connection);
- connection.setInteractive(interactive);
- connection.prepareNewConnection();
- thread.start();
- scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
- } else {
- disconnect(account, force);
- account.getRoster().clearPresences();
- connection.resetEverything();
- account.getAxolotlService().resetBrokenness();
- }
- }
- }
-
- public void reconnectAccountInBackground(final Account account) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- reconnectAccount(account, false, true);
- }
- }).start();
- }
-
- public void invite(Conversation conversation, Jid contact) {
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": inviting " + contact + " to " + conversation.getJid().toBareJid());
- MessagePacket packet = mMessageGenerator.invite(conversation, contact);
- sendMessagePacket(conversation.getAccount(), packet);
- }
-
- public void directInvite(Conversation conversation, Jid jid) {
- MessagePacket packet = mMessageGenerator.directInvite(conversation, jid);
- sendMessagePacket(conversation.getAccount(), packet);
- }
-
- public void resetSendingToWaiting(Account account) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": reset 'sending' messages to 'waiting'");
- for (Conversation conversation : getConversations()) {
- if (conversation.getAccount() == account) {
- conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
-
- @Override
- public void onMessageFound(Message message) {
- markMessage(message, Message.STATUS_WAITING);
- }
- });
- }
- }
- }
-
- public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status) {
- return markMessage(account, recipient, uuid, status, null);
- }
-
- public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status, String errorMessage) {
- if (uuid == null) {
- return null;
- }
- for (Conversation conversation : getConversations()) {
- if (conversation.getJid().toBareJid().equals(recipient) && conversation.getAccount() == account) {
- final Message message = conversation.findSentMessageWithUuidOrRemoteId(uuid);
- if (message != null) {
- markMessage(message, status, errorMessage);
- }
- return message;
- }
- }
- return null;
- }
-
- public boolean markMessage(Conversation conversation, String uuid, int status) {
- if (uuid == null) {
- return false;
- } else {
- Message message = conversation.findSentMessageWithUuid(uuid);
- if (message != null) {
- markMessage(message, status);
- return true;
- } else {
- return false;
- }
- }
- }
-
- public void markMessage(Message message, int status) {
- markMessage(message, status, null);
- }
-
- public void markMessage(Message message, int status, String errorMessage) {
- if (status == Message.STATUS_SEND_FAILED
- && (message.getStatus() == Message.STATUS_SEND_RECEIVED || message
- .getStatus() == Message.STATUS_SEND_DISPLAYED)) {
- return;
- }
- message.setErrorMessage(errorMessage);
- message.setStatus(status);
- databaseBackend.updateMessage(message);
- updateConversationUi();
- }
-
- public SharedPreferences getPreferences() {
- return PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext());
- }
-
- public boolean confirmMessages() {
- return getPreferences().getBoolean("confirm_messages", true);
- }
-
- public boolean allowMessageCorrection() {
- return getPreferences().getBoolean("allow_message_correction", true);
- }
-
- public boolean sendChatStates() {
- return getPreferences().getBoolean("chat_states", true);
- }
-
- public boolean saveEncryptedMessages() {
- return !getPreferences().getBoolean("dont_save_encrypted", false);
- }
-
- private boolean respectAutojoin() {
- return getPreferences().getBoolean("autojoin", true);
- }
-
- public boolean indicateReceived() {
- return getPreferences().getBoolean("indicate_received", true);
- }
-
- public boolean useTorToConnect() {
- return Config.FORCE_ORBOT || getPreferences().getBoolean("use_tor", false);
- }
-
- public boolean showExtendedConnectionOptions() {
- return getPreferences().getBoolean("show_connection_options", false);
- }
-
- public boolean broadcastLastActivity() {
- return getPreferences().getBoolean("last_activity", true);
- }
-
- public int unreadCount() {
- int count = 0;
- for (Conversation conversation : getConversations()) {
- count += conversation.unreadCount();
- }
- return count;
- }
-
- public void vibrate() {
- Log.d(Config.LOGTAG,"Notification: short vibrate");
- Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
- vibrator.vibrate(100);
- }
-
-
- public void showErrorToastInUi(int resId) {
- if (mOnShowErrorToast != null) {
- mOnShowErrorToast.onShowErrorToast(resId);
- }
- }
-
- public void updateConversationUi() {
- if (mOnConversationUpdate != null) {
- mOnConversationUpdate.onConversationUpdate();
- }
- }
-
- public void updateAccountUi() {
- if (mOnAccountUpdate != null) {
- mOnAccountUpdate.onAccountUpdate();
- }
- }
-
- public void updateRosterUi() {
- if (mOnRosterUpdate != null) {
- mOnRosterUpdate.onRosterUpdate();
- }
- }
-
- public boolean displayCaptchaRequest(Account account, String id, Data data, Bitmap captcha) {
- if (mOnCaptchaRequested != null) {
- DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics();
- Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int) (captcha.getWidth() * metrics.scaledDensity),
- (int) (captcha.getHeight() * metrics.scaledDensity), false);
-
- mOnCaptchaRequested.onCaptchaRequested(account, id, data, scaled);
- return true;
- }
- return false;
- }
-
- public void updateBlocklistUi(final OnUpdateBlocklist.Status status) {
- if (mOnUpdateBlocklist != null) {
- mOnUpdateBlocklist.OnUpdateBlocklist(status);
- }
- }
-
- public void updateMucRosterUi() {
- if (mOnMucRosterUpdate != null) {
- mOnMucRosterUpdate.onMucRosterUpdate();
- }
- }
-
- public void keyStatusUpdated(AxolotlService.FetchStatus report) {
- if (mOnKeyStatusUpdated != null) {
- mOnKeyStatusUpdated.onKeyStatusUpdated(report);
- }
- }
-
- public Account findAccountByJid(final Jid accountJid) {
- for (Account account : this.accounts) {
- if (account.getJid().toBareJid().equals(accountJid.toBareJid())) {
- return account;
- }
- }
- return null;
- }
-
- public Conversation findConversationByUuid(String uuid) {
- for (Conversation conversation : getConversations()) {
- if (conversation.getUuid().equals(uuid)) {
- return conversation;
- }
- }
- return null;
- }
-
- public boolean markRead(final Conversation conversation) {
- return markRead(conversation,true);
- }
-
- public boolean markRead(final Conversation conversation, boolean clear) {
- if (clear) {
- mNotificationService.clear(conversation);
- }
- final List<Message> readMessages = conversation.markRead();
- if (readMessages.size() > 0) {
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- for (Message message : readMessages) {
- databaseBackend.updateMessage(message);
- }
- }
- };
- mDatabaseExecutor.execute(runnable);
- updateUnreadCountBadge();
- return true;
- } else {
- return false;
- }
- }
-
- public synchronized void updateUnreadCountBadge() {
- int count = unreadCount();
- if (unreadCount != count) {
- Log.d(Config.LOGTAG, "update unread count to " + count);
- if (count > 0) {
- ShortcutBadger.applyCount(getApplicationContext(), count);
- } else {
- ShortcutBadger.removeCount(getApplicationContext());
- }
- unreadCount = count;
- }
- }
-
- public void sendReadMarker(final Conversation conversation) {
- final Message markable = conversation.getLatestMarkableMessage();
- if (this.markRead(conversation)) {
- updateConversationUi();
- }
- if (confirmMessages() && markable != null && markable.trusted() && markable.getRemoteMsgId() != null) {
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending read marker to " + markable.getCounterpart().toString());
- Account account = conversation.getAccount();
- final Jid to = markable.getCounterpart();
- MessagePacket packet = mMessageGenerator.confirm(account, to, markable.getRemoteMsgId());
- this.sendMessagePacket(conversation.getAccount(), packet);
- }
- }
-
- public SecureRandom getRNG() {
- return this.mRandom;
- }
-
- public MemorizingTrustManager getMemorizingTrustManager() {
- return this.mMemorizingTrustManager;
- }
-
- public void setMemorizingTrustManager(MemorizingTrustManager trustManager) {
- this.mMemorizingTrustManager = trustManager;
- }
-
- public void updateMemorizingTrustmanager() {
- final MemorizingTrustManager tm;
- final boolean dontTrustSystemCAs = getPreferences().getBoolean("dont_trust_system_cas", false);
- if (dontTrustSystemCAs) {
- tm = new MemorizingTrustManager(getApplicationContext(), null);
- } else {
- tm = new MemorizingTrustManager(getApplicationContext());
- }
- setMemorizingTrustManager(tm);
- }
-
- public PowerManager getPowerManager() {
- return this.pm;
- }
-
- public LruCache<String, Bitmap> getBitmapCache() {
- return this.mBitmapCache;
- }
-
- public void syncRosterToDisk(final Account account) {
- Runnable runnable = new Runnable() {
-
- @Override
- public void run() {
- databaseBackend.writeRoster(account.getRoster());
- }
- };
- mDatabaseExecutor.execute(runnable);
-
- }
-
- public List<String> getKnownHosts() {
- final List<String> hosts = new ArrayList<>();
- for (final Account account : getAccounts()) {
- if (!hosts.contains(account.getServer().toString())) {
- hosts.add(account.getServer().toString());
- }
- for (final Contact contact : account.getRoster().getContacts()) {
- if (contact.showInRoster()) {
- final String server = contact.getServer().toString();
- if (server != null && !hosts.contains(server)) {
- hosts.add(server);
- }
- }
- }
- }
- if(Config.DOMAIN_LOCK != null && !hosts.contains(Config.DOMAIN_LOCK)) {
- hosts.add(Config.DOMAIN_LOCK);
- }
- if(Config.MAGIC_CREATE_DOMAIN != null && !hosts.contains(Config.MAGIC_CREATE_DOMAIN)) {
- hosts.add(Config.MAGIC_CREATE_DOMAIN);
- }
- return hosts;
- }
-
- public List<String> getKnownConferenceHosts() {
- final ArrayList<String> mucServers = new ArrayList<>();
- for (final Account account : accounts) {
- if (account.getXmppConnection() != null) {
- final String server = account.getXmppConnection().getMucServer();
- if (server != null && !mucServers.contains(server)) {
- mucServers.add(server);
- }
- }
- }
- return mucServers;
- }
-
- public void sendMessagePacket(Account account, MessagePacket packet) {
- XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- connection.sendMessagePacket(packet);
- }
- }
-
- public void sendPresencePacket(Account account, PresencePacket packet) {
- XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- connection.sendPresencePacket(packet);
- }
- }
-
- public void sendCreateAccountWithCaptchaPacket(Account account, String id, Data data) {
- final XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- IqPacket request = mIqGenerator.generateCreateAccountWithCaptcha(account, id, data);
- connection.sendUnmodifiedIqPacket(request, connection.registrationResponseListener);
- }
- }
-
- public void sendIqPacket(final Account account, final IqPacket packet, final OnIqPacketReceived callback) {
- final XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- connection.sendIqPacket(packet, callback);
- }
- }
-
- public void sendPresence(final Account account) {
- sendPresence(account, checkListeners() && broadcastLastActivity());
- }
-
- private void sendPresence(final Account account, final boolean includeIdleTimestamp) {
- PresencePacket packet;
- if (manuallyChangePresence()) {
- packet = mPresenceGenerator.selfPresence(account, account.getPresenceStatus());
- String message = account.getPresenceStatusMessage();
- if (message != null && !message.isEmpty()) {
- packet.addChild(new Element("status").setContent(message));
- }
- } else {
- packet = mPresenceGenerator.selfPresence(account, getTargetPresence());
- }
- if (mLastActivity > 0 && includeIdleTimestamp) {
- long since = Math.min(mLastActivity, System.currentTimeMillis()); //don't send future dates
- packet.addChild("idle","urn:xmpp:idle:1").setAttribute("since", AbstractGenerator.getTimestamp(since));
- }
- sendPresencePacket(account, packet);
- }
-
- private void deactivateGracePeriod() {
- for(Account account : getAccounts()) {
- account.deactivateGracePeriod();
- }
- }
-
- public void refreshAllPresences() {
- boolean includeIdleTimestamp = checkListeners() && broadcastLastActivity();
- for (Account account : getAccounts()) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- sendPresence(account, includeIdleTimestamp);
- }
- }
- }
-
- private void refreshAllGcmTokens() {
- for(Account account : getAccounts()) {
- if (account.isOnlineAndConnected() && mPushManagementService.available(account)) {
- mPushManagementService.registerPushTokenOnServer(account);
- }
- }
- }
-
- private void sendOfflinePresence(final Account account) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": sending offline presence");
- sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account));
- }
-
- public MessageGenerator getMessageGenerator() {
- return this.mMessageGenerator;
- }
-
- public PresenceGenerator getPresenceGenerator() {
- return this.mPresenceGenerator;
- }
-
- public IqGenerator getIqGenerator() {
- return this.mIqGenerator;
- }
-
- public IqParser getIqParser() {
- return this.mIqParser;
- }
-
- public JingleConnectionManager getJingleConnectionManager() {
- return this.mJingleConnectionManager;
- }
-
- public MessageArchiveService getMessageArchiveService() {
- return this.mMessageArchiveService;
- }
-
- public List<Contact> findContacts(Jid jid) {
- ArrayList<Contact> contacts = new ArrayList<>();
- for (Account account : getAccounts()) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- Contact contact = account.getRoster().getContactFromRoster(jid);
- if (contact != null) {
- contacts.add(contact);
- }
- }
- }
- return contacts;
- }
-
- public Conversation findFirstMuc(Jid jid) {
- for(Conversation conversation : getConversations()) {
- if (conversation.getJid().toBareJid().equals(jid.toBareJid())
- && conversation.getMode() == Conversation.MODE_MULTI) {
- return conversation;
- }
- }
- return null;
- }
-
- public NotificationService getNotificationService() {
- return this.mNotificationService;
- }
-
- public HttpConnectionManager getHttpConnectionManager() {
- return this.mHttpConnectionManager;
- }
-
- public void resendFailedMessages(final Message message) {
- final Collection<Message> messages = new ArrayList<>();
- Message current = message;
- while (current.getStatus() == Message.STATUS_SEND_FAILED) {
- messages.add(current);
- if (current.mergeable(current.next())) {
- current = current.next();
- } else {
- break;
- }
- }
- for (final Message msg : messages) {
- msg.setTime(System.currentTimeMillis());
- markMessage(msg, Message.STATUS_WAITING);
- this.resendMessage(msg, false);
- }
- }
-
- public void clearConversationHistory(final Conversation conversation) {
- conversation.clearMessages();
- conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam
- conversation.setLastClearHistory(System.currentTimeMillis());
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- databaseBackend.deleteMessagesInConversation(conversation);
- databaseBackend.updateConversation(conversation);
-
- }
- };
- mDatabaseExecutor.execute(runnable);
- }
-
- public void sendBlockRequest(final Blockable blockable, boolean reportSpam) {
- if (blockable != null && blockable.getBlockedJid() != null) {
- final Jid jid = blockable.getBlockedJid();
- this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam), new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.getBlocklist().add(jid);
- updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
- }
- }
- });
- }
- }
-
- public void sendUnblockRequest(final Blockable blockable) {
- if (blockable != null && blockable.getJid() != null) {
- final Jid jid = blockable.getBlockedJid();
- this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetUnblockRequest(jid), new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.getBlocklist().remove(jid);
- updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
- }
- }
- });
- }
- }
-
- public void publishDisplayName(Account account) {
- String displayName = account.getDisplayName();
- if (displayName != null && !displayName.isEmpty()) {
- IqPacket publish = mIqGenerator.publishNick(displayName);
- sendIqPacket(account, publish, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.ERROR) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not publish nick");
- }
- }
- });
- }
- }
-
- public ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair<String, String> key) {
- ServiceDiscoveryResult result = discoCache.get(key);
- if (result != null) {
- return result;
- } else {
- result = databaseBackend.findDiscoveryResult(key.first, key.second);
- if (result != null) {
- discoCache.put(key, result);
- }
- return result;
- }
- }
-
- public void fetchCaps(Account account, final Jid jid, final Presence presence) {
- final Pair<String,String> key = new Pair<>(presence.getHash(), presence.getVer());
- ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key);
- if (disco != null) {
- presence.setServiceDiscoveryResult(disco);
- } else {
- if (!account.inProgressDiscoFetches.contains(key)) {
- account.inProgressDiscoFetches.add(key);
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.setTo(jid);
- request.query("http://jabber.org/protocol/disco#info");
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": making disco request for "+key.second+" to "+jid);
- sendIqPacket(account, request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket discoPacket) {
- if (discoPacket.getType() == IqPacket.TYPE.RESULT) {
- ServiceDiscoveryResult disco = new ServiceDiscoveryResult(discoPacket);
- if (presence.getVer().equals(disco.getVer())) {
- databaseBackend.insertDiscoveryResult(disco);
- injectServiceDiscorveryResult(account.getRoster(), presence.getHash(), presence.getVer(), disco);
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + disco.getVer());
- }
- }
- account.inProgressDiscoFetches.remove(key);
- }
- });
- }
- }
- }
-
- private void injectServiceDiscorveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) {
- for(Contact contact : roster.getContacts()) {
- for(Presence presence : contact.getPresences().getPresences().values()) {
- if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) {
- presence.setServiceDiscoveryResult(disco);
- }
- }
- }
- }
-
- public void fetchMamPreferences(Account account, final OnMamPreferencesFetched callback) {
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.addChild("prefs","urn:xmpp:mam:0");
- sendIqPacket(account, request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element prefs = packet.findChild("prefs","urn:xmpp:mam:0");
- if (packet.getType() == IqPacket.TYPE.RESULT && prefs != null) {
- callback.onPreferencesFetched(prefs);
- } else {
- callback.onPreferencesFetchFailed();
- }
- }
- });
- }
-
- public PushManagementService getPushManagementService() {
- return mPushManagementService;
- }
-
- public Account getPendingAccount() {
- Account pending = null;
- for(Account account : getAccounts()) {
- if (account.isOptionSet(Account.OPTION_REGISTER)) {
- pending = account;
- } else {
- return null;
- }
- }
- return pending;
- }
-
- public void changeStatus(Account account, Presence.Status status, String statusMessage, boolean send) {
- if (!statusMessage.isEmpty()) {
- databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
- }
- changeStatusReal(account, status, statusMessage, send);
- }
-
- private void changeStatusReal(Account account, Presence.Status status, String statusMessage, boolean send) {
- account.setPresenceStatus(status);
- account.setPresenceStatusMessage(statusMessage);
- databaseBackend.updateAccount(account);
- if (!account.isOptionSet(Account.OPTION_DISABLED) && send) {
- sendPresence(account);
- }
- }
-
- public void changeStatus(Presence.Status status, String statusMessage) {
- if (!statusMessage.isEmpty()) {
- databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
- }
- for(Account account : getAccounts()) {
- changeStatusReal(account, status, statusMessage, true);
- }
- }
-
- public List<PresenceTemplate> getPresenceTemplates(Account account) {
- List<PresenceTemplate> templates = databaseBackend.getPresenceTemplates();
- for(PresenceTemplate template : account.getSelfContact().getPresences().asTemplates()) {
- if (!templates.contains(template)) {
- templates.add(0, template);
- }
- }
- return templates;
- }
-
- public void saveConversationAsBookmark(Conversation conversation, String name) {
- Account account = conversation.getAccount();
- Bookmark bookmark = new Bookmark(account, conversation.getJid().toBareJid());
- if (!conversation.getJid().isBareJid()) {
- bookmark.setNick(conversation.getJid().getResourcepart());
- }
- if (name != null && !name.trim().isEmpty()) {
- bookmark.setBookmarkName(name.trim());
- }
- bookmark.setAutojoin(getPreferences().getBoolean("autojoin",true));
- account.getBookmarks().add(bookmark);
- pushBookmarks(account);
- conversation.setBookmark(bookmark);
- }
-
- public void clearStartTimeCounter() {
- mDatabaseExecutor.execute(new Runnable() {
- @Override
- public void run() {
- databaseBackend.clearStartTimeCounter();
- }
- });
- }
-
- public void verifyFingerprints(Contact contact, List<XmppUri.Fingerprint> fingerprints) {
- boolean needsRosterWrite = false;
- final AxolotlService axolotlService = contact.getAccount().getAxolotlService();
- for(XmppUri.Fingerprint fp : fingerprints) {
- if (fp.type == XmppUri.FingerprintType.OTR) {
- needsRosterWrite |= contact.addOtrFingerprint(fp.fingerprint);
- } else if (fp.type == XmppUri.FingerprintType.OMEMO) {
- String fingerprint = "05"+fp.fingerprint.replaceAll("\\s","");
- FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint);
- if (fingerprintStatus != null) {
- if (!fingerprintStatus.isVerified()) {
- axolotlService.setFingerprintTrust(fingerprint,fingerprintStatus.toVerified());
- }
- } else {
- axolotlService.preVerifyFingerprint(contact,fingerprint);
- }
- }
- }
- if (needsRosterWrite) {
- syncRosterToDisk(contact.getAccount());
- }
- }
-
- public boolean verifyFingerprints(Account account, List<XmppUri.Fingerprint> fingerprints) {
- final AxolotlService axolotlService = account.getAxolotlService();
- boolean verifiedSomething = false;
- for(XmppUri.Fingerprint fp : fingerprints) {
- if (fp.type == XmppUri.FingerprintType.OMEMO) {
- String fingerprint = "05"+fp.fingerprint.replaceAll("\\s","");
- Log.d(Config.LOGTAG,"trying to verify own fp="+fingerprint);
- FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint);
- if (fingerprintStatus != null) {
- if (!fingerprintStatus.isVerified()) {
- axolotlService.setFingerprintTrust(fingerprint,fingerprintStatus.toVerified());
- verifiedSomething = true;
- }
- } else {
- axolotlService.preVerifyFingerprint(account,fingerprint);
- verifiedSomething = true;
- }
- }
- }
- return verifiedSomething;
- }
-
- public interface OnMamPreferencesFetched {
- void onPreferencesFetched(Element prefs);
- void onPreferencesFetchFailed();
- }
-
- public void pushMamPreferences(Account account, Element prefs) {
- IqPacket set = new IqPacket(IqPacket.TYPE.SET);
- set.addChild(prefs);
- sendIqPacket(account, set, null);
- }
-
- public interface OnAccountCreated {
- void onAccountCreated(Account account);
-
- void informUser(int r);
- }
-
- public interface OnMoreMessagesLoaded {
- void onMoreMessagesLoaded(int count, Conversation conversation);
-
- void informUser(int r);
- }
-
- public interface OnAccountPasswordChanged {
- void onPasswordChangeSucceeded();
-
- void onPasswordChangeFailed();
- }
-
- public interface OnAffiliationChanged {
- void onAffiliationChangedSuccessful(Jid jid);
-
- void onAffiliationChangeFailed(Jid jid, int resId);
- }
-
- public interface OnRoleChanged {
- void onRoleChangedSuccessful(String nick);
-
- void onRoleChangeFailed(String nick, int resid);
- }
-
- public interface OnConversationUpdate {
- void onConversationUpdate();
- }
-
- public interface OnAccountUpdate {
- void onAccountUpdate();
- }
-
- public interface OnCaptchaRequested {
- void onCaptchaRequested(Account account,
- String id,
- Data data,
- Bitmap captcha);
- }
-
- public interface OnRosterUpdate {
- void onRosterUpdate();
- }
-
- public interface OnMucRosterUpdate {
- void onMucRosterUpdate();
- }
-
- public interface OnConferenceConfigurationFetched {
- void onConferenceConfigurationFetched(Conversation conversation);
-
- void onFetchFailed(Conversation conversation, Element error);
- }
-
- public interface OnConferenceJoined {
- void onConferenceJoined(Conversation conversation);
- }
-
- public interface OnConferenceOptionsPushed {
- void onPushSucceeded();
-
- void onPushFailed();
- }
-
- public interface OnShowErrorToast {
- void onShowErrorToast(int resId);
- }
-
- public class XmppConnectionBinder extends Binder {
- public XmppConnectionService getService() {
- return XmppConnectionService.this;
- }
- }
-
- public void ScheduleAutomaticExport() {
- //start export log service every day at given time
- if (Config.ExportLogs) {
- if (Config.ExportLogs_Hour >= 0 && Config.ExportLogs_Hour <= 23 && Config.ExportLogs_Minute >= 0 && Config.ExportLogs_Minute <= 59) {
- Log.d(Config.LOGTAG, "Schedule automatic export logs at " + Config.ExportLogs_Hour + ":" + Config.ExportLogs_Minute);
- Calendar calendar = Calendar.getInstance();
- calendar.setTimeInMillis(System.currentTimeMillis());
- calendar.set(Calendar.HOUR_OF_DAY, Config.ExportLogs_Hour);
- calendar.set(Calendar.MINUTE, Config.ExportLogs_Minute);
- Intent intent = new Intent(this, AlarmReceiver.class);
- intent.setAction("exportlogs");
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, AlarmReceiver.SCHEDULE_ALARM_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- ((AlarmManager) getSystemService(ALARM_SERVICE)).setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
- }
- }
- }
-
- public void CancelAutomaticExport() {
- if (Config.ExportLogs) {
- Log.d(Config.LOGTAG, "Cancel scheduled automatic export");
- Intent intent = new Intent(this, AlarmReceiver.class);
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, AlarmReceiver.SCHEDULE_ALARM_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- ((AlarmManager) this.getSystemService(ALARM_SERVICE)).cancel(pendingIntent);
- }
- }
+ public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations";
+ public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
+ public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground";
+ public static final String ACTION_TRY_AGAIN = "try_again";
+ public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = "dismiss_error";
+ public static final String ACTION_IDLE_PING = "idle_ping";
+ private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
+ public static final String ACTION_GCM_TOKEN_REFRESH = "gcm_token_refresh";
+ public static final String ACTION_GCM_MESSAGE_RECEIVED = "gcm_message_received";
+ private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor();
+ private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor();
+ private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true);
+ private final IBinder mBinder = new XmppConnectionBinder();
+ private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
+ private final IqGenerator mIqGenerator = new IqGenerator(this);
+ private final List<String> mInProgressAvatarFetches = new ArrayList<>();
+ private final HashSet<Jid> mLowPingTimeoutMode = new HashSet<>();
+ private WakeLock wakeLock;
+ private long mLastActivity = 0;
+ public static VideoCompressor CompressVideo;
+ private NotificationManager mNotifyManager;
+
+ public DatabaseBackend databaseBackend;
+ private ContentObserver contactObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ Intent intent = new Intent(getApplicationContext(),
+ XmppConnectionService.class);
+ intent.setAction(ACTION_MERGE_PHONE_CONTACTS);
+ startService(intent);
+ }
+ };
+ private FileBackend fileBackend = new FileBackend(this);
+ private MemorizingTrustManager mMemorizingTrustManager;
+ private NotificationService mNotificationService = new NotificationService(
+ this);
+ private OnMessagePacketReceived mMessageParser = new MessageParser(this);
+ private OnPresencePacketReceived mPresenceParser = new PresenceParser(this);
+ private IqParser mIqParser = new IqParser(this);
+ private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() != IqPacket.TYPE.RESULT) {
+ Element error = packet.findChild("error");
+ String text = error != null ? error.findChildContent("text") : null;
+ if (text != null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": received iq error - " + text);
+ }
+ }
+ }
+ };
+ 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 = new OnContactStatusChanged() {
+
+ @Override
+ public void onContactStatusChanged(Contact contact, boolean online) {
+ Conversation conversation = find(getConversations(), contact);
+ if (conversation != null) {
+ if (online) {
+ conversation.endOtrIfNeeded();
+ if (contact.getPresences().size() == 1) {
+ sendUnsentMessages(conversation);
+ }
+ } else {
+ //check if the resource we are haveing a conversation with is still online
+ if (conversation.hasValidOtrSession()) {
+ String otrResource = conversation.getOtrSession().getSessionID().getUserID();
+ if (!(Arrays.asList(contact.getPresences().toResourceArray()).contains(otrResource))) {
+ conversation.endOtrIfNeeded();
+ }
+ }
+ }
+ }
+ }
+ };
+ private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
+ this);
+ private AvatarService mAvatarService = new AvatarService(this);
+ private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
+ private PushManagementService mPushManagementService = new PushManagementService(this);
+ private OnConversationUpdate mOnConversationUpdate = null;
+
+
+ private final ConversationsFileObserver fileObserver = new ConversationsFileObserver(
+ Environment.getExternalStorageDirectory().getAbsolutePath()
+ ) {
+ @Override
+ public void onEvent(int event, String path) {
+ markFileDeleted(path);
+ }
+ };
+ private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
+
+ @Override
+ public void onJinglePacketReceived(Account account, JinglePacket packet) {
+ mJingleConnectionManager.deliverPacket(account, packet);
+ }
+ };
+ private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
+
+ @Override
+ public void onMessageAcknowledged(Account account, String uuid) {
+ for (final Conversation conversation : getConversations()) {
+ if (conversation.getAccount() == account) {
+ Message message = conversation.findUnsentMessageWithUuid(uuid);
+ if (message != null) {
+ markMessage(message, Message.STATUS_SEND);
+ }
+ }
+ }
+ }
+ };
+ private int convChangedListenerCount = 0;
+ private OnShowErrorToast mOnShowErrorToast = null;
+ private int showErrorToastListenerCount = 0;
+ private int unreadCount = -1;
+ private OnAccountUpdate mOnAccountUpdate = null;
+ private OnCaptchaRequested mOnCaptchaRequested = null;
+ private int accountChangedListenerCount = 0;
+ private int captchaRequestedListenerCount = 0;
+ private OnRosterUpdate mOnRosterUpdate = null;
+ private OnUpdateBlocklist mOnUpdateBlocklist = null;
+ private int updateBlocklistListenerCount = 0;
+ private int rosterChangedListenerCount = 0;
+ private OnMucRosterUpdate mOnMucRosterUpdate = null;
+ private int mucRosterChangedListenerCount = 0;
+ private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
+ private int keyStatusUpdatedListenerCount = 0;
+ private SecureRandom mRandom;
+ private LruCache<Pair<String, String>, ServiceDiscoveryResult> discoCache = new LruCache<>(20);
+ private final OnBindListener mOnBindListener = new OnBindListener() {
+
+ @Override
+ public void onBind(final Account account) {
+ synchronized (mInProgressAvatarFetches) {
+ for (Iterator<String> iterator = mInProgressAvatarFetches.iterator(); iterator.hasNext(); ) {
+ final String KEY = iterator.next();
+ if (KEY.startsWith(account.getJid().toBareJid() + "_")) {
+ iterator.remove();
+ }
+ }
+ }
+ account.getRoster().clearPresences();
+ mJingleConnectionManager.cancelInTransmission();
+ fetchRosterFromServer(account);
+ fetchBookmarks(account);
+ sendPresence(account);
+ if (mPushManagementService.available(account)) {
+ mPushManagementService.registerPushTokenOnServer(account);
+ }
+ connectMultiModeConversations(account);
+ syncDirtyContacts(account);
+ }
+ };
+ private OnStatusChanged statusListener = new OnStatusChanged() {
+
+ @Override
+ public void onStatusChanged(final Account account) {
+ XmppConnection connection = account.getXmppConnection();
+ if (mOnAccountUpdate != null) {
+ mOnAccountUpdate.onAccountUpdate();
+ }
+ if (account.getStatus() == Account.State.ONLINE) {
+ synchronized (mLowPingTimeoutMode) {
+ if (mLowPingTimeoutMode.remove(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": leaving low ping timeout mode");
+ }
+ }
+ if (account.setShowErrorNotification(true)) {
+ databaseBackend.updateAccount(account);
+ }
+ mMessageArchiveService.executePendingQueries(account);
+ if (connection != null && connection.getFeatures().csi()) {
+ if (checkListeners()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//inactive");
+ connection.sendInactive();
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//active");
+ connection.sendActive();
+ }
+ }
+ List<Conversation> conversations = getConversations();
+ for (Conversation conversation : conversations) {
+ if (conversation.getAccount() == account
+ && !account.pendingConferenceJoins.contains(conversation)) {
+ if (!conversation.startOtrIfNeeded()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": couldn't start OTR with " + conversation.getContact().getJid() + " when needed");
+ }
+ sendUnsentMessages(conversation);
+ }
+ }
+ for (Conversation conversation : account.pendingConferenceLeaves) {
+ leaveMuc(conversation);
+ }
+ account.pendingConferenceLeaves.clear();
+ for (Conversation conversation : account.pendingConferenceJoins) {
+ joinMuc(conversation);
+ }
+ account.pendingConferenceJoins.clear();
+ scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
+ } else if (account.getStatus() == Account.State.OFFLINE || account.getStatus() == Account.State.DISABLED) {
+ resetSendingToWaiting(account);
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ synchronized (mLowPingTimeoutMode) {
+ if (mLowPingTimeoutMode.contains(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": went into offline state during low ping mode. reconnecting now");
+ reconnectAccount(account, true, false);
+ } else {
+ int timeToReconnect = mRandom.nextInt(20) + 10;
+ scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode());
+ }
+ }
+ }
+ } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) {
+ databaseBackend.updateAccount(account);
+ reconnectAccount(account, true, false);
+ } else if ((account.getStatus() != Account.State.CONNECTING)
+ && (account.getStatus() != Account.State.NO_INTERNET)) {
+ resetSendingToWaiting(account);
+ if (connection != null) {
+ int next = connection.getTimeToNextAttempt();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": error connecting account. try again in "
+ + next + "s for the "
+ + (connection.getAttempt() + 1) + " time");
+ scheduleWakeUpCall(next, account.getUuid().hashCode());
+ }
+ }
+ getNotificationService().updateErrorNotification();
+ }
+ };
+ private OpenPgpServiceConnection pgpServiceConnection;
+ private PgpEngine mPgpEngine = null;
+ private PowerManager pm;
+ private LruCache<String, Bitmap> mBitmapCache;
+ private EventReceiver mEventReceiver = new EventReceiver();
+
+ private boolean mRestoredFromDatabase = false;
+
+ private static String generateFetchKey(Account account, final Avatar avatar) {
+ return account.getJid().toBareJid() + "_" + avatar.owner + "_" + avatar.sha1sum;
+ }
+
+ public boolean areMessagesInitialized() {
+ return this.mRestoredFromDatabase;
+ }
+
+ public PgpEngine getPgpEngine() {
+ if (!Config.supportOpenPgp()) {
+ return null;
+ } else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
+ if (this.mPgpEngine == null) {
+ this.mPgpEngine = new PgpEngine(new OpenPgpApi(
+ getApplicationContext(),
+ pgpServiceConnection.getService()), this);
+ }
+ return mPgpEngine;
+ } else {
+ return null;
+ }
+
+ }
+
+ public OpenPgpApi getOpenPgpApi() {
+ if (!Config.supportOpenPgp()) {
+ return null;
+ } else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
+ return new OpenPgpApi(this, pgpServiceConnection.getService());
+ } else {
+ return null;
+ }
+ }
+
+ public FileBackend getFileBackend() {
+ return this.fileBackend;
+ }
+
+ public AvatarService getAvatarService() {
+ return this.mAvatarService;
+ }
+
+ public void attachLocationToConversation(final Conversation conversation,
+ final Uri uri,
+ final UiCallback<Message> callback) {
+ int encryption = conversation.getNextEncryption();
+ if (encryption == Message.ENCRYPTION_PGP) {
+ encryption = Message.ENCRYPTION_DECRYPTED;
+ }
+ Message message = new Message(conversation, uri.toString(), encryption);
+ if (conversation.getNextCounterpart() != null) {
+ message.setCounterpart(conversation.getNextCounterpart());
+ }
+ if (encryption == Message.ENCRYPTION_DECRYPTED) {
+ getPgpEngine().encrypt(message, callback);
+ } else {
+ callback.success(message);
+ }
+ }
+
+ public void attachFileToConversation(final Conversation conversation,
+ final Uri uri,
+ final UiCallback<Message> callback) {
+ if (FileBackend.weOwnFile(this, uri)) {
+ Log.d(Config.LOGTAG, "trying to attach file that belonged to us");
+ callback.error(R.string.security_error_invalid_file_access, null);
+ return;
+ }
+ final Message message;
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+ message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
+ } else {
+ message = new Message(conversation, "", conversation.getNextEncryption());
+ }
+ message.setCounterpart(conversation.getNextCounterpart());
+ message.setType(Message.TYPE_FILE);
+ final String path = getFileBackend().getOriginalPath(uri);
+ Log.d(Config.LOGTAG, "File path = " + path);
+ mFileAddingExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ if (path != null) {
+ message.setRelativeFilePath(path);
+ getFileBackend().updateFileParams(message);
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ getPgpEngine().encrypt(message, callback);
+ } else {
+ callback.success(message);
+ }
+ } else {
+ try {
+ getFileBackend().copyFileToPrivateStorage(message, uri);
+ getFileBackend().updateFileParams(message);
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ final PgpEngine pgpEngine = getPgpEngine();
+ if (pgpEngine != null) {
+ pgpEngine.encrypt(message, callback);
+ } else if (callback != null) {
+ callback.error(R.string.unable_to_connect_to_keychain, null);
+ }
+ } else {
+ callback.success(message);
+ }
+ } catch (FileBackend.FileCopyException e) {
+ callback.error(e.getResId(), message);
+ }
+ }
+ }
+ });
+ }
+
+ public void attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
+ if (FileBackend.weOwnFile(this, uri)) {
+ Log.d(Config.LOGTAG, "trying to attach file that belonged to us");
+ callback.error(R.string.security_error_invalid_file_access, null);
+ return;
+ }
+ final String compressPictures = getCompressPicturesPreference();
+ if ("never".equals(compressPictures)
+ || ("auto".equals(compressPictures) && getFileBackend().useImageAsIs(uri))) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": not compressing picture. sending as file");
+ attachFileToConversation(conversation, uri, callback);
+ return;
+ }
+ final Message message;
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+ message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
+ } else {
+ message = new Message(conversation, "", conversation.getNextEncryption());
+ }
+ message.setCounterpart(conversation.getNextCounterpart());
+ message.setType(Message.TYPE_IMAGE);
+ mFileAddingExecutor.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ getFileBackend().copyImageToPrivateStorage(message, uri);
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+ final PgpEngine pgpEngine = getPgpEngine();
+ if (pgpEngine != null) {
+ pgpEngine.encrypt(message, callback);
+ } else if (callback != null) {
+ callback.error(R.string.unable_to_connect_to_keychain, null);
+ }
+ } else {
+ callback.success(message);
+ }
+ } catch (final FileBackend.FileCopyException e) {
+ callback.error(e.getResId(), message);
+ }
+ }
+ });
+ }
+
+ public void attachVideoToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
+ File f = new File(FileUtils.getPath(this, uri));
+ long filesize = f.length();
+ String path = f.toString();
+ final String compressVideos = getCompressVideoPreference();
+ boolean sendVideoAsIs = false;
+ final Integer NOTIFICATION_ID = (int) (new Date().getTime() / 1000);
+ mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext());
+ mBuilder.setContentTitle(getString(R.string.app_name))
+ .setContentText(getString(R.string.compressing_video))
+ .setSmallIcon(R.drawable.ic_play_box_outline_white_24dp)
+ .setOngoing(true)
+ .setProgress(0, 0, true);
+ mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
+ if (FileBackend.weOwnFile(this, uri)) {
+ Log.d(Config.LOGTAG, "trying to attach video that belonged to us");
+ mNotifyManager.cancel(NOTIFICATION_ID);
+ callback.error(R.string.security_error_invalid_file_access, null);
+ return;
+ }
+ Log.d(Config.LOGTAG, "Video file (size) :" + f.toString() + "(" + filesize / 1024 / 1024 + "MB)");
+ if (filesize == 0) {
+ Log.d(Config.LOGTAG, "Error with file, size = 0");
+ mNotifyManager.cancel(NOTIFICATION_ID);
+ callback.error(R.string.error_file_corrupt, null);
+ return;
+ }
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+ File compressed_file = new File(FileBackend.getConversationsVideoDirectory() + "/"
+ + dateFormat.format(new Date())
+ + "_komp.mp4");
+ final String compressed_path = compressed_file.toString();
+ final Uri compressed_uri = Uri.fromFile(compressed_file);
+ if ("never".equals(compressVideos)) {
+ sendVideoAsIs = true;
+ } else if ("always".equals(compressVideos) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ sendVideoAsIs = false;
+ } else if ("auto".equals(compressVideos) && filesize > Config.VIDEO_MAX_SIZE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ sendVideoAsIs = false;
+ } else {
+ sendVideoAsIs = true;
+ }
+ if (!sendVideoAsIs) {
+ CompressVideo = new VideoCompressor(path, compressed_path, new Interface() {
+ @Override
+ public void videocompressed(boolean result) {
+ if (result) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending compressed video.");
+ mNotifyManager.cancel(NOTIFICATION_ID);
+ attachFileToConversation(conversation, compressed_uri, callback);
+ }
+ }
+ });
+ CompressVideo.execute();
+ } else {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": not compressing video. sending as file");
+ mNotifyManager.cancel(NOTIFICATION_ID);
+ attachFileToConversation(conversation, uri, callback);
+ }
+ }
+
+ public interface Interface {
+ void videocompressed(boolean result);
+ }
+
+ class VideoCompressor extends AsyncTask<String, Void, Boolean> {
+ private String originalpath;
+ private String compressedpath;
+ private Interface mListener;
+
+ VideoCompressor(String path, String compressed_path, Interface mListener) {
+ originalpath = path;
+ compressedpath = compressed_path;
+ this.mListener = mListener;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ Log.d(Config.LOGTAG, "Start video compression");
+ wakeLock.acquire();
+ }
+
+ @Override
+ protected Boolean doInBackground(String... params) {
+ return MediaController.getInstance().convertVideo(originalpath, compressedpath);
+ }
+
+ @Override
+ protected void onPostExecute(Boolean compressed) {
+ super.onPostExecute(compressed);
+ wakeLock.release();
+ File video = new File(compressedpath);
+ if (mListener != null) {
+ if (video.exists() && video.length() > 0) {
+ mListener.videocompressed(compressed);
+ Log.d(Config.LOGTAG, "Compression successfully!");
+ } else {
+ mListener.videocompressed(false);
+ Log.d(Config.LOGTAG, "Compression failed!");
+ }
+ }
+ }
+ }
+
+ public Conversation find(Bookmark bookmark) {
+ return find(bookmark.getAccount(), bookmark.getJid());
+ }
+
+ public Conversation find(final Account account, final Jid jid) {
+ return find(getConversations(), account, jid);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ final String action = intent == null ? null : intent.getAction();
+ String pushedAccountHash = null;
+ boolean interactive = false;
+ if (action != null) {
+ final Conversation c = findConversationByUuid(intent.getStringExtra("uuid"));
+ switch (action) {
+ case ConnectivityManager.CONNECTIVITY_ACTION:
+ if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) {
+ resetAllAttemptCounts(true);
+ }
+ break;
+ case ACTION_MERGE_PHONE_CONTACTS:
+ if (mRestoredFromDatabase) {
+ loadPhoneContacts();
+ }
+ return START_STICKY;
+ case Intent.ACTION_SHUTDOWN:
+ logoutAndSave(true);
+ return START_NOT_STICKY;
+ case ACTION_CLEAR_NOTIFICATION:
+ if (c != null) {
+ mNotificationService.clear(c);
+ } else {
+ mNotificationService.clear();
+ }
+ break;
+ case ACTION_DISMISS_ERROR_NOTIFICATIONS:
+ dismissErrorNotifications();
+ break;
+ case ACTION_TRY_AGAIN:
+ resetAllAttemptCounts(false);
+ interactive = true;
+ break;
+ case ACTION_REPLY_TO_CONVERSATION:
+ Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+ if (remoteInput != null && c != null) {
+ final CharSequence body = remoteInput.getCharSequence("text_reply");
+ if (body != null && body.length() > 0) {
+ directReply(c, body.toString(), intent.getBooleanExtra("dismiss_notification", false));
+ }
+ }
+ break;
+ case AudioManager.RINGER_MODE_CHANGED_ACTION:
+ if (xaOnSilentMode()) {
+ refreshAllPresences();
+ }
+ break;
+ case Intent.ACTION_SCREEN_ON:
+ deactivateGracePeriod();
+ case Intent.ACTION_SCREEN_OFF:
+ if (awayWhenScreenOff()) {
+ refreshAllPresences();
+ }
+ break;
+ case ACTION_GCM_TOKEN_REFRESH:
+ refreshAllGcmTokens();
+ break;
+ case ACTION_IDLE_PING:
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ scheduleNextIdlePing();
+ }
+ break;
+ case ACTION_GCM_MESSAGE_RECEIVED:
+ Log.d(Config.LOGTAG, "gcm push message arrived in service. extras=" + intent.getExtras());
+ pushedAccountHash = intent.getStringExtra("account");
+ break;
+ }
+ }
+ this.wakeLock.acquire();
+
+ boolean pingNow = false;
+ HashSet<Account> pingCandidates = new HashSet<>();
+
+ for (Account account : accounts) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ if (!hasInternetConnection()) {
+ account.setStatus(Account.State.NO_INTERNET);
+ if (statusListener != null) {
+ statusListener.onStatusChanged(account);
+ }
+ } else {
+ if (account.getStatus() == Account.State.NO_INTERNET) {
+ account.setStatus(Account.State.OFFLINE);
+ if (statusListener != null) {
+ statusListener.onStatusChanged(account);
+ }
+ }
+ if (account.getStatus() == Account.State.ONLINE) {
+ synchronized (mLowPingTimeoutMode) {
+ long lastReceived = account.getXmppConnection().getLastPacketReceived();
+ long lastSent = account.getXmppConnection().getLastPingSent();
+ long pingInterval = "ui".equals(action) ? Config.PING_MIN_INTERVAL * 1000 : Config.PING_MAX_INTERVAL * 1000;
+ long msToNextPing = (Math.max(lastReceived, lastSent) + pingInterval) - SystemClock.elapsedRealtime();
+ int pingTimeout = mLowPingTimeoutMode.contains(account.getJid().toBareJid()) ? Config.LOW_PING_TIMEOUT * 1000 : Config.PING_TIMEOUT * 1000;
+ long pingTimeoutIn = (lastSent + pingTimeout) - SystemClock.elapsedRealtime();
+ if (lastSent > lastReceived) {
+ if (pingTimeoutIn < 0) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout");
+ this.reconnectAccount(account, true, interactive);
+ } else {
+ int secs = (int) (pingTimeoutIn / 1000);
+ this.scheduleWakeUpCall(secs, account.getUuid().hashCode());
+ }
+ } else {
+ pingCandidates.add(account);
+ if (CryptoHelper.getAccountFingerprint(account).equals(pushedAccountHash)) {
+ pingNow = true;
+ if (mLowPingTimeoutMode.add(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": entering low ping timeout mode");
+ }
+ } else if (msToNextPing <= 0) {
+ pingNow = true;
+ } else {
+ this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode());
+ if (mLowPingTimeoutMode.remove(account.getJid().toBareJid())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": leaving low ping timeout mode");
+ }
+ }
+ }
+ }
+ } else if (account.getStatus() == Account.State.OFFLINE) {
+ reconnectAccount(account, true, interactive);
+ } else if (account.getStatus() == Account.State.CONNECTING) {
+ long secondsSinceLastConnect = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastConnect()) / 1000;
+ long secondsSinceLastDisco = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastDiscoStarted()) / 1000;
+ long discoTimeout = Config.CONNECT_DISCO_TIMEOUT - secondsSinceLastDisco;
+ long timeout = Config.CONNECT_TIMEOUT - secondsSinceLastConnect;
+ if (timeout < 0) {
+ Log.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting");
+ account.getXmppConnection().resetAttemptCount();
+ reconnectAccount(account, true, interactive);
+ } else if (discoTimeout < 0) {
+ account.getXmppConnection().sendDiscoTimeout();
+ scheduleWakeUpCall((int) Math.min(timeout, discoTimeout), account.getUuid().hashCode());
+ } else {
+ scheduleWakeUpCall((int) Math.min(timeout, discoTimeout), account.getUuid().hashCode());
+ }
+ } else {
+ if (account.getXmppConnection().getTimeToNextAttempt() <= 0) {
+ reconnectAccount(account, true, interactive);
+ }
+ }
+ }
+ if (mOnAccountUpdate != null) {
+ mOnAccountUpdate.onAccountUpdate();
+ }
+ }
+ }
+ if (pingNow) {
+ for (Account account : pingCandidates) {
+ synchronized (mLowPingTimeoutMode) {
+ final boolean lowTimeout = mLowPingTimeoutMode.contains(account.getJid().toBareJid());
+ account.getXmppConnection().sendPing();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " send ping (action=" + action + ",lowTimeout=" + Boolean.toString(lowTimeout) + ")");
+ scheduleWakeUpCall(lowTimeout ? Config.LOW_PING_TIMEOUT : Config.PING_TIMEOUT, account.getUuid().hashCode());
+ }
+ }
+ }
+ if (wakeLock.isHeld()) {
+ try {
+ wakeLock.release();
+ } catch (final RuntimeException ignored) {
+ }
+ }
+ return START_STICKY;
+ }
+
+ public boolean isDataSaverDisabled() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
+ return !connectivityManager.isActiveNetworkMetered()
+ || connectivityManager.getRestrictBackgroundStatus() == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+ } else {
+ return true;
+ }
+ }
+
+ private void directReply(Conversation conversation, String body, final boolean dismissAfterReply) {
+ Message message = new Message(conversation, body, conversation.getNextEncryption());
+ message.markUnread();
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ getPgpEngine().encrypt(message, new UiCallback<Message>() {
+ @Override
+ public void success(Message message) {
+ message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ sendMessage(message);
+ if (dismissAfterReply) {
+ markRead(message.getConversation(), true);
+ } else {
+ mNotificationService.pushFromDirectReply(message);
+ }
+ }
+
+ @Override
+ public void error(int errorCode, Message object) {
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
+
+ }
+ });
+ } else {
+ sendMessage(message);
+ if (dismissAfterReply) {
+ markRead(conversation, true);
+ } else {
+ mNotificationService.pushFromDirectReply(message);
+ }
+ }
+ }
+
+ private boolean xaOnSilentMode() {
+ return getPreferences().getBoolean("xa_on_silent_mode", false);
+ }
+
+ private boolean manuallyChangePresence() {
+ return getPreferences().getBoolean("manually_change_presence", false);
+ }
+
+ private boolean treatVibrateAsSilent() {
+ return getPreferences().getBoolean("treat_vibrate_as_silent", false);
+ }
+
+ private boolean awayWhenScreenOff() {
+ return getPreferences().getBoolean("away_when_screen_off", false);
+ }
+
+ private String getCompressPicturesPreference() {
+ return getPreferences().getString("picture_compression", "auto");
+ }
+
+ private String getCompressVideoPreference() {
+ return getPreferences().getString("video_compression", "auto");
+ }
+
+ private Presence.Status getTargetPresence() {
+ if (xaOnSilentMode() && isPhoneSilenced()) {
+ return Presence.Status.XA;
+ } else if (awayWhenScreenOff() && !isInteractive()) {
+ return Presence.Status.AWAY;
+ } else {
+ return Presence.Status.ONLINE;
+ }
+ }
+
+ @SuppressLint("NewApi")
+ @SuppressWarnings("deprecation")
+ public boolean isInteractive() {
+ final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+
+ final boolean isScreenOn;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ isScreenOn = pm.isScreenOn();
+ } else {
+ isScreenOn = pm.isInteractive();
+ }
+ return isScreenOn;
+ }
+
+ private boolean isPhoneSilenced() {
+ AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ try {
+ if (treatVibrateAsSilent()) {
+ return audioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+ } else {
+ return audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT;
+ }
+ } catch (Throwable throwable) {
+ Log.d(Config.LOGTAG, "platform bug in isPhoneSilenced (" + throwable.getMessage() + ")");
+ return false;
+ }
+ }
+
+ private void resetAllAttemptCounts(boolean reallyAll) {
+ Log.d(Config.LOGTAG, "resetting all attempt counts");
+ for (Account account : accounts) {
+ if (account.hasErrorStatus() || reallyAll) {
+ final XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.resetAttemptCount();
+ }
+ }
+ if (account.setShowErrorNotification(true)) {
+ databaseBackend.updateAccount(account);
+ }
+ }
+ mNotificationService.updateErrorNotification();
+ }
+
+ private void dismissErrorNotifications() {
+ for (final Account account : this.accounts) {
+ if (account.hasErrorStatus()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": dismissing error notification");
+ if (account.setShowErrorNotification(false)) {
+ databaseBackend.updateAccount(account);
+ }
+ }
+ }
+ }
+
+ public boolean hasInternetConnection() {
+ ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ return activeNetwork != null && activeNetwork.isConnected();
+ }
+
+ public boolean isWIFI() {
+ ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ if (activeNetwork != null) { // connected to the internet
+ if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isMobile() {
+ ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ if (activeNetwork != null) { // connected to the internet
+ if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
+ if (!activeNetwork.isRoaming()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean isMobileRoaming() {
+ ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ if (activeNetwork != null) { // connected to the internet
+ if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
+ if (activeNetwork.isRoaming()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @SuppressLint("TrulyRandom")
+ @Override
+ public void onCreate() {
+ ExceptionHelper.init(getApplicationContext());
+ PRNGFixes.apply();
+ this.mRandom = new SecureRandom();
+ updateMemorizingTrustmanager();
+ final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
+ final int cacheSize = maxMemory / 8;
+ this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) {
+ @Override
+ protected int sizeOf(final String key, final Bitmap bitmap) {
+ return bitmap.getByteCount() / 1024;
+ }
+ };
+
+ this.databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
+ this.accounts = databaseBackend.getAccounts();
+
+ if (databaseBackend.startTimeCountExceedsThreshold()) {
+ Log.d(Config.LOGTAG, "number of restarts exceeds threshold.");
+ }
+
+ restoreFromDatabase();
+
+ getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ fileObserver.startWatching();
+ }
+ }).start();
+ if (Config.supportOpenPgp()) {
+ this.pgpServiceConnection = new OpenPgpServiceConnection(getApplicationContext(), "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() {
+ @Override
+ public void onBound(IOpenPgpService2 service) {
+ for (Account account : accounts) {
+ final PgpDecryptionService pgp = account.getPgpDecryptionService();
+ if (pgp != null) {
+ pgp.continueDecryption(true);
+ }
+ }
+ }
+
+ @Override
+ public void onError(Exception e) {
+ }
+ });
+ this.pgpServiceConnection.bindToService();
+ }
+
+ this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService");
+ toggleForegroundService();
+ updateUnreadCountBadge();
+ toggleScreenEventReceiver();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ scheduleNextIdlePing();
+ }
+ //start export log service every day at given time
+ ScheduleAutomaticExport();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ registerReceiver(this.mEventReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+ }
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ if (level >= TRIM_MEMORY_COMPLETE) {
+ Log.d(Config.LOGTAG, "clear cache due to low memory");
+ getBitmapCache().evictAll();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ try {
+ unregisterReceiver(this.mEventReceiver);
+ } catch (IllegalArgumentException e) {
+ //ignored
+ }
+ fileObserver.stopWatching();
+ super.onDestroy();
+ // cancel scheduled exporter
+ CancelAutomaticExport();
+ }
+
+ public void toggleScreenEventReceiver() {
+ if (awayWhenScreenOff() && !manuallyChangePresence()) {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ registerReceiver(this.mEventReceiver, filter);
+ } else {
+ try {
+ unregisterReceiver(this.mEventReceiver);
+ } catch (IllegalArgumentException e) {
+ //ignored
+ }
+ }
+ }
+
+ public void toggleForegroundService() {
+ if (Config.USE_ALWAYS_FOREGROUND) {
+ startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification());
+ } else {
+ stopForeground(true);
+ }
+ }
+
+ @Override
+ public void onTaskRemoved(final Intent rootIntent) {
+ super.onTaskRemoved(rootIntent);
+ }
+
+ private void logoutAndSave(boolean stop) {
+ int activeAccounts = 0;
+ databaseBackend.clearStartTimeCounter(); // regular swipes don't count towards restart counter
+ for (final Account account : accounts) {
+ if (account.getStatus() != Account.State.DISABLED) {
+ activeAccounts++;
+ }
+ databaseBackend.writeRoster(account.getRoster());
+ if (account.getXmppConnection() != null) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ disconnect(account, false);
+ }
+ }).start();
+ }
+ }
+ if (stop || activeAccounts == 0) {
+ Log.d(Config.LOGTAG, "good bye");
+ stopSelf();
+ }
+ }
+
+ public void scheduleWakeUpCall(int seconds, int requestCode) {
+ final long timeToWake = SystemClock.elapsedRealtime() + (seconds < 0 ? 1 : seconds + 1) * 1000;
+ AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ Intent intent = new Intent(this, EventReceiver.class);
+ intent.setAction("ping");
+ PendingIntent alarmIntent = PendingIntent.getBroadcast(this, requestCode, intent, 0);
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToWake, alarmIntent);
+ }
+
+ @TargetApi(Build.VERSION_CODES.M)
+ private void scheduleNextIdlePing() {
+ Log.d(Config.LOGTAG, "schedule next idle ping");
+ AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ Intent intent = new Intent(this, EventReceiver.class);
+ intent.setAction(ACTION_IDLE_PING);
+ alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + (Config.IDLE_PING_INTERVAL * 1000),
+ PendingIntent.getBroadcast(this, 0, intent, 0)
+ );
+ }
+
+ public XmppConnection createConnection(final Account account) {
+ final SharedPreferences sharedPref = getPreferences();
+ String resource;
+ try {
+ resource = sharedPref.getString("resource", getString(R.string.default_resource)).toLowerCase(Locale.ENGLISH);
+ if (resource.trim().isEmpty()) {
+ throw new Exception();
+ }
+ } catch (Exception e) {
+ resource = "Pix-Art Messenger";
+ }
+ account.setResource(resource);
+ final XmppConnection connection = new XmppConnection(account, this);
+ connection.setOnMessagePacketReceivedListener(this.mMessageParser);
+ connection.setOnStatusChangedListener(this.statusListener);
+ connection.setOnPresencePacketReceivedListener(this.mPresenceParser);
+ connection.setOnUnregisteredIqPacketReceivedListener(this.mIqParser);
+ connection.setOnJinglePacketReceivedListener(this.jingleListener);
+ connection.setOnBindListener(this.mOnBindListener);
+ connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
+ connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
+ connection.addOnAdvancedStreamFeaturesAvailableListener(this.mAvatarService);
+ AxolotlService axolotlService = account.getAxolotlService();
+ if (axolotlService != null) {
+ connection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
+ }
+ return connection;
+ }
+
+ public void sendChatState(Conversation conversation) {
+ if (sendChatStates()) {
+ MessagePacket packet = mMessageGenerator.generateChatState(conversation);
+ sendMessagePacket(conversation.getAccount(), packet);
+ }
+ }
+
+ private void sendFileMessage(final Message message, final boolean delay) {
+ Log.d(Config.LOGTAG, "send file message");
+ final Account account = message.getConversation().getAccount();
+ if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize())) {
+ mHttpConnectionManager.createNewUploadConnection(message, delay);
+ } else {
+ mJingleConnectionManager.createNewConnection(message);
+ }
+ }
+
+ public void sendMessage(final Message message) {
+ sendMessage(message, false, false);
+ }
+
+ private void sendMessage(final Message message, final boolean resend, final boolean delay) {
+ final Account account = message.getConversation().getAccount();
+ if (account.setShowErrorNotification(true)) {
+ databaseBackend.updateAccount(account);
+ mNotificationService.updateErrorNotification();
+ }
+ final Conversation conversation = message.getConversation();
+ account.deactivateGracePeriod();
+ MessagePacket packet = null;
+ final boolean addToConversation = (conversation.getMode() != Conversation.MODE_MULTI
+ || account.getServerIdentity() != XmppConnection.Identity.SLACK)
+ && !message.edited();
+ boolean saveInDb = addToConversation;
+ message.setStatus(Message.STATUS_WAITING);
+
+ if (!resend && message.getEncryption() != Message.ENCRYPTION_OTR) {
+ message.getConversation().endOtrIfNeeded();
+ message.getConversation().findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR,
+ new Conversation.OnMessageFound() {
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message, Message.STATUS_SEND_FAILED);
+ }
+ });
+ }
+
+ if (account.isOnlineAndConnected()) {
+ switch (message.getEncryption()) {
+ case Message.ENCRYPTION_NONE:
+ if (message.needsUploading()) {
+ if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize())
+ || message.fixCounterpart()) {
+ this.sendFileMessage(message, delay);
+ } else {
+ break;
+ }
+ } else {
+ packet = mMessageGenerator.generateChat(message);
+ }
+ break;
+ case Message.ENCRYPTION_PGP:
+ case Message.ENCRYPTION_DECRYPTED:
+ if (message.needsUploading()) {
+ if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize())
+ || message.fixCounterpart()) {
+ this.sendFileMessage(message, delay);
+ } else {
+ break;
+ }
+ } else {
+ packet = mMessageGenerator.generatePgpChat(message);
+ }
+ break;
+ case Message.ENCRYPTION_OTR:
+ SessionImpl otrSession = conversation.getOtrSession();
+ if (otrSession != null && otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
+ try {
+ message.setCounterpart(Jid.fromSessionID(otrSession.getSessionID()));
+ } catch (InvalidJidException e) {
+ break;
+ }
+ if (message.needsUploading()) {
+ mJingleConnectionManager.createNewConnection(message);
+ } else {
+ packet = mMessageGenerator.generateOtrChat(message);
+ }
+ } else if (otrSession == null) {
+ if (message.fixCounterpart()) {
+ conversation.startOtrSession(message.getCounterpart().getResourcepart(), true);
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fix counterpart for OTR message to contact " + message.getContact().getJid());
+ break;
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " OTR session with " + message.getContact() + " is in wrong state: " + otrSession.getSessionStatus().toString());
+ }
+ break;
+ case Message.ENCRYPTION_AXOLOTL:
+ message.setFingerprint(account.getAxolotlService().getOwnFingerprint());
+ if (message.needsUploading()) {
+ if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize())
+ || message.fixCounterpart()) {
+ this.sendFileMessage(message, delay);
+ } else {
+ break;
+ }
+ } else {
+ XmppAxolotlMessage axolotlMessage = account.getAxolotlService().fetchAxolotlMessageFromCache(message);
+ if (axolotlMessage == null) {
+ account.getAxolotlService().preparePayloadMessage(message, delay);
+ } else {
+ packet = mMessageGenerator.generateAxolotlChat(message, axolotlMessage);
+ }
+ }
+ break;
+
+ }
+ if (packet != null) {
+ if (account.getXmppConnection().getFeatures().sm() || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
+ message.setStatus(Message.STATUS_UNSEND);
+ } else {
+ message.setStatus(Message.STATUS_SEND);
+ }
+ }
+ } else {
+ switch (message.getEncryption()) {
+ case Message.ENCRYPTION_DECRYPTED:
+ if (!message.needsUploading()) {
+ String pgpBody = message.getEncryptedBody();
+ String decryptedBody = message.getBody();
+ message.setBody(pgpBody);
+ message.setEncryption(Message.ENCRYPTION_PGP);
+ if (message.edited()) {
+ message.setBody(decryptedBody);
+ message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ databaseBackend.updateMessage(message, message.getEditedId());
+ updateConversationUi();
+ return;
+ } else {
+ databaseBackend.createMessage(message);
+ saveInDb = false;
+ message.setBody(decryptedBody);
+ message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ }
+ }
+ break;
+ case Message.ENCRYPTION_OTR:
+ if (!conversation.hasValidOtrSession() && message.getCounterpart() != null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": create otr session without starting for " + message.getContact().getJid());
+ conversation.startOtrSession(message.getCounterpart().getResourcepart(), false);
+ }
+ break;
+ case Message.ENCRYPTION_AXOLOTL:
+ message.setFingerprint(account.getAxolotlService().getOwnFingerprint());
+ break;
+ }
+ }
+
+ if (resend) {
+ if (packet != null && addToConversation) {
+ if (account.getXmppConnection().getFeatures().sm() || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
+ markMessage(message, Message.STATUS_UNSEND);
+ } else {
+ markMessage(message, Message.STATUS_SEND);
+ }
+ }
+ } else {
+ if (addToConversation) {
+ conversation.add(message);
+ }
+ if (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages()) {
+ if (saveInDb) {
+ databaseBackend.createMessage(message);
+ } else if (message.edited()) {
+ databaseBackend.updateMessage(message, message.getEditedId());
+ }
+ }
+ updateConversationUi();
+ }
+ if (packet != null) {
+ if (delay) {
+ mMessageGenerator.addDelay(packet, message.getTimeSent());
+ }
+ if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+ if (this.sendChatStates()) {
+ packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
+ }
+ }
+ sendMessagePacket(account, packet);
+ }
+ }
+
+ private void sendUnsentMessages(final Conversation conversation) {
+ conversation.findWaitingMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ resendMessage(message, true);
+ }
+ });
+ }
+
+ public void resendMessage(final Message message, final boolean delay) {
+ sendMessage(message, true, delay);
+ }
+
+ public void fetchRosterFromServer(final Account account) {
+ final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET);
+ if (!"".equals(account.getRosterVersion())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": fetching roster version " + account.getRosterVersion());
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster");
+ }
+ iqPacket.query(Xmlns.ROSTER).setAttribute("ver", account.getRosterVersion());
+ sendIqPacket(account, iqPacket, mIqParser);
+ }
+
+ 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() {
+
+ @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) {
+ conversation.setBookmark(bookmark);
+ } else if (bookmark.autojoin() && bookmark.getJid() != null && autojoin) {
+ conversation = findOrCreateConversation(
+ account, bookmark.getJid(), true);
+ conversation.setBookmark(bookmark);
+ joinMuc(conversation);
+ }
+ }
+ }
+ }
+ account.setBookmarks(new ArrayList<>(bookmarks.values()));
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fetch bookmarks");
+ }
+ }
+ };
+ sendIqPacket(account, iqPacket, callback);
+ }
+
+ public void pushBookmarks(Account account) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": pushing bookmarks");
+ IqPacket iqPacket = new IqPacket(IqPacket.TYPE.SET);
+ Element query = iqPacket.query("jabber:iq:private");
+ Element storage = query.addChild("storage", "storage:bookmarks");
+ for (Bookmark bookmark : account.getBookmarks()) {
+ storage.addChild(bookmark);
+ }
+ sendIqPacket(account, iqPacket, mDefaultIqHandler);
+ }
+
+ private void restoreFromDatabase() {
+ synchronized (this.conversations) {
+ final Map<String, Account> accountLookupTable = new Hashtable<>();
+ for (Account account : this.accounts) {
+ accountLookupTable.put(account.getUuid(), account);
+ }
+ this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE));
+ for (Conversation conversation : this.conversations) {
+ Account account = accountLookupTable.get(conversation.getAccountUuid());
+ conversation.setAccount(account);
+ }
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ Log.d(Config.LOGTAG, "restoring roster");
+ for (Account account : accounts) {
+ databaseBackend.readRoster(account.getRoster());
+ account.initAccountServices(XmppConnectionService.this); //roster needs to be loaded at this stage
+ }
+ getBitmapCache().evictAll();
+ loadPhoneContacts();
+ Log.d(Config.LOGTAG, "restoring messages");
+ for (Conversation conversation : conversations) {
+ conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
+ checkDeletedFiles(conversation);
+ conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message, Message.STATUS_WAITING);
+ }
+ });
+ conversation.findUnreadMessages(new Conversation.OnMessageFound() {
+ @Override
+ public void onMessageFound(Message message) {
+ mNotificationService.pushFromBacklog(message);
+ }
+ });
+ }
+ mNotificationService.finishBacklog(false);
+ mRestoredFromDatabase = true;
+ Log.d(Config.LOGTAG, "restored all messages");
+ updateConversationUi();
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+ }
+ }
+
+ public void loadPhoneContacts() {
+ mContactMergerExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ PhoneHelper.loadPhoneContacts(XmppConnectionService.this, new OnPhoneContactsLoadedListener() {
+ @Override
+ public void onPhoneContactsLoaded(List<Bundle> phoneContacts) {
+ Log.d(Config.LOGTAG, "start merging phone contacts with roster");
+ for (Account account : accounts) {
+ List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts();
+ for (Bundle phoneContact : phoneContacts) {
+ Jid jid;
+ try {
+ jid = Jid.fromString(phoneContact.getString("jid"));
+ } catch (final InvalidJidException e) {
+ continue;
+ }
+ final Contact contact = account.getRoster().getContact(jid);
+ String systemAccount = phoneContact.getInt("phoneid")
+ + "#"
+ + phoneContact.getString("lookup");
+ contact.setSystemAccount(systemAccount);
+ if (contact.setPhotoUri(phoneContact.getString("photouri"))) {
+ getAvatarService().clear(contact);
+ }
+ contact.setSystemName(phoneContact.getString("displayname"));
+ withSystemAccounts.remove(contact);
+ }
+ for (Contact contact : withSystemAccounts) {
+ contact.setSystemAccount(null);
+ contact.setSystemName(null);
+ if (contact.setPhotoUri(null)) {
+ getAvatarService().clear(contact);
+ }
+ }
+ }
+ Log.d(Config.LOGTAG, "finished merging phone contacts");
+ updateAccountUi();
+ }
+ });
+ }
+ });
+ }
+
+ public List<Conversation> getConversations() {
+ return this.conversations;
+ }
+
+ private void checkDeletedFiles(Conversation conversation) {
+ conversation.findMessagesWithFiles(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ if (!getFileBackend().isFileAvailable(message)) {
+ message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ final int s = message.getStatus();
+ if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
+ markMessage(message, Message.STATUS_SEND_FAILED);
+ }
+ }
+ }
+ });
+ }
+
+ private void markFileDeleted(final String path) {
+ Log.d(Config.LOGTAG, "deleted file " + path);
+ for (Conversation conversation : getConversations()) {
+ conversation.findMessagesWithFiles(new Conversation.OnMessageFound() {
+ @Override
+ public void onMessageFound(Message message) {
+ DownloadableFile file = fileBackend.getFile(message);
+ if (file.getAbsolutePath().equals(path)) {
+ if (!file.exists()) {
+ message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ final int s = message.getStatus();
+ if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
+ markMessage(message, Message.STATUS_SEND_FAILED);
+ } else {
+ updateConversationUi();
+ }
+ } else {
+ Log.d(Config.LOGTAG, "found matching message for file " + path + " but file still exists");
+ }
+ }
+ }
+ });
+ }
+ }
+
+ public void populateWithOrderedConversations(final List<Conversation> list) {
+ populateWithOrderedConversations(list, true);
+ }
+
+ public void populateWithOrderedConversations(final List<Conversation> list, boolean includeNoFileUpload) {
+ list.clear();
+ if (includeNoFileUpload) {
+ list.addAll(getConversations());
+ } else {
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE
+ || conversation.getAccount().httpUploadAvailable()) {
+ list.add(conversation);
+ }
+ }
+ }
+ try {
+ Collections.sort(list);
+ } catch (IllegalArgumentException e) {
+ //ignore
+ }
+ }
+
+ public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) {
+ if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation, callback)) {
+ return;
+ } else if (timestamp == 0) {
+ return;
+ }
+ Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp));
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ final Account account = conversation.getAccount();
+ List<Message> messages = databaseBackend.getMessages(conversation, 50, timestamp);
+ if (messages.size() > 0) {
+ conversation.addAll(0, messages);
+ checkDeletedFiles(conversation);
+ callback.onMoreMessagesLoaded(messages.size(), conversation);
+ } else if (conversation.hasMessagesLeftOnServer()
+ && account.isOnlineAndConnected()
+ && conversation.getLastClearHistory() == 0) {
+ if ((conversation.getMode() == Conversation.MODE_SINGLE && account.getXmppConnection().getFeatures().mam())
+ || (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().mamSupport())) {
+ MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp);
+ if (query != null) {
+ query.setCallback(callback);
+ }
+ callback.informUser(R.string.fetching_history_from_server);
+ }
+ }
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+ }
+
+ public List<Account> getAccounts() {
+ return this.accounts;
+ }
+
+ public List<Conversation> findAllConferencesWith(Contact contact) {
+ ArrayList<Conversation> results = new ArrayList<>();
+ for (Conversation conversation : conversations) {
+ if (conversation.getMode() == Conversation.MODE_MULTI
+ && conversation.getMucOptions().isContactInRoom(contact)) {
+ results.add(conversation);
+ }
+ }
+ return results;
+ }
+
+ public Conversation find(final Iterable<Conversation> haystack, final Contact contact) {
+ for (final Conversation conversation : haystack) {
+ if (conversation.getContact() == contact) {
+ return conversation;
+ }
+ }
+ return null;
+ }
+
+ public Conversation find(final Iterable<Conversation> haystack, final Account account, final Jid jid) {
+ if (jid == null) {
+ return null;
+ }
+ for (final Conversation conversation : haystack) {
+ if ((account == null || conversation.getAccount() == account)
+ && (conversation.getJid().toBareJid().equals(jid.toBareJid()))) {
+ return conversation;
+ }
+ }
+ return null;
+ }
+
+ public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc) {
+ return this.findOrCreateConversation(account, jid, muc, null);
+ }
+
+ public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc, final MessageArchiveService.Query query) {
+ synchronized (this.conversations) {
+ Conversation conversation = find(account, jid);
+ if (conversation != null) {
+ return conversation;
+ }
+ conversation = databaseBackend.findConversation(account, jid);
+ if (conversation != null) {
+ conversation.setStatus(Conversation.STATUS_AVAILABLE);
+ conversation.setAccount(account);
+ if (muc) {
+ conversation.setMode(Conversation.MODE_MULTI);
+ conversation.setContactJid(jid);
+ } else {
+ conversation.setMode(Conversation.MODE_SINGLE);
+ conversation.setContactJid(jid.toBareJid());
+ }
+ conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
+ this.databaseBackend.updateConversation(conversation);
+ } else {
+ String conversationName;
+ Contact contact = account.getRoster().getContact(jid);
+ if (contact != null) {
+ conversationName = contact.getDisplayName();
+ } else {
+ conversationName = jid.getLocalpart();
+ }
+ if (muc) {
+ conversation = new Conversation(conversationName, account, jid,
+ Conversation.MODE_MULTI);
+ } else {
+ conversation = new Conversation(conversationName, account, jid.toBareJid(),
+ Conversation.MODE_SINGLE);
+ }
+ this.databaseBackend.createConversation(conversation);
+ }
+ if (account.getXmppConnection() != null
+ && account.getXmppConnection().getFeatures().mam()
+ && !muc) {
+ if (query == null) {
+ this.mMessageArchiveService.query(conversation);
+ } else {
+ if (query.getConversation() == null) {
+ this.mMessageArchiveService.query(conversation, query.getStart());
+ }
+ }
+ }
+ checkDeletedFiles(conversation);
+ this.conversations.add(conversation);
+ updateConversationUi();
+ return conversation;
+ }
+ }
+
+ public void archiveConversation(Conversation conversation) {
+ getNotificationService().clear(conversation);
+ conversation.setStatus(Conversation.STATUS_ARCHIVED);
+ synchronized (this.conversations) {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
+ Bookmark bookmark = conversation.getBookmark();
+ if (bookmark != null && bookmark.autojoin() && respectAutojoin()) {
+ bookmark.setAutojoin(false);
+ pushBookmarks(bookmark.getAccount());
+ }
+ }
+ leaveMuc(conversation);
+ } else {
+ conversation.endOtrIfNeeded();
+ if (conversation.getContact().getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ Log.d(Config.LOGTAG, "Canceling presence request from " + conversation.getJid().toString());
+ sendPresencePacket(
+ conversation.getAccount(),
+ mPresenceGenerator.stopPresenceUpdatesTo(conversation.getContact())
+ );
+ }
+ }
+ updateConversation(conversation);
+ this.conversations.remove(conversation);
+ updateConversationUi();
+ }
+ }
+
+ public void createAccount(final Account account) {
+ account.initAccountServices(this);
+ databaseBackend.createAccount(account);
+ this.accounts.add(account);
+ this.reconnectAccountInBackground(account);
+ updateAccountUi();
+ }
+
+ public void createAccountFromKey(final String alias, final OnAccountCreated callback) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
+ Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]);
+ if (findAccountByJid(info.first) == null) {
+ Account account = new Account(info.first, "");
+ account.setPrivateKeyAlias(alias);
+ account.setOption(Account.OPTION_DISABLED, true);
+ account.setDisplayName(info.second);
+ createAccount(account);
+ callback.onAccountCreated(account);
+ if (Config.X509_VERIFICATION) {
+ try {
+ getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA");
+ } catch (CertificateException e) {
+ callback.informUser(R.string.certificate_chain_is_not_trusted);
+ }
+ }
+ } else {
+ callback.informUser(R.string.account_already_exists);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ callback.informUser(R.string.unable_to_parse_certificate);
+ }
+ }
+ }).start();
+
+ }
+
+ public void updateKeyInAccount(final Account account, final String alias) {
+ Log.d(Config.LOGTAG, "update key in account " + alias);
+ try {
+ X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
+ Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]);
+ if (account.getJid().toBareJid().equals(info.first)) {
+ account.setPrivateKeyAlias(alias);
+ account.setDisplayName(info.second);
+ databaseBackend.updateAccount(account);
+ if (Config.X509_VERIFICATION) {
+ try {
+ getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA");
+ } catch (CertificateException e) {
+ showErrorToastInUi(R.string.certificate_chain_is_not_trusted);
+ }
+ account.getAxolotlService().regenerateKeys(true);
+ }
+ } else {
+ showErrorToastInUi(R.string.jid_does_not_match_certificate);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean updateAccount(final Account account) {
+ if (databaseBackend.updateAccount(account)) {
+ account.setShowErrorNotification(true);
+ this.statusListener.onStatusChanged(account);
+ databaseBackend.updateAccount(account);
+ reconnectAccountInBackground(account);
+ updateAccountUi();
+ getNotificationService().updateErrorNotification();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void updateAccountPasswordOnServer(final Account account, final String newPassword, final OnAccountPasswordChanged callback) {
+ final IqPacket iq = getIqGenerator().generateSetPassword(account, newPassword);
+ sendIqPacket(account, iq, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.setPassword(newPassword);
+ account.setOption(Account.OPTION_MAGIC_CREATE, false);
+ databaseBackend.updateAccount(account);
+ callback.onPasswordChangeSucceeded();
+ } else {
+ callback.onPasswordChangeFailed();
+ }
+ }
+ });
+ }
+
+ public void deleteAccount(final Account account) {
+ synchronized (this.conversations) {
+ for (final Conversation conversation : conversations) {
+ if (conversation.getAccount() == account) {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ leaveMuc(conversation);
+ } else if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ conversation.endOtrIfNeeded();
+ }
+ conversations.remove(conversation);
+ }
+ }
+ if (account.getXmppConnection() != null) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ disconnect(account, true);
+ }
+ }).start();
+ }
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (!databaseBackend.deleteAccount(account)) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": unable to delete account");
+ }
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+ this.accounts.remove(account);
+ updateAccountUi();
+ getNotificationService().updateErrorNotification();
+ }
+ }
+
+ public void setOnConversationListChangedListener(OnConversationUpdate listener) {
+ synchronized (this) {
+ this.mLastActivity = System.currentTimeMillis();
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnConversationUpdate = listener;
+ this.mNotificationService.setIsInForeground(true);
+ if (this.convChangedListenerCount < 2) {
+ this.convChangedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnConversationListChangedListener() {
+ synchronized (this) {
+ this.convChangedListenerCount--;
+ if (this.convChangedListenerCount <= 0) {
+ this.convChangedListenerCount = 0;
+ this.mOnConversationUpdate = null;
+ this.mNotificationService.setIsInForeground(false);
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnShowErrorToastListener(OnShowErrorToast onShowErrorToast) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnShowErrorToast = onShowErrorToast;
+ if (this.showErrorToastListenerCount < 2) {
+ this.showErrorToastListenerCount++;
+ }
+ }
+ this.mOnShowErrorToast = onShowErrorToast;
+ }
+
+ public void removeOnShowErrorToastListener() {
+ synchronized (this) {
+ this.showErrorToastListenerCount--;
+ if (this.showErrorToastListenerCount <= 0) {
+ this.showErrorToastListenerCount = 0;
+ this.mOnShowErrorToast = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnAccountListChangedListener(OnAccountUpdate listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnAccountUpdate = listener;
+ if (this.accountChangedListenerCount < 2) {
+ this.accountChangedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnAccountListChangedListener() {
+ synchronized (this) {
+ this.accountChangedListenerCount--;
+ if (this.accountChangedListenerCount <= 0) {
+ this.mOnAccountUpdate = null;
+ this.accountChangedListenerCount = 0;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnCaptchaRequestedListener(OnCaptchaRequested listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnCaptchaRequested = listener;
+ if (this.captchaRequestedListenerCount < 2) {
+ this.captchaRequestedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnCaptchaRequestedListener() {
+ synchronized (this) {
+ this.captchaRequestedListenerCount--;
+ if (this.captchaRequestedListenerCount <= 0) {
+ this.mOnCaptchaRequested = null;
+ this.captchaRequestedListenerCount = 0;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnRosterUpdateListener(final OnRosterUpdate listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnRosterUpdate = listener;
+ if (this.rosterChangedListenerCount < 2) {
+ this.rosterChangedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnRosterUpdateListener() {
+ synchronized (this) {
+ this.rosterChangedListenerCount--;
+ if (this.rosterChangedListenerCount <= 0) {
+ this.rosterChangedListenerCount = 0;
+ this.mOnRosterUpdate = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnUpdateBlocklistListener(final OnUpdateBlocklist listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnUpdateBlocklist = listener;
+ if (this.updateBlocklistListenerCount < 2) {
+ this.updateBlocklistListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnUpdateBlocklistListener() {
+ synchronized (this) {
+ this.updateBlocklistListenerCount--;
+ if (this.updateBlocklistListenerCount <= 0) {
+ this.updateBlocklistListenerCount = 0;
+ this.mOnUpdateBlocklist = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnKeyStatusUpdatedListener(final OnKeyStatusUpdated listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnKeyStatusUpdated = listener;
+ if (this.keyStatusUpdatedListenerCount < 2) {
+ this.keyStatusUpdatedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnNewKeysAvailableListener() {
+ synchronized (this) {
+ this.keyStatusUpdatedListenerCount--;
+ if (this.keyStatusUpdatedListenerCount <= 0) {
+ this.keyStatusUpdatedListenerCount = 0;
+ this.mOnKeyStatusUpdated = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) {
+ synchronized (this) {
+ if (checkListeners()) {
+ switchToForeground();
+ }
+ this.mOnMucRosterUpdate = listener;
+ if (this.mucRosterChangedListenerCount < 2) {
+ this.mucRosterChangedListenerCount++;
+ }
+ }
+ }
+
+ public void removeOnMucRosterUpdateListener() {
+ synchronized (this) {
+ this.mucRosterChangedListenerCount--;
+ if (this.mucRosterChangedListenerCount <= 0) {
+ this.mucRosterChangedListenerCount = 0;
+ this.mOnMucRosterUpdate = null;
+ if (checkListeners()) {
+ switchToBackground();
+ }
+ }
+ }
+ }
+
+ public boolean checkListeners() {
+ return (this.mOnAccountUpdate == null
+ && this.mOnConversationUpdate == null
+ && this.mOnRosterUpdate == null
+ && this.mOnCaptchaRequested == null
+ && this.mOnUpdateBlocklist == null
+ && this.mOnShowErrorToast == null
+ && this.mOnKeyStatusUpdated == null);
+ }
+
+ private void switchToForeground() {
+ final boolean broadcastLastActivity = broadcastLastActivity();
+ for (Conversation conversation : getConversations()) {
+ conversation.setIncomingChatState(ChatState.ACTIVE);
+ }
+ for (Account account : getAccounts()) {
+ if (account.getStatus() == Account.State.ONLINE) {
+ account.deactivateGracePeriod();
+ final XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ if (connection.getFeatures().csi()) {
+ connection.sendActive();
+ }
+ if (broadcastLastActivity) {
+ sendPresence(account, false); //send new presence but don't include idle because we are not
+ }
+ }
+ }
+ }
+ Log.d(Config.LOGTAG, "app switched into foreground");
+ }
+
+ private void switchToBackground() {
+ final boolean broadcastLastActivity = broadcastLastActivity();
+ for (Account account : getAccounts()) {
+ if (account.getStatus() == Account.State.ONLINE) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ if (broadcastLastActivity) {
+ sendPresence(account, broadcastLastActivity);
+ }
+ if (connection.getFeatures().csi()) {
+ connection.sendInactive();
+ }
+ }
+ }
+ }
+ this.mNotificationService.setIsInForeground(false);
+ Log.d(Config.LOGTAG, "app switched into background");
+ }
+
+ private void connectMultiModeConversations(Account account) {
+ List<Conversation> conversations = getConversations();
+ for (Conversation conversation : conversations) {
+ if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getAccount() == account) {
+ joinMuc(conversation);
+ }
+ }
+ }
+
+ public void joinMuc(Conversation conversation) {
+ joinMuc(conversation, null);
+ }
+
+ private void joinMuc(Conversation conversation, final OnConferenceJoined onConferenceJoined) {
+ Account account = conversation.getAccount();
+ account.pendingConferenceJoins.remove(conversation);
+ account.pendingConferenceLeaves.remove(conversation);
+ if (account.getStatus() == Account.State.ONLINE) {
+ conversation.resetMucOptions();
+ if (onConferenceJoined != null) {
+ conversation.getMucOptions().flagNoAutoPushConfiguration();
+ }
+ conversation.setHasMessagesLeftOnServer(false);
+ fetchConferenceConfiguration(conversation, new OnConferenceConfigurationFetched() {
+
+ private void join(Conversation conversation) {
+ Account account = conversation.getAccount();
+ final MucOptions mucOptions = conversation.getMucOptions();
+ final Jid joinJid = mucOptions.getSelf().getFullJid();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": joining conversation " + joinJid.toString());
+ PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, mucOptions.nonanonymous());
+ packet.setTo(joinJid);
+ Element x = packet.addChild("x", "http://jabber.org/protocol/muc");
+ if (conversation.getMucOptions().getPassword() != null) {
+ x.addChild("password").setContent(mucOptions.getPassword());
+ }
+
+ if (mucOptions.mamSupport()) {
+ // Use MAM instead of the limited muc history to get history
+ x.addChild("history").setAttribute("maxchars", "0");
+ } else {
+ // Fallback to muc history
+ x.addChild("history").setAttribute("since", PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted()));
+ }
+ sendPresencePacket(account, packet);
+ if (onConferenceJoined != null) {
+ onConferenceJoined.onConferenceJoined(conversation);
+ }
+ if (!joinJid.equals(conversation.getJid())) {
+ conversation.setContactJid(joinJid);
+ databaseBackend.updateConversation(conversation);
+ }
+
+ if (mucOptions.mamSupport()) {
+ getMessageArchiveService().catchupMUC(conversation);
+ }
+ if (mucOptions.membersOnly() && mucOptions.nonanonymous()) {
+ fetchConferenceMembers(conversation);
+ }
+ sendUnsentMessages(conversation);
+ }
+
+ @Override
+ public void onConferenceConfigurationFetched(Conversation conversation) {
+ join(conversation);
+ }
+
+ @Override
+ public void onFetchFailed(final Conversation conversation, Element error) {
+ if (error != null && "remote-server-not-found".equals(error.getName())) {
+ conversation.getMucOptions().setError(MucOptions.Error.SERVER_NOT_FOUND);
+ } else {
+ join(conversation);
+ fetchConferenceConfiguration(conversation);
+ }
+ }
+ });
+ updateConversationUi();
+ } else {
+ account.pendingConferenceJoins.add(conversation);
+ conversation.resetMucOptions();
+ conversation.setHasMessagesLeftOnServer(false);
+ updateConversationUi();
+ }
+ }
+
+ private void fetchConferenceMembers(final Conversation conversation) {
+ final Account account = conversation.getAccount();
+ final String[] affiliations = {"member", "admin", "owner"};
+ OnIqPacketReceived callback = new OnIqPacketReceived() {
+
+ private int i = 0;
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+
+ Element query = packet.query("http://jabber.org/protocol/muc#admin");
+ if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
+ for (Element child : query.getChildren()) {
+ if ("item".equals(child.getName())) {
+ MucOptions.User user = AbstractParser.parseItem(conversation, child);
+ if (!user.realJidMatchesAccount()) {
+ conversation.getMucOptions().updateUser(user);
+ }
+ }
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not request affiliation " + affiliations[i] + " in " + conversation.getJid().toBareJid());
+ }
+ ++i;
+ if (i >= affiliations.length) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": retrieved members for " + conversation.getJid().toBareJid() + ": " + conversation.getMucOptions().getMembers());
+ getAvatarService().clear(conversation);
+ updateMucRosterUi();
+ updateConversationUi();
+ }
+ }
+ };
+ for (String affiliation : affiliations) {
+ sendIqPacket(account, mIqGenerator.queryAffiliation(conversation, affiliation), callback);
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching members for " + conversation.getName());
+ }
+
+ public void providePasswordForMuc(Conversation conversation, String password) {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ conversation.getMucOptions().setPassword(password);
+ if (conversation.getBookmark() != null) {
+ if (respectAutojoin()) {
+ conversation.getBookmark().setAutojoin(true);
+ }
+ pushBookmarks(conversation.getAccount());
+ }
+ updateConversation(conversation);
+ joinMuc(conversation);
+ }
+ }
+
+ public void renameInMuc(final Conversation conversation, final String nick, final UiCallback<Conversation> callback) {
+ final MucOptions options = conversation.getMucOptions();
+ final Jid joinJid = options.createJoinJid(nick);
+ if (options.online()) {
+ Account account = conversation.getAccount();
+ options.setOnRenameListener(new OnRenameListener() {
+
+ @Override
+ public void onSuccess() {
+ conversation.setContactJid(joinJid);
+ databaseBackend.updateConversation(conversation);
+ Bookmark bookmark = conversation.getBookmark();
+ if (bookmark != null) {
+ bookmark.setNick(nick);
+ pushBookmarks(bookmark.getAccount());
+ }
+ callback.success(conversation);
+ }
+
+ @Override
+ public void onFailure() {
+ callback.error(R.string.nick_in_use, conversation);
+ }
+ });
+
+ PresencePacket packet = new PresencePacket();
+ packet.setTo(joinJid);
+ packet.setFrom(conversation.getAccount().getJid());
+
+ String sig = account.getPgpSignature();
+ if (sig != null) {
+ packet.addChild("status").setContent("online");
+ packet.addChild("x", "jabber:x:signed").setContent(sig);
+ }
+ sendPresencePacket(account, packet);
+ } else {
+ conversation.setContactJid(joinJid);
+ databaseBackend.updateConversation(conversation);
+ if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
+ Bookmark bookmark = conversation.getBookmark();
+ if (bookmark != null) {
+ bookmark.setNick(nick);
+ pushBookmarks(bookmark.getAccount());
+ }
+ joinMuc(conversation);
+ }
+ }
+ }
+
+ public void leaveMuc(Conversation conversation) {
+ leaveMuc(conversation, false);
+ }
+
+ private void leaveMuc(Conversation conversation, boolean now) {
+ Account account = conversation.getAccount();
+ account.pendingConferenceJoins.remove(conversation);
+ account.pendingConferenceLeaves.remove(conversation);
+ if (account.getStatus() == Account.State.ONLINE || now) {
+ PresencePacket packet = new PresencePacket();
+ packet.setTo(conversation.getMucOptions().getSelf().getFullJid());
+ packet.setFrom(conversation.getAccount().getJid());
+ packet.setAttribute("type", "unavailable");
+ sendPresencePacket(conversation.getAccount(), packet);
+ conversation.getMucOptions().setOffline();
+ conversation.deregisterWithBookmark();
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid()
+ + ": leaving muc " + conversation.getJid());
+ } else {
+ account.pendingConferenceLeaves.add(conversation);
+ }
+ }
+
+ private String findConferenceServer(final Account account) {
+ String server;
+ if (account.getXmppConnection() != null) {
+ server = account.getXmppConnection().getMucServer();
+ if (server != null) {
+ return server;
+ }
+ }
+ for (Account other : getAccounts()) {
+ if (other != account && other.getXmppConnection() != null) {
+ server = other.getXmppConnection().getMucServer();
+ if (server != null) {
+ return server;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void createAdhocConference(final Account account,
+ final String subject,
+ final Iterable<Jid> jids,
+ final UiCallback<Conversation> callback) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": creating adhoc conference with " + jids.toString());
+ if (account.getStatus() == Account.State.ONLINE) {
+ try {
+ String server = findConferenceServer(account);
+ if (server == null) {
+ if (callback != null) {
+ callback.error(R.string.no_conference_server_found, null);
+ }
+ return;
+ }
+ final Jid jid = Jid.fromParts(new BigInteger(64, getRNG()).toString(Character.MAX_RADIX), server, null);
+ final Conversation conversation = findOrCreateConversation(account, jid, true);
+ joinMuc(conversation, new OnConferenceJoined() {
+ @Override
+ public void onConferenceJoined(final Conversation conversation) {
+ pushConferenceConfiguration(conversation, IqGenerator.defaultRoomConfiguration(), new OnConferenceOptionsPushed() {
+ @Override
+ public void onPushSucceeded() {
+ if (subject != null && !subject.trim().isEmpty()) {
+ pushSubjectToConference(conversation, subject.trim());
+ }
+ for (Jid invite : jids) {
+ invite(conversation, invite);
+ }
+ if (account.countPresences() > 1) {
+ directInvite(conversation, account.getJid().toBareJid());
+ }
+ saveConversationAsBookmark(conversation, subject);
+ if (callback != null) {
+ callback.success(conversation);
+ }
+ }
+
+ @Override
+ public void onPushFailed() {
+ archiveConversation(conversation);
+ if (callback != null) {
+ callback.error(R.string.conference_creation_failed, conversation);
+ }
+ }
+ });
+ }
+ });
+ } catch (InvalidJidException e) {
+ if (callback != null) {
+ callback.error(R.string.conference_creation_failed, null);
+ }
+ }
+ } else {
+ if (callback != null) {
+ callback.error(R.string.not_connected_try_again, null);
+ }
+ }
+ }
+
+ public void fetchConferenceConfiguration(final Conversation conversation) {
+ fetchConferenceConfiguration(conversation, null);
+ }
+
+ public void fetchConferenceConfiguration(final Conversation conversation, final OnConferenceConfigurationFetched callback) {
+ IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ request.setTo(conversation.getJid().toBareJid());
+ request.query("http://jabber.org/protocol/disco#info");
+ sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element query = packet.findChild("query", "http://jabber.org/protocol/disco#info");
+ if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
+ ArrayList<String> features = new ArrayList<>();
+ for (Element child : query.getChildren()) {
+ if (child != null && child.getName().equals("feature")) {
+ String var = child.getAttribute("var");
+ if (var != null) {
+ features.add(var);
+ }
+ }
+ }
+ Element form = query.findChild("x", "jabber:x:data");
+ if (form != null) {
+ conversation.getMucOptions().updateFormData(Data.parse(form));
+ }
+ conversation.getMucOptions().updateFeatures(features);
+ if (callback != null) {
+ callback.onConferenceConfigurationFetched(conversation);
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetched muc configuration for " + conversation.getJid().toBareJid() + " - " + features.toString());
+ updateConversationUi();
+ } else if (packet.getType() == IqPacket.TYPE.ERROR) {
+ if (callback != null) {
+ callback.onFetchFailed(conversation, packet.getError());
+ }
+ }
+ }
+ });
+ }
+
+ public void pushConferenceConfiguration(final Conversation conversation, final Bundle options, final OnConferenceOptionsPushed callback) {
+ IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ request.setTo(conversation.getJid().toBareJid());
+ request.query("http://jabber.org/protocol/muc#owner");
+ sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Data data = Data.parse(packet.query().findChild("x", "jabber:x:data"));
+ for (Field field : data.getFields()) {
+ if (options.containsKey(field.getFieldName())) {
+ field.setValue(options.getString(field.getFieldName()));
+ }
+ }
+ data.submit();
+ IqPacket set = new IqPacket(IqPacket.TYPE.SET);
+ set.setTo(conversation.getJid().toBareJid());
+ set.query("http://jabber.org/protocol/muc#owner").addChild(data);
+ sendIqPacket(account, set, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (callback != null) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ callback.onPushSucceeded();
+ } else {
+ callback.onPushFailed();
+ }
+ }
+ }
+ });
+ } else {
+ if (callback != null) {
+ callback.onPushFailed();
+ }
+ }
+ }
+ });
+ }
+
+ public void pushSubjectToConference(final Conversation conference, final String subject) {
+ MessagePacket packet = this.getMessageGenerator().conferenceSubject(conference, subject);
+ this.sendMessagePacket(conference.getAccount(), packet);
+ final MucOptions mucOptions = conference.getMucOptions();
+ final MucOptions.User self = mucOptions.getSelf();
+ if (!mucOptions.persistent() && self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
+ Bundle options = new Bundle();
+ options.putString("muc#roomconfig_persistentroom", "1");
+ this.pushConferenceConfiguration(conference, options, null);
+ }
+ }
+
+ public void changeAffiliationInConference(final Conversation conference, Jid user, final MucOptions.Affiliation affiliation, final OnAffiliationChanged callback) {
+ final Jid jid = user.toBareJid();
+ IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString());
+ sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ conference.getMucOptions().changeAffiliation(jid, affiliation);
+ getAvatarService().clear(conference);
+ callback.onAffiliationChangedSuccessful(jid);
+ } else {
+ callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation);
+ }
+ }
+ });
+ }
+
+ public void changeAffiliationsInConference(final Conversation conference, MucOptions.Affiliation before, MucOptions.Affiliation after) {
+ List<Jid> jids = new ArrayList<>();
+ for (MucOptions.User user : conference.getMucOptions().getUsers()) {
+ if (user.getAffiliation() == before && user.getRealJid() != null) {
+ jids.add(user.getRealJid());
+ }
+ }
+ IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString());
+ sendIqPacket(conference.getAccount(), request, mDefaultIqHandler);
+ }
+
+ public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role, final OnRoleChanged callback) {
+ IqPacket request = this.mIqGenerator.changeRole(conference, nick, role.toString());
+ Log.d(Config.LOGTAG, request.toString());
+ sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Log.d(Config.LOGTAG, packet.toString());
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ callback.onRoleChangedSuccessful(nick);
+ } else {
+ callback.onRoleChangeFailed(nick, R.string.could_not_change_role);
+ }
+ }
+ });
+ }
+
+ private void disconnect(Account account, boolean force) {
+ if ((account.getStatus() == Account.State.ONLINE)
+ || (account.getStatus() == Account.State.DISABLED)) {
+ final XmppConnection connection = account.getXmppConnection();
+ if (!force) {
+ List<Conversation> conversations = getConversations();
+ for (Conversation conversation : conversations) {
+ if (conversation.getAccount() == account) {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ leaveMuc(conversation, true);
+ } else {
+ if (conversation.endOtrIfNeeded()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": ended otr session with "
+ + conversation.getJid());
+ }
+ }
+ }
+ }
+ sendOfflinePresence(account);
+ }
+ connection.disconnect(force);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ public void updateMessage(Message message) {
+ databaseBackend.updateMessage(message);
+ updateConversationUi();
+ }
+
+ public void updateMessage(Message message, String uuid) {
+ databaseBackend.updateMessage(message, uuid);
+ updateConversationUi();
+ }
+
+ protected void syncDirtyContacts(Account account) {
+ for (Contact contact : account.getRoster().getContacts()) {
+ if (contact.getOption(Contact.Options.DIRTY_PUSH)) {
+ pushContactToServer(contact);
+ }
+ if (contact.getOption(Contact.Options.DIRTY_DELETE)) {
+ deleteContactOnServer(contact);
+ }
+ }
+ }
+
+ public void createContact(Contact contact) {
+ boolean autoGrant = getPreferences().getBoolean("grant_new_contacts", true);
+ if (autoGrant) {
+ contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
+ contact.setOption(Contact.Options.ASKING);
+ }
+ pushContactToServer(contact);
+ }
+
+ public void onOtrSessionEstablished(Conversation conversation) {
+ final Account account = conversation.getAccount();
+ final Session otrSession = conversation.getOtrSession();
+ Log.d(Config.LOGTAG,
+ account.getJid().toBareJid() + " otr session established with "
+ + conversation.getJid() + "/"
+ + otrSession.getSessionID().getUserID());
+ conversation.findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR, new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ SessionID id = otrSession.getSessionID();
+ try {
+ message.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
+ } catch (InvalidJidException e) {
+ return;
+ }
+ if (message.needsUploading()) {
+ mJingleConnectionManager.createNewConnection(message);
+ } else {
+ MessagePacket outPacket = mMessageGenerator.generateOtrChat(message);
+ if (outPacket != null) {
+ mMessageGenerator.addDelay(outPacket, message.getTimeSent());
+ message.setStatus(Message.STATUS_SEND);
+ databaseBackend.updateMessage(message);
+ sendMessagePacket(account, outPacket);
+ }
+ }
+ updateConversationUi();
+ }
+ });
+ }
+
+ public boolean renewSymmetricKey(Conversation conversation) {
+ Account account = conversation.getAccount();
+ byte[] symmetricKey = new byte[32];
+ this.mRandom.nextBytes(symmetricKey);
+ Session otrSession = conversation.getOtrSession();
+ if (otrSession != null) {
+ MessagePacket packet = new MessagePacket();
+ packet.setType(MessagePacket.TYPE_CHAT);
+ packet.setFrom(account.getJid());
+ MessageGenerator.addMessageHints(packet);
+ packet.setAttribute("to", otrSession.getSessionID().getAccountID() + "/"
+ + otrSession.getSessionID().getUserID());
+ try {
+ packet.setBody(otrSession
+ .transformSending(CryptoHelper.FILETRANSFER
+ + CryptoHelper.bytesToHex(symmetricKey))[0]);
+ sendMessagePacket(account, packet);
+ conversation.setSymmetricKey(symmetricKey);
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public void pushContactToServer(final Contact contact) {
+ contact.resetOption(Contact.Options.DIRTY_DELETE);
+ contact.setOption(Contact.Options.DIRTY_PUSH);
+ final Account account = contact.getAccount();
+ if (account.getStatus() == Account.State.ONLINE) {
+ final boolean ask = contact.getOption(Contact.Options.ASKING);
+ final boolean sendUpdates = contact
+ .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)
+ && contact.getOption(Contact.Options.PREEMPTIVE_GRANT);
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.query(Xmlns.ROSTER).addChild(contact.asElement());
+ account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
+ if (sendUpdates) {
+ sendPresencePacket(account,
+ mPresenceGenerator.sendPresenceUpdatesTo(contact));
+ }
+ if (ask) {
+ sendPresencePacket(account,
+ mPresenceGenerator.requestPresenceUpdatesFrom(contact));
+ }
+ }
+ }
+
+ public void publishAvatar(Account account, Uri image, UiCallback<Avatar> callback) {
+ final Bitmap.CompressFormat format = Config.AVATAR_FORMAT;
+ final int size = Config.AVATAR_SIZE;
+ final Avatar avatar = getFileBackend().getPepAvatar(image, size, format);
+ if (avatar != null) {
+ avatar.height = size;
+ avatar.width = size;
+ if (format.equals(Bitmap.CompressFormat.WEBP)) {
+ avatar.type = "image/webp";
+ } else if (format.equals(Bitmap.CompressFormat.JPEG)) {
+ avatar.type = "image/jpeg";
+ } else if (format.equals(Bitmap.CompressFormat.PNG)) {
+ avatar.type = "image/png";
+ }
+ if (!getFileBackend().save(avatar)) {
+ callback.error(R.string.error_saving_avatar, avatar);
+ return;
+ }
+ publishAvatar(account, avatar, callback);
+ } else {
+ callback.error(R.string.error_publish_avatar_converting, null);
+ }
+ }
+
+ public void publishAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
+ final IqPacket packet = this.mIqGenerator.publishAvatar(avatar);
+ this.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE.RESULT) {
+ final IqPacket packet = XmppConnectionService.this.mIqGenerator
+ .publishAvatarMetadata(avatar);
+ sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE.RESULT) {
+ if (account.setAvatar(avatar.getFilename())) {
+ getAvatarService().clear(account);
+ databaseBackend.updateAccount(account);
+ }
+ if (callback != null) {
+ callback.success(avatar);
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": published avatar");
+ }
+ } else {
+ if (callback != null) {
+ callback.error(
+ R.string.error_publish_avatar_server_reject,
+ avatar);
+ }
+ }
+ }
+ });
+ } else {
+ if (callback != null) {
+ callback.error(
+ R.string.error_publish_avatar_server_reject,
+ avatar);
+ }
+ }
+ }
+ });
+ }
+
+ public void republishAvatarIfNeeded(Account account) {
+ if (account.getAxolotlService().isPepBroken()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": skipping republication of avatar because pep is broken");
+ return;
+ }
+ IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
+ this.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ private Avatar parseAvatar(IqPacket packet) {
+ Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
+ if (pubsub != null) {
+ Element items = pubsub.findChild("items");
+ if (items != null) {
+ return Avatar.parseMetadata(items);
+ }
+ }
+ return null;
+ }
+
+ private boolean errorIsItemNotFound(IqPacket packet) {
+ Element error = packet.findChild("error");
+ return packet.getType() == IqPacket.TYPE.ERROR
+ && error != null
+ && error.hasChild("item-not-found");
+ }
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT || errorIsItemNotFound(packet)) {
+ Avatar serverAvatar = parseAvatar(packet);
+ if (serverAvatar == null && account.getAvatar() != null) {
+ Avatar avatar = fileBackend.getStoredPepAvatar(account.getAvatar());
+ if (avatar != null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": avatar on server was null. republishing");
+ publishAvatar(account, fileBackend.getStoredPepAvatar(account.getAvatar()), null);
+ } else {
+ Log.e(Config.LOGTAG, account.getJid().toBareJid() + ": error rereading avatar");
+ }
+ }
+ }
+ }
+ });
+ }
+
+ public void fetchAvatar(Account account, Avatar avatar) {
+ fetchAvatar(account, avatar, null);
+ }
+
+ public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
+ final String KEY = generateFetchKey(account, avatar);
+ synchronized (this.mInProgressAvatarFetches) {
+ if (!this.mInProgressAvatarFetches.contains(KEY)) {
+ switch (avatar.origin) {
+ case PEP:
+ this.mInProgressAvatarFetches.add(KEY);
+ fetchAvatarPep(account, avatar, callback);
+ break;
+ case VCARD:
+ this.mInProgressAvatarFetches.add(KEY);
+ fetchAvatarVcard(account, avatar, callback);
+ break;
+ }
+ }
+ }
+ }
+
+ private void fetchAvatarPep(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
+ IqPacket packet = this.mIqGenerator.retrievePepAvatar(avatar);
+ sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ synchronized (mInProgressAvatarFetches) {
+ mInProgressAvatarFetches.remove(generateFetchKey(account, avatar));
+ }
+ final String ERROR = account.getJid().toBareJid()
+ + ": fetching avatar for " + avatar.owner + " failed ";
+ if (result.getType() == IqPacket.TYPE.RESULT) {
+ avatar.image = mIqParser.avatarData(result);
+ if (avatar.image != null) {
+ if (getFileBackend().save(avatar)) {
+ if (account.getJid().toBareJid().equals(avatar.owner)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ databaseBackend.updateAccount(account);
+ }
+ getAvatarService().clear(account);
+ updateConversationUi();
+ updateAccountUi();
+ } else {
+ Contact contact = account.getRoster()
+ .getContact(avatar.owner);
+ contact.setAvatar(avatar);
+ getAvatarService().clear(contact);
+ updateConversationUi();
+ updateRosterUi();
+ }
+ if (callback != null) {
+ callback.success(avatar);
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": successfully fetched pep avatar for " + avatar.owner);
+ return;
+ }
+ } else {
+
+ Log.d(Config.LOGTAG, ERROR + "(parsing error)");
+ }
+ } else {
+ Element error = result.findChild("error");
+ if (error == null) {
+ Log.d(Config.LOGTAG, ERROR + "(server error)");
+ } else {
+ Log.d(Config.LOGTAG, ERROR + error.toString());
+ }
+ }
+ if (callback != null) {
+ callback.error(0, null);
+ }
+
+ }
+ });
+ }
+
+ private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
+ IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar);
+ this.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ synchronized (mInProgressAvatarFetches) {
+ mInProgressAvatarFetches.remove(generateFetchKey(account, avatar));
+ }
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Element vCard = packet.findChild("vCard", "vcard-temp");
+ Element photo = vCard != null ? vCard.findChild("PHOTO") : null;
+ String image = photo != null ? photo.findChildContent("BINVAL") : null;
+ if (image != null) {
+ avatar.image = image;
+ if (getFileBackend().save(avatar)) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": successfully fetched vCard avatar for " + avatar.owner);
+ if (avatar.owner.isBareJid()) {
+ if (account.getJid().toBareJid().equals(avatar.owner) && account.getAvatar() == null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": had no avatar. replacing with vcard");
+ account.setAvatar(avatar.getFilename());
+ databaseBackend.updateAccount(account);
+ getAvatarService().clear(account);
+ updateAccountUi();
+ } else {
+ Contact contact = account.getRoster().getContact(avatar.owner);
+ contact.setAvatar(avatar);
+ getAvatarService().clear(contact);
+ updateRosterUi();
+ }
+ updateConversationUi();
+ } else {
+ Conversation conversation = find(account, avatar.owner.toBareJid());
+ if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
+ MucOptions.User user = conversation.getMucOptions().findUserByFullJid(avatar.owner);
+ if (user != null) {
+ if (user.setAvatar(avatar)) {
+ getAvatarService().clear(user);
+ updateConversationUi();
+ updateMucRosterUi();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+
+ public void checkForAvatar(Account account, final UiCallback<Avatar> callback) {
+ IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
+ this.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
+ if (pubsub != null) {
+ Element items = pubsub.findChild("items");
+ if (items != null) {
+ Avatar avatar = Avatar.parseMetadata(items);
+ if (avatar != null) {
+ avatar.owner = account.getJid().toBareJid();
+ if (fileBackend.isAvatarCached(avatar)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ databaseBackend.updateAccount(account);
+ }
+ getAvatarService().clear(account);
+ callback.success(avatar);
+ } else {
+ fetchAvatarPep(account, avatar, callback);
+ }
+ return;
+ }
+ }
+ }
+ }
+ callback.error(0, null);
+ }
+ });
+ }
+
+ public void deleteContactOnServer(Contact contact) {
+ contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
+ contact.resetOption(Contact.Options.DIRTY_PUSH);
+ contact.setOption(Contact.Options.DIRTY_DELETE);
+ Account account = contact.getAccount();
+ if (account.getStatus() == Account.State.ONLINE) {
+ IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ Element item = iq.query(Xmlns.ROSTER).addChild("item");
+ item.setAttribute("jid", contact.getJid().toString());
+ item.setAttribute("subscription", "remove");
+ account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
+ }
+ }
+
+ public void updateConversation(final Conversation conversation) {
+ mDatabaseExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ databaseBackend.updateConversation(conversation);
+ }
+ });
+ }
+
+ private void reconnectAccount(final Account account, final boolean force, final boolean interactive) {
+ synchronized (account) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection == null) {
+ connection = createConnection(account);
+ account.setXmppConnection(connection);
+ } else {
+ connection.interrupt();
+ }
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ if (!force) {
+ disconnect(account, false);
+ }
+ Thread thread = new Thread(connection);
+ connection.setInteractive(interactive);
+ connection.prepareNewConnection();
+ thread.start();
+ scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
+ } else {
+ disconnect(account, force);
+ account.getRoster().clearPresences();
+ connection.resetEverything();
+ account.getAxolotlService().resetBrokenness();
+ }
+ }
+ }
+
+ public void reconnectAccountInBackground(final Account account) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ reconnectAccount(account, false, true);
+ }
+ }).start();
+ }
+
+ public void invite(Conversation conversation, Jid contact) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": inviting " + contact + " to " + conversation.getJid().toBareJid());
+ MessagePacket packet = mMessageGenerator.invite(conversation, contact);
+ sendMessagePacket(conversation.getAccount(), packet);
+ }
+
+ public void directInvite(Conversation conversation, Jid jid) {
+ MessagePacket packet = mMessageGenerator.directInvite(conversation, jid);
+ sendMessagePacket(conversation.getAccount(), packet);
+ }
+
+ public void resetSendingToWaiting(Account account) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": reset 'sending' messages to 'waiting'");
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getAccount() == account) {
+ conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message, Message.STATUS_WAITING);
+ }
+ });
+ }
+ }
+ }
+
+ public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status) {
+ return markMessage(account, recipient, uuid, status, null);
+ }
+
+ public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status, String errorMessage) {
+ if (uuid == null) {
+ return null;
+ }
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getJid().toBareJid().equals(recipient) && conversation.getAccount() == account) {
+ final Message message = conversation.findSentMessageWithUuidOrRemoteId(uuid);
+ if (message != null) {
+ markMessage(message, status, errorMessage);
+ }
+ return message;
+ }
+ }
+ return null;
+ }
+
+ public boolean markMessage(Conversation conversation, String uuid, int status) {
+ if (uuid == null) {
+ return false;
+ } else {
+ Message message = conversation.findSentMessageWithUuid(uuid);
+ if (message != null) {
+ markMessage(message, status);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ public void markMessage(Message message, int status) {
+ markMessage(message, status, null);
+ }
+
+ public void markMessage(Message message, int status, String errorMessage) {
+ if (status == Message.STATUS_SEND_FAILED
+ && (message.getStatus() == Message.STATUS_SEND_RECEIVED || message
+ .getStatus() == Message.STATUS_SEND_DISPLAYED)) {
+ return;
+ }
+ message.setErrorMessage(errorMessage);
+ message.setStatus(status);
+ databaseBackend.updateMessage(message);
+ updateConversationUi();
+ }
+
+ public SharedPreferences getPreferences() {
+ return PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+ }
+
+ public boolean confirmMessages() {
+ return getPreferences().getBoolean("confirm_messages", true);
+ }
+
+ public boolean allowMessageCorrection() {
+ return getPreferences().getBoolean("allow_message_correction", true);
+ }
+
+ public boolean sendChatStates() {
+ return getPreferences().getBoolean("chat_states", true);
+ }
+
+ public boolean saveEncryptedMessages() {
+ return !getPreferences().getBoolean("dont_save_encrypted", false);
+ }
+
+ private boolean respectAutojoin() {
+ return getPreferences().getBoolean("autojoin", true);
+ }
+
+ public boolean indicateReceived() {
+ return getPreferences().getBoolean("indicate_received", true);
+ }
+
+ public boolean useTorToConnect() {
+ return Config.FORCE_ORBOT || getPreferences().getBoolean("use_tor", false);
+ }
+
+ public boolean showExtendedConnectionOptions() {
+ return getPreferences().getBoolean("show_connection_options", false);
+ }
+
+ public boolean broadcastLastActivity() {
+ return getPreferences().getBoolean("last_activity", true);
+ }
+
+ public int unreadCount() {
+ int count = 0;
+ for (Conversation conversation : getConversations()) {
+ count += conversation.unreadCount();
+ }
+ return count;
+ }
+
+ public void vibrate() {
+ Log.d(Config.LOGTAG, "Notification: short vibrate");
+ Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
+ vibrator.vibrate(100);
+ }
+
+
+ public void showErrorToastInUi(int resId) {
+ if (mOnShowErrorToast != null) {
+ mOnShowErrorToast.onShowErrorToast(resId);
+ }
+ }
+
+ public void updateConversationUi() {
+ if (mOnConversationUpdate != null) {
+ mOnConversationUpdate.onConversationUpdate();
+ }
+ }
+
+ public void updateAccountUi() {
+ if (mOnAccountUpdate != null) {
+ mOnAccountUpdate.onAccountUpdate();
+ }
+ }
+
+ public void updateRosterUi() {
+ if (mOnRosterUpdate != null) {
+ mOnRosterUpdate.onRosterUpdate();
+ }
+ }
+
+ public boolean displayCaptchaRequest(Account account, String id, Data data, Bitmap captcha) {
+ if (mOnCaptchaRequested != null) {
+ DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics();
+ Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int) (captcha.getWidth() * metrics.scaledDensity),
+ (int) (captcha.getHeight() * metrics.scaledDensity), false);
+
+ mOnCaptchaRequested.onCaptchaRequested(account, id, data, scaled);
+ return true;
+ }
+ return false;
+ }
+
+ public void updateBlocklistUi(final OnUpdateBlocklist.Status status) {
+ if (mOnUpdateBlocklist != null) {
+ mOnUpdateBlocklist.OnUpdateBlocklist(status);
+ }
+ }
+
+ public void updateMucRosterUi() {
+ if (mOnMucRosterUpdate != null) {
+ mOnMucRosterUpdate.onMucRosterUpdate();
+ }
+ }
+
+ public void keyStatusUpdated(AxolotlService.FetchStatus report) {
+ if (mOnKeyStatusUpdated != null) {
+ mOnKeyStatusUpdated.onKeyStatusUpdated(report);
+ }
+ }
+
+ public Account findAccountByJid(final Jid accountJid) {
+ for (Account account : this.accounts) {
+ if (account.getJid().toBareJid().equals(accountJid.toBareJid())) {
+ return account;
+ }
+ }
+ return null;
+ }
+
+ public Conversation findConversationByUuid(String uuid) {
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getUuid().equals(uuid)) {
+ return conversation;
+ }
+ }
+ return null;
+ }
+
+ public boolean markRead(final Conversation conversation) {
+ return markRead(conversation, true);
+ }
+
+ public boolean markRead(final Conversation conversation, boolean clear) {
+ if (clear) {
+ mNotificationService.clear(conversation);
+ }
+ final List<Message> readMessages = conversation.markRead();
+ if (readMessages.size() > 0) {
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ for (Message message : readMessages) {
+ databaseBackend.updateMessage(message);
+ }
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+ updateUnreadCountBadge();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public synchronized void updateUnreadCountBadge() {
+ int count = unreadCount();
+ if (unreadCount != count) {
+ Log.d(Config.LOGTAG, "update unread count to " + count);
+ if (count > 0) {
+ ShortcutBadger.applyCount(getApplicationContext(), count);
+ } else {
+ ShortcutBadger.removeCount(getApplicationContext());
+ }
+ unreadCount = count;
+ }
+ }
+
+ public void sendReadMarker(final Conversation conversation) {
+ final Message markable = conversation.getLatestMarkableMessage();
+ if (this.markRead(conversation)) {
+ updateConversationUi();
+ }
+ if (confirmMessages() && markable != null && markable.trusted() && markable.getRemoteMsgId() != null) {
+ Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending read marker to " + markable.getCounterpart().toString());
+ Account account = conversation.getAccount();
+ final Jid to = markable.getCounterpart();
+ MessagePacket packet = mMessageGenerator.confirm(account, to, markable.getRemoteMsgId());
+ this.sendMessagePacket(conversation.getAccount(), packet);
+ }
+ }
+
+ public SecureRandom getRNG() {
+ return this.mRandom;
+ }
+
+ public MemorizingTrustManager getMemorizingTrustManager() {
+ return this.mMemorizingTrustManager;
+ }
+
+ public void setMemorizingTrustManager(MemorizingTrustManager trustManager) {
+ this.mMemorizingTrustManager = trustManager;
+ }
+
+ public void updateMemorizingTrustmanager() {
+ final MemorizingTrustManager tm;
+ final boolean dontTrustSystemCAs = getPreferences().getBoolean("dont_trust_system_cas", false);
+ if (dontTrustSystemCAs) {
+ tm = new MemorizingTrustManager(getApplicationContext(), null);
+ } else {
+ tm = new MemorizingTrustManager(getApplicationContext());
+ }
+ setMemorizingTrustManager(tm);
+ }
+
+ public PowerManager getPowerManager() {
+ return this.pm;
+ }
+
+ public LruCache<String, Bitmap> getBitmapCache() {
+ return this.mBitmapCache;
+ }
+
+ public void syncRosterToDisk(final Account account) {
+ Runnable runnable = new Runnable() {
+
+ @Override
+ public void run() {
+ databaseBackend.writeRoster(account.getRoster());
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+
+ }
+
+ public List<String> getKnownHosts() {
+ final List<String> hosts = new ArrayList<>();
+ for (final Account account : getAccounts()) {
+ if (!hosts.contains(account.getServer().toString())) {
+ hosts.add(account.getServer().toString());
+ }
+ for (final Contact contact : account.getRoster().getContacts()) {
+ if (contact.showInRoster()) {
+ final String server = contact.getServer().toString();
+ if (server != null && !hosts.contains(server)) {
+ hosts.add(server);
+ }
+ }
+ }
+ }
+ if (Config.DOMAIN_LOCK != null && !hosts.contains(Config.DOMAIN_LOCK)) {
+ hosts.add(Config.DOMAIN_LOCK);
+ }
+ if (Config.MAGIC_CREATE_DOMAIN != null && !hosts.contains(Config.MAGIC_CREATE_DOMAIN)) {
+ hosts.add(Config.MAGIC_CREATE_DOMAIN);
+ }
+ return hosts;
+ }
+
+ public List<String> getKnownConferenceHosts() {
+ final ArrayList<String> mucServers = new ArrayList<>();
+ for (final Account account : accounts) {
+ if (account.getXmppConnection() != null) {
+ final String server = account.getXmppConnection().getMucServer();
+ if (server != null && !mucServers.contains(server)) {
+ mucServers.add(server);
+ }
+ }
+ }
+ return mucServers;
+ }
+
+ public void sendMessagePacket(Account account, MessagePacket packet) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendMessagePacket(packet);
+ }
+ }
+
+ public void sendPresencePacket(Account account, PresencePacket packet) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendPresencePacket(packet);
+ }
+ }
+
+ public void sendCreateAccountWithCaptchaPacket(Account account, String id, Data data) {
+ final XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ IqPacket request = mIqGenerator.generateCreateAccountWithCaptcha(account, id, data);
+ connection.sendUnmodifiedIqPacket(request, connection.registrationResponseListener);
+ }
+ }
+
+ public void sendIqPacket(final Account account, final IqPacket packet, final OnIqPacketReceived callback) {
+ final XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendIqPacket(packet, callback);
+ }
+ }
+
+ public void sendPresence(final Account account) {
+ sendPresence(account, checkListeners() && broadcastLastActivity());
+ }
+
+ private void sendPresence(final Account account, final boolean includeIdleTimestamp) {
+ PresencePacket packet;
+ if (manuallyChangePresence()) {
+ packet = mPresenceGenerator.selfPresence(account, account.getPresenceStatus());
+ String message = account.getPresenceStatusMessage();
+ if (message != null && !message.isEmpty()) {
+ packet.addChild(new Element("status").setContent(message));
+ }
+ } else {
+ packet = mPresenceGenerator.selfPresence(account, getTargetPresence());
+ }
+ if (mLastActivity > 0 && includeIdleTimestamp) {
+ long since = Math.min(mLastActivity, System.currentTimeMillis()); //don't send future dates
+ packet.addChild("idle", "urn:xmpp:idle:1").setAttribute("since", AbstractGenerator.getTimestamp(since));
+ }
+ sendPresencePacket(account, packet);
+ }
+
+ private void deactivateGracePeriod() {
+ for (Account account : getAccounts()) {
+ account.deactivateGracePeriod();
+ }
+ }
+
+ public void refreshAllPresences() {
+ boolean includeIdleTimestamp = checkListeners() && broadcastLastActivity();
+ for (Account account : getAccounts()) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ sendPresence(account, includeIdleTimestamp);
+ }
+ }
+ }
+
+ private void refreshAllGcmTokens() {
+ for (Account account : getAccounts()) {
+ if (account.isOnlineAndConnected() && mPushManagementService.available(account)) {
+ mPushManagementService.registerPushTokenOnServer(account);
+ }
+ }
+ }
+
+ private void sendOfflinePresence(final Account account) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": sending offline presence");
+ sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account));
+ }
+
+ public MessageGenerator getMessageGenerator() {
+ return this.mMessageGenerator;
+ }
+
+ public PresenceGenerator getPresenceGenerator() {
+ return this.mPresenceGenerator;
+ }
+
+ public IqGenerator getIqGenerator() {
+ return this.mIqGenerator;
+ }
+
+ public IqParser getIqParser() {
+ return this.mIqParser;
+ }
+
+ public JingleConnectionManager getJingleConnectionManager() {
+ return this.mJingleConnectionManager;
+ }
+
+ public MessageArchiveService getMessageArchiveService() {
+ return this.mMessageArchiveService;
+ }
+
+ public List<Contact> findContacts(Jid jid) {
+ ArrayList<Contact> contacts = new ArrayList<>();
+ for (Account account : getAccounts()) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ Contact contact = account.getRoster().getContactFromRoster(jid);
+ if (contact != null) {
+ contacts.add(contact);
+ }
+ }
+ }
+ return contacts;
+ }
+
+ public Conversation findFirstMuc(Jid jid) {
+ for (Conversation conversation : getConversations()) {
+ if (conversation.getJid().toBareJid().equals(jid.toBareJid())
+ && conversation.getMode() == Conversation.MODE_MULTI) {
+ return conversation;
+ }
+ }
+ return null;
+ }
+
+ public NotificationService getNotificationService() {
+ return this.mNotificationService;
+ }
+
+ public HttpConnectionManager getHttpConnectionManager() {
+ return this.mHttpConnectionManager;
+ }
+
+ public void resendFailedMessages(final Message message) {
+ final Collection<Message> messages = new ArrayList<>();
+ Message current = message;
+ while (current.getStatus() == Message.STATUS_SEND_FAILED) {
+ messages.add(current);
+ if (current.mergeable(current.next())) {
+ current = current.next();
+ } else {
+ break;
+ }
+ }
+ for (final Message msg : messages) {
+ msg.setTime(System.currentTimeMillis());
+ markMessage(msg, Message.STATUS_WAITING);
+ this.resendMessage(msg, false);
+ }
+ }
+
+ public void clearConversationHistory(final Conversation conversation) {
+ conversation.clearMessages();
+ conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam
+ conversation.setLastClearHistory(System.currentTimeMillis());
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ databaseBackend.deleteMessagesInConversation(conversation);
+ databaseBackend.updateConversation(conversation);
+
+ }
+ };
+ mDatabaseExecutor.execute(runnable);
+ }
+
+ public void sendBlockRequest(final Blockable blockable, boolean reportSpam) {
+ if (blockable != null && blockable.getBlockedJid() != null) {
+ final Jid jid = blockable.getBlockedJid();
+ this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam), new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.getBlocklist().add(jid);
+ updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
+ }
+ }
+ });
+ }
+ }
+
+ public void sendUnblockRequest(final Blockable blockable) {
+ if (blockable != null && blockable.getJid() != null) {
+ final Jid jid = blockable.getBlockedJid();
+ this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetUnblockRequest(jid), new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.getBlocklist().remove(jid);
+ updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
+ }
+ }
+ });
+ }
+ }
+
+ public void publishDisplayName(Account account) {
+ String displayName = account.getDisplayName();
+ if (displayName != null && !displayName.isEmpty()) {
+ IqPacket publish = mIqGenerator.publishNick(displayName);
+ sendIqPacket(account, publish, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.ERROR) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not publish nick");
+ }
+ }
+ });
+ }
+ }
+
+ public ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair<String, String> key) {
+ ServiceDiscoveryResult result = discoCache.get(key);
+ if (result != null) {
+ return result;
+ } else {
+ result = databaseBackend.findDiscoveryResult(key.first, key.second);
+ if (result != null) {
+ discoCache.put(key, result);
+ }
+ return result;
+ }
+ }
+
+ public void fetchCaps(Account account, final Jid jid, final Presence presence) {
+ final Pair<String, String> key = new Pair<>(presence.getHash(), presence.getVer());
+ ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key);
+ if (disco != null) {
+ presence.setServiceDiscoveryResult(disco);
+ } else {
+ if (!account.inProgressDiscoFetches.contains(key)) {
+ account.inProgressDiscoFetches.add(key);
+ IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ request.setTo(jid);
+ request.query("http://jabber.org/protocol/disco#info");
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": making disco request for " + key.second + " to " + jid);
+ sendIqPacket(account, request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket discoPacket) {
+ if (discoPacket.getType() == IqPacket.TYPE.RESULT) {
+ ServiceDiscoveryResult disco = new ServiceDiscoveryResult(discoPacket);
+ if (presence.getVer().equals(disco.getVer())) {
+ databaseBackend.insertDiscoveryResult(disco);
+ injectServiceDiscorveryResult(account.getRoster(), presence.getHash(), presence.getVer(), disco);
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + disco.getVer());
+ }
+ }
+ account.inProgressDiscoFetches.remove(key);
+ }
+ });
+ }
+ }
+ }
+
+ private void injectServiceDiscorveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) {
+ for (Contact contact : roster.getContacts()) {
+ for (Presence presence : contact.getPresences().getPresences().values()) {
+ if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) {
+ presence.setServiceDiscoveryResult(disco);
+ }
+ }
+ }
+ }
+
+ public void fetchMamPreferences(Account account, final OnMamPreferencesFetched callback) {
+ IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ request.addChild("prefs", "urn:xmpp:mam:0");
+ sendIqPacket(account, request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element prefs = packet.findChild("prefs", "urn:xmpp:mam:0");
+ if (packet.getType() == IqPacket.TYPE.RESULT && prefs != null) {
+ callback.onPreferencesFetched(prefs);
+ } else {
+ callback.onPreferencesFetchFailed();
+ }
+ }
+ });
+ }
+
+ public PushManagementService getPushManagementService() {
+ return mPushManagementService;
+ }
+
+ public Account getPendingAccount() {
+ Account pending = null;
+ for (Account account : getAccounts()) {
+ if (account.isOptionSet(Account.OPTION_REGISTER)) {
+ pending = account;
+ } else {
+ return null;
+ }
+ }
+ return pending;
+ }
+
+ public void changeStatus(Account account, Presence.Status status, String statusMessage, boolean send) {
+ if (!statusMessage.isEmpty()) {
+ databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
+ }
+ changeStatusReal(account, status, statusMessage, send);
+ }
+
+ private void changeStatusReal(Account account, Presence.Status status, String statusMessage, boolean send) {
+ account.setPresenceStatus(status);
+ account.setPresenceStatusMessage(statusMessage);
+ databaseBackend.updateAccount(account);
+ if (!account.isOptionSet(Account.OPTION_DISABLED) && send) {
+ sendPresence(account);
+ }
+ }
+
+ public void changeStatus(Presence.Status status, String statusMessage) {
+ if (!statusMessage.isEmpty()) {
+ databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
+ }
+ for (Account account : getAccounts()) {
+ changeStatusReal(account, status, statusMessage, true);
+ }
+ }
+
+ public List<PresenceTemplate> getPresenceTemplates(Account account) {
+ List<PresenceTemplate> templates = databaseBackend.getPresenceTemplates();
+ for (PresenceTemplate template : account.getSelfContact().getPresences().asTemplates()) {
+ if (!templates.contains(template)) {
+ templates.add(0, template);
+ }
+ }
+ return templates;
+ }
+
+ public void saveConversationAsBookmark(Conversation conversation, String name) {
+ Account account = conversation.getAccount();
+ Bookmark bookmark = new Bookmark(account, conversation.getJid().toBareJid());
+ if (!conversation.getJid().isBareJid()) {
+ bookmark.setNick(conversation.getJid().getResourcepart());
+ }
+ if (name != null && !name.trim().isEmpty()) {
+ bookmark.setBookmarkName(name.trim());
+ }
+ bookmark.setAutojoin(getPreferences().getBoolean("autojoin", true));
+ account.getBookmarks().add(bookmark);
+ pushBookmarks(account);
+ conversation.setBookmark(bookmark);
+ }
+
+ public void clearStartTimeCounter() {
+ mDatabaseExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ databaseBackend.clearStartTimeCounter();
+ }
+ });
+ }
+
+ public void verifyFingerprints(Contact contact, List<XmppUri.Fingerprint> fingerprints) {
+ boolean needsRosterWrite = false;
+ final AxolotlService axolotlService = contact.getAccount().getAxolotlService();
+ for (XmppUri.Fingerprint fp : fingerprints) {
+ if (fp.type == XmppUri.FingerprintType.OTR) {
+ needsRosterWrite |= contact.addOtrFingerprint(fp.fingerprint);
+ } else if (fp.type == XmppUri.FingerprintType.OMEMO) {
+ String fingerprint = "05" + fp.fingerprint.replaceAll("\\s", "");
+ FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint);
+ if (fingerprintStatus != null) {
+ if (!fingerprintStatus.isVerified()) {
+ axolotlService.setFingerprintTrust(fingerprint, fingerprintStatus.toVerified());
+ }
+ } else {
+ axolotlService.preVerifyFingerprint(contact, fingerprint);
+ }
+ }
+ }
+ if (needsRosterWrite) {
+ syncRosterToDisk(contact.getAccount());
+ }
+ }
+
+ public boolean verifyFingerprints(Account account, List<XmppUri.Fingerprint> fingerprints) {
+ final AxolotlService axolotlService = account.getAxolotlService();
+ boolean verifiedSomething = false;
+ for (XmppUri.Fingerprint fp : fingerprints) {
+ if (fp.type == XmppUri.FingerprintType.OMEMO) {
+ String fingerprint = "05" + fp.fingerprint.replaceAll("\\s", "");
+ Log.d(Config.LOGTAG, "trying to verify own fp=" + fingerprint);
+ FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint);
+ if (fingerprintStatus != null) {
+ if (!fingerprintStatus.isVerified()) {
+ axolotlService.setFingerprintTrust(fingerprint, fingerprintStatus.toVerified());
+ verifiedSomething = true;
+ }
+ } else {
+ axolotlService.preVerifyFingerprint(account, fingerprint);
+ verifiedSomething = true;
+ }
+ }
+ }
+ return verifiedSomething;
+ }
+
+ public interface OnMamPreferencesFetched {
+ void onPreferencesFetched(Element prefs);
+
+ void onPreferencesFetchFailed();
+ }
+
+ public void pushMamPreferences(Account account, Element prefs) {
+ IqPacket set = new IqPacket(IqPacket.TYPE.SET);
+ set.addChild(prefs);
+ sendIqPacket(account, set, null);
+ }
+
+ public interface OnAccountCreated {
+ void onAccountCreated(Account account);
+
+ void informUser(int r);
+ }
+
+ public interface OnMoreMessagesLoaded {
+ void onMoreMessagesLoaded(int count, Conversation conversation);
+
+ void informUser(int r);
+ }
+
+ public interface OnAccountPasswordChanged {
+ void onPasswordChangeSucceeded();
+
+ void onPasswordChangeFailed();
+ }
+
+ public interface OnAffiliationChanged {
+ void onAffiliationChangedSuccessful(Jid jid);
+
+ void onAffiliationChangeFailed(Jid jid, int resId);
+ }
+
+ public interface OnRoleChanged {
+ void onRoleChangedSuccessful(String nick);
+
+ void onRoleChangeFailed(String nick, int resid);
+ }
+
+ public interface OnConversationUpdate {
+ void onConversationUpdate();
+ }
+
+ public interface OnAccountUpdate {
+ void onAccountUpdate();
+ }
+
+ public interface OnCaptchaRequested {
+ void onCaptchaRequested(Account account,
+ String id,
+ Data data,
+ Bitmap captcha);
+ }
+
+ public interface OnRosterUpdate {
+ void onRosterUpdate();
+ }
+
+ public interface OnMucRosterUpdate {
+ void onMucRosterUpdate();
+ }
+
+ public interface OnConferenceConfigurationFetched {
+ void onConferenceConfigurationFetched(Conversation conversation);
+
+ void onFetchFailed(Conversation conversation, Element error);
+ }
+
+ public interface OnConferenceJoined {
+ void onConferenceJoined(Conversation conversation);
+ }
+
+ public interface OnConferenceOptionsPushed {
+ void onPushSucceeded();
+
+ void onPushFailed();
+ }
+
+ public interface OnShowErrorToast {
+ void onShowErrorToast(int resId);
+ }
+
+ public class XmppConnectionBinder extends Binder {
+ public XmppConnectionService getService() {
+ return XmppConnectionService.this;
+ }
+ }
+
+ public void ScheduleAutomaticExport() {
+ //start export log service every day at given time
+ if (Config.ExportLogs) {
+ if (Config.ExportLogs_Hour >= 0 && Config.ExportLogs_Hour <= 23 && Config.ExportLogs_Minute >= 0 && Config.ExportLogs_Minute <= 59) {
+ Log.d(Config.LOGTAG, "Schedule automatic export logs at " + Config.ExportLogs_Hour + ":" + Config.ExportLogs_Minute);
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(System.currentTimeMillis());
+ calendar.set(Calendar.HOUR_OF_DAY, Config.ExportLogs_Hour);
+ calendar.set(Calendar.MINUTE, Config.ExportLogs_Minute);
+ Intent intent = new Intent(this, AlarmReceiver.class);
+ intent.setAction("exportlogs");
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, AlarmReceiver.SCHEDULE_ALARM_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ ((AlarmManager) getSystemService(ALARM_SERVICE)).setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
+ }
+ }
+ }
+
+ public void CancelAutomaticExport() {
+ if (Config.ExportLogs) {
+ Log.d(Config.LOGTAG, "Cancel scheduled automatic export");
+ Intent intent = new Intent(this, AlarmReceiver.class);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, AlarmReceiver.SCHEDULE_ALARM_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ ((AlarmManager) this.getSystemService(ALARM_SERVICE)).cancel(pendingIntent);
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/AboutPreference.java b/src/main/java/de/pixart/messenger/ui/AboutPreference.java
index d4cfa982b..239ef53fb 100644
--- a/src/main/java/de/pixart/messenger/ui/AboutPreference.java
+++ b/src/main/java/de/pixart/messenger/ui/AboutPreference.java
@@ -8,15 +8,15 @@ import android.util.AttributeSet;
import de.pixart.messenger.utils.PhoneHelper;
public class AboutPreference extends Preference {
- public AboutPreference(final Context context, final AttributeSet attrs, final int defStyle) {
- super(context, attrs, defStyle);
- setSummary();
- }
+ public AboutPreference(final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context, attrs, defStyle);
+ setSummary();
+ }
- public AboutPreference(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- setSummary();
- }
+ public AboutPreference(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ setSummary();
+ }
@Override
protected void onClick() {
@@ -26,7 +26,7 @@ public class AboutPreference extends Preference {
}
private void setSummary() {
- setSummary("Pix-Art Messenger " + PhoneHelper.getVersionName(getContext()));
- }
+ setSummary("Pix-Art Messenger " + PhoneHelper.getVersionName(getContext()));
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/AbstractSearchableListItemActivity.java b/src/main/java/de/pixart/messenger/ui/AbstractSearchableListItemActivity.java
index 9b12e43ef..768e738ba 100644
--- a/src/main/java/de/pixart/messenger/ui/AbstractSearchableListItemActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/AbstractSearchableListItemActivity.java
@@ -20,105 +20,105 @@ import de.pixart.messenger.entities.ListItem;
import de.pixart.messenger.ui.adapter.ListItemAdapter;
public abstract class AbstractSearchableListItemActivity extends XmppActivity {
- private ListView mListView;
- private final List<ListItem> listItems = new ArrayList<>();
- private ArrayAdapter<ListItem> mListItemsAdapter;
-
- private EditText mSearchEditText;
-
- private final MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
-
- @Override
- public boolean onMenuItemActionExpand(final MenuItem item) {
- mSearchEditText.post(new Runnable() {
-
- @Override
- public void run() {
- mSearchEditText.requestFocus();
- final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(mSearchEditText,
- InputMethodManager.SHOW_IMPLICIT);
- }
- });
-
- return true;
- }
-
- @Override
- public boolean onMenuItemActionCollapse(final MenuItem item) {
- final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
- InputMethodManager.HIDE_IMPLICIT_ONLY);
- mSearchEditText.setText("");
- filterContacts();
- return true;
- }
- };
-
- private final TextWatcher mSearchTextWatcher = new TextWatcher() {
-
- @Override
- public void afterTextChanged(final Editable editable) {
- filterContacts(editable.toString());
- }
-
- @Override
- public void beforeTextChanged(final CharSequence s, final int start, final int count,
- final int after) {
- }
-
- @Override
- public void onTextChanged(final CharSequence s, final int start, final int before,
- final int count) {
- }
- };
-
- public ListView getListView() {
- return mListView;
- }
-
- public List<ListItem> getListItems() {
- return listItems;
- }
-
- public EditText getSearchEditText() {
- return mSearchEditText;
- }
-
- public ArrayAdapter<ListItem> getListItemAdapter() {
- return mListItemsAdapter;
- }
-
- @Override
- public void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_choose_contact);
- mListView = (ListView) findViewById(R.id.choose_contact_list);
- mListView.setFastScrollEnabled(true);
- mListItemsAdapter = new ListItemAdapter(this, listItems);
- mListView.setAdapter(mListItemsAdapter);
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- getMenuInflater().inflate(R.menu.choose_contact, menu);
- final MenuItem menuSearchView = menu.findItem(R.id.action_search);
- final View mSearchView = menuSearchView.getActionView();
- mSearchEditText = (EditText) mSearchView
- .findViewById(R.id.search_field);
- mSearchEditText.addTextChangedListener(mSearchTextWatcher);
- menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
- return true;
- }
-
- protected void filterContacts() {
- filterContacts(null);
- }
-
- protected abstract void filterContacts(final String needle);
-
- @Override
- void onBackendConnected() {
- filterContacts();
- }
+ private ListView mListView;
+ private final List<ListItem> listItems = new ArrayList<>();
+ private ArrayAdapter<ListItem> mListItemsAdapter;
+
+ private EditText mSearchEditText;
+
+ private final MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
+
+ @Override
+ public boolean onMenuItemActionExpand(final MenuItem item) {
+ mSearchEditText.post(new Runnable() {
+
+ @Override
+ public void run() {
+ mSearchEditText.requestFocus();
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mSearchEditText,
+ InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
+
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(final MenuItem item) {
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
+ InputMethodManager.HIDE_IMPLICIT_ONLY);
+ mSearchEditText.setText("");
+ filterContacts();
+ return true;
+ }
+ };
+
+ private final TextWatcher mSearchTextWatcher = new TextWatcher() {
+
+ @Override
+ public void afterTextChanged(final Editable editable) {
+ filterContacts(editable.toString());
+ }
+
+ @Override
+ public void beforeTextChanged(final CharSequence s, final int start, final int count,
+ final int after) {
+ }
+
+ @Override
+ public void onTextChanged(final CharSequence s, final int start, final int before,
+ final int count) {
+ }
+ };
+
+ public ListView getListView() {
+ return mListView;
+ }
+
+ public List<ListItem> getListItems() {
+ return listItems;
+ }
+
+ public EditText getSearchEditText() {
+ return mSearchEditText;
+ }
+
+ public ArrayAdapter<ListItem> getListItemAdapter() {
+ return mListItemsAdapter;
+ }
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_choose_contact);
+ mListView = (ListView) findViewById(R.id.choose_contact_list);
+ mListView.setFastScrollEnabled(true);
+ mListItemsAdapter = new ListItemAdapter(this, listItems);
+ mListView.setAdapter(mListItemsAdapter);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ getMenuInflater().inflate(R.menu.choose_contact, menu);
+ final MenuItem menuSearchView = menu.findItem(R.id.action_search);
+ final View mSearchView = menuSearchView.getActionView();
+ mSearchEditText = (EditText) mSearchView
+ .findViewById(R.id.search_field);
+ mSearchEditText.addTextChangedListener(mSearchTextWatcher);
+ menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
+ return true;
+ }
+
+ protected void filterContacts() {
+ filterContacts(null);
+ }
+
+ protected abstract void filterContacts(final String needle);
+
+ @Override
+ void onBackendConnected() {
+ filterContacts();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java b/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java
index f72a365b9..d1c6b56d6 100644
--- a/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java
+++ b/src/main/java/de/pixart/messenger/ui/BlockContactDialog.java
@@ -17,48 +17,48 @@ import de.pixart.messenger.entities.Blockable;
import de.pixart.messenger.services.XmppConnectionService;
public final class BlockContactDialog {
- public static void show(final Context context,
- final XmppConnectionService xmppConnectionService,
- final Blockable blockable) {
- final AlertDialog.Builder builder = new AlertDialog.Builder(context);
- final boolean isBlocked = blockable.isBlocked();
- builder.setNegativeButton(R.string.cancel, null);
- LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- LinearLayout view = (LinearLayout) inflater.inflate(R.layout.dialog_block_contact,null);
- TextView message = (TextView) view.findViewById(R.id.text);
- final CheckBox report = (CheckBox) view.findViewById(R.id.report_spam);
- final boolean reporting = blockable.getAccount().getXmppConnection().getFeatures().spamReporting();
- report.setVisibility(!isBlocked && reporting ? View.VISIBLE : View.GONE);
- builder.setView(view);
+ public static void show(final Context context,
+ final XmppConnectionService xmppConnectionService,
+ final Blockable blockable) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ final boolean isBlocked = blockable.isBlocked();
+ builder.setNegativeButton(R.string.cancel, null);
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LinearLayout view = (LinearLayout) inflater.inflate(R.layout.dialog_block_contact, null);
+ TextView message = (TextView) view.findViewById(R.id.text);
+ final CheckBox report = (CheckBox) view.findViewById(R.id.report_spam);
+ final boolean reporting = blockable.getAccount().getXmppConnection().getFeatures().spamReporting();
+ report.setVisibility(!isBlocked && reporting ? View.VISIBLE : View.GONE);
+ builder.setView(view);
- String value;
- SpannableString spannable;
- if (blockable.getJid().isDomainJid() || blockable.getAccount().isBlocked(blockable.getJid().toDomainJid())) {
- builder.setTitle(isBlocked ? R.string.action_unblock_domain : R.string.action_block_domain);
- value = blockable.getJid().toDomainJid().toString();
- spannable = new SpannableString(context.getString(isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text, value));
- message.setText(spannable);
- } else {
- builder.setTitle(isBlocked ? R.string.action_unblock_contact : R.string.action_block_contact);
- value = blockable.getJid().toBareJid().toString();
- spannable = new SpannableString(context.getString(isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text, value));
- }
- int start = spannable.toString().indexOf(value);
- if (start >= 0) {
- spannable.setSpan(new TypefaceSpan("monospace"),start,start + value.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- message.setText(spannable);
- builder.setPositiveButton(isBlocked ? R.string.unblock : R.string.block, new DialogInterface.OnClickListener() {
+ String value;
+ SpannableString spannable;
+ if (blockable.getJid().isDomainJid() || blockable.getAccount().isBlocked(blockable.getJid().toDomainJid())) {
+ builder.setTitle(isBlocked ? R.string.action_unblock_domain : R.string.action_block_domain);
+ value = blockable.getJid().toDomainJid().toString();
+ spannable = new SpannableString(context.getString(isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text, value));
+ message.setText(spannable);
+ } else {
+ builder.setTitle(isBlocked ? R.string.action_unblock_contact : R.string.action_block_contact);
+ value = blockable.getJid().toBareJid().toString();
+ spannable = new SpannableString(context.getString(isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text, value));
+ }
+ int start = spannable.toString().indexOf(value);
+ if (start >= 0) {
+ spannable.setSpan(new TypefaceSpan("monospace"), start, start + value.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ message.setText(spannable);
+ builder.setPositiveButton(isBlocked ? R.string.unblock : R.string.block, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(final DialogInterface dialog, final int which) {
- if (isBlocked) {
- xmppConnectionService.sendUnblockRequest(blockable);
- } else {
- xmppConnectionService.sendBlockRequest(blockable, report.isChecked());
- }
- }
- });
- builder.create().show();
- }
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ if (isBlocked) {
+ xmppConnectionService.sendUnblockRequest(blockable);
+ } else {
+ xmppConnectionService.sendBlockRequest(blockable, report.isChecked());
+ }
+ }
+ });
+ builder.create().show();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java b/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java
index cefa17de6..cb5a0112b 100644
--- a/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/BlocklistActivity.java
@@ -14,61 +14,61 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class BlocklistActivity extends AbstractSearchableListItemActivity implements OnUpdateBlocklist {
- private Account account = null;
+ private Account account = null;
- @Override
- public void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(final AdapterView<?> parent,
- final View view,
- final int position,
- final long id) {
- BlockContactDialog.show(parent.getContext(), xmppConnectionService,(Contact) getListItems().get(position));
- return true;
- }
- });
- }
+ @Override
+ public boolean onItemLongClick(final AdapterView<?> parent,
+ final View view,
+ final int position,
+ final long id) {
+ BlockContactDialog.show(parent.getContext(), xmppConnectionService, (Contact) getListItems().get(position));
+ return true;
+ }
+ });
+ }
- @Override
- public void onBackendConnected() {
- for (final Account account : xmppConnectionService.getAccounts()) {
- if (account.getJid().toString().equals(getIntent().getStringExtra(EXTRA_ACCOUNT))) {
- this.account = account;
- break;
- }
- }
- filterContacts();
- }
+ @Override
+ public void onBackendConnected() {
+ for (final Account account : xmppConnectionService.getAccounts()) {
+ if (account.getJid().toString().equals(getIntent().getStringExtra(EXTRA_ACCOUNT))) {
+ this.account = account;
+ break;
+ }
+ }
+ filterContacts();
+ }
- @Override
- protected void filterContacts(final String needle) {
- getListItems().clear();
- if (account != null) {
- for (final Jid jid : account.getBlocklist()) {
- final Contact contact = account.getRoster().getContact(jid);
- if (contact.match(this, needle) && contact.isBlocked()) {
- getListItems().add(contact);
- }
- }
- Collections.sort(getListItems());
- }
- getListItemAdapter().notifyDataSetChanged();
- }
+ @Override
+ protected void filterContacts(final String needle) {
+ getListItems().clear();
+ if (account != null) {
+ for (final Jid jid : account.getBlocklist()) {
+ final Contact contact = account.getRoster().getContact(jid);
+ if (contact.match(this, needle) && contact.isBlocked()) {
+ getListItems().add(contact);
+ }
+ }
+ Collections.sort(getListItems());
+ }
+ getListItemAdapter().notifyDataSetChanged();
+ }
- protected void refreshUiReal() {
- final Editable editable = getSearchEditText().getText();
- if (editable != null) {
- filterContacts(editable.toString());
- } else {
- filterContacts();
- }
- }
+ protected void refreshUiReal() {
+ final Editable editable = getSearchEditText().getText();
+ if (editable != null) {
+ filterContacts(editable.toString());
+ } else {
+ filterContacts();
+ }
+ }
- @Override
- public void OnUpdateBlocklist(final OnUpdateBlocklist.Status status) {
- refreshUi();
- }
+ @Override
+ public void OnUpdateBlocklist(final OnUpdateBlocklist.Status status) {
+ refreshUi();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java b/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java
index 361caf182..ddbbbca36 100644
--- a/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ChangePasswordActivity.java
@@ -14,109 +14,109 @@ import de.pixart.messenger.services.XmppConnectionService;
public class ChangePasswordActivity extends XmppActivity implements XmppConnectionService.OnAccountPasswordChanged {
- private Button mChangePasswordButton;
- private View.OnClickListener mOnChangePasswordButtonClicked = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mAccount != null) {
- final String currentPassword = mCurrentPassword.getText().toString();
- final String newPassword = mNewPassword.getText().toString();
- final String newPasswordConfirm = mNewPasswordConfirm.getText().toString();
- if (!mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && !currentPassword.equals(mAccount.getPassword())) {
- mCurrentPassword.requestFocus();
- mCurrentPassword.setError(getString(R.string.account_status_unauthorized));
- } else if (!newPassword.equals(newPasswordConfirm)) {
- mNewPasswordConfirm.requestFocus();
- mNewPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
- } else if (newPassword.trim().isEmpty()) {
- mNewPassword.requestFocus();
- mNewPassword.setError(getString(R.string.password_should_not_be_empty));
- } else {
- mCurrentPassword.setError(null);
- mNewPassword.setError(null);
- mNewPasswordConfirm.setError(null);
- xmppConnectionService.updateAccountPasswordOnServer(mAccount, newPassword, ChangePasswordActivity.this);
- mChangePasswordButton.setEnabled(false);
- mChangePasswordButton.setTextColor(getSecondaryTextColor());
- mChangePasswordButton.setText(R.string.updating);
- }
- }
- }
- };
- private TextView mCurrentPasswordLabel;
- private EditText mCurrentPassword;
- private EditText mNewPassword;
- private EditText mNewPasswordConfirm;
- private Account mAccount;
+ private Button mChangePasswordButton;
+ private View.OnClickListener mOnChangePasswordButtonClicked = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mAccount != null) {
+ final String currentPassword = mCurrentPassword.getText().toString();
+ final String newPassword = mNewPassword.getText().toString();
+ final String newPasswordConfirm = mNewPasswordConfirm.getText().toString();
+ if (!mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && !currentPassword.equals(mAccount.getPassword())) {
+ mCurrentPassword.requestFocus();
+ mCurrentPassword.setError(getString(R.string.account_status_unauthorized));
+ } else if (!newPassword.equals(newPasswordConfirm)) {
+ mNewPasswordConfirm.requestFocus();
+ mNewPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
+ } else if (newPassword.trim().isEmpty()) {
+ mNewPassword.requestFocus();
+ mNewPassword.setError(getString(R.string.password_should_not_be_empty));
+ } else {
+ mCurrentPassword.setError(null);
+ mNewPassword.setError(null);
+ mNewPasswordConfirm.setError(null);
+ xmppConnectionService.updateAccountPasswordOnServer(mAccount, newPassword, ChangePasswordActivity.this);
+ mChangePasswordButton.setEnabled(false);
+ mChangePasswordButton.setTextColor(getSecondaryTextColor());
+ mChangePasswordButton.setText(R.string.updating);
+ }
+ }
+ }
+ };
+ private TextView mCurrentPasswordLabel;
+ private EditText mCurrentPassword;
+ private EditText mNewPassword;
+ private EditText mNewPasswordConfirm;
+ private Account mAccount;
- @Override
- void onBackendConnected() {
- this.mAccount = extractAccount(getIntent());
- if (this.mAccount != null && this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
- this.mCurrentPasswordLabel.setVisibility(View.GONE);
- this.mCurrentPassword.setVisibility(View.GONE);
- } else {
- this.mCurrentPasswordLabel.setVisibility(View.VISIBLE);
- this.mCurrentPassword.setVisibility(View.VISIBLE);
- }
- }
+ @Override
+ void onBackendConnected() {
+ this.mAccount = extractAccount(getIntent());
+ if (this.mAccount != null && this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
+ this.mCurrentPasswordLabel.setVisibility(View.GONE);
+ this.mCurrentPassword.setVisibility(View.GONE);
+ } else {
+ this.mCurrentPasswordLabel.setVisibility(View.VISIBLE);
+ this.mCurrentPassword.setVisibility(View.VISIBLE);
+ }
+ }
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_change_password);
- Button mCancelButton = (Button) findViewById(R.id.left_button);
- mCancelButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- finish();
- }
- });
- this.mChangePasswordButton = (Button) findViewById(R.id.right_button);
- this.mChangePasswordButton.setOnClickListener(this.mOnChangePasswordButtonClicked);
- this.mCurrentPasswordLabel = (TextView) findViewById(R.id.current_password_label);
- this.mCurrentPassword = (EditText) findViewById(R.id.current_password);
- this.mNewPassword = (EditText) findViewById(R.id.new_password);
- this.mNewPasswordConfirm = (EditText) findViewById(R.id.new_password_confirm);
- }
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_change_password);
+ Button mCancelButton = (Button) findViewById(R.id.left_button);
+ mCancelButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ finish();
+ }
+ });
+ this.mChangePasswordButton = (Button) findViewById(R.id.right_button);
+ this.mChangePasswordButton.setOnClickListener(this.mOnChangePasswordButtonClicked);
+ this.mCurrentPasswordLabel = (TextView) findViewById(R.id.current_password_label);
+ this.mCurrentPassword = (EditText) findViewById(R.id.current_password);
+ this.mNewPassword = (EditText) findViewById(R.id.new_password);
+ this.mNewPasswordConfirm = (EditText) findViewById(R.id.new_password_confirm);
+ }
- @Override
- protected void onStart() {
- super.onStart();
- Intent intent = getIntent();
- String password = intent != null ? intent.getStringExtra("password") : null;
- if (password != null) {
- this.mNewPassword.getEditableText().clear();
- this.mNewPassword.getEditableText().append(password);
- }
- }
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Intent intent = getIntent();
+ String password = intent != null ? intent.getStringExtra("password") : null;
+ if (password != null) {
+ this.mNewPassword.getEditableText().clear();
+ this.mNewPassword.getEditableText().append(password);
+ }
+ }
- @Override
- public void onPasswordChangeSucceeded() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ChangePasswordActivity.this,R.string.password_changed,Toast.LENGTH_LONG).show();
- finish();
- }
- });
- }
+ @Override
+ public void onPasswordChangeSucceeded() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ChangePasswordActivity.this, R.string.password_changed, Toast.LENGTH_LONG).show();
+ finish();
+ }
+ });
+ }
- @Override
- public void onPasswordChangeFailed() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mNewPassword.setError(getString(R.string.could_not_change_password));
- mChangePasswordButton.setEnabled(true);
- mChangePasswordButton.setTextColor(getPrimaryTextColor());
- mChangePasswordButton.setText(R.string.change_password);
- }
- });
+ @Override
+ public void onPasswordChangeFailed() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mNewPassword.setError(getString(R.string.could_not_change_password));
+ mChangePasswordButton.setEnabled(true);
+ mChangePasswordButton.setTextColor(getPrimaryTextColor());
+ mChangePasswordButton.setText(R.string.change_password);
+ }
+ });
- }
+ }
- public void refreshUiReal() {
+ public void refreshUiReal() {
- }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java b/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java
index e38433893..b1dd48a5f 100644
--- a/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ChooseContactActivity.java
@@ -29,218 +29,218 @@ import de.pixart.messenger.entities.ListItem;
import de.pixart.messenger.xmpp.jid.Jid;
public class ChooseContactActivity extends AbstractSearchableListItemActivity {
- private List<String> mActivatedAccounts = new ArrayList<String>();
- private List<String> mKnownHosts;
-
- private Set<Contact> selected;
- private Set<String> filterContacts;
- public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
-
- @Override
- public void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- filterContacts = new HashSet<>();
- String[] contacts = getIntent().getStringArrayExtra("filter_contacts");
- if (contacts != null) {
- Collections.addAll(filterContacts, contacts);
- }
-
- if (getIntent().getBooleanExtra("multiple", false)) {
- getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
- getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(),
- InputMethodManager.HIDE_IMPLICIT_ONLY);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.select_multiple, menu);
- selected = new HashSet<Contact>();
- return true;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- switch(item.getItemId()) {
- case R.id.selection_submit:
- final Intent request = getIntent();
- final Intent data = new Intent();
- data.putExtra("conversation",
- request.getStringExtra("conversation"));
- String[] selection = getSelectedContactJids();
- data.putExtra("contacts", selection);
- data.putExtra("multiple", true);
- data.putExtra(EXTRA_ACCOUNT,request.getStringExtra(EXTRA_ACCOUNT));
- data.putExtra("subject", request.getStringExtra("subject"));
- setResult(RESULT_OK, data);
- finish();
- return true;
- }
- return false;
- }
-
- @Override
- public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
- Contact item = (Contact) getListItems().get(position);
- if (checked) {
- selected.add(item);
- } else {
- selected.remove(item);
- }
- int numSelected = selected.size();
- MenuItem selectButton = mode.getMenu().findItem(R.id.selection_submit);
- String buttonText = getResources().getQuantityString(R.plurals.select_contact,
- numSelected, numSelected);
- selectButton.setTitle(buttonText);
- }
- });
- }
-
- getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
-
- @Override
- public void onItemClick(final AdapterView<?> parent, final View view,
- final int position, final long id) {
- final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(),
- InputMethodManager.HIDE_IMPLICIT_ONLY);
- final Intent request = getIntent();
- final Intent data = new Intent();
- final ListItem mListItem = getListItems().get(position);
- data.putExtra("contact", mListItem.getJid().toString());
- String account = request.getStringExtra(EXTRA_ACCOUNT);
- if (account == null && mListItem instanceof Contact) {
- account = ((Contact) mListItem).getAccount().getJid().toBareJid().toString();
- }
- data.putExtra(EXTRA_ACCOUNT, account);
- data.putExtra("conversation",
- request.getStringExtra("conversation"));
- data.putExtra("multiple", false);
- data.putExtra("subject", request.getStringExtra("subject"));
- setResult(RESULT_OK, data);
- finish();
- }
- });
-
- }
-
- @Override
- public void onStart() {
- super.onStart();
- Intent intent = getIntent();
- @StringRes
- int res = intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID,R.string.title_activity_choose_contact) : R.string.title_activity_choose_contact;
- ActionBar bar = getActionBar();
- if (bar != null) {
- try {
- bar.setTitle(res);
- } catch (Exception e) {
- bar.setTitle(R.string.title_activity_choose_contact);
- }
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- super.onCreateOptionsMenu(menu);
- final Intent i = getIntent();
- boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
- menu.findItem(R.id.action_create_contact).setVisible(showEnterJid);
- return true;
- }
-
- protected void filterContacts(final String needle) {
- getListItems().clear();
- for (final Account account : xmppConnectionService.getAccounts()) {
- if (account.getStatus() != Account.State.DISABLED) {
- for (final Contact contact : account.getRoster().getContacts()) {
- if (contact.showInRoster() &&
- !filterContacts.contains(contact.getJid().toBareJid().toString())
- && contact.match(this, needle)) {
- getListItems().add(contact);
- }
- }
- }
- }
- Collections.sort(getListItems());
- getListItemAdapter().notifyDataSetChanged();
- }
-
- private String[] getSelectedContactJids() {
- List<String> result = new ArrayList<>();
- for (Contact contact : selected) {
- result.add(contact.getJid().toString());
- }
- return result.toArray(new String[result.size()]);
- }
-
-
- public void refreshUiReal() {
- //nothing to do. This Activity doesn't implement any listeners
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_create_contact:
- showEnterJidDialog();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- protected void showEnterJidDialog() {
- EnterJidDialog dialog = new EnterJidDialog(
- this, mKnownHosts, mActivatedAccounts,
- getString(R.string.enter_contact), getString(R.string.select),
- null, getIntent().getStringExtra(EXTRA_ACCOUNT), true
- );
-
- dialog.setOnEnterJidDialogPositiveListener(new EnterJidDialog.OnEnterJidDialogPositiveListener() {
- @Override
- public boolean onEnterJidDialogPositive(Jid accountJid, Jid contactJid) throws EnterJidDialog.JidError {
- final Intent request = getIntent();
- final Intent data = new Intent();
- data.putExtra("contact", contactJid.toString());
- data.putExtra(EXTRA_ACCOUNT, accountJid.toString());
- data.putExtra("conversation",
- request.getStringExtra("conversation"));
- data.putExtra("multiple", false);
- data.putExtra("subject", request.getStringExtra("subject"));
- setResult(RESULT_OK, data);
- finish();
-
- return true;
- }
- });
-
- dialog.show();
- }
-
- @Override
- void onBackendConnected() {
- filterContacts();
-
- this.mActivatedAccounts.clear();
- for (Account account : xmppConnectionService.getAccounts()) {
- if (account.getStatus() != Account.State.DISABLED) {
- if (Config.DOMAIN_LOCK != null) {
- this.mActivatedAccounts.add(account.getJid().getLocalpart());
- } else {
- this.mActivatedAccounts.add(account.getJid().toBareJid().toString());
- }
- }
- }
- this.mKnownHosts = xmppConnectionService.getKnownHosts();
- }
+ private List<String> mActivatedAccounts = new ArrayList<String>();
+ private List<String> mKnownHosts;
+
+ private Set<Contact> selected;
+ private Set<String> filterContacts;
+ public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ filterContacts = new HashSet<>();
+ String[] contacts = getIntent().getStringArrayExtra("filter_contacts");
+ if (contacts != null) {
+ Collections.addAll(filterContacts, contacts);
+ }
+
+ if (getIntent().getBooleanExtra("multiple", false)) {
+ getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+ getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(),
+ InputMethodManager.HIDE_IMPLICIT_ONLY);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.select_multiple, menu);
+ selected = new HashSet<Contact>();
+ return true;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.selection_submit:
+ final Intent request = getIntent();
+ final Intent data = new Intent();
+ data.putExtra("conversation",
+ request.getStringExtra("conversation"));
+ String[] selection = getSelectedContactJids();
+ data.putExtra("contacts", selection);
+ data.putExtra("multiple", true);
+ data.putExtra(EXTRA_ACCOUNT, request.getStringExtra(EXTRA_ACCOUNT));
+ data.putExtra("subject", request.getStringExtra("subject"));
+ setResult(RESULT_OK, data);
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
+ Contact item = (Contact) getListItems().get(position);
+ if (checked) {
+ selected.add(item);
+ } else {
+ selected.remove(item);
+ }
+ int numSelected = selected.size();
+ MenuItem selectButton = mode.getMenu().findItem(R.id.selection_submit);
+ String buttonText = getResources().getQuantityString(R.plurals.select_contact,
+ numSelected, numSelected);
+ selectButton.setTitle(buttonText);
+ }
+ });
+ }
+
+ getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+ @Override
+ public void onItemClick(final AdapterView<?> parent, final View view,
+ final int position, final long id) {
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(),
+ InputMethodManager.HIDE_IMPLICIT_ONLY);
+ final Intent request = getIntent();
+ final Intent data = new Intent();
+ final ListItem mListItem = getListItems().get(position);
+ data.putExtra("contact", mListItem.getJid().toString());
+ String account = request.getStringExtra(EXTRA_ACCOUNT);
+ if (account == null && mListItem instanceof Contact) {
+ account = ((Contact) mListItem).getAccount().getJid().toBareJid().toString();
+ }
+ data.putExtra(EXTRA_ACCOUNT, account);
+ data.putExtra("conversation",
+ request.getStringExtra("conversation"));
+ data.putExtra("multiple", false);
+ data.putExtra("subject", request.getStringExtra("subject"));
+ setResult(RESULT_OK, data);
+ finish();
+ }
+ });
+
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Intent intent = getIntent();
+ @StringRes
+ int res = intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID, R.string.title_activity_choose_contact) : R.string.title_activity_choose_contact;
+ ActionBar bar = getActionBar();
+ if (bar != null) {
+ try {
+ bar.setTitle(res);
+ } catch (Exception e) {
+ bar.setTitle(R.string.title_activity_choose_contact);
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ final Intent i = getIntent();
+ boolean showEnterJid = i != null && i.getBooleanExtra("show_enter_jid", false);
+ menu.findItem(R.id.action_create_contact).setVisible(showEnterJid);
+ return true;
+ }
+
+ protected void filterContacts(final String needle) {
+ getListItems().clear();
+ for (final Account account : xmppConnectionService.getAccounts()) {
+ if (account.getStatus() != Account.State.DISABLED) {
+ for (final Contact contact : account.getRoster().getContacts()) {
+ if (contact.showInRoster() &&
+ !filterContacts.contains(contact.getJid().toBareJid().toString())
+ && contact.match(this, needle)) {
+ getListItems().add(contact);
+ }
+ }
+ }
+ }
+ Collections.sort(getListItems());
+ getListItemAdapter().notifyDataSetChanged();
+ }
+
+ private String[] getSelectedContactJids() {
+ List<String> result = new ArrayList<>();
+ for (Contact contact : selected) {
+ result.add(contact.getJid().toString());
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+
+ public void refreshUiReal() {
+ //nothing to do. This Activity doesn't implement any listeners
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_create_contact:
+ showEnterJidDialog();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ protected void showEnterJidDialog() {
+ EnterJidDialog dialog = new EnterJidDialog(
+ this, mKnownHosts, mActivatedAccounts,
+ getString(R.string.enter_contact), getString(R.string.select),
+ null, getIntent().getStringExtra(EXTRA_ACCOUNT), true
+ );
+
+ dialog.setOnEnterJidDialogPositiveListener(new EnterJidDialog.OnEnterJidDialogPositiveListener() {
+ @Override
+ public boolean onEnterJidDialogPositive(Jid accountJid, Jid contactJid) throws EnterJidDialog.JidError {
+ final Intent request = getIntent();
+ final Intent data = new Intent();
+ data.putExtra("contact", contactJid.toString());
+ data.putExtra(EXTRA_ACCOUNT, accountJid.toString());
+ data.putExtra("conversation",
+ request.getStringExtra("conversation"));
+ data.putExtra("multiple", false);
+ data.putExtra("subject", request.getStringExtra("subject"));
+ setResult(RESULT_OK, data);
+ finish();
+
+ return true;
+ }
+ });
+
+ dialog.show();
+ }
+
+ @Override
+ void onBackendConnected() {
+ filterContacts();
+
+ this.mActivatedAccounts.clear();
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.getStatus() != Account.State.DISABLED) {
+ if (Config.DOMAIN_LOCK != null) {
+ this.mActivatedAccounts.add(account.getJid().getLocalpart());
+ } else {
+ this.mActivatedAccounts.add(account.getJid().toBareJid().toString());
+ }
+ }
+ }
+ this.mKnownHosts = xmppConnectionService.getKnownHosts();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
index d91c6e192..672dec4a1 100644
--- a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
@@ -41,232 +41,232 @@ import de.pixart.messenger.services.XmppConnectionService.OnMucRosterUpdate;
import de.pixart.messenger.xmpp.jid.Jid;
public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConferenceOptionsPushed {
- public static final String ACTION_VIEW_MUC = "view_muc";
- private Conversation mConversation;
- private OnClickListener inviteListener = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- inviteToConversation(mConversation);
- }
- };
- private TextView mYourNick;
- private ImageView mYourPhoto;
- private ImageButton mEditNickButton;
- private TextView mRoleAffiliaton;
- private TextView mFullJid;
- private TextView mAccountJid;
- private LinearLayout membersView;
- private LinearLayout mMoreDetails;
- private TextView mConferenceType;
- private LinearLayout mConferenceInfoTable;
- private TextView mConferenceInfoMam;
- private TextView mNotifyStatusText;
- private ImageButton mChangeConferenceSettingsButton;
- private ImageButton mNotifyStatusButton;
- private Button mInviteButton;
- private String uuid = null;
- private User mSelectedUser = null;
-
- private boolean mAdvancedMode = false;
-
- private UiCallback<Conversation> renameCallback = new UiCallback<Conversation>() {
- @Override
- public void success(Conversation object) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ConferenceDetailsActivity.this,getString(R.string.your_nick_has_been_changed),Toast.LENGTH_SHORT).show();
- updateView();
- }
- });
-
- }
-
- @Override
- public void error(final int errorCode, Conversation object) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ConferenceDetailsActivity.this,getString(errorCode),Toast.LENGTH_SHORT).show();
- }
- });
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Conversation object) {
-
- }
- };
-
- private OnClickListener mNotifyStatusClickListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
- builder.setTitle(R.string.pref_notification_settings);
- String[] choices = {
- getString(R.string.notify_on_all_messages),
- getString(R.string.notify_only_when_highlighted),
- getString(R.string.notify_never)
- };
- final AtomicInteger choice;
- if (mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL,0) == Long.MAX_VALUE) {
- choice = new AtomicInteger(2);
- } else {
- choice = new AtomicInteger(mConversation.alwaysNotify() ? 0 : 1);
- }
- builder.setSingleChoiceItems(choices, choice.get(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- choice.set(which);
- }
- });
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (choice.get() == 2) {
- mConversation.setMutedTill(Long.MAX_VALUE);
- } else {
- mConversation.setMutedTill(0);
- mConversation.setAttribute(Conversation.ATTRIBUTE_ALWAYS_NOTIFY,String.valueOf(choice.get() == 0));
- }
- xmppConnectionService.updateConversation(mConversation);
- updateView();
- }
- });
- builder.create().show();
- }
- };
-
- private OnClickListener mChangeConferenceSettings = new OnClickListener() {
- @Override
- public void onClick(View v) {
- final MucOptions mucOptions = mConversation.getMucOptions();
- AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
- builder.setTitle(R.string.conference_options);
- final String[] options;
- final boolean[] values;
- if (mAdvancedMode) {
- options = new String[]{
- getString(R.string.members_only),
- getString(R.string.moderated),
- getString(R.string.non_anonymous)
- };
- values = new boolean[]{
- mucOptions.membersOnly(),
- mucOptions.moderated(),
- mucOptions.nonanonymous()
- };
- } else {
- options = new String[]{
- getString(R.string.members_only),
- getString(R.string.non_anonymous)
- };
- values = new boolean[]{
- mucOptions.membersOnly(),
- mucOptions.nonanonymous()
- };
- }
- builder.setMultiChoiceItems(options,values,new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- values[which] = isChecked;
- }
- });
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.confirm,new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (!mucOptions.membersOnly() && values[0]) {
- xmppConnectionService.changeAffiliationsInConference(mConversation,
- MucOptions.Affiliation.NONE,
- MucOptions.Affiliation.MEMBER);
- }
- Bundle options = new Bundle();
- options.putString("muc#roomconfig_membersonly", values[0] ? "1" : "0");
- if (values.length == 2) {
- options.putString("muc#roomconfig_whois", values[1] ? "anyone" : "moderators");
- } else if (values.length == 3) {
- options.putString("muc#roomconfig_moderatedroom", values[1] ? "1" : "0");
- options.putString("muc#roomconfig_whois", values[2] ? "anyone" : "moderators");
- }
- options.putString("muc#roomconfig_persistentroom", "1");
- xmppConnectionService.pushConferenceConfiguration(mConversation,
- options,
- ConferenceDetailsActivity.this);
- }
- });
- builder.create().show();
- }
- };
- private OnValueEdited onSubjectEdited = new OnValueEdited() {
-
- @Override
- public void onValueEdited(String value) {
- xmppConnectionService.pushSubjectToConference(mConversation,value);
- }
- };
-
- @Override
- public void onConversationUpdate() {
- refreshUi();
- }
-
- @Override
- public void onMucRosterUpdate() {
- refreshUi();
- }
-
- @Override
- protected void refreshUiReal() {
- updateView();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_muc_details);
- mYourNick = (TextView) findViewById(R.id.muc_your_nick);
- mYourPhoto = (ImageView) findViewById(R.id.your_photo);
- mEditNickButton = (ImageButton) findViewById(R.id.edit_nick_button);
- mFullJid = (TextView) findViewById(R.id.muc_jabberid);
- membersView = (LinearLayout) findViewById(R.id.muc_members);
- mAccountJid = (TextView) findViewById(R.id.details_account);
- mMoreDetails = (LinearLayout) findViewById(R.id.muc_more_details);
- mMoreDetails.setVisibility(View.GONE);
- mChangeConferenceSettingsButton = (ImageButton) findViewById(R.id.change_conference_button);
- mChangeConferenceSettingsButton.setOnClickListener(this.mChangeConferenceSettings);
- mInviteButton = (Button) findViewById(R.id.invite);
- mInviteButton.setOnClickListener(inviteListener);
- mConferenceType = (TextView) findViewById(R.id.muc_conference_type);
- if (getActionBar() != null) {
- getActionBar().setHomeButtonEnabled(true);
- getActionBar().setDisplayHomeAsUpEnabled(true);
- }
- mEditNickButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- quickEdit(mConversation.getMucOptions().getActualNick(),
- 0,
- new OnValueEdited() {
-
- @Override
- public void onValueEdited(String value) {
- xmppConnectionService.renameInMuc(mConversation,value,renameCallback);
- }
- });
- }
- });
- this.mAdvancedMode = getPreferences().getBoolean("advanced_muc_mode", false);
- this.mConferenceInfoTable = (LinearLayout) findViewById(R.id.muc_info_more);
- mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
- this.mConferenceInfoMam = (TextView) findViewById(R.id.muc_info_mam);
- this.mNotifyStatusButton = (ImageButton) findViewById(R.id.notification_status_button);
- this.mNotifyStatusButton.setOnClickListener(this.mNotifyStatusClickListener);
- this.mNotifyStatusText = (TextView) findViewById(R.id.notification_status_text);
- }
+ public static final String ACTION_VIEW_MUC = "view_muc";
+ private Conversation mConversation;
+ private OnClickListener inviteListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ inviteToConversation(mConversation);
+ }
+ };
+ private TextView mYourNick;
+ private ImageView mYourPhoto;
+ private ImageButton mEditNickButton;
+ private TextView mRoleAffiliaton;
+ private TextView mFullJid;
+ private TextView mAccountJid;
+ private LinearLayout membersView;
+ private LinearLayout mMoreDetails;
+ private TextView mConferenceType;
+ private LinearLayout mConferenceInfoTable;
+ private TextView mConferenceInfoMam;
+ private TextView mNotifyStatusText;
+ private ImageButton mChangeConferenceSettingsButton;
+ private ImageButton mNotifyStatusButton;
+ private Button mInviteButton;
+ private String uuid = null;
+ private User mSelectedUser = null;
+
+ private boolean mAdvancedMode = false;
+
+ private UiCallback<Conversation> renameCallback = new UiCallback<Conversation>() {
+ @Override
+ public void success(Conversation object) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ConferenceDetailsActivity.this, getString(R.string.your_nick_has_been_changed), Toast.LENGTH_SHORT).show();
+ updateView();
+ }
+ });
+
+ }
+
+ @Override
+ public void error(final int errorCode, Conversation object) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ConferenceDetailsActivity.this, getString(errorCode), Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Conversation object) {
+
+ }
+ };
+
+ private OnClickListener mNotifyStatusClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
+ builder.setTitle(R.string.pref_notification_settings);
+ String[] choices = {
+ getString(R.string.notify_on_all_messages),
+ getString(R.string.notify_only_when_highlighted),
+ getString(R.string.notify_never)
+ };
+ final AtomicInteger choice;
+ if (mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0) == Long.MAX_VALUE) {
+ choice = new AtomicInteger(2);
+ } else {
+ choice = new AtomicInteger(mConversation.alwaysNotify() ? 0 : 1);
+ }
+ builder.setSingleChoiceItems(choices, choice.get(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ choice.set(which);
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (choice.get() == 2) {
+ mConversation.setMutedTill(Long.MAX_VALUE);
+ } else {
+ mConversation.setMutedTill(0);
+ mConversation.setAttribute(Conversation.ATTRIBUTE_ALWAYS_NOTIFY, String.valueOf(choice.get() == 0));
+ }
+ xmppConnectionService.updateConversation(mConversation);
+ updateView();
+ }
+ });
+ builder.create().show();
+ }
+ };
+
+ private OnClickListener mChangeConferenceSettings = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final MucOptions mucOptions = mConversation.getMucOptions();
+ AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this);
+ builder.setTitle(R.string.conference_options);
+ final String[] options;
+ final boolean[] values;
+ if (mAdvancedMode) {
+ options = new String[]{
+ getString(R.string.members_only),
+ getString(R.string.moderated),
+ getString(R.string.non_anonymous)
+ };
+ values = new boolean[]{
+ mucOptions.membersOnly(),
+ mucOptions.moderated(),
+ mucOptions.nonanonymous()
+ };
+ } else {
+ options = new String[]{
+ getString(R.string.members_only),
+ getString(R.string.non_anonymous)
+ };
+ values = new boolean[]{
+ mucOptions.membersOnly(),
+ mucOptions.nonanonymous()
+ };
+ }
+ builder.setMultiChoiceItems(options, values, new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ values[which] = isChecked;
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (!mucOptions.membersOnly() && values[0]) {
+ xmppConnectionService.changeAffiliationsInConference(mConversation,
+ MucOptions.Affiliation.NONE,
+ MucOptions.Affiliation.MEMBER);
+ }
+ Bundle options = new Bundle();
+ options.putString("muc#roomconfig_membersonly", values[0] ? "1" : "0");
+ if (values.length == 2) {
+ options.putString("muc#roomconfig_whois", values[1] ? "anyone" : "moderators");
+ } else if (values.length == 3) {
+ options.putString("muc#roomconfig_moderatedroom", values[1] ? "1" : "0");
+ options.putString("muc#roomconfig_whois", values[2] ? "anyone" : "moderators");
+ }
+ options.putString("muc#roomconfig_persistentroom", "1");
+ xmppConnectionService.pushConferenceConfiguration(mConversation,
+ options,
+ ConferenceDetailsActivity.this);
+ }
+ });
+ builder.create().show();
+ }
+ };
+ private OnValueEdited onSubjectEdited = new OnValueEdited() {
+
+ @Override
+ public void onValueEdited(String value) {
+ xmppConnectionService.pushSubjectToConference(mConversation, value);
+ }
+ };
+
+ @Override
+ public void onConversationUpdate() {
+ refreshUi();
+ }
+
+ @Override
+ public void onMucRosterUpdate() {
+ refreshUi();
+ }
+
+ @Override
+ protected void refreshUiReal() {
+ updateView();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_muc_details);
+ mYourNick = (TextView) findViewById(R.id.muc_your_nick);
+ mYourPhoto = (ImageView) findViewById(R.id.your_photo);
+ mEditNickButton = (ImageButton) findViewById(R.id.edit_nick_button);
+ mFullJid = (TextView) findViewById(R.id.muc_jabberid);
+ membersView = (LinearLayout) findViewById(R.id.muc_members);
+ mAccountJid = (TextView) findViewById(R.id.details_account);
+ mMoreDetails = (LinearLayout) findViewById(R.id.muc_more_details);
+ mMoreDetails.setVisibility(View.GONE);
+ mChangeConferenceSettingsButton = (ImageButton) findViewById(R.id.change_conference_button);
+ mChangeConferenceSettingsButton.setOnClickListener(this.mChangeConferenceSettings);
+ mInviteButton = (Button) findViewById(R.id.invite);
+ mInviteButton.setOnClickListener(inviteListener);
+ mConferenceType = (TextView) findViewById(R.id.muc_conference_type);
+ if (getActionBar() != null) {
+ getActionBar().setHomeButtonEnabled(true);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ mEditNickButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ quickEdit(mConversation.getMucOptions().getActualNick(),
+ 0,
+ new OnValueEdited() {
+
+ @Override
+ public void onValueEdited(String value) {
+ xmppConnectionService.renameInMuc(mConversation, value, renameCallback);
+ }
+ });
+ }
+ });
+ this.mAdvancedMode = getPreferences().getBoolean("advanced_muc_mode", false);
+ this.mConferenceInfoTable = (LinearLayout) findViewById(R.id.muc_info_more);
+ mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
+ this.mConferenceInfoMam = (TextView) findViewById(R.id.muc_info_mam);
+ this.mNotifyStatusButton = (ImageButton) findViewById(R.id.notification_status_button);
+ this.mNotifyStatusButton.setOnClickListener(this.mNotifyStatusClickListener);
+ this.mNotifyStatusText = (TextView) findViewById(R.id.notification_status_text);
+ }
@Override
protected void onStart() {
@@ -277,245 +277,245 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
}
- @Override
- public boolean onOptionsItemSelected(MenuItem menuItem) {
- switch (menuItem.getItemId()) {
- case android.R.id.home:
- finish();
- break;
- case R.id.action_edit_subject:
- if (mConversation != null) {
- quickEdit(mConversation.getMucOptions().getSubject(),
- R.string.edit_subject_hint,
- this.onSubjectEdited);
- }
- break;
- case R.id.action_share:
- shareUri();
- break;
- case R.id.action_save_as_bookmark:
- saveAsBookmark();
- break;
- case R.id.action_delete_bookmark:
- deleteBookmark();
- break;
- case R.id.action_advanced_mode:
- this.mAdvancedMode = !menuItem.isChecked();
- menuItem.setChecked(this.mAdvancedMode);
- getPreferences().edit().putBoolean("advanced_muc_mode", mAdvancedMode).commit();
- mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
- invalidateOptionsMenu();
- updateView();
- break;
- }
- return super.onOptionsItemSelected(menuItem);
- }
-
- @Override
- protected String getShareableUri() {
- if (mConversation != null) {
- return "xmpp:" + mConversation.getJid().toBareJid().toString() + "?join";
- } else {
- return "";
- }
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
- MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
- MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode);
- MenuItem menuItemChangeSubject = menu.findItem(R.id.action_edit_subject);
- menuItemAdvancedMode.setChecked(mAdvancedMode);
- if (mConversation == null) {
- return true;
- }
- Account account = mConversation.getAccount();
- if (account.hasBookmarkFor(mConversation.getJid().toBareJid())) {
- menuItemSaveBookmark.setVisible(false);
- menuItemDeleteBookmark.setVisible(true);
- } else {
- menuItemDeleteBookmark.setVisible(false);
- menuItemSaveBookmark.setVisible(true);
- }
- menuItemChangeSubject.setVisible(mConversation.getMucOptions().canChangeSubject());
- return true;
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.muc_details, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- Object tag = v.getTag();
- if (tag instanceof User) {
- getMenuInflater().inflate(R.menu.muc_details_context,menu);
- final User user = (User) tag;
- final User self = mConversation.getMucOptions().getSelf();
- this.mSelectedUser = user;
- String name;
- final Contact contact = user.getContact();
- if (contact != null) {
- name = contact.getDisplayName();
- } else if (user.getRealJid() != null){
- name = user.getRealJid().toBareJid().toString();
- } else {
- name = user.getName();
- }
- menu.setHeaderTitle(name);
- if (user.getRealJid() != null) {
- MenuItem startConversation = menu.findItem(R.id.start_conversation);
- MenuItem giveMembership = menu.findItem(R.id.give_membership);
- MenuItem removeMembership = menu.findItem(R.id.remove_membership);
- MenuItem giveAdminPrivileges = menu.findItem(R.id.give_admin_privileges);
- MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
- MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
- MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
- MenuItem invite = menu.findItem(R.id.invite);
- startConversation.setVisible(true);
- if (user.getRole() == MucOptions.Role.NONE) {
- invite.setVisible(true);
- }
- if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) &&
- self.getAffiliation().outranks(user.getAffiliation())) {
- if (mAdvancedMode) {
- if (user.getAffiliation() == MucOptions.Affiliation.NONE) {
- giveMembership.setVisible(true);
- } else {
- removeMembership.setVisible(true);
- }
- banFromConference.setVisible(true);
- } else {
- removeFromRoom.setVisible(true);
- }
- if (user.getAffiliation() != MucOptions.Affiliation.ADMIN) {
- giveAdminPrivileges.setVisible(true);
- } else {
- removeAdminPrivileges.setVisible(true);
- }
- }
- } else {
- MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
- sendPrivateMessage.setVisible(user.getRole().ranks(MucOptions.Role.PARTICIPANT));
- }
-
- }
- super.onCreateContextMenu(menu, v, menuInfo);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- Jid jid = mSelectedUser.getRealJid();
- switch (item.getItemId()) {
- case R.id.start_conversation:
- startConversation(mSelectedUser);
- return true;
- case R.id.give_admin_privileges:
- xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.ADMIN,this);
- return true;
- case R.id.give_membership:
- xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER,this);
- return true;
- case R.id.remove_membership:
- xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.NONE,this);
- return true;
- case R.id.remove_admin_privileges:
- xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER,this);
- return true;
- case R.id.remove_from_room:
- removeFromRoom(mSelectedUser);
- return true;
- case R.id.ban_from_conference:
- xmppConnectionService.changeAffiliationInConference(mConversation,jid, MucOptions.Affiliation.OUTCAST,this);
- if (mSelectedUser.getRole() != MucOptions.Role.NONE) {
- xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, this);
- }
- return true;
- case R.id.send_private_message:
- privateMsgInMuc(mConversation,mSelectedUser.getName());
- return true;
- case R.id.invite:
- xmppConnectionService.directInvite(mConversation, jid);
- return true;
- default:
- return super.onContextItemSelected(item);
- }
- }
-
- private void removeFromRoom(final User user) {
- if (mConversation.getMucOptions().membersOnly()) {
- xmppConnectionService.changeAffiliationInConference(mConversation,user.getRealJid(), MucOptions.Affiliation.NONE,this);
- if (user.getRole() != MucOptions.Role.NONE) {
- xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
- }
- } else {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.ban_from_conference);
- builder.setMessage(getString(R.string.removing_from_public_conference,user.getName()));
- builder.setNegativeButton(R.string.cancel,null);
- builder.setPositiveButton(R.string.ban_now,new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- xmppConnectionService.changeAffiliationInConference(mConversation,user.getRealJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this);
- if (user.getRole() != MucOptions.Role.NONE) {
- xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
- }
- }
- });
- builder.create().show();
- }
- }
-
- protected void startConversation(User user) {
- if (user.getRealJid() != null) {
- Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getRealJid().toBareJid(),false);
- switchToConversation(conversation);
- }
- }
-
- protected void saveAsBookmark() {
- xmppConnectionService.saveConversationAsBookmark(mConversation,
- mConversation.getMucOptions().getSubject());
- }
-
- protected void deleteBookmark() {
- Account account = mConversation.getAccount();
- Bookmark bookmark = mConversation.getBookmark();
- bookmark.unregisterConversation();
- account.getBookmarks().remove(bookmark);
- xmppConnectionService.pushBookmarks(account);
- }
-
- @Override
- void onBackendConnected() {
- if (mPendingConferenceInvite != null) {
- mPendingConferenceInvite.execute(this);
- mPendingConferenceInvite = null;
- }
- if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
- this.uuid = getIntent().getExtras().getString("uuid");
- }
- if (uuid != null) {
- this.mConversation = xmppConnectionService
- .findConversationByUuid(uuid);
- if (this.mConversation != null) {
- updateView();
- }
- }
- }
-
- private void updateView() {
- final MucOptions mucOptions = mConversation.getMucOptions();
- final User self = mucOptions.getSelf();
- String account;
- if (Config.DOMAIN_LOCK != null) {
- account = mConversation.getAccount().getJid().getLocalpart();
- } else {
- account = mConversation.getAccount().getJid().toBareJid().toString();
- }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ switch (menuItem.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.action_edit_subject:
+ if (mConversation != null) {
+ quickEdit(mConversation.getMucOptions().getSubject(),
+ R.string.edit_subject_hint,
+ this.onSubjectEdited);
+ }
+ break;
+ case R.id.action_share:
+ shareUri();
+ break;
+ case R.id.action_save_as_bookmark:
+ saveAsBookmark();
+ break;
+ case R.id.action_delete_bookmark:
+ deleteBookmark();
+ break;
+ case R.id.action_advanced_mode:
+ this.mAdvancedMode = !menuItem.isChecked();
+ menuItem.setChecked(this.mAdvancedMode);
+ getPreferences().edit().putBoolean("advanced_muc_mode", mAdvancedMode).commit();
+ mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE);
+ invalidateOptionsMenu();
+ updateView();
+ break;
+ }
+ return super.onOptionsItemSelected(menuItem);
+ }
+
+ @Override
+ protected String getShareableUri() {
+ if (mConversation != null) {
+ return "xmpp:" + mConversation.getJid().toBareJid().toString() + "?join";
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
+ MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
+ MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode);
+ MenuItem menuItemChangeSubject = menu.findItem(R.id.action_edit_subject);
+ menuItemAdvancedMode.setChecked(mAdvancedMode);
+ if (mConversation == null) {
+ return true;
+ }
+ Account account = mConversation.getAccount();
+ if (account.hasBookmarkFor(mConversation.getJid().toBareJid())) {
+ menuItemSaveBookmark.setVisible(false);
+ menuItemDeleteBookmark.setVisible(true);
+ } else {
+ menuItemDeleteBookmark.setVisible(false);
+ menuItemSaveBookmark.setVisible(true);
+ }
+ menuItemChangeSubject.setVisible(mConversation.getMucOptions().canChangeSubject());
+ return true;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.muc_details, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ Object tag = v.getTag();
+ if (tag instanceof User) {
+ getMenuInflater().inflate(R.menu.muc_details_context, menu);
+ final User user = (User) tag;
+ final User self = mConversation.getMucOptions().getSelf();
+ this.mSelectedUser = user;
+ String name;
+ final Contact contact = user.getContact();
+ if (contact != null) {
+ name = contact.getDisplayName();
+ } else if (user.getRealJid() != null) {
+ name = user.getRealJid().toBareJid().toString();
+ } else {
+ name = user.getName();
+ }
+ menu.setHeaderTitle(name);
+ if (user.getRealJid() != null) {
+ MenuItem startConversation = menu.findItem(R.id.start_conversation);
+ MenuItem giveMembership = menu.findItem(R.id.give_membership);
+ MenuItem removeMembership = menu.findItem(R.id.remove_membership);
+ MenuItem giveAdminPrivileges = menu.findItem(R.id.give_admin_privileges);
+ MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
+ MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
+ MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
+ MenuItem invite = menu.findItem(R.id.invite);
+ startConversation.setVisible(true);
+ if (user.getRole() == MucOptions.Role.NONE) {
+ invite.setVisible(true);
+ }
+ if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) &&
+ self.getAffiliation().outranks(user.getAffiliation())) {
+ if (mAdvancedMode) {
+ if (user.getAffiliation() == MucOptions.Affiliation.NONE) {
+ giveMembership.setVisible(true);
+ } else {
+ removeMembership.setVisible(true);
+ }
+ banFromConference.setVisible(true);
+ } else {
+ removeFromRoom.setVisible(true);
+ }
+ if (user.getAffiliation() != MucOptions.Affiliation.ADMIN) {
+ giveAdminPrivileges.setVisible(true);
+ } else {
+ removeAdminPrivileges.setVisible(true);
+ }
+ }
+ } else {
+ MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
+ sendPrivateMessage.setVisible(user.getRole().ranks(MucOptions.Role.PARTICIPANT));
+ }
+
+ }
+ super.onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ Jid jid = mSelectedUser.getRealJid();
+ switch (item.getItemId()) {
+ case R.id.start_conversation:
+ startConversation(mSelectedUser);
+ return true;
+ case R.id.give_admin_privileges:
+ xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.ADMIN, this);
+ return true;
+ case R.id.give_membership:
+ xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER, this);
+ return true;
+ case R.id.remove_membership:
+ xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.NONE, this);
+ return true;
+ case R.id.remove_admin_privileges:
+ xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.MEMBER, this);
+ return true;
+ case R.id.remove_from_room:
+ removeFromRoom(mSelectedUser);
+ return true;
+ case R.id.ban_from_conference:
+ xmppConnectionService.changeAffiliationInConference(mConversation, jid, MucOptions.Affiliation.OUTCAST, this);
+ if (mSelectedUser.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, this);
+ }
+ return true;
+ case R.id.send_private_message:
+ privateMsgInMuc(mConversation, mSelectedUser.getName());
+ return true;
+ case R.id.invite:
+ xmppConnectionService.directInvite(mConversation, jid);
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ private void removeFromRoom(final User user) {
+ if (mConversation.getMucOptions().membersOnly()) {
+ xmppConnectionService.changeAffiliationInConference(mConversation, user.getRealJid(), MucOptions.Affiliation.NONE, this);
+ if (user.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
+ }
+ } else {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.ban_from_conference);
+ builder.setMessage(getString(R.string.removing_from_public_conference, user.getName()));
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.ban_now, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ xmppConnectionService.changeAffiliationInConference(mConversation, user.getRealJid(), MucOptions.Affiliation.OUTCAST, ConferenceDetailsActivity.this);
+ if (user.getRole() != MucOptions.Role.NONE) {
+ xmppConnectionService.changeRoleInConference(mConversation, mSelectedUser.getName(), MucOptions.Role.NONE, ConferenceDetailsActivity.this);
+ }
+ }
+ });
+ builder.create().show();
+ }
+ }
+
+ protected void startConversation(User user) {
+ if (user.getRealJid() != null) {
+ Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(), user.getRealJid().toBareJid(), false);
+ switchToConversation(conversation);
+ }
+ }
+
+ protected void saveAsBookmark() {
+ xmppConnectionService.saveConversationAsBookmark(mConversation,
+ mConversation.getMucOptions().getSubject());
+ }
+
+ protected void deleteBookmark() {
+ Account account = mConversation.getAccount();
+ Bookmark bookmark = mConversation.getBookmark();
+ bookmark.unregisterConversation();
+ account.getBookmarks().remove(bookmark);
+ xmppConnectionService.pushBookmarks(account);
+ }
+
+ @Override
+ void onBackendConnected() {
+ if (mPendingConferenceInvite != null) {
+ mPendingConferenceInvite.execute(this);
+ mPendingConferenceInvite = null;
+ }
+ if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
+ this.uuid = getIntent().getExtras().getString("uuid");
+ }
+ if (uuid != null) {
+ this.mConversation = xmppConnectionService
+ .findConversationByUuid(uuid);
+ if (this.mConversation != null) {
+ updateView();
+ }
+ }
+ }
+
+ private void updateView() {
+ final MucOptions mucOptions = mConversation.getMucOptions();
+ final User self = mucOptions.getSelf();
+ String account;
+ if (Config.DOMAIN_LOCK != null) {
+ account = mConversation.getAccount().getJid().getLocalpart();
+ } else {
+ account = mConversation.getAccount().getJid().toBareJid().toString();
+ }
if (getActionBar() != null) {
final ActionBar ab = getActionBar();
ab.setCustomView(R.layout.ab_title);
@@ -528,61 +528,61 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
absubtitle.setVisibility(View.GONE);
absubtitle.setClickable(false);
}
- mAccountJid.setText(getString(R.string.using_account, account));
- mYourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48)));
- setTitle(mConversation.getName());
- mFullJid.setText(mConversation.getJid().toBareJid().toString());
- mYourNick.setText(mucOptions.getActualNick());
- mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
- if (mucOptions.online()) {
- mMoreDetails.setVisibility(View.VISIBLE);
- final String status = getStatus(self);
- if (status != null) {
- mRoleAffiliaton.setVisibility(View.VISIBLE);
- mRoleAffiliaton.setText(status);
- } else {
- mRoleAffiliaton.setVisibility(View.GONE);
- }
- if (mucOptions.membersOnly()) {
- mConferenceType.setText(R.string.private_conference);
- } else {
- mConferenceType.setText(R.string.public_conference);
- }
- if (mucOptions.mamSupport()) {
- mConferenceInfoMam.setText(R.string.server_info_available);
- } else {
- mConferenceInfoMam.setText(R.string.server_info_unavailable);
- }
- if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
- mChangeConferenceSettingsButton.setVisibility(View.VISIBLE);
- } else {
- mChangeConferenceSettingsButton.setVisibility(View.GONE);
- }
- }
-
- long mutedTill = mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL,0);
- if (mutedTill == Long.MAX_VALUE) {
- mNotifyStatusText.setText(R.string.notify_never);
- mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_off_grey600_24dp);
- } else if (System.currentTimeMillis() < mutedTill) {
- mNotifyStatusText.setText(R.string.notify_paused);
- mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_paused_grey600_24dp);
- } else if (mConversation.alwaysNotify()) {
- mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_grey600_24dp);
- mNotifyStatusText.setText(R.string.notify_on_all_messages);
- } else {
- mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_none_grey600_24dp);
- mNotifyStatusText.setText(R.string.notify_only_when_highlighted);
- }
-
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- membersView.removeAllViews();
- final ArrayList<User> users = mucOptions.getUsers();
+ mAccountJid.setText(getString(R.string.using_account, account));
+ mYourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48)));
+ setTitle(mConversation.getName());
+ mFullJid.setText(mConversation.getJid().toBareJid().toString());
+ mYourNick.setText(mucOptions.getActualNick());
+ mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
+ if (mucOptions.online()) {
+ mMoreDetails.setVisibility(View.VISIBLE);
+ final String status = getStatus(self);
+ if (status != null) {
+ mRoleAffiliaton.setVisibility(View.VISIBLE);
+ mRoleAffiliaton.setText(status);
+ } else {
+ mRoleAffiliaton.setVisibility(View.GONE);
+ }
+ if (mucOptions.membersOnly()) {
+ mConferenceType.setText(R.string.private_conference);
+ } else {
+ mConferenceType.setText(R.string.public_conference);
+ }
+ if (mucOptions.mamSupport()) {
+ mConferenceInfoMam.setText(R.string.server_info_available);
+ } else {
+ mConferenceInfoMam.setText(R.string.server_info_unavailable);
+ }
+ if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
+ mChangeConferenceSettingsButton.setVisibility(View.VISIBLE);
+ } else {
+ mChangeConferenceSettingsButton.setVisibility(View.GONE);
+ }
+ }
+
+ long mutedTill = mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
+ if (mutedTill == Long.MAX_VALUE) {
+ mNotifyStatusText.setText(R.string.notify_never);
+ mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_off_grey600_24dp);
+ } else if (System.currentTimeMillis() < mutedTill) {
+ mNotifyStatusText.setText(R.string.notify_paused);
+ mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_paused_grey600_24dp);
+ } else if (mConversation.alwaysNotify()) {
+ mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_grey600_24dp);
+ mNotifyStatusText.setText(R.string.notify_on_all_messages);
+ } else {
+ mNotifyStatusButton.setImageResource(R.drawable.ic_notifications_none_grey600_24dp);
+ mNotifyStatusText.setText(R.string.notify_only_when_highlighted);
+ }
+
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ membersView.removeAllViews();
+ final ArrayList<User> users = mucOptions.getUsers();
Collections.sort(users);
- for (final User user : users) {
+ for (final User user : users) {
final Contact contact = user.getContact();
- View view = inflater.inflate(R.layout.contact, membersView,false);
- this.setListItemBackgroundOnView(view);
+ View view = inflater.inflate(R.layout.contact, membersView, false);
+ this.setListItemBackgroundOnView(view);
if (contact != null) {
view.setOnClickListener(new OnClickListener() {
@Override
@@ -591,105 +591,105 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
});
}
- registerForContextMenu(view);
- view.setTag(user);
- TextView tvDisplayName = (TextView) view.findViewById(R.id.contact_display_name);
- TextView tvKey = (TextView) view.findViewById(R.id.key);
- TextView tvStatus = (TextView) view.findViewById(R.id.contact_jid);
- if (mAdvancedMode && user.getPgpKeyId() != 0) {
- tvKey.setVisibility(View.VISIBLE);
- tvKey.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- viewPgpKey(user);
- }
- });
- tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
- }
- String name = user.getName();
- if (contact != null) {
- tvDisplayName.setText(contact.getDisplayName());
- tvStatus.setText((name != null ? name+ " \u2022 " : "") + getStatus(user));
- } else {
- tvDisplayName.setText(name == null ? "" : name);
- tvStatus.setText(getStatus(user));
-
- }
- ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
- iv.setImageBitmap(avatarService().get(user, getPixel(48), false));
- membersView.addView(view);
- if (mConversation.getMucOptions().canInvite()) {
- mInviteButton.setVisibility(View.VISIBLE);
- } else {
- mInviteButton.setVisibility(View.GONE);
- }
- }
- }
-
- private String getStatus(User user) {
- if (mAdvancedMode) {
- StringBuilder builder = new StringBuilder();
- builder.append(getString(user.getAffiliation().getResId()));
- builder.append(" (");
- builder.append(getString(user.getRole().getResId()));
- builder.append(')');
- return builder.toString();
- } else {
- return getString(user.getAffiliation().getResId());
- }
- }
-
- private void viewPgpKey(User user) {
- PgpEngine pgp = xmppConnectionService.getPgpEngine();
- if (pgp != null) {
- PendingIntent intent = pgp.getIntentForKey(user.getPgpKeyId());
- if (intent != null) {
- try {
- startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0);
- } catch (SendIntentException ignored) {
-
- }
- }
- }
- }
-
- @Override
- public void onAffiliationChangedSuccessful(Jid jid) {
- refreshUi();
- }
-
- @Override
- public void onAffiliationChangeFailed(Jid jid, int resId) {
- displayToast(getString(resId,jid.toBareJid().toString()));
- }
-
- @Override
- public void onRoleChangedSuccessful(String nick) {
-
- }
-
- @Override
- public void onRoleChangeFailed(String nick, int resId) {
- displayToast(getString(resId,nick));
- }
-
- @Override
- public void onPushSucceeded() {
- displayToast(getString(R.string.modified_conference_options));
- }
-
- @Override
- public void onPushFailed() {
- displayToast(getString(R.string.could_not_modify_conference_options));
- }
-
- private void displayToast(final String msg) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ConferenceDetailsActivity.this,msg,Toast.LENGTH_SHORT).show();
- }
- });
- }
+ registerForContextMenu(view);
+ view.setTag(user);
+ TextView tvDisplayName = (TextView) view.findViewById(R.id.contact_display_name);
+ TextView tvKey = (TextView) view.findViewById(R.id.key);
+ TextView tvStatus = (TextView) view.findViewById(R.id.contact_jid);
+ if (mAdvancedMode && user.getPgpKeyId() != 0) {
+ tvKey.setVisibility(View.VISIBLE);
+ tvKey.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ viewPgpKey(user);
+ }
+ });
+ tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
+ }
+ String name = user.getName();
+ if (contact != null) {
+ tvDisplayName.setText(contact.getDisplayName());
+ tvStatus.setText((name != null ? name + " \u2022 " : "") + getStatus(user));
+ } else {
+ tvDisplayName.setText(name == null ? "" : name);
+ tvStatus.setText(getStatus(user));
+
+ }
+ ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
+ iv.setImageBitmap(avatarService().get(user, getPixel(48), false));
+ membersView.addView(view);
+ if (mConversation.getMucOptions().canInvite()) {
+ mInviteButton.setVisibility(View.VISIBLE);
+ } else {
+ mInviteButton.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private String getStatus(User user) {
+ if (mAdvancedMode) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(getString(user.getAffiliation().getResId()));
+ builder.append(" (");
+ builder.append(getString(user.getRole().getResId()));
+ builder.append(')');
+ return builder.toString();
+ } else {
+ return getString(user.getAffiliation().getResId());
+ }
+ }
+
+ private void viewPgpKey(User user) {
+ PgpEngine pgp = xmppConnectionService.getPgpEngine();
+ if (pgp != null) {
+ PendingIntent intent = pgp.getIntentForKey(user.getPgpKeyId());
+ if (intent != null) {
+ try {
+ startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0);
+ } catch (SendIntentException ignored) {
+
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onAffiliationChangedSuccessful(Jid jid) {
+ refreshUi();
+ }
+
+ @Override
+ public void onAffiliationChangeFailed(Jid jid, int resId) {
+ displayToast(getString(resId, jid.toBareJid().toString()));
+ }
+
+ @Override
+ public void onRoleChangedSuccessful(String nick) {
+
+ }
+
+ @Override
+ public void onRoleChangeFailed(String nick, int resId) {
+ displayToast(getString(resId, nick));
+ }
+
+ @Override
+ public void onPushSucceeded() {
+ displayToast(getString(R.string.modified_conference_options));
+ }
+
+ @Override
+ public void onPushFailed() {
+ displayToast(getString(R.string.could_not_modify_conference_options));
+ }
+
+ private void displayToast(final String msg) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ConferenceDetailsActivity.this, msg, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
index 51b9b33ec..31d131549 100644
--- a/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
@@ -54,502 +54,502 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class ContactDetailsActivity extends OmemoActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated {
- public static final String ACTION_VIEW_CONTACT = "view_contact";
-
- private Conversation mConversation;
- private Contact contact;
- private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- xmppConnectionService.deleteContactOnServer(contact);
- }
- };
- private OnCheckedChangeListener mOnSendCheckedChange = new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- if (isChecked) {
- if (contact
- .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- xmppConnectionService.sendPresencePacket(contact
- .getAccount(),
- xmppConnectionService.getPresenceGenerator()
- .sendPresenceUpdatesTo(contact));
- } else {
- contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
- }
- } else {
- contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- xmppConnectionService.sendPresencePacket(contact.getAccount(),
- xmppConnectionService.getPresenceGenerator()
- .stopPresenceUpdatesTo(contact));
- }
- }
- };
- private OnCheckedChangeListener mOnReceiveCheckedChange = new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- if (isChecked) {
- xmppConnectionService.sendPresencePacket(contact.getAccount(),
- xmppConnectionService.getPresenceGenerator()
- .requestPresenceUpdatesFrom(contact));
- } else {
- xmppConnectionService.sendPresencePacket(contact.getAccount(),
- xmppConnectionService.getPresenceGenerator()
- .stopPresenceUpdatesFrom(contact));
- }
- }
- };
- private Jid accountJid;
- private TextView lastseen;
- private Jid contactJid;
- private TextView contactJidTv;
- private TextView accountJidTv;
- private TextView statusMessage;
- private CheckBox send;
- private CheckBox receive;
- private Button addContactButton;
- private QuickContactBadge badge;
- private LinearLayout keys;
- private FlowLayout tags;
- private boolean showDynamicTags = false;
- private boolean showLastSeen = false;
- private String messageFingerprint;
-
- private DialogInterface.OnClickListener addToPhonebook = new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- intent.setType(Contacts.CONTENT_ITEM_TYPE);
- intent.putExtra(Intents.Insert.IM_HANDLE, contact.getJid().toString());
- intent.putExtra(Intents.Insert.IM_PROTOCOL,
- CommonDataKinds.Im.PROTOCOL_JABBER);
- intent.putExtra("finishActivityOnSaveCompleted", true);
- ContactDetailsActivity.this.startActivityForResult(intent, 0);
- }
- };
-
- private OnClickListener onBadgeClick = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Uri systemAccount = contact.getSystemAccount();
- if (systemAccount == null) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- ContactDetailsActivity.this);
- builder.setTitle(getString(R.string.action_add_phone_book));
- builder.setMessage(getString(R.string.add_phone_book_text,
- contact.getDisplayJid()));
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.add), addToPhonebook);
- builder.create().show();
- } else {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(systemAccount);
- startActivity(intent);
- }
- }
- };
-
- @Override
- public void onRosterUpdate() {
- refreshUi();
- }
-
- @Override
- public void onAccountUpdate() {
- refreshUi();
- }
-
- @Override
- public void OnUpdateBlocklist(final Status status) {
- refreshUi();
- }
-
- @Override
- protected void refreshUiReal() {
- invalidateOptionsMenu();
- populateView();
- }
-
- @Override
- protected String getShareableUri() {
- if (contact != null) {
- return "xmpp:"+contact.getJid().toBareJid().toString();
- } else {
- return "";
- }
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) {
- try {
- this.accountJid = Jid.fromString(getIntent().getExtras().getString(EXTRA_ACCOUNT));
- } catch (final InvalidJidException ignored) {
- }
- try {
- this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact"));
- } catch (final InvalidJidException ignored) {
- }
- }
- this.messageFingerprint = getIntent().getStringExtra("fingerprint");
- setContentView(R.layout.activity_contact_details);
-
- contactJidTv = (TextView) findViewById(R.id.details_contactjid);
- accountJidTv = (TextView) findViewById(R.id.details_account);
- lastseen = (TextView) findViewById(R.id.details_lastseen);
- statusMessage = (TextView) findViewById(R.id.status_message);
- send = (CheckBox) findViewById(R.id.details_send_presence);
- receive = (CheckBox) findViewById(R.id.details_receive_presence);
- badge = (QuickContactBadge) findViewById(R.id.details_contact_badge);
- addContactButton = (Button) findViewById(R.id.add_contact_button);
- addContactButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- showAddToRosterDialog(contact);
- }
- });
- keys = (LinearLayout) findViewById(R.id.details_contact_keys);
- tags = (FlowLayout) findViewById(R.id.tags);
- if (getActionBar() != null) {
- getActionBar().setHomeButtonEnabled(true);
- getActionBar().setDisplayHomeAsUpEnabled(true);
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
- final int theme = findTheme();
- if (this.mTheme != theme) {
- recreate();
- } else {
- final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- this.showDynamicTags = preferences.getBoolean("show_dynamic_tags", false);
- this.showLastSeen = preferences.getBoolean("last_activity", false);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem menuItem) {
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setNegativeButton(getString(R.string.cancel), null);
- switch (menuItem.getItemId()) {
- case android.R.id.home:
- finish();
- break;
- case R.id.action_share:
- shareUri();
- break;
- case R.id.action_delete_contact:
- builder.setTitle(getString(R.string.action_delete_contact))
- .setMessage(
- getString(R.string.remove_contact_text,
- contact.getDisplayJid()))
- .setPositiveButton(getString(R.string.delete),
- removeFromRoster).create().show();
- break;
- case R.id.action_edit_contact:
- Uri systemAccount = contact.getSystemAccount();
- if (systemAccount == null) {
- quickEdit(contact.getDisplayName(), 0, new OnValueEdited() {
-
- @Override
- public void onValueEdited(String value) {
- contact.setServerName(value);
- ContactDetailsActivity.this.xmppConnectionService
- .pushContactToServer(contact);
- populateView();
- }
- });
- } else {
- Intent intent = new Intent(Intent.ACTION_EDIT);
- intent.setDataAndType(systemAccount, Contacts.CONTENT_ITEM_TYPE);
- intent.putExtra("finishActivityOnSaveCompleted", true);
- startActivity(intent);
- }
- break;
- case R.id.action_block:
- BlockContactDialog.show(this, xmppConnectionService, contact);
- break;
- case R.id.action_unblock:
- BlockContactDialog.show(this, xmppConnectionService, contact);
- break;
- }
- return super.onOptionsItemSelected(menuItem);
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- getMenuInflater().inflate(R.menu.contact_details, menu);
- MenuItem block = menu.findItem(R.id.action_block);
- MenuItem unblock = menu.findItem(R.id.action_unblock);
- MenuItem edit = menu.findItem(R.id.action_edit_contact);
- MenuItem delete = menu.findItem(R.id.action_delete_contact);
- if (contact == null) {
- return true;
- }
- final XmppConnection connection = contact.getAccount().getXmppConnection();
- if (connection != null && connection.getFeatures().blocking()) {
- if (this.contact.isBlocked()) {
- block.setVisible(false);
- } else {
- unblock.setVisible(false);
- }
- } else {
- unblock.setVisible(false);
- block.setVisible(false);
- }
- if (!contact.showInRoster()) {
- edit.setVisible(false);
- delete.setVisible(false);
- }
- return super.onCreateOptionsMenu(menu);
- }
-
- private void populateView() {
- if (contact == null) {
- return;
- }
- if (getActionBar() != null) {
- final ActionBar ab = getActionBar();
- ab.setCustomView(R.layout.ab_title);
- ab.setDisplayShowCustomEnabled(true);
- TextView abtitle = (TextView) findViewById(android.R.id.text1);
- TextView absubtitle = (TextView) findViewById(android.R.id.text2);
- abtitle.setText(contact.getDisplayName());
- abtitle.setSelected(true);
- abtitle.setClickable(false);
- absubtitle.setVisibility(View.GONE);
- absubtitle.setClickable(false);
- }
-
- invalidateOptionsMenu();
- setTitle(contact.getDisplayName());
- if (contact.showInRoster()) {
- send.setVisibility(View.VISIBLE);
- receive.setVisibility(View.VISIBLE);
- addContactButton.setVisibility(View.GONE);
- send.setOnCheckedChangeListener(null);
- receive.setOnCheckedChangeListener(null);
-
- List<String> statusMessages = contact.getPresences().getStatusMessages();
- if (statusMessages.size() == 0) {
- statusMessage.setVisibility(View.GONE);
- } else {
- StringBuilder builder = new StringBuilder();
- statusMessage.setVisibility(View.VISIBLE);
- int s = statusMessages.size();
- for(int i = 0; i < s; ++i) {
- if (s > 1) {
- builder.append("• ");
- }
- builder.append(statusMessages.get(i));
- if (i < s - 1) {
- builder.append("\n");
- }
- }
- statusMessage.setText(builder);
- }
-
- if (contact.getOption(Contact.Options.FROM)) {
- send.setText(R.string.send_presence_updates);
- send.setChecked(true);
- } else if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- send.setChecked(false);
- send.setText(R.string.send_presence_updates);
- } else {
- send.setText(R.string.preemptively_grant);
- if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
- send.setChecked(true);
- } else {
- send.setChecked(false);
- }
- }
- if (contact.getOption(Contact.Options.TO)) {
- receive.setText(R.string.receive_presence_updates);
- receive.setChecked(true);
- } else {
- receive.setText(R.string.ask_for_presence_updates);
- if (contact.getOption(Contact.Options.ASKING)) {
- receive.setChecked(true);
- } else {
- receive.setChecked(false);
- }
- }
- if (contact.getAccount().isOnlineAndConnected()) {
- receive.setEnabled(true);
- send.setEnabled(true);
- } else {
- receive.setEnabled(false);
- send.setEnabled(false);
- }
- send.setOnCheckedChangeListener(this.mOnSendCheckedChange);
- receive.setOnCheckedChangeListener(this.mOnReceiveCheckedChange);
- } else {
- addContactButton.setVisibility(View.VISIBLE);
- send.setVisibility(View.GONE);
- receive.setVisibility(View.GONE);
- statusMessage.setVisibility(View.GONE);
- }
-
- if (contact.isBlocked() && !this.showDynamicTags) {
- lastseen.setVisibility(View.VISIBLE);
- lastseen.setText(R.string.contact_blocked);
- } else {
- if (showLastSeen && contact.getLastseen() > 0) {
- lastseen.setVisibility(View.VISIBLE);
- lastseen.setText(UIHelper.lastseen(getApplicationContext(), contact.isActive(), contact.getLastseen()));
- } else {
- lastseen.setVisibility(View.GONE);
- }
- }
-
- if (contact.getPresences().size() > 1) {
- contactJidTv.setText(contact.getDisplayJid() + " ("
- + contact.getPresences().size() + ")");
- } else {
- contactJidTv.setText(contact.getDisplayJid());
- }
- String account;
- if (Config.DOMAIN_LOCK != null) {
- account = contact.getAccount().getJid().getLocalpart();
- } else {
- account = contact.getAccount().getJid().toBareJid().toString();
- }
- accountJidTv.setText(getString(R.string.using_account, account));
- badge.setImageBitmap(avatarService().get(contact, getPixel(Config.AVATAR_SIZE)));
- badge.setOnClickListener(this.onBadgeClick);
-
- keys.removeAllViews();
- boolean hasKeys = false;
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- if (Config.supportOtr()) {
- for (final String otrFingerprint : contact.getOtrFingerprints()) {
- hasKeys = true;
- View view = inflater.inflate(R.layout.contact_key, keys, false);
- TextView key = (TextView) view.findViewById(R.id.key);
- TextView keyType = (TextView) view.findViewById(R.id.key_type);
- ImageButton removeButton = (ImageButton) view
- .findViewById(R.id.button_remove);
- removeButton.setVisibility(View.VISIBLE);
- key.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
- if (otrFingerprint != null && otrFingerprint.equals(messageFingerprint)) {
- keyType.setText(R.string.otr_fingerprint_selected_message);
- keyType.setTextColor(getResources().getColor(R.color.accent));
- } else {
- keyType.setText(R.string.otr_fingerprint);
- }
- keys.addView(view);
- removeButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- confirmToDeleteFingerprint(otrFingerprint);
- }
- });
- }
- }
- if (Config.supportOmemo()) {
- for (final String fingerprint : contact.getAccount().getAxolotlService().getFingerprintsForContact(contact)) {
- boolean highlight = fingerprint.equals(messageFingerprint);
- hasKeys |= addFingerprintRow(keys, contact.getAccount(), fingerprint, highlight);
- }
- }
- if (Config.supportOpenPgp() && contact.getPgpKeyId() != 0) {
- hasKeys = true;
- View view = inflater.inflate(R.layout.contact_key, keys, false);
- TextView key = (TextView) view.findViewById(R.id.key);
- TextView keyType = (TextView) view.findViewById(R.id.key_type);
- keyType.setText(R.string.openpgp_key_id);
- if ("pgp".equals(messageFingerprint)) {
- keyType.setTextColor(getResources().getColor(R.color.accent));
- }
- key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId()));
- view.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- PgpEngine pgp = ContactDetailsActivity.this.xmppConnectionService
- .getPgpEngine();
- if (pgp != null) {
- PendingIntent intent = pgp.getIntentForKey(contact);
- if (intent != null) {
- try {
- startIntentSenderForResult(
- intent.getIntentSender(), 0, null, 0,
- 0, 0);
- } catch (SendIntentException e) {
-
- }
- }
- }
- }
- });
- keys.addView(view);
- }
- if (hasKeys) {
- keys.setVisibility(View.VISIBLE);
- } else {
- keys.setVisibility(View.GONE);
- }
-
- List<ListItem.Tag> tagList = contact.getTags(this);
- if (tagList.size() == 0 || !this.showDynamicTags) {
- tags.setVisibility(View.GONE);
- } else {
- tags.setVisibility(View.VISIBLE);
- tags.removeAllViewsInLayout();
- for(final ListItem.Tag tag : tagList) {
- final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,tags,false);
- tv.setText(tag.getName());
- tv.setBackgroundColor(tag.getColor());
- tags.addView(tv);
- }
- }
- }
-
- protected void confirmToDeleteFingerprint(final String fingerprint) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.delete_fingerprint);
- builder.setMessage(R.string.sure_delete_fingerprint);
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.delete,
- new android.content.DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (contact.deleteOtrFingerprint(fingerprint)) {
- populateView();
- xmppConnectionService.syncRosterToDisk(contact.getAccount());
- }
- }
-
- });
- builder.create().show();
- }
-
- public void onBackendConnected() {
- if ((accountJid != null) && (contactJid != null)) {
- Account account = xmppConnectionService
- .findAccountByJid(accountJid);
- if (account == null) {
- return;
- }
- this.contact = account.getRoster().getContact(contactJid);
- populateView();
- }
- }
-
- @Override
- public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
- refreshUi();
- }
+ public static final String ACTION_VIEW_CONTACT = "view_contact";
+
+ private Conversation mConversation;
+ private Contact contact;
+ private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ xmppConnectionService.deleteContactOnServer(contact);
+ }
+ };
+ private OnCheckedChangeListener mOnSendCheckedChange = new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ if (isChecked) {
+ if (contact
+ .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ xmppConnectionService.sendPresencePacket(contact
+ .getAccount(),
+ xmppConnectionService.getPresenceGenerator()
+ .sendPresenceUpdatesTo(contact));
+ } else {
+ contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
+ }
+ } else {
+ contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
+ xmppConnectionService.sendPresencePacket(contact.getAccount(),
+ xmppConnectionService.getPresenceGenerator()
+ .stopPresenceUpdatesTo(contact));
+ }
+ }
+ };
+ private OnCheckedChangeListener mOnReceiveCheckedChange = new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ if (isChecked) {
+ xmppConnectionService.sendPresencePacket(contact.getAccount(),
+ xmppConnectionService.getPresenceGenerator()
+ .requestPresenceUpdatesFrom(contact));
+ } else {
+ xmppConnectionService.sendPresencePacket(contact.getAccount(),
+ xmppConnectionService.getPresenceGenerator()
+ .stopPresenceUpdatesFrom(contact));
+ }
+ }
+ };
+ private Jid accountJid;
+ private TextView lastseen;
+ private Jid contactJid;
+ private TextView contactJidTv;
+ private TextView accountJidTv;
+ private TextView statusMessage;
+ private CheckBox send;
+ private CheckBox receive;
+ private Button addContactButton;
+ private QuickContactBadge badge;
+ private LinearLayout keys;
+ private FlowLayout tags;
+ private boolean showDynamicTags = false;
+ private boolean showLastSeen = false;
+ private String messageFingerprint;
+
+ private DialogInterface.OnClickListener addToPhonebook = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ intent.setType(Contacts.CONTENT_ITEM_TYPE);
+ intent.putExtra(Intents.Insert.IM_HANDLE, contact.getJid().toString());
+ intent.putExtra(Intents.Insert.IM_PROTOCOL,
+ CommonDataKinds.Im.PROTOCOL_JABBER);
+ intent.putExtra("finishActivityOnSaveCompleted", true);
+ ContactDetailsActivity.this.startActivityForResult(intent, 0);
+ }
+ };
+
+ private OnClickListener onBadgeClick = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Uri systemAccount = contact.getSystemAccount();
+ if (systemAccount == null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ ContactDetailsActivity.this);
+ builder.setTitle(getString(R.string.action_add_phone_book));
+ builder.setMessage(getString(R.string.add_phone_book_text,
+ contact.getDisplayJid()));
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton(getString(R.string.add), addToPhonebook);
+ builder.create().show();
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(systemAccount);
+ startActivity(intent);
+ }
+ }
+ };
+
+ @Override
+ public void onRosterUpdate() {
+ refreshUi();
+ }
+
+ @Override
+ public void onAccountUpdate() {
+ refreshUi();
+ }
+
+ @Override
+ public void OnUpdateBlocklist(final Status status) {
+ refreshUi();
+ }
+
+ @Override
+ protected void refreshUiReal() {
+ invalidateOptionsMenu();
+ populateView();
+ }
+
+ @Override
+ protected String getShareableUri() {
+ if (contact != null) {
+ return "xmpp:" + contact.getJid().toBareJid().toString();
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) {
+ try {
+ this.accountJid = Jid.fromString(getIntent().getExtras().getString(EXTRA_ACCOUNT));
+ } catch (final InvalidJidException ignored) {
+ }
+ try {
+ this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact"));
+ } catch (final InvalidJidException ignored) {
+ }
+ }
+ this.messageFingerprint = getIntent().getStringExtra("fingerprint");
+ setContentView(R.layout.activity_contact_details);
+
+ contactJidTv = (TextView) findViewById(R.id.details_contactjid);
+ accountJidTv = (TextView) findViewById(R.id.details_account);
+ lastseen = (TextView) findViewById(R.id.details_lastseen);
+ statusMessage = (TextView) findViewById(R.id.status_message);
+ send = (CheckBox) findViewById(R.id.details_send_presence);
+ receive = (CheckBox) findViewById(R.id.details_receive_presence);
+ badge = (QuickContactBadge) findViewById(R.id.details_contact_badge);
+ addContactButton = (Button) findViewById(R.id.add_contact_button);
+ addContactButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ showAddToRosterDialog(contact);
+ }
+ });
+ keys = (LinearLayout) findViewById(R.id.details_contact_keys);
+ tags = (FlowLayout) findViewById(R.id.tags);
+ if (getActionBar() != null) {
+ getActionBar().setHomeButtonEnabled(true);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ final int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ } else {
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ this.showDynamicTags = preferences.getBoolean("show_dynamic_tags", false);
+ this.showLastSeen = preferences.getBoolean("last_activity", false);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem menuItem) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ switch (menuItem.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.action_share:
+ shareUri();
+ break;
+ case R.id.action_delete_contact:
+ builder.setTitle(getString(R.string.action_delete_contact))
+ .setMessage(
+ getString(R.string.remove_contact_text,
+ contact.getDisplayJid()))
+ .setPositiveButton(getString(R.string.delete),
+ removeFromRoster).create().show();
+ break;
+ case R.id.action_edit_contact:
+ Uri systemAccount = contact.getSystemAccount();
+ if (systemAccount == null) {
+ quickEdit(contact.getDisplayName(), 0, new OnValueEdited() {
+
+ @Override
+ public void onValueEdited(String value) {
+ contact.setServerName(value);
+ ContactDetailsActivity.this.xmppConnectionService
+ .pushContactToServer(contact);
+ populateView();
+ }
+ });
+ } else {
+ Intent intent = new Intent(Intent.ACTION_EDIT);
+ intent.setDataAndType(systemAccount, Contacts.CONTENT_ITEM_TYPE);
+ intent.putExtra("finishActivityOnSaveCompleted", true);
+ startActivity(intent);
+ }
+ break;
+ case R.id.action_block:
+ BlockContactDialog.show(this, xmppConnectionService, contact);
+ break;
+ case R.id.action_unblock:
+ BlockContactDialog.show(this, xmppConnectionService, contact);
+ break;
+ }
+ return super.onOptionsItemSelected(menuItem);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ getMenuInflater().inflate(R.menu.contact_details, menu);
+ MenuItem block = menu.findItem(R.id.action_block);
+ MenuItem unblock = menu.findItem(R.id.action_unblock);
+ MenuItem edit = menu.findItem(R.id.action_edit_contact);
+ MenuItem delete = menu.findItem(R.id.action_delete_contact);
+ if (contact == null) {
+ return true;
+ }
+ final XmppConnection connection = contact.getAccount().getXmppConnection();
+ if (connection != null && connection.getFeatures().blocking()) {
+ if (this.contact.isBlocked()) {
+ block.setVisible(false);
+ } else {
+ unblock.setVisible(false);
+ }
+ } else {
+ unblock.setVisible(false);
+ block.setVisible(false);
+ }
+ if (!contact.showInRoster()) {
+ edit.setVisible(false);
+ delete.setVisible(false);
+ }
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ private void populateView() {
+ if (contact == null) {
+ return;
+ }
+ if (getActionBar() != null) {
+ final ActionBar ab = getActionBar();
+ ab.setCustomView(R.layout.ab_title);
+ ab.setDisplayShowCustomEnabled(true);
+ TextView abtitle = (TextView) findViewById(android.R.id.text1);
+ TextView absubtitle = (TextView) findViewById(android.R.id.text2);
+ abtitle.setText(contact.getDisplayName());
+ abtitle.setSelected(true);
+ abtitle.setClickable(false);
+ absubtitle.setVisibility(View.GONE);
+ absubtitle.setClickable(false);
+ }
+
+ invalidateOptionsMenu();
+ setTitle(contact.getDisplayName());
+ if (contact.showInRoster()) {
+ send.setVisibility(View.VISIBLE);
+ receive.setVisibility(View.VISIBLE);
+ addContactButton.setVisibility(View.GONE);
+ send.setOnCheckedChangeListener(null);
+ receive.setOnCheckedChangeListener(null);
+
+ List<String> statusMessages = contact.getPresences().getStatusMessages();
+ if (statusMessages.size() == 0) {
+ statusMessage.setVisibility(View.GONE);
+ } else {
+ StringBuilder builder = new StringBuilder();
+ statusMessage.setVisibility(View.VISIBLE);
+ int s = statusMessages.size();
+ for (int i = 0; i < s; ++i) {
+ if (s > 1) {
+ builder.append("• ");
+ }
+ builder.append(statusMessages.get(i));
+ if (i < s - 1) {
+ builder.append("\n");
+ }
+ }
+ statusMessage.setText(builder);
+ }
+
+ if (contact.getOption(Contact.Options.FROM)) {
+ send.setText(R.string.send_presence_updates);
+ send.setChecked(true);
+ } else if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ send.setChecked(false);
+ send.setText(R.string.send_presence_updates);
+ } else {
+ send.setText(R.string.preemptively_grant);
+ if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
+ send.setChecked(true);
+ } else {
+ send.setChecked(false);
+ }
+ }
+ if (contact.getOption(Contact.Options.TO)) {
+ receive.setText(R.string.receive_presence_updates);
+ receive.setChecked(true);
+ } else {
+ receive.setText(R.string.ask_for_presence_updates);
+ if (contact.getOption(Contact.Options.ASKING)) {
+ receive.setChecked(true);
+ } else {
+ receive.setChecked(false);
+ }
+ }
+ if (contact.getAccount().isOnlineAndConnected()) {
+ receive.setEnabled(true);
+ send.setEnabled(true);
+ } else {
+ receive.setEnabled(false);
+ send.setEnabled(false);
+ }
+ send.setOnCheckedChangeListener(this.mOnSendCheckedChange);
+ receive.setOnCheckedChangeListener(this.mOnReceiveCheckedChange);
+ } else {
+ addContactButton.setVisibility(View.VISIBLE);
+ send.setVisibility(View.GONE);
+ receive.setVisibility(View.GONE);
+ statusMessage.setVisibility(View.GONE);
+ }
+
+ if (contact.isBlocked() && !this.showDynamicTags) {
+ lastseen.setVisibility(View.VISIBLE);
+ lastseen.setText(R.string.contact_blocked);
+ } else {
+ if (showLastSeen && contact.getLastseen() > 0) {
+ lastseen.setVisibility(View.VISIBLE);
+ lastseen.setText(UIHelper.lastseen(getApplicationContext(), contact.isActive(), contact.getLastseen()));
+ } else {
+ lastseen.setVisibility(View.GONE);
+ }
+ }
+
+ if (contact.getPresences().size() > 1) {
+ contactJidTv.setText(contact.getDisplayJid() + " ("
+ + contact.getPresences().size() + ")");
+ } else {
+ contactJidTv.setText(contact.getDisplayJid());
+ }
+ String account;
+ if (Config.DOMAIN_LOCK != null) {
+ account = contact.getAccount().getJid().getLocalpart();
+ } else {
+ account = contact.getAccount().getJid().toBareJid().toString();
+ }
+ accountJidTv.setText(getString(R.string.using_account, account));
+ badge.setImageBitmap(avatarService().get(contact, getPixel(Config.AVATAR_SIZE)));
+ badge.setOnClickListener(this.onBadgeClick);
+
+ keys.removeAllViews();
+ boolean hasKeys = false;
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ if (Config.supportOtr()) {
+ for (final String otrFingerprint : contact.getOtrFingerprints()) {
+ hasKeys = true;
+ View view = inflater.inflate(R.layout.contact_key, keys, false);
+ TextView key = (TextView) view.findViewById(R.id.key);
+ TextView keyType = (TextView) view.findViewById(R.id.key_type);
+ ImageButton removeButton = (ImageButton) view
+ .findViewById(R.id.button_remove);
+ removeButton.setVisibility(View.VISIBLE);
+ key.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
+ if (otrFingerprint != null && otrFingerprint.equals(messageFingerprint)) {
+ keyType.setText(R.string.otr_fingerprint_selected_message);
+ keyType.setTextColor(getResources().getColor(R.color.accent));
+ } else {
+ keyType.setText(R.string.otr_fingerprint);
+ }
+ keys.addView(view);
+ removeButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ confirmToDeleteFingerprint(otrFingerprint);
+ }
+ });
+ }
+ }
+ if (Config.supportOmemo()) {
+ for (final String fingerprint : contact.getAccount().getAxolotlService().getFingerprintsForContact(contact)) {
+ boolean highlight = fingerprint.equals(messageFingerprint);
+ hasKeys |= addFingerprintRow(keys, contact.getAccount(), fingerprint, highlight);
+ }
+ }
+ if (Config.supportOpenPgp() && contact.getPgpKeyId() != 0) {
+ hasKeys = true;
+ View view = inflater.inflate(R.layout.contact_key, keys, false);
+ TextView key = (TextView) view.findViewById(R.id.key);
+ TextView keyType = (TextView) view.findViewById(R.id.key_type);
+ keyType.setText(R.string.openpgp_key_id);
+ if ("pgp".equals(messageFingerprint)) {
+ keyType.setTextColor(getResources().getColor(R.color.accent));
+ }
+ key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId()));
+ view.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ PgpEngine pgp = ContactDetailsActivity.this.xmppConnectionService
+ .getPgpEngine();
+ if (pgp != null) {
+ PendingIntent intent = pgp.getIntentForKey(contact);
+ if (intent != null) {
+ try {
+ startIntentSenderForResult(
+ intent.getIntentSender(), 0, null, 0,
+ 0, 0);
+ } catch (SendIntentException e) {
+
+ }
+ }
+ }
+ }
+ });
+ keys.addView(view);
+ }
+ if (hasKeys) {
+ keys.setVisibility(View.VISIBLE);
+ } else {
+ keys.setVisibility(View.GONE);
+ }
+
+ List<ListItem.Tag> tagList = contact.getTags(this);
+ if (tagList.size() == 0 || !this.showDynamicTags) {
+ tags.setVisibility(View.GONE);
+ } else {
+ tags.setVisibility(View.VISIBLE);
+ tags.removeAllViewsInLayout();
+ for (final ListItem.Tag tag : tagList) {
+ final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, tags, false);
+ tv.setText(tag.getName());
+ tv.setBackgroundColor(tag.getColor());
+ tags.addView(tv);
+ }
+ }
+ }
+
+ protected void confirmToDeleteFingerprint(final String fingerprint) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.delete_fingerprint);
+ builder.setMessage(R.string.sure_delete_fingerprint);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.delete,
+ new android.content.DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (contact.deleteOtrFingerprint(fingerprint)) {
+ populateView();
+ xmppConnectionService.syncRosterToDisk(contact.getAccount());
+ }
+ }
+
+ });
+ builder.create().show();
+ }
+
+ public void onBackendConnected() {
+ if ((accountJid != null) && (contactJid != null)) {
+ Account account = xmppConnectionService
+ .findAccountByJid(accountJid);
+ if (account == null) {
+ return;
+ }
+ this.contact = account.getRoster().getContact(contactJid);
+ populateView();
+ }
+ }
+
+ @Override
+ public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
+ refreshUi();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
index 1fb23ff69..47992b221 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
@@ -86,119 +86,119 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.timroes.android.listview.EnhancedListView;
public class ConversationActivity extends XmppActivity
- implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast, View.OnClickListener {
-
- public static final String ACTION_VIEW_CONVERSATION = "de.pixart.messenger.VIEW";
- public static final String CONVERSATION = "conversationUuid";
- public static final String EXTRA_DOWNLOAD_UUID = "de.pixart.messenger.download_uuid";
- public static final String TEXT = "text";
- public static final String NICK = "nick";
- public static final String PRIVATE_MESSAGE = "pm";
-
- public static final int REQUEST_SEND_MESSAGE = 0x0201;
- public static final int REQUEST_DECRYPT_PGP = 0x0202;
- public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
- public static final int REQUEST_TRUST_KEYS_TEXT = 0x0208;
- public static final int REQUEST_TRUST_KEYS_MENU = 0x0209;
- public static final int REQUEST_START_DOWNLOAD = 0x0210;
- public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
- public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
- public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
- public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
- public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
+ implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast, View.OnClickListener {
+
+ public static final String ACTION_VIEW_CONVERSATION = "de.pixart.messenger.VIEW";
+ public static final String CONVERSATION = "conversationUuid";
+ public static final String EXTRA_DOWNLOAD_UUID = "de.pixart.messenger.download_uuid";
+ public static final String TEXT = "text";
+ public static final String NICK = "nick";
+ public static final String PRIVATE_MESSAGE = "pm";
+
+ public static final int REQUEST_SEND_MESSAGE = 0x0201;
+ public static final int REQUEST_DECRYPT_PGP = 0x0202;
+ public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
+ public static final int REQUEST_TRUST_KEYS_TEXT = 0x0208;
+ public static final int REQUEST_TRUST_KEYS_MENU = 0x0209;
+ public static final int REQUEST_START_DOWNLOAD = 0x0210;
+ public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
+ public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
+ public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
+ public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
+ public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
public static final int ATTACHMENT_CHOICE_CHOOSE_VIDEO = 0x0306;
public static final int ATTACHMENT_CHOICE_INVALID = 0x0399;
- private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
- private static final String STATE_PANEL_OPEN = "state_panel_open";
- private static final String STATE_PENDING_IMAGE_URI = "state_pending_image_uri";
+ private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
+ private static final String STATE_PANEL_OPEN = "state_panel_open";
+ private static final String STATE_PENDING_IMAGE_URI = "state_pending_image_uri";
private static final String STATE_PENDING_PHOTO_URI = "state_pending_photo_uri";
private static final String STATE_FIRST_VISIBLE = "first_visible";
private static final String STATE_OFFSET_FROM_TOP = "offset_from_top";
- final private List<Uri> mPendingImageUris = new ArrayList<>();
+ final private List<Uri> mPendingImageUris = new ArrayList<>();
final private List<Uri> mPendingPhotoUris = new ArrayList<>();
- final private List<Uri> mPendingFileUris = new ArrayList<>();
+ final private List<Uri> mPendingFileUris = new ArrayList<>();
final private List<Uri> mPendingVideoUris = new ArrayList<>();
- private String mOpenConversation = null;
- private boolean mPanelOpen = true;
- private Pair<Integer,Integer> mScrollPosition = null;
- private Uri mPendingGeoUri = null;
- private boolean forbidProcessingPendings = false;
- private Message mPendingDownloadableMessage = null;
+ private String mOpenConversation = null;
+ private boolean mPanelOpen = true;
+ private Pair<Integer, Integer> mScrollPosition = null;
+ private Uri mPendingGeoUri = null;
+ private boolean forbidProcessingPendings = false;
+ private Message mPendingDownloadableMessage = null;
- private boolean conversationWasSelectedByKeyboard = false;
+ private boolean conversationWasSelectedByKeyboard = false;
- private View mContentView;
+ private View mContentView;
- private List<Conversation> conversationList = new ArrayList<>();
- private Conversation swipedConversation = null;
- private Conversation mSelectedConversation = null;
- private EnhancedListView listView;
- private ConversationFragment mConversationFragment;
+ private List<Conversation> conversationList = new ArrayList<>();
+ private Conversation swipedConversation = null;
+ private Conversation mSelectedConversation = null;
+ private EnhancedListView listView;
+ private ConversationFragment mConversationFragment;
- private ArrayAdapter<Conversation> listAdapter;
+ private ArrayAdapter<Conversation> listAdapter;
- private boolean mActivityPaused = false;
- private AtomicBoolean mRedirected = new AtomicBoolean(false);
- private Pair<Integer, Intent> mPostponedActivityResult;
+ private boolean mActivityPaused = false;
+ private AtomicBoolean mRedirected = new AtomicBoolean(false);
+ private Pair<Integer, Intent> mPostponedActivityResult;
private boolean mUnprocessedNewIntent = false;
long FirstStartTime = -1;
- public Conversation getSelectedConversation() {
- return this.mSelectedConversation;
- }
-
- public void setSelectedConversation(Conversation conversation) {
- this.mSelectedConversation = conversation;
- }
-
- public void showConversationsOverview() {
- if (mContentView instanceof SlidingPaneLayout) {
- SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
- mSlidingPaneLayout.openPane();
- }
- }
-
- @Override
- protected String getShareableUri() {
- Conversation conversation = getSelectedConversation();
- if (conversation != null) {
- return conversation.getAccount().getShareableUri();
- } else {
- return "";
- }
- }
-
- public void hideConversationsOverview() {
- if (mContentView instanceof SlidingPaneLayout) {
- SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
- mSlidingPaneLayout.closePane();
- }
- }
-
- public boolean isConversationsOverviewHideable() {
- if (mContentView instanceof SlidingPaneLayout) {
- return true;
- } else {
- return false;
- }
- }
-
- public boolean isConversationsOverviewVisable() {
- if (mContentView instanceof SlidingPaneLayout) {
- SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
- return mSlidingPaneLayout.isOpen();
- } else {
- return true;
- }
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (savedInstanceState != null) {
- mOpenConversation = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null);
- mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
+ public Conversation getSelectedConversation() {
+ return this.mSelectedConversation;
+ }
+
+ public void setSelectedConversation(Conversation conversation) {
+ this.mSelectedConversation = conversation;
+ }
+
+ public void showConversationsOverview() {
+ if (mContentView instanceof SlidingPaneLayout) {
+ SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+ mSlidingPaneLayout.openPane();
+ }
+ }
+
+ @Override
+ protected String getShareableUri() {
+ Conversation conversation = getSelectedConversation();
+ if (conversation != null) {
+ return conversation.getAccount().getShareableUri();
+ } else {
+ return "";
+ }
+ }
+
+ public void hideConversationsOverview() {
+ if (mContentView instanceof SlidingPaneLayout) {
+ SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+ mSlidingPaneLayout.closePane();
+ }
+ }
+
+ public boolean isConversationsOverviewHideable() {
+ if (mContentView instanceof SlidingPaneLayout) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isConversationsOverviewVisable() {
+ if (mContentView instanceof SlidingPaneLayout) {
+ SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+ return mSlidingPaneLayout.isOpen();
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mOpenConversation = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null);
+ mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
int pos = savedInstanceState.getInt(STATE_FIRST_VISIBLE, -1);
int offset = savedInstanceState.getInt(STATE_OFFSET_FROM_TOP, 1);
if (pos >= 0 && offset <= 0) {
@@ -208,166 +208,166 @@ public class ConversationActivity extends XmppActivity
mScrollPosition = null;
}
String pending_image = savedInstanceState.getString(STATE_PENDING_IMAGE_URI, null);
- if (pending_image != null) {
- Log.d(Config.LOGTAG,"ConversationActivity.onCreate() - restoring pending image uri");
- mPendingImageUris.clear();
- mPendingImageUris.add(Uri.parse(pending_image));
- }
+ if (pending_image != null) {
+ Log.d(Config.LOGTAG, "ConversationActivity.onCreate() - restoring pending image uri");
+ mPendingImageUris.clear();
+ mPendingImageUris.add(Uri.parse(pending_image));
+ }
String pending_photo = savedInstanceState.getString(STATE_PENDING_PHOTO_URI, null);
if (pending_photo != null) {
- Log.d(Config.LOGTAG,"ConversationActivity.onCreate() - restoring pending photo uri");
+ Log.d(Config.LOGTAG, "ConversationActivity.onCreate() - restoring pending photo uri");
mPendingPhotoUris.clear();
mPendingPhotoUris.add(Uri.parse(pending_photo));
}
- }
+ }
- setContentView(R.layout.fragment_conversations_overview);
+ setContentView(R.layout.fragment_conversations_overview);
this.mConversationFragment = new ConversationFragment();
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation");
- transaction.commit();
-
- listView = (EnhancedListView) findViewById(R.id.list);
- this.listAdapter = new ConversationAdapter(this, conversationList);
- listView.setAdapter(this.listAdapter);
-
- if (getActionBar() != null) {
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
- }
-
- listView.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> arg0, View clickedView,
- int position, long arg3) {
- if (getSelectedConversation() != conversationList.get(position)) {
- setSelectedConversation(conversationList.get(position));
- ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
- conversationWasSelectedByKeyboard = false;
- }
- hideConversationsOverview();
- openConversation();
- }
- });
-
- listView.setDismissCallback(new EnhancedListView.OnDismissCallback() {
-
- @Override
- public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) {
-
- final int index = listView.getFirstVisiblePosition();
- View v = listView.getChildAt(0);
- final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
-
- try {
- swipedConversation = listAdapter.getItem(position);
- } catch (IndexOutOfBoundsException e) {
- return null;
- }
- listAdapter.remove(swipedConversation);
- xmppConnectionService.markRead(swipedConversation);
-
- final boolean formerlySelected = (getSelectedConversation() == swipedConversation);
- if (position == 0 && listAdapter.getCount() == 0) {
- endConversation(swipedConversation, false, true);
- return null;
- } else if (formerlySelected) {
- setSelectedConversation(listAdapter.getItem(0));
- ConversationActivity.this.mConversationFragment
- .reInit(getSelectedConversation());
- }
-
- return new EnhancedListView.Undoable() {
-
- @Override
- public void undo() {
- listAdapter.insert(swipedConversation, position);
- if (formerlySelected) {
- setSelectedConversation(swipedConversation);
- ConversationActivity.this.mConversationFragment
- .reInit(getSelectedConversation());
- }
- swipedConversation = null;
- listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top);
- }
-
- @Override
- public void discard() {
- if (!swipedConversation.isRead()
- && swipedConversation.getMode() == Conversation.MODE_SINGLE) {
- swipedConversation = null;
- return;
- }
- endConversation(swipedConversation, false, false);
- swipedConversation = null;
- }
-
- @Override
- public String getTitle() {
- if (swipedConversation.getMode() == Conversation.MODE_MULTI) {
- return getResources().getString(R.string.title_undo_swipe_out_muc);
- } else {
- return getResources().getString(R.string.title_undo_swipe_out_conversation);
- }
- }
- };
- }
- });
- //listView.enableSwipeToDismiss();
- listView.setSwipingLayout(R.id.swipeable_item);
- listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP);
- listView.setUndoHideDelay(10000);
- listView.setRequireTouchBeforeDismiss(false);
- listView.setSwipeDirection(EnhancedListView.SwipeDirection.START); // swipe to left to close conversation
-
- mContentView = findViewById(R.id.content_view_spl);
- if (mContentView == null) {
- mContentView = findViewById(R.id.content_view_ll);
- }
- if (mContentView instanceof SlidingPaneLayout) {
- SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
- mSlidingPaneLayout.setParallaxDistance(150);
- mSlidingPaneLayout
- .setShadowResourceLeft(R.drawable.es_slidingpane_shadow);
- mSlidingPaneLayout.setSliderFadeColor(0);
- mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
-
- @Override
- public void onPanelOpened(View arg0) {
- updateActionBarTitle();
- invalidateOptionsMenu();
- hideKeyboard();
- if (xmppConnectionServiceBound) {
- xmppConnectionService.getNotificationService()
- .setOpenConversation(null);
- }
- closeContextMenu();
- }
-
- @Override
- public void onPanelClosed(View arg0) {
- listView.discardUndo();
- openConversation();
- }
-
- @Override
- public void onPanelSlide(View arg0, float arg1) {
- // TODO Auto-generated method stub
-
- }
- });
- }
- }
-
- private boolean isPackageInstalled(String targetPackage){
+ FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation");
+ transaction.commit();
+
+ listView = (EnhancedListView) findViewById(R.id.list);
+ this.listAdapter = new ConversationAdapter(this, conversationList);
+ listView.setAdapter(this.listAdapter);
+
+ if (getActionBar() != null) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setHomeButtonEnabled(false);
+ }
+
+ listView.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View clickedView,
+ int position, long arg3) {
+ if (getSelectedConversation() != conversationList.get(position)) {
+ setSelectedConversation(conversationList.get(position));
+ ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
+ conversationWasSelectedByKeyboard = false;
+ }
+ hideConversationsOverview();
+ openConversation();
+ }
+ });
+
+ listView.setDismissCallback(new EnhancedListView.OnDismissCallback() {
+
+ @Override
+ public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) {
+
+ final int index = listView.getFirstVisiblePosition();
+ View v = listView.getChildAt(0);
+ final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
+
+ try {
+ swipedConversation = listAdapter.getItem(position);
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+ listAdapter.remove(swipedConversation);
+ xmppConnectionService.markRead(swipedConversation);
+
+ final boolean formerlySelected = (getSelectedConversation() == swipedConversation);
+ if (position == 0 && listAdapter.getCount() == 0) {
+ endConversation(swipedConversation, false, true);
+ return null;
+ } else if (formerlySelected) {
+ setSelectedConversation(listAdapter.getItem(0));
+ ConversationActivity.this.mConversationFragment
+ .reInit(getSelectedConversation());
+ }
+
+ return new EnhancedListView.Undoable() {
+
+ @Override
+ public void undo() {
+ listAdapter.insert(swipedConversation, position);
+ if (formerlySelected) {
+ setSelectedConversation(swipedConversation);
+ ConversationActivity.this.mConversationFragment
+ .reInit(getSelectedConversation());
+ }
+ swipedConversation = null;
+ listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top);
+ }
+
+ @Override
+ public void discard() {
+ if (!swipedConversation.isRead()
+ && swipedConversation.getMode() == Conversation.MODE_SINGLE) {
+ swipedConversation = null;
+ return;
+ }
+ endConversation(swipedConversation, false, false);
+ swipedConversation = null;
+ }
+
+ @Override
+ public String getTitle() {
+ if (swipedConversation.getMode() == Conversation.MODE_MULTI) {
+ return getResources().getString(R.string.title_undo_swipe_out_muc);
+ } else {
+ return getResources().getString(R.string.title_undo_swipe_out_conversation);
+ }
+ }
+ };
+ }
+ });
+ //listView.enableSwipeToDismiss();
+ listView.setSwipingLayout(R.id.swipeable_item);
+ listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP);
+ listView.setUndoHideDelay(10000);
+ listView.setRequireTouchBeforeDismiss(false);
+ listView.setSwipeDirection(EnhancedListView.SwipeDirection.START); // swipe to left to close conversation
+
+ mContentView = findViewById(R.id.content_view_spl);
+ if (mContentView == null) {
+ mContentView = findViewById(R.id.content_view_ll);
+ }
+ if (mContentView instanceof SlidingPaneLayout) {
+ SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
+ mSlidingPaneLayout.setParallaxDistance(150);
+ mSlidingPaneLayout
+ .setShadowResourceLeft(R.drawable.es_slidingpane_shadow);
+ mSlidingPaneLayout.setSliderFadeColor(0);
+ mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
+
+ @Override
+ public void onPanelOpened(View arg0) {
+ updateActionBarTitle();
+ invalidateOptionsMenu();
+ hideKeyboard();
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.getNotificationService()
+ .setOpenConversation(null);
+ }
+ closeContextMenu();
+ }
+
+ @Override
+ public void onPanelClosed(View arg0) {
+ listView.discardUndo();
+ openConversation();
+ }
+
+ @Override
+ public void onPanelSlide(View arg0, float arg1) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+ }
+ }
+
+ private boolean isPackageInstalled(String targetPackage) {
List<ApplicationInfo> packages;
PackageManager pm;
pm = getPackageManager();
packages = pm.getInstalledApplications(0);
for (ApplicationInfo packageInfo : packages) {
- if(packageInfo.packageName.equals(targetPackage)) return true;
+ if (packageInfo.packageName.equals(targetPackage)) return true;
}
return false;
}
@@ -454,36 +454,36 @@ public class ConversationActivity extends XmppActivity
}
}
- @Override
- public void switchToConversation(Conversation conversation) {
- setSelectedConversation(conversation);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
- openConversation();
- }
- });
- }
+ @Override
+ public void switchToConversation(Conversation conversation) {
+ setSelectedConversation(conversation);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
+ openConversation();
+ }
+ });
+ }
- private void updateActionBarTitle() {
- updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable());
- }
+ private void updateActionBarTitle() {
+ updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable());
+ }
- private void updateActionBarTitle(boolean titleShouldBeName) {
+ private void updateActionBarTitle(boolean titleShouldBeName) {
final ActionBar ab = getActionBar();
final Conversation conversation = getSelectedConversation();
if (ab != null) {
if (titleShouldBeName && conversation != null) {
ab.setDisplayHomeAsUpEnabled(true);
ab.setHomeButtonEnabled(true);
- ab.setDisplayShowTitleEnabled(false);
- ab.setDisplayShowCustomEnabled(true);
- ab.setCustomView(R.layout.ab_title);
+ ab.setDisplayShowTitleEnabled(false);
+ ab.setDisplayShowCustomEnabled(true);
+ ab.setCustomView(R.layout.ab_title);
TextView abtitle = (TextView) findViewById(android.R.id.text1);
TextView absubtitle = (TextView) findViewById(android.R.id.text2);
if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) {
- abtitle.setText(conversation.getName());
+ abtitle.setText(conversation.getName());
abtitle.setOnClickListener(this);
abtitle.setSelected(true);
if (conversation.getMode() == Conversation.MODE_SINGLE && !this.getSelectedConversation().withSelf()) {
@@ -510,14 +510,14 @@ public class ConversationActivity extends XmppActivity
}
} else if (useSubjectToIdentifyConference()) {
if (conversation.getParticipants() != null) {
- absubtitle.setText(conversation.getParticipants());
+ absubtitle.setText(conversation.getParticipants());
absubtitle.setSelected(true);
- absubtitle.setOnClickListener(this);
- } else {
+ absubtitle.setOnClickListener(this);
+ } else {
absubtitle.setText(R.string.no_participants);
abtitle.setSelected(true);
absubtitle.setOnClickListener(this);
- }
+ }
}
} else {
abtitle.setText(conversation.getJid().toBareJid().toString());
@@ -537,206 +537,206 @@ public class ConversationActivity extends XmppActivity
}
}
- private void openConversation() {
- this.updateActionBarTitle();
- this.invalidateOptionsMenu();
- if (xmppConnectionServiceBound) {
- final Conversation conversation = getSelectedConversation();
- xmppConnectionService.getNotificationService().setOpenConversation(conversation);
- sendReadMarkerIfNecessary(conversation);
- }
- listAdapter.notifyDataSetChanged();
- }
+ private void openConversation() {
+ this.updateActionBarTitle();
+ this.invalidateOptionsMenu();
+ if (xmppConnectionServiceBound) {
+ final Conversation conversation = getSelectedConversation();
+ xmppConnectionService.getNotificationService().setOpenConversation(conversation);
+ sendReadMarkerIfNecessary(conversation);
+ }
+ listAdapter.notifyDataSetChanged();
+ }
- public void sendReadMarkerIfNecessary(final Conversation conversation) {
- if (!mActivityPaused && !mUnprocessedNewIntent && conversation != null) {
- xmppConnectionService.sendReadMarker(conversation);
- }
- }
+ public void sendReadMarkerIfNecessary(final Conversation conversation) {
+ if (!mActivityPaused && !mUnprocessedNewIntent && conversation != null) {
+ xmppConnectionService.sendReadMarker(conversation);
+ }
+ }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.conversations, menu);
- final MenuItem menuSecure = menu.findItem(R.id.action_security);
- final MenuItem menuArchiveChat = menu.findItem(R.id.action_archive_chat);
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.conversations, menu);
+ final MenuItem menuSecure = menu.findItem(R.id.action_security);
+ final MenuItem menuArchiveChat = menu.findItem(R.id.action_archive_chat);
final MenuItem menuArchiveMuc = menu.findItem(R.id.action_archive_muc);
- final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
- final MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history);
- final MenuItem menuAdd = menu.findItem(R.id.action_add);
- final MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
- final MenuItem menuMute = menu.findItem(R.id.action_mute);
- final MenuItem menuUnmute = menu.findItem(R.id.action_unmute);
- final MenuItem menuUpdater = menu.findItem(R.id.action_check_updates);
- final MenuItem menuInviteUser = menu.findItem(R.id.action_invite_user);
-
- if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) {
- menuArchiveChat.setVisible(false);
+ final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
+ final MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history);
+ final MenuItem menuAdd = menu.findItem(R.id.action_add);
+ final MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
+ final MenuItem menuMute = menu.findItem(R.id.action_mute);
+ final MenuItem menuUnmute = menu.findItem(R.id.action_unmute);
+ final MenuItem menuUpdater = menu.findItem(R.id.action_check_updates);
+ final MenuItem menuInviteUser = menu.findItem(R.id.action_invite_user);
+
+ if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) {
+ menuArchiveChat.setVisible(false);
menuArchiveMuc.setVisible(false);
- menuSecure.setVisible(false);
- menuInviteContact.setVisible(false);
- menuAttach.setVisible(false);
- menuClearHistory.setVisible(false);
- menuMute.setVisible(false);
- menuUnmute.setVisible(false);
- } else {
- menuAdd.setVisible(!isConversationsOverviewHideable());
- //hide settings, accounts and updater in all menus except in main window
- menuUpdater.setVisible(false);
- menuInviteUser.setVisible(false);
-
- if (this.getSelectedConversation() != null) {
+ menuSecure.setVisible(false);
+ menuInviteContact.setVisible(false);
+ menuAttach.setVisible(false);
+ menuClearHistory.setVisible(false);
+ menuMute.setVisible(false);
+ menuUnmute.setVisible(false);
+ } else {
+ menuAdd.setVisible(!isConversationsOverviewHideable());
+ //hide settings, accounts and updater in all menus except in main window
+ menuUpdater.setVisible(false);
+ menuInviteUser.setVisible(false);
+
+ if (this.getSelectedConversation() != null) {
if (this.getSelectedConversation().getMode() == Conversation.MODE_SINGLE) {
menuArchiveMuc.setVisible(false);
} else {
menuArchiveChat.setVisible(false);
}
- if (this.getSelectedConversation().getNextEncryption() != Message.ENCRYPTION_NONE) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- menuSecure.setIcon(R.drawable.ic_lock_white_24dp);
- } else {
- menuSecure.setIcon(R.drawable.ic_action_secure);
- }
- }
- if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
- menuAttach.setVisible(getSelectedConversation().getAccount().httpUploadAvailable() && getSelectedConversation().getMucOptions().participating());
- menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite());
- menuSecure.setVisible((Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices()); //only if pgp is supported we have a choice
- } else {
- menuSecure.setVisible(Config.multipleEncryptionChoices());
- }
- if (this.getSelectedConversation().isMuted()) {
- menuMute.setVisible(false);
- } else {
- menuUnmute.setVisible(false);
- }
- }
- }
- if (Config.supportOmemo()) {
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- View view = findViewById(R.id.action_security);
- if (view != null) {
- view.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- return quickOmemoDebugger(getSelectedConversation());
- }
- });
- }
- }
- });
- }
- return super.onCreateOptionsMenu(menu);
- }
-
- private boolean quickOmemoDebugger(Conversation c) {
- if (c != null) {
+ if (this.getSelectedConversation().getNextEncryption() != Message.ENCRYPTION_NONE) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ menuSecure.setIcon(R.drawable.ic_lock_white_24dp);
+ } else {
+ menuSecure.setIcon(R.drawable.ic_action_secure);
+ }
+ }
+ if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
+ menuAttach.setVisible(getSelectedConversation().getAccount().httpUploadAvailable() && getSelectedConversation().getMucOptions().participating());
+ menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite());
+ menuSecure.setVisible((Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices()); //only if pgp is supported we have a choice
+ } else {
+ menuSecure.setVisible(Config.multipleEncryptionChoices());
+ }
+ if (this.getSelectedConversation().isMuted()) {
+ menuMute.setVisible(false);
+ } else {
+ menuUnmute.setVisible(false);
+ }
+ }
+ }
+ if (Config.supportOmemo()) {
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ View view = findViewById(R.id.action_security);
+ if (view != null) {
+ view.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ return quickOmemoDebugger(getSelectedConversation());
+ }
+ });
+ }
+ }
+ });
+ }
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ private boolean quickOmemoDebugger(Conversation c) {
+ if (c != null) {
boolean single = c.getMode() == Conversation.MODE_SINGLE;
- AxolotlService axolotlService = c.getAccount().getAxolotlService();
- Pair<AxolotlService.AxolotlCapability,Jid> capabilityJidPair = axolotlService.isConversationAxolotlCapableDetailed(c);
- switch (capabilityJidPair.first) {
- case MISSING_PRESENCE:
- Toast.makeText(ConversationActivity.this,single ? getString(R.string.missing_presence_subscription) : getString(R.string.missing_presence_subscription_with_x,capabilityJidPair.second.toBareJid().toString()),Toast.LENGTH_SHORT).show();
- return true;
- case MISSING_KEYS:
- Toast.makeText(ConversationActivity.this,single ? getString(R.string.missing_omemo_keys) : getString(R.string.missing_keys_from_x,capabilityJidPair.second.toBareJid().toString()),Toast.LENGTH_SHORT).show();
+ AxolotlService axolotlService = c.getAccount().getAxolotlService();
+ Pair<AxolotlService.AxolotlCapability, Jid> capabilityJidPair = axolotlService.isConversationAxolotlCapableDetailed(c);
+ switch (capabilityJidPair.first) {
+ case MISSING_PRESENCE:
+ Toast.makeText(ConversationActivity.this, single ? getString(R.string.missing_presence_subscription) : getString(R.string.missing_presence_subscription_with_x, capabilityJidPair.second.toBareJid().toString()), Toast.LENGTH_SHORT).show();
return true;
- case WRONG_CONFIGURATION:
- Toast.makeText(ConversationActivity.this,R.string.wrong_conference_configuration, Toast.LENGTH_SHORT).show();
- return true;
- case NO_MEMBERS:
- Toast.makeText(ConversationActivity.this,R.string.this_conference_has_no_members, Toast.LENGTH_SHORT).show();
- return true;
- }
- }
- return false;
- }
-
- protected void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) {
- final Conversation conversation = getSelectedConversation();
- final Account account = conversation.getAccount();
- final OnPresenceSelected callback = new OnPresenceSelected() {
-
- @Override
- public void onPresenceSelected() {
- Intent intent = new Intent();
- boolean chooser = false;
- String fallbackPackageId = null;
- switch (attachmentChoice) {
- case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
- intent.setAction(Intent.ACTION_GET_CONTENT);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
- }
- intent.setType("image/*");
- chooser = true;
- break;
+ case MISSING_KEYS:
+ Toast.makeText(ConversationActivity.this, single ? getString(R.string.missing_omemo_keys) : getString(R.string.missing_keys_from_x, capabilityJidPair.second.toBareJid().toString()), Toast.LENGTH_SHORT).show();
+ return true;
+ case WRONG_CONFIGURATION:
+ Toast.makeText(ConversationActivity.this, R.string.wrong_conference_configuration, Toast.LENGTH_SHORT).show();
+ return true;
+ case NO_MEMBERS:
+ Toast.makeText(ConversationActivity.this, R.string.this_conference_has_no_members, Toast.LENGTH_SHORT).show();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) {
+ final Conversation conversation = getSelectedConversation();
+ final Account account = conversation.getAccount();
+ final OnPresenceSelected callback = new OnPresenceSelected() {
+
+ @Override
+ public void onPresenceSelected() {
+ Intent intent = new Intent();
+ boolean chooser = false;
+ String fallbackPackageId = null;
+ switch (attachmentChoice) {
+ case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
+ intent.setAction(Intent.ACTION_GET_CONTENT);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ }
+ intent.setType("image/*");
+ chooser = true;
+ break;
case ATTACHMENT_CHOICE_CHOOSE_VIDEO:
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("video/*");
chooser = true;
break;
- case ATTACHMENT_CHOICE_TAKE_PHOTO:
- Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
- intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
- mPendingPhotoUris.clear();
- mPendingPhotoUris.add(uri);
- break;
- case ATTACHMENT_CHOICE_CHOOSE_FILE:
- chooser = true;
- intent.setType("*/*");
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setAction(Intent.ACTION_GET_CONTENT);
- break;
+ case ATTACHMENT_CHOICE_TAKE_PHOTO:
+ Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
+ intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
+ mPendingPhotoUris.clear();
+ mPendingPhotoUris.add(uri);
+ break;
+ case ATTACHMENT_CHOICE_CHOOSE_FILE:
+ chooser = true;
+ intent.setType("*/*");
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setAction(Intent.ACTION_GET_CONTENT);
+ break;
case ATTACHMENT_CHOICE_RECORD_VOICE:
- startActivityForResult(new Intent(getApplicationContext(), RecordingActivity.class),attachmentChoice);
+ startActivityForResult(new Intent(getApplicationContext(), RecordingActivity.class), attachmentChoice);
break;
case ATTACHMENT_CHOICE_LOCATION:
- startActivityForResult(new Intent(getApplicationContext(), ShareLocationActivity.class),attachmentChoice);
- break;
- }
- if (intent.resolveActivity(getPackageManager()) != null) {
+ startActivityForResult(new Intent(getApplicationContext(), ShareLocationActivity.class), attachmentChoice);
+ break;
+ }
+ if (intent.resolveActivity(getPackageManager()) != null) {
Log.d(Config.LOGTAG, "Attachment: " + attachmentChoice);
- if (chooser) {
- startActivityForResult(
- Intent.createChooser(intent, getString(R.string.perform_action_with)),
- attachmentChoice);
- } else {
- startActivityForResult(intent, attachmentChoice);
- }
- } else if (fallbackPackageId != null) {
- startActivity(getInstallApkIntent(fallbackPackageId));
- }
- }
- };
- if ((account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) && encryption != Message.ENCRYPTION_OTR) {
- conversation.setNextCounterpart(null);
- callback.onPresenceSelected();
- } else {
- selectPresence(conversation, callback);
- }
- }
-
- private Intent getInstallApkIntent(final String packageId) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse("market://details?id=" + packageId));
- if (intent.resolveActivity(getPackageManager()) != null) {
- return intent;
- } else {
- intent.setData(Uri.parse("http://play.google.com/store/apps/details?id=" + packageId));
- return intent;
- }
- }
-
- public void attachFile(final int attachmentChoice) {
- if (attachmentChoice != ATTACHMENT_CHOICE_LOCATION) {
- if (!hasStoragePermission(attachmentChoice)) {
- return;
- }
- }
+ if (chooser) {
+ startActivityForResult(
+ Intent.createChooser(intent, getString(R.string.perform_action_with)),
+ attachmentChoice);
+ } else {
+ startActivityForResult(intent, attachmentChoice);
+ }
+ } else if (fallbackPackageId != null) {
+ startActivity(getInstallApkIntent(fallbackPackageId));
+ }
+ }
+ };
+ if ((account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) && encryption != Message.ENCRYPTION_OTR) {
+ conversation.setNextCounterpart(null);
+ callback.onPresenceSelected();
+ } else {
+ selectPresence(conversation, callback);
+ }
+ }
+
+ private Intent getInstallApkIntent(final String packageId) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse("market://details?id=" + packageId));
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ return intent;
+ } else {
+ intent.setData(Uri.parse("http://play.google.com/store/apps/details?id=" + packageId));
+ return intent;
+ }
+ }
+
+ public void attachFile(final int attachmentChoice) {
+ if (attachmentChoice != ATTACHMENT_CHOICE_LOCATION) {
+ if (!hasStoragePermission(attachmentChoice)) {
+ return;
+ }
+ }
if (attachmentChoice == ATTACHMENT_CHOICE_RECORD_VOICE) {
if (!hasMicPermission(attachmentChoice)) {
return;
@@ -748,128 +748,128 @@ public class ConversationActivity extends XmppActivity
}
}
switch (attachmentChoice) {
- case ATTACHMENT_CHOICE_LOCATION:
- getPreferences().edit().putString("recently_used_quick_action", "location").apply();
- break;
- case ATTACHMENT_CHOICE_RECORD_VOICE:
- getPreferences().edit().putString("recently_used_quick_action", "voice").apply();
- break;
- case ATTACHMENT_CHOICE_TAKE_PHOTO:
- getPreferences().edit().putString("recently_used_quick_action", "photo").apply();
- break;
- case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
- getPreferences().edit().putString("recently_used_quick_action", "picture").apply();
- break;
- }
- final Conversation conversation = getSelectedConversation();
- final int encryption = conversation.getNextEncryption();
- final int mode = conversation.getMode();
- if (encryption == Message.ENCRYPTION_PGP) {
- if (hasPgp()) {
- if (mode == Conversation.MODE_SINGLE && conversation.getContact().getPgpKeyId() != 0) {
- xmppConnectionService.getPgpEngine().hasKey(
- conversation.getContact(),
- new UiCallback<Contact>() {
-
- @Override
- public void userInputRequried(PendingIntent pi, Contact contact) {
- ConversationActivity.this.runIntent(pi, attachmentChoice);
- }
-
- @Override
- public void success(Contact contact) {
- selectPresenceToAttachFile(attachmentChoice, encryption);
- }
-
- @Override
- public void error(int error, Contact contact) {
- replaceToast(getString(error));
- }
- });
- } else if (mode == Conversation.MODE_MULTI && conversation.getMucOptions().pgpKeysInUse()) {
- if (!conversation.getMucOptions().everybodyHasKeys()) {
- Toast warning = Toast
- .makeText(this,
- R.string.missing_public_keys,
- Toast.LENGTH_LONG);
- warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
- warning.show();
- }
- selectPresenceToAttachFile(attachmentChoice, encryption);
- } else {
- final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
- .findFragmentByTag("conversation");
- if (fragment != null) {
- fragment.showNoPGPKeyDialog(false,
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
+ case ATTACHMENT_CHOICE_LOCATION:
+ getPreferences().edit().putString("recently_used_quick_action", "location").apply();
+ break;
+ case ATTACHMENT_CHOICE_RECORD_VOICE:
+ getPreferences().edit().putString("recently_used_quick_action", "voice").apply();
+ break;
+ case ATTACHMENT_CHOICE_TAKE_PHOTO:
+ getPreferences().edit().putString("recently_used_quick_action", "photo").apply();
+ break;
+ case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
+ getPreferences().edit().putString("recently_used_quick_action", "picture").apply();
+ break;
+ }
+ final Conversation conversation = getSelectedConversation();
+ final int encryption = conversation.getNextEncryption();
+ final int mode = conversation.getMode();
+ if (encryption == Message.ENCRYPTION_PGP) {
+ if (hasPgp()) {
+ if (mode == Conversation.MODE_SINGLE && conversation.getContact().getPgpKeyId() != 0) {
+ xmppConnectionService.getPgpEngine().hasKey(
+ conversation.getContact(),
+ new UiCallback<Contact>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Contact contact) {
+ ConversationActivity.this.runIntent(pi, attachmentChoice);
+ }
+
+ @Override
+ public void success(Contact contact) {
+ selectPresenceToAttachFile(attachmentChoice, encryption);
+ }
+
+ @Override
+ public void error(int error, Contact contact) {
+ replaceToast(getString(error));
+ }
+ });
+ } else if (mode == Conversation.MODE_MULTI && conversation.getMucOptions().pgpKeysInUse()) {
+ if (!conversation.getMucOptions().everybodyHasKeys()) {
+ Toast warning = Toast
+ .makeText(this,
+ R.string.missing_public_keys,
+ Toast.LENGTH_LONG);
+ warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
+ warning.show();
+ }
+ selectPresenceToAttachFile(attachmentChoice, encryption);
+ } else {
+ final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
+ .findFragmentByTag("conversation");
+ if (fragment != null) {
+ fragment.showNoPGPKeyDialog(false,
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
conversation.setNextEncryption(Message.ENCRYPTION_NONE);
xmppConnectionService.updateConversation(conversation);
selectPresenceToAttachFile(attachmentChoice, Message.ENCRYPTION_NONE);
- }
- });
- }
- }
- } else {
- showInstallPgpDialog();
- }
- } else {
- if (encryption != Message.ENCRYPTION_AXOLOTL || !trustKeysIfNeeded(REQUEST_TRUST_KEYS_MENU, attachmentChoice)) {
- selectPresenceToAttachFile(attachmentChoice, encryption);
- }
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
- if (grantResults.length > 0)
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- if (requestCode == REQUEST_START_DOWNLOAD) {
- if (this.mPendingDownloadableMessage != null) {
- startDownloadable(this.mPendingDownloadableMessage);
- }
- } else {
- attachFile(requestCode);
- }
- } else {
- Toast.makeText(this, R.string.no_permission, Toast.LENGTH_SHORT).show();
- }
- }
-
- public void startDownloadable(Message message) {
- if (!hasStoragePermission(ConversationActivity.REQUEST_START_DOWNLOAD)) {
- this.mPendingDownloadableMessage = message;
- return;
- }
- Transferable transferable = message.getTransferable();
- if (transferable != null) {
- if (!transferable.start()) {
- Toast.makeText(this, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show();
- }
- } else if (message.treatAsDownloadable() != Message.Decision.NEVER) {
- xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message, true);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- showConversationsOverview();
- return true;
- } else if (item.getItemId() == R.id.action_add) {
- startActivity(new Intent(this, StartConversationActivity.class));
- return true;
- } else if (getSelectedConversation() != null) {
- switch (item.getItemId()) {
- case R.id.action_attach_file:
- attachFileDialog();
- break;
- case R.id.action_archive_chat:
- this.endConversation(getSelectedConversation());
- break;
+ }
+ });
+ }
+ }
+ } else {
+ showInstallPgpDialog();
+ }
+ } else {
+ if (encryption != Message.ENCRYPTION_AXOLOTL || !trustKeysIfNeeded(REQUEST_TRUST_KEYS_MENU, attachmentChoice)) {
+ selectPresenceToAttachFile(attachmentChoice, encryption);
+ }
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+ if (grantResults.length > 0)
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (requestCode == REQUEST_START_DOWNLOAD) {
+ if (this.mPendingDownloadableMessage != null) {
+ startDownloadable(this.mPendingDownloadableMessage);
+ }
+ } else {
+ attachFile(requestCode);
+ }
+ } else {
+ Toast.makeText(this, R.string.no_permission, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public void startDownloadable(Message message) {
+ if (!hasStoragePermission(ConversationActivity.REQUEST_START_DOWNLOAD)) {
+ this.mPendingDownloadableMessage = message;
+ return;
+ }
+ Transferable transferable = message.getTransferable();
+ if (transferable != null) {
+ if (!transferable.start()) {
+ Toast.makeText(this, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show();
+ }
+ } else if (message.treatAsDownloadable() != Message.Decision.NEVER) {
+ xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message, true);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ showConversationsOverview();
+ return true;
+ } else if (item.getItemId() == R.id.action_add) {
+ startActivity(new Intent(this, StartConversationActivity.class));
+ return true;
+ } else if (getSelectedConversation() != null) {
+ switch (item.getItemId()) {
+ case R.id.action_attach_file:
+ attachFileDialog();
+ break;
+ case R.id.action_archive_chat:
+ this.endConversation(getSelectedConversation());
+ break;
case R.id.action_archive_muc:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.action_end_conversation_muc));
@@ -884,80 +884,80 @@ public class ConversationActivity extends XmppActivity
});
builder.create().show();
break;
- case R.id.action_invite:
- inviteToConversation(getSelectedConversation());
- break;
- case R.id.action_security:
- selectEncryptionDialog(getSelectedConversation());
- break;
- case R.id.action_clear_history:
- clearHistoryDialog(getSelectedConversation());
- break;
- case R.id.action_mute:
- muteConversationDialog(getSelectedConversation());
- break;
- case R.id.action_unmute:
- unmuteConversation(getSelectedConversation());
- break;
- case R.id.action_block:
- BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
- break;
- case R.id.action_unblock:
- BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
- break;
- default:
- break;
- }
- return super.onOptionsItemSelected(item);
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- public void endConversation(Conversation conversation) {
- endConversation(conversation, true, true);
- }
-
- public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) {
- if (showOverview) {
- showConversationsOverview();
- }
- xmppConnectionService.archiveConversation(conversation);
- if (reinit) {
- if (conversationList.size() > 0) {
- setSelectedConversation(conversationList.get(0));
- this.mConversationFragment.reInit(getSelectedConversation());
- } else {
- setSelectedConversation(null);
- if (mRedirected.compareAndSet(false, true)) {
- Intent intent = new Intent(this, StartConversationActivity.class);
- intent.putExtra("init", true);
- startActivity(intent);
- finish();
- }
- }
- }
- }
-
- @SuppressLint("InflateParams")
- protected void clearHistoryDialog(final Conversation conversation) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(getString(R.string.clear_conversation_history));
- View dialogView = getLayoutInflater().inflate(
- R.layout.dialog_clear_history, null);
+ case R.id.action_invite:
+ inviteToConversation(getSelectedConversation());
+ break;
+ case R.id.action_security:
+ selectEncryptionDialog(getSelectedConversation());
+ break;
+ case R.id.action_clear_history:
+ clearHistoryDialog(getSelectedConversation());
+ break;
+ case R.id.action_mute:
+ muteConversationDialog(getSelectedConversation());
+ break;
+ case R.id.action_unmute:
+ unmuteConversation(getSelectedConversation());
+ break;
+ case R.id.action_block:
+ BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
+ break;
+ case R.id.action_unblock:
+ BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
+ break;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ public void endConversation(Conversation conversation) {
+ endConversation(conversation, true, true);
+ }
+
+ public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) {
+ if (showOverview) {
+ showConversationsOverview();
+ }
+ xmppConnectionService.archiveConversation(conversation);
+ if (reinit) {
+ if (conversationList.size() > 0) {
+ setSelectedConversation(conversationList.get(0));
+ this.mConversationFragment.reInit(getSelectedConversation());
+ } else {
+ setSelectedConversation(null);
+ if (mRedirected.compareAndSet(false, true)) {
+ Intent intent = new Intent(this, StartConversationActivity.class);
+ intent.putExtra("init", true);
+ startActivity(intent);
+ finish();
+ }
+ }
+ }
+ }
+
+ @SuppressLint("InflateParams")
+ protected void clearHistoryDialog(final Conversation conversation) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.clear_conversation_history));
+ View dialogView = getLayoutInflater().inflate(
+ R.layout.dialog_clear_history, null);
final CheckBox endConversationCheckBox = (CheckBox) dialogView
.findViewById(R.id.end_conversation_checkbox);
if (conversation.getMode() == Conversation.MODE_SINGLE) {
endConversationCheckBox.setVisibility(View.VISIBLE);
}
- builder.setView(dialogView);
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.delete_messages),
- new OnClickListener() {
+ builder.setView(dialogView);
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton(getString(R.string.delete_messages),
+ new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation);
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation);
if (conversation.getMode() == Conversation.MODE_SINGLE) {
if (endConversationCheckBox.isChecked()) {
endConversation(conversation);
@@ -969,415 +969,415 @@ public class ConversationActivity extends XmppActivity
updateConversationList();
ConversationActivity.this.mConversationFragment.updateMessages();
}
- }
- });
- builder.create().show();
- }
-
- protected void attachFileDialog() {
- View menuAttachFile = findViewById(R.id.action_attach_file);
- if (menuAttachFile == null) {
- return;
- }
- PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
- attachFilePopup.inflate(R.menu.attachment_choices);
- if (new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(getPackageManager()) == null) {
- attachFilePopup.getMenu().findItem(R.id.attach_record_voice).setVisible(false);
- }
- if (new Intent("de.pixart.messenger.location.request").resolveActivity(getPackageManager()) == null) {
- attachFilePopup.getMenu().findItem(R.id.attach_location).setVisible(false);
- }
- attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.attach_choose_picture:
- attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
- break;
- case R.id.attach_take_picture:
- attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
- break;
+ }
+ });
+ builder.create().show();
+ }
+
+ protected void attachFileDialog() {
+ View menuAttachFile = findViewById(R.id.action_attach_file);
+ if (menuAttachFile == null) {
+ return;
+ }
+ PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
+ attachFilePopup.inflate(R.menu.attachment_choices);
+ if (new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(getPackageManager()) == null) {
+ attachFilePopup.getMenu().findItem(R.id.attach_record_voice).setVisible(false);
+ }
+ if (new Intent("de.pixart.messenger.location.request").resolveActivity(getPackageManager()) == null) {
+ attachFilePopup.getMenu().findItem(R.id.attach_location).setVisible(false);
+ }
+ attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.attach_choose_picture:
+ attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
+ break;
+ case R.id.attach_take_picture:
+ attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
+ break;
case R.id.attach_choose_video:
attachFile(ATTACHMENT_CHOICE_CHOOSE_VIDEO);
break;
- case R.id.attach_choose_file:
- attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
- break;
- case R.id.attach_record_voice:
- attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
- break;
- case R.id.attach_location:
- attachFile(ATTACHMENT_CHOICE_LOCATION);
- break;
- }
- return false;
- }
- });
- attachFilePopup.show();
- }
-
- public void verifyOtrSessionDialog(final Conversation conversation, View view) {
- if (!conversation.hasValidOtrSession() || conversation.getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
- Toast.makeText(this, R.string.otr_session_not_started, Toast.LENGTH_LONG).show();
- return;
- }
- if (view == null) {
- return;
- }
- PopupMenu popup = new PopupMenu(this, view);
- popup.inflate(R.menu.verification_choices);
- popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem menuItem) {
- Intent intent = new Intent(ConversationActivity.this, VerifyOTRActivity.class);
- intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
- intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
- intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
- switch (menuItem.getItemId()) {
- case R.id.scan_fingerprint:
- intent.putExtra("mode", VerifyOTRActivity.MODE_SCAN_FINGERPRINT);
- break;
- case R.id.ask_question:
- intent.putExtra("mode", VerifyOTRActivity.MODE_ASK_QUESTION);
- break;
- case R.id.manual_verification:
- intent.putExtra("mode", VerifyOTRActivity.MODE_MANUAL_VERIFICATION);
- break;
- }
- startActivity(intent);
- return true;
- }
- });
- popup.show();
- }
-
- protected void selectEncryptionDialog(final Conversation conversation) {
- View menuItemView = findViewById(R.id.action_security);
- if (menuItemView == null) {
- return;
- }
- PopupMenu popup = new PopupMenu(this, menuItemView);
- final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
- .findFragmentByTag("conversation");
- if (fragment != null) {
- popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.encryption_choice_none:
- conversation.setNextEncryption(Message.ENCRYPTION_NONE);
- item.setChecked(true);
- break;
- case R.id.encryption_choice_otr:
- conversation.setNextEncryption(Message.ENCRYPTION_OTR);
- item.setChecked(true);
- break;
- case R.id.encryption_choice_pgp:
- if (hasPgp()) {
- if (conversation.getAccount().getPgpSignature() != null) {
- conversation.setNextEncryption(Message.ENCRYPTION_PGP);
- item.setChecked(true);
- } else {
- announcePgp(conversation.getAccount(), conversation, onOpenPGPKeyPublished);
- }
- } else {
- showInstallPgpDialog();
- }
- break;
- case R.id.encryption_choice_axolotl:
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(conversation.getAccount())
- + "Enabled axolotl for Contact " + conversation.getContact().getJid());
- conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL);
- item.setChecked(true);
- break;
- default:
- conversation.setNextEncryption(Message.ENCRYPTION_NONE);
- break;
- }
+ case R.id.attach_choose_file:
+ attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
+ break;
+ case R.id.attach_record_voice:
+ attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
+ break;
+ case R.id.attach_location:
+ attachFile(ATTACHMENT_CHOICE_LOCATION);
+ break;
+ }
+ return false;
+ }
+ });
+ attachFilePopup.show();
+ }
+
+ public void verifyOtrSessionDialog(final Conversation conversation, View view) {
+ if (!conversation.hasValidOtrSession() || conversation.getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
+ Toast.makeText(this, R.string.otr_session_not_started, Toast.LENGTH_LONG).show();
+ return;
+ }
+ if (view == null) {
+ return;
+ }
+ PopupMenu popup = new PopupMenu(this, view);
+ popup.inflate(R.menu.verification_choices);
+ popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ Intent intent = new Intent(ConversationActivity.this, VerifyOTRActivity.class);
+ intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
+ intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
+ intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
+ switch (menuItem.getItemId()) {
+ case R.id.scan_fingerprint:
+ intent.putExtra("mode", VerifyOTRActivity.MODE_SCAN_FINGERPRINT);
+ break;
+ case R.id.ask_question:
+ intent.putExtra("mode", VerifyOTRActivity.MODE_ASK_QUESTION);
+ break;
+ case R.id.manual_verification:
+ intent.putExtra("mode", VerifyOTRActivity.MODE_MANUAL_VERIFICATION);
+ break;
+ }
+ startActivity(intent);
+ return true;
+ }
+ });
+ popup.show();
+ }
+
+ protected void selectEncryptionDialog(final Conversation conversation) {
+ View menuItemView = findViewById(R.id.action_security);
+ if (menuItemView == null) {
+ return;
+ }
+ PopupMenu popup = new PopupMenu(this, menuItemView);
+ final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
+ .findFragmentByTag("conversation");
+ if (fragment != null) {
+ popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.encryption_choice_none:
+ conversation.setNextEncryption(Message.ENCRYPTION_NONE);
+ item.setChecked(true);
+ break;
+ case R.id.encryption_choice_otr:
+ conversation.setNextEncryption(Message.ENCRYPTION_OTR);
+ item.setChecked(true);
+ break;
+ case R.id.encryption_choice_pgp:
+ if (hasPgp()) {
+ if (conversation.getAccount().getPgpSignature() != null) {
+ conversation.setNextEncryption(Message.ENCRYPTION_PGP);
+ item.setChecked(true);
+ } else {
+ announcePgp(conversation.getAccount(), conversation, onOpenPGPKeyPublished);
+ }
+ } else {
+ showInstallPgpDialog();
+ }
+ break;
+ case R.id.encryption_choice_axolotl:
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(conversation.getAccount())
+ + "Enabled axolotl for Contact " + conversation.getContact().getJid());
+ conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL);
+ item.setChecked(true);
+ break;
+ default:
+ conversation.setNextEncryption(Message.ENCRYPTION_NONE);
+ break;
+ }
xmppConnectionService.updateConversation(conversation);
- fragment.updateChatMsgHint();
- invalidateOptionsMenu();
- refreshUi();
- return true;
- }
- });
- popup.inflate(R.menu.encryption_choices);
- MenuItem otr = popup.getMenu().findItem(R.id.encryption_choice_otr);
- MenuItem none = popup.getMenu().findItem(R.id.encryption_choice_none);
- MenuItem pgp = popup.getMenu().findItem(R.id.encryption_choice_pgp);
- MenuItem axolotl = popup.getMenu().findItem(R.id.encryption_choice_axolotl);
- pgp.setVisible(Config.supportOpenPgp());
- none.setVisible(Config.supportUnencrypted() || conversation.getMode() == Conversation.MODE_MULTI);
- otr.setVisible(Config.supportOtr());
- axolotl.setVisible(Config.supportOmemo());
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- otr.setVisible(false);
- }
- if (!conversation.getAccount().getAxolotlService().isConversationAxolotlCapable(conversation)) {
- axolotl.setEnabled(false);
- }
- switch (conversation.getNextEncryption()) {
- case Message.ENCRYPTION_NONE:
- none.setChecked(true);
- break;
- case Message.ENCRYPTION_OTR:
- otr.setChecked(true);
- break;
- case Message.ENCRYPTION_PGP:
- pgp.setChecked(true);
- break;
- case Message.ENCRYPTION_AXOLOTL:
- axolotl.setChecked(true);
- break;
- default:
- none.setChecked(true);
- break;
- }
- popup.show();
- }
- }
-
- protected void muteConversationDialog(final Conversation conversation) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.disable_notifications);
- final int[] durations = getResources().getIntArray(R.array.mute_options_durations);
- builder.setItems(R.array.mute_options_descriptions,
- new OnClickListener() {
-
- @Override
- public void onClick(final DialogInterface dialog, final int which) {
- final long till;
- if (durations[which] == -1) {
- till = Long.MAX_VALUE;
- } else {
- till = System.currentTimeMillis() + (durations[which] * 1000);
- }
- conversation.setMutedTill(till);
+ fragment.updateChatMsgHint();
+ invalidateOptionsMenu();
+ refreshUi();
+ return true;
+ }
+ });
+ popup.inflate(R.menu.encryption_choices);
+ MenuItem otr = popup.getMenu().findItem(R.id.encryption_choice_otr);
+ MenuItem none = popup.getMenu().findItem(R.id.encryption_choice_none);
+ MenuItem pgp = popup.getMenu().findItem(R.id.encryption_choice_pgp);
+ MenuItem axolotl = popup.getMenu().findItem(R.id.encryption_choice_axolotl);
+ pgp.setVisible(Config.supportOpenPgp());
+ none.setVisible(Config.supportUnencrypted() || conversation.getMode() == Conversation.MODE_MULTI);
+ otr.setVisible(Config.supportOtr());
+ axolotl.setVisible(Config.supportOmemo());
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ otr.setVisible(false);
+ }
+ if (!conversation.getAccount().getAxolotlService().isConversationAxolotlCapable(conversation)) {
+ axolotl.setEnabled(false);
+ }
+ switch (conversation.getNextEncryption()) {
+ case Message.ENCRYPTION_NONE:
+ none.setChecked(true);
+ break;
+ case Message.ENCRYPTION_OTR:
+ otr.setChecked(true);
+ break;
+ case Message.ENCRYPTION_PGP:
+ pgp.setChecked(true);
+ break;
+ case Message.ENCRYPTION_AXOLOTL:
+ axolotl.setChecked(true);
+ break;
+ default:
+ none.setChecked(true);
+ break;
+ }
+ popup.show();
+ }
+ }
+
+ protected void muteConversationDialog(final Conversation conversation) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.disable_notifications);
+ final int[] durations = getResources().getIntArray(R.array.mute_options_durations);
+ builder.setItems(R.array.mute_options_descriptions,
+ new OnClickListener() {
+
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ final long till;
+ if (durations[which] == -1) {
+ till = Long.MAX_VALUE;
+ } else {
+ till = System.currentTimeMillis() + (durations[which] * 1000);
+ }
+ conversation.setMutedTill(till);
ConversationActivity.this.xmppConnectionService.updateConversation(conversation);
- updateConversationList();
- ConversationActivity.this.mConversationFragment.updateMessages();
- invalidateOptionsMenu();
- }
- });
- builder.create().show();
- }
-
- public void unmuteConversation(final Conversation conversation) {
- conversation.setMutedTill(0);
+ updateConversationList();
+ ConversationActivity.this.mConversationFragment.updateMessages();
+ invalidateOptionsMenu();
+ }
+ });
+ builder.create().show();
+ }
+
+ public void unmuteConversation(final Conversation conversation) {
+ conversation.setMutedTill(0);
this.xmppConnectionService.updateConversation(conversation);
- updateConversationList();
- ConversationActivity.this.mConversationFragment.updateMessages();
- invalidateOptionsMenu();
- }
-
- @Override
- public void onBackPressed() {
- if (!isConversationsOverviewVisable()) {
- showConversationsOverview();
- } else {
- moveTaskToBack(true);
- }
- }
-
- @Override
- public boolean onKeyUp(int key, KeyEvent event) {
- int rotation = getWindowManager().getDefaultDisplay().getRotation();
- final int upKey;
- final int downKey;
- switch (rotation) {
- case Surface.ROTATION_90:
- upKey = KeyEvent.KEYCODE_DPAD_LEFT;
- downKey = KeyEvent.KEYCODE_DPAD_RIGHT;
- break;
- case Surface.ROTATION_180:
- upKey = KeyEvent.KEYCODE_DPAD_DOWN;
- downKey = KeyEvent.KEYCODE_DPAD_UP;
- break;
- case Surface.ROTATION_270:
- upKey = KeyEvent.KEYCODE_DPAD_RIGHT;
- downKey = KeyEvent.KEYCODE_DPAD_LEFT;
- break;
- default:
- upKey = KeyEvent.KEYCODE_DPAD_UP;
- downKey = KeyEvent.KEYCODE_DPAD_DOWN;
- }
- final boolean modifier = event.isCtrlPressed() || (event.getMetaState() & KeyEvent.META_ALT_LEFT_ON) != 0;
- if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) {
- toggleConversationsOverview();
- return true;
- } else if (modifier && key == KeyEvent.KEYCODE_SPACE) {
- startActivity(new Intent(this, StartConversationActivity.class));
- return true;
- } else if (modifier && key == downKey) {
- if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
- showConversationsOverview();
- }
- return selectDownConversation();
- } else if (modifier && key == upKey) {
- if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
- showConversationsOverview();
- }
- return selectUpConversation();
- } else if (modifier && key == KeyEvent.KEYCODE_1) {
- return openConversationByIndex(0);
- } else if (modifier && key == KeyEvent.KEYCODE_2) {
- return openConversationByIndex(1);
- } else if (modifier && key == KeyEvent.KEYCODE_3) {
- return openConversationByIndex(2);
- } else if (modifier && key == KeyEvent.KEYCODE_4) {
- return openConversationByIndex(3);
- } else if (modifier && key == KeyEvent.KEYCODE_5) {
- return openConversationByIndex(4);
- } else if (modifier && key == KeyEvent.KEYCODE_6) {
- return openConversationByIndex(5);
- } else if (modifier && key == KeyEvent.KEYCODE_7) {
- return openConversationByIndex(6);
- } else if (modifier && key == KeyEvent.KEYCODE_8) {
- return openConversationByIndex(7);
- } else if (modifier && key == KeyEvent.KEYCODE_9) {
- return openConversationByIndex(8);
- } else if (modifier && key == KeyEvent.KEYCODE_0) {
- return openConversationByIndex(9);
- } else {
- return super.onKeyUp(key, event);
- }
- }
-
- private void toggleConversationsOverview() {
- if (isConversationsOverviewVisable()) {
- hideConversationsOverview();
- if (mConversationFragment != null) {
- mConversationFragment.setFocusOnInputField();
- }
- } else {
- showConversationsOverview();
- }
- }
-
- private boolean selectUpConversation() {
- if (this.mSelectedConversation != null) {
- int index = this.conversationList.indexOf(this.mSelectedConversation);
- if (index > 0) {
- return openConversationByIndex(index - 1);
- }
- }
- return false;
- }
-
- private boolean selectDownConversation() {
- if (this.mSelectedConversation != null) {
- int index = this.conversationList.indexOf(this.mSelectedConversation);
- if (index != -1 && index < this.conversationList.size() - 1) {
- return openConversationByIndex(index + 1);
- }
- }
- return false;
- }
-
- private boolean openConversationByIndex(int index) {
- try {
- this.conversationWasSelectedByKeyboard = true;
- setSelectedConversation(this.conversationList.get(index));
- this.mConversationFragment.reInit(getSelectedConversation());
- if (index > listView.getLastVisiblePosition() - 1 || index < listView.getFirstVisiblePosition() + 1) {
- this.listView.setSelection(index);
- }
- openConversation();
- return true;
- } catch (IndexOutOfBoundsException e) {
- return false;
- }
- }
-
- @Override
- protected void onNewIntent(final Intent intent) {
- if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
- mOpenConversation = null;
+ updateConversationList();
+ ConversationActivity.this.mConversationFragment.updateMessages();
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (!isConversationsOverviewVisable()) {
+ showConversationsOverview();
+ } else {
+ moveTaskToBack(true);
+ }
+ }
+
+ @Override
+ public boolean onKeyUp(int key, KeyEvent event) {
+ int rotation = getWindowManager().getDefaultDisplay().getRotation();
+ final int upKey;
+ final int downKey;
+ switch (rotation) {
+ case Surface.ROTATION_90:
+ upKey = KeyEvent.KEYCODE_DPAD_LEFT;
+ downKey = KeyEvent.KEYCODE_DPAD_RIGHT;
+ break;
+ case Surface.ROTATION_180:
+ upKey = KeyEvent.KEYCODE_DPAD_DOWN;
+ downKey = KeyEvent.KEYCODE_DPAD_UP;
+ break;
+ case Surface.ROTATION_270:
+ upKey = KeyEvent.KEYCODE_DPAD_RIGHT;
+ downKey = KeyEvent.KEYCODE_DPAD_LEFT;
+ break;
+ default:
+ upKey = KeyEvent.KEYCODE_DPAD_UP;
+ downKey = KeyEvent.KEYCODE_DPAD_DOWN;
+ }
+ final boolean modifier = event.isCtrlPressed() || (event.getMetaState() & KeyEvent.META_ALT_LEFT_ON) != 0;
+ if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) {
+ toggleConversationsOverview();
+ return true;
+ } else if (modifier && key == KeyEvent.KEYCODE_SPACE) {
+ startActivity(new Intent(this, StartConversationActivity.class));
+ return true;
+ } else if (modifier && key == downKey) {
+ if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
+ showConversationsOverview();
+ }
+ return selectDownConversation();
+ } else if (modifier && key == upKey) {
+ if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) {
+ showConversationsOverview();
+ }
+ return selectUpConversation();
+ } else if (modifier && key == KeyEvent.KEYCODE_1) {
+ return openConversationByIndex(0);
+ } else if (modifier && key == KeyEvent.KEYCODE_2) {
+ return openConversationByIndex(1);
+ } else if (modifier && key == KeyEvent.KEYCODE_3) {
+ return openConversationByIndex(2);
+ } else if (modifier && key == KeyEvent.KEYCODE_4) {
+ return openConversationByIndex(3);
+ } else if (modifier && key == KeyEvent.KEYCODE_5) {
+ return openConversationByIndex(4);
+ } else if (modifier && key == KeyEvent.KEYCODE_6) {
+ return openConversationByIndex(5);
+ } else if (modifier && key == KeyEvent.KEYCODE_7) {
+ return openConversationByIndex(6);
+ } else if (modifier && key == KeyEvent.KEYCODE_8) {
+ return openConversationByIndex(7);
+ } else if (modifier && key == KeyEvent.KEYCODE_9) {
+ return openConversationByIndex(8);
+ } else if (modifier && key == KeyEvent.KEYCODE_0) {
+ return openConversationByIndex(9);
+ } else {
+ return super.onKeyUp(key, event);
+ }
+ }
+
+ private void toggleConversationsOverview() {
+ if (isConversationsOverviewVisable()) {
+ hideConversationsOverview();
+ if (mConversationFragment != null) {
+ mConversationFragment.setFocusOnInputField();
+ }
+ } else {
+ showConversationsOverview();
+ }
+ }
+
+ private boolean selectUpConversation() {
+ if (this.mSelectedConversation != null) {
+ int index = this.conversationList.indexOf(this.mSelectedConversation);
+ if (index > 0) {
+ return openConversationByIndex(index - 1);
+ }
+ }
+ return false;
+ }
+
+ private boolean selectDownConversation() {
+ if (this.mSelectedConversation != null) {
+ int index = this.conversationList.indexOf(this.mSelectedConversation);
+ if (index != -1 && index < this.conversationList.size() - 1) {
+ return openConversationByIndex(index + 1);
+ }
+ }
+ return false;
+ }
+
+ private boolean openConversationByIndex(int index) {
+ try {
+ this.conversationWasSelectedByKeyboard = true;
+ setSelectedConversation(this.conversationList.get(index));
+ this.mConversationFragment.reInit(getSelectedConversation());
+ if (index > listView.getLastVisiblePosition() - 1 || index < listView.getFirstVisiblePosition() + 1) {
+ this.listView.setSelection(index);
+ }
+ openConversation();
+ return true;
+ } catch (IndexOutOfBoundsException e) {
+ return false;
+ }
+ }
+
+ @Override
+ protected void onNewIntent(final Intent intent) {
+ if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
+ mOpenConversation = null;
mUnprocessedNewIntent = true;
- if (xmppConnectionServiceBound) {
- handleViewConversationIntent(intent);
- intent.setAction(Intent.ACTION_MAIN);
- } else {
- setIntent(intent);
- }
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
- this.mRedirected.set(false);
- if (this.xmppConnectionServiceBound) {
- this.onBackendConnected();
- }
- if (conversationList.size() >= 1) {
- this.onConversationUpdate();
- }
- }
-
- @Override
- public void onPause() {
- listView.discardUndo();
- super.onPause();
- this.mActivityPaused = true;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- final int theme = findTheme();
- final boolean usingEnterKey = usingEnterKey();
- if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) {
- recreate();
- }
- this.mActivityPaused = false;
-
- if (!isConversationsOverviewVisable() || !isConversationsOverviewHideable()) {
- sendReadMarkerIfNecessary(getSelectedConversation());
- }
- }
-
- @Override
- public void onSaveInstanceState(final Bundle savedInstanceState) {
- Conversation conversation = getSelectedConversation();
- if (conversation != null) {
- savedInstanceState.putString(STATE_OPEN_CONVERSATION, conversation.getUuid());
+ if (xmppConnectionServiceBound) {
+ handleViewConversationIntent(intent);
+ intent.setAction(Intent.ACTION_MAIN);
+ } else {
+ setIntent(intent);
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ this.mRedirected.set(false);
+ if (this.xmppConnectionServiceBound) {
+ this.onBackendConnected();
+ }
+ if (conversationList.size() >= 1) {
+ this.onConversationUpdate();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ listView.discardUndo();
+ super.onPause();
+ this.mActivityPaused = true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ final int theme = findTheme();
+ final boolean usingEnterKey = usingEnterKey();
+ if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) {
+ recreate();
+ }
+ this.mActivityPaused = false;
+
+ if (!isConversationsOverviewVisable() || !isConversationsOverviewHideable()) {
+ sendReadMarkerIfNecessary(getSelectedConversation());
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(final Bundle savedInstanceState) {
+ Conversation conversation = getSelectedConversation();
+ if (conversation != null) {
+ savedInstanceState.putString(STATE_OPEN_CONVERSATION, conversation.getUuid());
Pair<Integer, Integer> scrollPosition = mConversationFragment.getScrollPosition();
if (scrollPosition != null) {
savedInstanceState.putInt(STATE_FIRST_VISIBLE, scrollPosition.first);
savedInstanceState.putInt(STATE_OFFSET_FROM_TOP, scrollPosition.second);
}
} else {
- savedInstanceState.remove(STATE_OPEN_CONVERSATION);
- }
- savedInstanceState.putBoolean(STATE_PANEL_OPEN, isConversationsOverviewVisable());
- if (this.mPendingImageUris.size() >= 1) {
- Log.d(Config.LOGTAG,"ConversationActivity.onSaveInstanceState() - saving pending image uri");
+ savedInstanceState.remove(STATE_OPEN_CONVERSATION);
+ }
+ savedInstanceState.putBoolean(STATE_PANEL_OPEN, isConversationsOverviewVisable());
+ if (this.mPendingImageUris.size() >= 1) {
+ Log.d(Config.LOGTAG, "ConversationActivity.onSaveInstanceState() - saving pending image uri");
savedInstanceState.putString(STATE_PENDING_IMAGE_URI, this.mPendingImageUris.get(0).toString());
} else if (this.mPendingPhotoUris.size() >= 1) {
savedInstanceState.putString(STATE_PENDING_PHOTO_URI, this.mPendingPhotoUris.get(0).toString());
} else {
- savedInstanceState.remove(STATE_PENDING_IMAGE_URI);
+ savedInstanceState.remove(STATE_PENDING_IMAGE_URI);
savedInstanceState.remove(STATE_PENDING_PHOTO_URI);
- }
- super.onSaveInstanceState(savedInstanceState);
- }
+ }
+ super.onSaveInstanceState(savedInstanceState);
+ }
- private void clearPending() {
- mPendingImageUris.clear();
- mPendingFileUris.clear();
+ private void clearPending() {
+ mPendingImageUris.clear();
+ mPendingFileUris.clear();
mPendingVideoUris.clear();
mPendingPhotoUris.clear();
- mPendingGeoUri = null;
- mPostponedActivityResult = null;
- }
+ mPendingGeoUri = null;
+ mPostponedActivityResult = null;
+ }
- @Override
- void onBackendConnected() {
- this.xmppConnectionService.getNotificationService().setIsInForeground(true);
- updateConversationList();
+ @Override
+ void onBackendConnected() {
+ this.xmppConnectionService.getNotificationService().setIsInForeground(true);
+ updateConversationList();
Bundle extras = getIntent().getExtras();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@@ -1390,13 +1390,13 @@ public class ConversationActivity extends XmppActivity
Log.d(Config.LOGTAG, "Device is running Android < SDK 23, no restart required: " + FirstStartTime);
}
- if (mPendingConferenceInvite != null) {
- if (mPendingConferenceInvite.execute(this)) {
- mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
- mToast.show();
- }
- mPendingConferenceInvite = null;
- }
+ if (mPendingConferenceInvite != null) {
+ if (mPendingConferenceInvite.execute(this)) {
+ mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
+ mToast.show();
+ }
+ mPendingConferenceInvite = null;
+ }
if (FirstStartTime == 0) {
Log.d(Config.LOGTAG, "First start time: " + FirstStartTime + ", restarting App");
@@ -1414,72 +1414,72 @@ public class ConversationActivity extends XmppActivity
startActivity(intent);
System.exit(0);
}
- final Intent intent = getIntent();
- if (xmppConnectionService.getAccounts().size() == 0) {
- if (mRedirected.compareAndSet(false, true)) {
- if (Config.X509_VERIFICATION) {
+ final Intent intent = getIntent();
+ if (xmppConnectionService.getAccounts().size() == 0) {
+ if (mRedirected.compareAndSet(false, true)) {
+ if (Config.X509_VERIFICATION) {
startActivity(new Intent(this, ManageAccountActivity.class));
} else if (Config.MAGIC_CREATE_DOMAIN != null) {
Log.d(Config.LOGTAG, "First start time: " + FirstStartTime);
startActivity(new Intent(this, WelcomeActivity.class));
} else {
Intent editAccount = new Intent(this, EditAccountActivity.class);
- editAccount.putExtra("init",true);
+ editAccount.putExtra("init", true);
startActivity(editAccount);
}
finish();
- }
- } else if (conversationList.size() <= 0) {
- if (mRedirected.compareAndSet(false, true)) {
- Account pendingAccount = xmppConnectionService.getPendingAccount();
- if (pendingAccount == null) {
- Intent startConversationActivity = new Intent(this, StartConversationActivity.class);
- intent.putExtra("init", true);
- startActivity(startConversationActivity);
- } else {
- switchToAccount(pendingAccount, true);
- }
- finish();
- }
- } else if (selectConversationByUuid(mOpenConversation)) {
- if (mPanelOpen) {
- showConversationsOverview();
- } else {
- if (isConversationsOverviewHideable()) {
- openConversation();
- updateActionBarTitle(true);
- }
- }
+ }
+ } else if (conversationList.size() <= 0) {
+ if (mRedirected.compareAndSet(false, true)) {
+ Account pendingAccount = xmppConnectionService.getPendingAccount();
+ if (pendingAccount == null) {
+ Intent startConversationActivity = new Intent(this, StartConversationActivity.class);
+ intent.putExtra("init", true);
+ startActivity(startConversationActivity);
+ } else {
+ switchToAccount(pendingAccount, true);
+ }
+ finish();
+ }
+ } else if (selectConversationByUuid(mOpenConversation)) {
+ if (mPanelOpen) {
+ showConversationsOverview();
+ } else {
+ if (isConversationsOverviewHideable()) {
+ openConversation();
+ updateActionBarTitle(true);
+ }
+ }
if (this.mConversationFragment.reInit(getSelectedConversation())) {
Log.d(Config.LOGTAG, "setting scroll position on fragment");
this.mConversationFragment.setScrollPosition(mScrollPosition);
}
mOpenConversation = null;
} else if (intent != null && ACTION_VIEW_CONVERSATION.equals(intent.getAction())) {
- clearPending();
- handleViewConversationIntent(intent);
- intent.setAction(Intent.ACTION_MAIN);
- } else if (getSelectedConversation() == null) {
- showConversationsOverview();
- clearPending();
- setSelectedConversation(conversationList.get(0));
- this.mConversationFragment.reInit(getSelectedConversation());
- } else {
- this.mConversationFragment.messageListAdapter.updatePreferences();
- this.mConversationFragment.messagesView.invalidateViews();
- this.mConversationFragment.setupIme();
- }
+ clearPending();
+ handleViewConversationIntent(intent);
+ intent.setAction(Intent.ACTION_MAIN);
+ } else if (getSelectedConversation() == null) {
+ showConversationsOverview();
+ clearPending();
+ setSelectedConversation(conversationList.get(0));
+ this.mConversationFragment.reInit(getSelectedConversation());
+ } else {
+ this.mConversationFragment.messageListAdapter.updatePreferences();
+ this.mConversationFragment.messagesView.invalidateViews();
+ this.mConversationFragment.setupIme();
+ }
if (xmppConnectionService.getAccounts().size() != 0) {
if (xmppConnectionService.hasInternetConnection()) {
if (xmppConnectionService.isWIFI() || (xmppConnectionService.isMobile() && !xmppConnectionService.isMobileRoaming()))
- AppUpdate();
+ AppUpdate();
}
}
- if (this.mPostponedActivityResult != null) {
- this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
- }
+ if (this.mPostponedActivityResult != null) {
+ this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
+ }
final boolean stopping;
if (Build.VERSION.SDK_INT >= 17) {
@@ -1488,193 +1488,193 @@ public class ConversationActivity extends XmppActivity
stopping = isFinishing();
}
- if (!forbidProcessingPendings) {
+ if (!forbidProcessingPendings) {
int ImageUrisCount = mPendingImageUris.size();
if (ImageUrisCount == 1) {
Uri uri = mPendingImageUris.get(0);
- Log.d(Config.LOGTAG,"ConversationActivity.onBackendConnected() - attaching image to conversations. stopping="+Boolean.toString(stopping));
+ Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching image to conversations. stopping=" + Boolean.toString(stopping));
attachImageToConversation(getSelectedConversation(), uri);
} else {
for (Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
Uri foo = i.next();
- Log.d(Config.LOGTAG,"ConversationActivity.onBackendConnected() - attaching images to conversations. stopping="+Boolean.toString(stopping));
+ Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching images to conversations. stopping=" + Boolean.toString(stopping));
attachImagesToConversation(getSelectedConversation(), foo);
}
}
for (Iterator<Uri> i = mPendingPhotoUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onBackendConnected() - attaching photo to conversations. stopping="+Boolean.toString(stopping));
+ Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching photo to conversations. stopping=" + Boolean.toString(stopping));
attachPhotoToConversation(getSelectedConversation(), i.next());
}
for (Iterator<Uri> i = mPendingVideoUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onBackendConnected() - attaching video to conversations. stopping="+Boolean.toString(stopping));
+ Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching video to conversations. stopping=" + Boolean.toString(stopping));
attachVideoToConversation(getSelectedConversation(), i.next());
}
- for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onBackendConnected() - attaching file to conversations. stopping="+Boolean.toString(stopping));
+ for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
+ Log.d(Config.LOGTAG, "ConversationActivity.onBackendConnected() - attaching file to conversations. stopping=" + Boolean.toString(stopping));
attachFileToConversation(getSelectedConversation(), i.next());
- }
-
- if (mPendingGeoUri != null) {
- attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
- mPendingGeoUri = null;
- }
- }
- forbidProcessingPendings = false;
-
- if (!ExceptionHelper.checkForCrash(this, this.xmppConnectionService)) {
- openBatteryOptimizationDialogIfNeeded();
- }
- }
-
- private void handleViewConversationIntent(final Intent intent) {
- final String uuid = intent.getStringExtra(CONVERSATION);
- final String downloadUuid = intent.getStringExtra(EXTRA_DOWNLOAD_UUID);
- final String text = intent.getStringExtra(TEXT);
- final String nick = intent.getStringExtra(NICK);
- final boolean pm = intent.getBooleanExtra(PRIVATE_MESSAGE, false);
- if (selectConversationByUuid(uuid)) {
- this.mConversationFragment.reInit(getSelectedConversation());
- if (nick != null) {
- if (pm) {
- Jid jid = getSelectedConversation().getJid();
- try {
- Jid next = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), nick);
- this.mConversationFragment.privateMessageWith(next);
- } catch (final InvalidJidException ignored) {
- //do nothing
- }
- } else {
- this.mConversationFragment.highlightInConference(nick);
- }
- } else {
- this.mConversationFragment.appendText(text);
- }
- hideConversationsOverview();
+ }
+
+ if (mPendingGeoUri != null) {
+ attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
+ mPendingGeoUri = null;
+ }
+ }
+ forbidProcessingPendings = false;
+
+ if (!ExceptionHelper.checkForCrash(this, this.xmppConnectionService)) {
+ openBatteryOptimizationDialogIfNeeded();
+ }
+ }
+
+ private void handleViewConversationIntent(final Intent intent) {
+ final String uuid = intent.getStringExtra(CONVERSATION);
+ final String downloadUuid = intent.getStringExtra(EXTRA_DOWNLOAD_UUID);
+ final String text = intent.getStringExtra(TEXT);
+ final String nick = intent.getStringExtra(NICK);
+ final boolean pm = intent.getBooleanExtra(PRIVATE_MESSAGE, false);
+ if (selectConversationByUuid(uuid)) {
+ this.mConversationFragment.reInit(getSelectedConversation());
+ if (nick != null) {
+ if (pm) {
+ Jid jid = getSelectedConversation().getJid();
+ try {
+ Jid next = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), nick);
+ this.mConversationFragment.privateMessageWith(next);
+ } catch (final InvalidJidException ignored) {
+ //do nothing
+ }
+ } else {
+ this.mConversationFragment.highlightInConference(nick);
+ }
+ } else {
+ this.mConversationFragment.appendText(text);
+ }
+ hideConversationsOverview();
mUnprocessedNewIntent = false;
- openConversation();
- if (mContentView instanceof SlidingPaneLayout) {
- updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet
- }
- if (downloadUuid != null) {
- final Message message = mSelectedConversation.findMessageWithFileAndUuid(downloadUuid);
- if (message != null) {
- startDownloadable(message);
- }
- } else {
+ openConversation();
+ if (mContentView instanceof SlidingPaneLayout) {
+ updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet
+ }
+ if (downloadUuid != null) {
+ final Message message = mSelectedConversation.findMessageWithFileAndUuid(downloadUuid);
+ if (message != null) {
+ startDownloadable(message);
+ }
+ } else {
mUnprocessedNewIntent = false;
}
- }
- }
-
- private boolean selectConversationByUuid(String uuid) {
- if (uuid == null) {
- return false;
- }
- for (Conversation aConversationList : conversationList) {
- if (aConversationList.getUuid().equals(uuid)) {
- setSelectedConversation(aConversationList);
- return true;
- }
- }
- return false;
- }
-
- @Override
- protected void unregisterListeners() {
- super.unregisterListeners();
- xmppConnectionService.getNotificationService().setOpenConversation(null);
- }
-
- @SuppressLint("NewApi")
- private static List<Uri> extractUriFromIntent(final Intent intent) {
- List<Uri> uris = new ArrayList<>();
- if (intent == null) {
- return uris;
- }
- Uri uri = intent.getData();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && uri == null) {
- final ClipData clipData = intent.getClipData();
- if (clipData != null) {
- for (int i = 0; i < clipData.getItemCount(); ++i) {
- uris.add(clipData.getItemAt(i).getUri());
- }
- }
- } else {
- uris.add(uri);
- }
- return uris;
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK) {
- if (requestCode == REQUEST_DECRYPT_PGP) {
- mConversationFragment.onActivityResult(requestCode, resultCode, data);
- } else if (requestCode == REQUEST_CHOOSE_PGP_ID) {
- // the user chose OpenPGP for encryption and selected his key in the PGP provider
- if (xmppConnectionServiceBound) {
- if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
- // associate selected PGP keyId with the account
- mSelectedConversation.getAccount().setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
- // we need to announce the key as described in XEP-027
- announcePgp(mSelectedConversation.getAccount(), null, onOpenPGPKeyPublished);
- } else {
- choosePgpSignId(mSelectedConversation.getAccount());
- }
- this.mPostponedActivityResult = null;
- } else {
- this.mPostponedActivityResult = new Pair<>(requestCode, data);
- }
- } else if (requestCode == REQUEST_ANNOUNCE_PGP) {
- if (xmppConnectionServiceBound) {
- announcePgp(mSelectedConversation.getAccount(), mSelectedConversation, onOpenPGPKeyPublished);
- this.mPostponedActivityResult = null;
- } else {
- this.mPostponedActivityResult = new Pair<>(requestCode, data);
- }
- } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
- mPendingImageUris.clear();
- mPendingImageUris.addAll(extractUriFromIntent(data));
- int ImageUrisCount = mPendingImageUris.size();
- if (xmppConnectionServiceBound) {
+ }
+ }
+
+ private boolean selectConversationByUuid(String uuid) {
+ if (uuid == null) {
+ return false;
+ }
+ for (Conversation aConversationList : conversationList) {
+ if (aConversationList.getUuid().equals(uuid)) {
+ setSelectedConversation(aConversationList);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void unregisterListeners() {
+ super.unregisterListeners();
+ xmppConnectionService.getNotificationService().setOpenConversation(null);
+ }
+
+ @SuppressLint("NewApi")
+ private static List<Uri> extractUriFromIntent(final Intent intent) {
+ List<Uri> uris = new ArrayList<>();
+ if (intent == null) {
+ return uris;
+ }
+ Uri uri = intent.getData();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && uri == null) {
+ final ClipData clipData = intent.getClipData();
+ if (clipData != null) {
+ for (int i = 0; i < clipData.getItemCount(); ++i) {
+ uris.add(clipData.getItemAt(i).getUri());
+ }
+ }
+ } else {
+ uris.add(uri);
+ }
+ return uris;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_DECRYPT_PGP) {
+ mConversationFragment.onActivityResult(requestCode, resultCode, data);
+ } else if (requestCode == REQUEST_CHOOSE_PGP_ID) {
+ // the user chose OpenPGP for encryption and selected his key in the PGP provider
+ if (xmppConnectionServiceBound) {
+ if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
+ // associate selected PGP keyId with the account
+ mSelectedConversation.getAccount().setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
+ // we need to announce the key as described in XEP-027
+ announcePgp(mSelectedConversation.getAccount(), null, onOpenPGPKeyPublished);
+ } else {
+ choosePgpSignId(mSelectedConversation.getAccount());
+ }
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+ } else if (requestCode == REQUEST_ANNOUNCE_PGP) {
+ if (xmppConnectionServiceBound) {
+ announcePgp(mSelectedConversation.getAccount(), mSelectedConversation, onOpenPGPKeyPublished);
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+ } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
+ mPendingImageUris.clear();
+ mPendingImageUris.addAll(extractUriFromIntent(data));
+ int ImageUrisCount = mPendingImageUris.size();
+ if (xmppConnectionServiceBound) {
if (ImageUrisCount == 1) {
Uri uri = mPendingImageUris.get(0);
- Log.d(Config.LOGTAG,"ConversationActivity.onActivityResult() - attaching image to conversations. CHOOSE_IMAGE");
+ Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching image to conversations. CHOOSE_IMAGE");
attachImageToConversation(getSelectedConversation(), uri);
} else {
for (Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onActivityResult() - attaching images to conversations. CHOOSE_IMAGES");
+ Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching images to conversations. CHOOSE_IMAGES");
attachImagesToConversation(getSelectedConversation(), i.next());
}
}
- }
- } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) {
- final List<Uri> uris = extractUriFromIntent(data);
- final Conversation c = getSelectedConversation();
- final OnPresenceSelected callback = new OnPresenceSelected() {
- @Override
- public void onPresenceSelected() {
- mPendingFileUris.clear();
- mPendingFileUris.addAll(uris);
- if (xmppConnectionServiceBound) {
- for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onActivityResult() - attaching file to conversations. CHOOSE_FILE/RECORD_VOICE");
- attachFileToConversation(c, i.next());
- }
- }
- }
- };
- if (c == null || c.getMode() == Conversation.MODE_MULTI
- || FileBackend.allFilesUnderSize(this, uris, getMaxHttpUploadSize(c))
- || c.getNextEncryption() == Message.ENCRYPTION_OTR) {
- callback.onPresenceSelected();
- } else {
- selectPresence(c, callback);
- }
+ }
+ } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) {
+ final List<Uri> uris = extractUriFromIntent(data);
+ final Conversation c = getSelectedConversation();
+ final OnPresenceSelected callback = new OnPresenceSelected() {
+ @Override
+ public void onPresenceSelected() {
+ mPendingFileUris.clear();
+ mPendingFileUris.addAll(uris);
+ if (xmppConnectionServiceBound) {
+ for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) {
+ Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching file to conversations. CHOOSE_FILE/RECORD_VOICE");
+ attachFileToConversation(c, i.next());
+ }
+ }
+ }
+ };
+ if (c == null || c.getMode() == Conversation.MODE_MULTI
+ || FileBackend.allFilesUnderSize(this, uris, getMaxHttpUploadSize(c))
+ || c.getNextEncryption() == Message.ENCRYPTION_OTR) {
+ callback.onPresenceSelected();
+ } else {
+ selectPresence(c, callback);
+ }
} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_VIDEO) {
final List<Uri> uris = extractUriFromIntent(data);
final Conversation c = getSelectedConversation();
@@ -1685,7 +1685,7 @@ public class ConversationActivity extends XmppActivity
mPendingVideoUris.addAll(uris);
if (xmppConnectionServiceBound) {
for (Iterator<Uri> i = mPendingVideoUris.iterator(); i.hasNext(); i.remove()) {
- Log.d(Config.LOGTAG,"ConversationActivity.onActivityResult() - attaching video to conversations. CHOOSE_VIDEO");
+ Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching video to conversations. CHOOSE_VIDEO");
attachVideoToConversation(c, i.next());
}
}
@@ -1698,163 +1698,163 @@ public class ConversationActivity extends XmppActivity
} else {
selectPresence(c, callback);
}
- } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
- if (mPendingPhotoUris.size() == 1) {
+ } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
+ if (mPendingPhotoUris.size() == 1) {
Uri uri = FileBackend.getIndexableTakePhotoUri(mPendingPhotoUris.get(0));
- mPendingPhotoUris.set(0, uri);
+ mPendingPhotoUris.set(0, uri);
if (xmppConnectionServiceBound) {
- Log.d(Config.LOGTAG,"ConversationActivity.onActivityResult() - attaching photo to conversations. TAKE_PHOTO");
+ Log.d(Config.LOGTAG, "ConversationActivity.onActivityResult() - attaching photo to conversations. TAKE_PHOTO");
attachPhotoToConversation(getSelectedConversation(), uri);
- mPendingPhotoUris.clear();
- }
- Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ mPendingPhotoUris.clear();
+ }
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(uri);
- sendBroadcast(intent);
- } else {
- mPendingPhotoUris.clear();
- }
+ sendBroadcast(intent);
+ } else {
+ mPendingPhotoUris.clear();
+ }
} else if (requestCode == ATTACHMENT_CHOICE_LOCATION) {
- double latitude = data.getDoubleExtra("latitude", 0);
- double longitude = data.getDoubleExtra("longitude", 0);
- this.mPendingGeoUri = Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude));
- if (xmppConnectionServiceBound) {
- attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
- this.mPendingGeoUri = null;
- }
- } else if (requestCode == REQUEST_TRUST_KEYS_TEXT || requestCode == REQUEST_TRUST_KEYS_MENU) {
- this.forbidProcessingPendings = !xmppConnectionServiceBound;
- if (xmppConnectionServiceBound) {
- mConversationFragment.onActivityResult(requestCode, resultCode, data);
- this.mPostponedActivityResult = null;
- } else {
- this.mPostponedActivityResult = new Pair<>(requestCode, data);
- }
-
- }
- } else {
- mPendingImageUris.clear();
- mPendingFileUris.clear();
+ double latitude = data.getDoubleExtra("latitude", 0);
+ double longitude = data.getDoubleExtra("longitude", 0);
+ this.mPendingGeoUri = Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude));
+ if (xmppConnectionServiceBound) {
+ attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
+ this.mPendingGeoUri = null;
+ }
+ } else if (requestCode == REQUEST_TRUST_KEYS_TEXT || requestCode == REQUEST_TRUST_KEYS_MENU) {
+ this.forbidProcessingPendings = !xmppConnectionServiceBound;
+ if (xmppConnectionServiceBound) {
+ mConversationFragment.onActivityResult(requestCode, resultCode, data);
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+
+ }
+ } else {
+ mPendingImageUris.clear();
+ mPendingFileUris.clear();
mPendingPhotoUris.clear();
- if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
- mConversationFragment.onActivityResult(requestCode, resultCode, data);
- }
- if (requestCode == REQUEST_BATTERY_OP) {
- setNeverAskForBatteryOptimizationsAgain();
- }
- }
- }
-
- private long getMaxHttpUploadSize(Conversation conversation) {
+ if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
+ mConversationFragment.onActivityResult(requestCode, resultCode, data);
+ }
+ if (requestCode == REQUEST_BATTERY_OP) {
+ setNeverAskForBatteryOptimizationsAgain();
+ }
+ }
+ }
+
+ private long getMaxHttpUploadSize(Conversation conversation) {
final XmppConnection connection = conversation.getAccount().getXmppConnection();
return connection == null ? -1 : connection.getFeatures().getMaxHttpUploadSize();
- }
-
- private void setNeverAskForBatteryOptimizationsAgain() {
- getPreferences().edit().putBoolean("show_battery_optimization", false).commit();
- }
-
- private void openBatteryOptimizationDialogIfNeeded() {
- if (hasAccountWithoutPush()
- && isOptimizingBattery()
- && getPreferences().getBoolean("show_battery_optimization", true)) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.battery_optimizations_enabled);
- builder.setMessage(R.string.battery_optimizations_enabled_dialog);
- builder.setPositiveButton(R.string.next, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
- Uri uri = Uri.parse("package:" + getPackageName());
- intent.setData(uri);
- try {
- startActivityForResult(intent, REQUEST_BATTERY_OP);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(ConversationActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
- }
- }
- });
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- setNeverAskForBatteryOptimizationsAgain();
- }
- });
- }
- builder.create().show();
- }
- }
-
- private boolean hasAccountWithoutPush() {
- for(Account account : xmppConnectionService.getAccounts()) {
- if (account.getStatus() != Account.State.DISABLED
- && !xmppConnectionService.getPushManagementService().available(account)) {
- return true;
- }
- }
- return false;
- }
-
- private void attachLocationToConversation(Conversation conversation, Uri uri) {
- if (conversation == null) {
- return;
- }
- xmppConnectionService.attachLocationToConversation(conversation, uri, new UiCallback<Message>() {
-
- @Override
- public void success(Message message) {
- xmppConnectionService.sendMessage(message);
- }
-
- @Override
- public void error(int errorCode, Message object) {
-
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
-
- }
- });
- }
-
- private void attachFileToConversation(Conversation conversation, Uri uri) {
- if (conversation == null) {
- return;
- }
- final Toast prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_file), Toast.LENGTH_LONG);
- prepareFileToast.show();
- xmppConnectionService.attachFileToConversation(conversation, uri, new UiCallback<Message>() {
- @Override
- public void success(Message message) {
- hidePrepareFileToast(prepareFileToast);
- xmppConnectionService.sendMessage(message);
- }
-
- @Override
- public void error(final int errorCode, Message message) {
- hidePrepareFileToast(prepareFileToast);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- replaceToast(getString(errorCode));
- }
- });
-
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Message message) {
- hidePrepareFileToast(prepareFileToast);
- }
- });
- }
+ }
+
+ private void setNeverAskForBatteryOptimizationsAgain() {
+ getPreferences().edit().putBoolean("show_battery_optimization", false).commit();
+ }
+
+ private void openBatteryOptimizationDialogIfNeeded() {
+ if (hasAccountWithoutPush()
+ && isOptimizingBattery()
+ && getPreferences().getBoolean("show_battery_optimization", true)) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.battery_optimizations_enabled);
+ builder.setMessage(R.string.battery_optimizations_enabled_dialog);
+ builder.setPositiveButton(R.string.next, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ Uri uri = Uri.parse("package:" + getPackageName());
+ intent.setData(uri);
+ try {
+ startActivityForResult(intent, REQUEST_BATTERY_OP);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(ConversationActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ setNeverAskForBatteryOptimizationsAgain();
+ }
+ });
+ }
+ builder.create().show();
+ }
+ }
+
+ private boolean hasAccountWithoutPush() {
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.getStatus() != Account.State.DISABLED
+ && !xmppConnectionService.getPushManagementService().available(account)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void attachLocationToConversation(Conversation conversation, Uri uri) {
+ if (conversation == null) {
+ return;
+ }
+ xmppConnectionService.attachLocationToConversation(conversation, uri, new UiCallback<Message>() {
+
+ @Override
+ public void success(Message message) {
+ xmppConnectionService.sendMessage(message);
+ }
+
+ @Override
+ public void error(int errorCode, Message object) {
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
+
+ }
+ });
+ }
+
+ private void attachFileToConversation(Conversation conversation, Uri uri) {
+ if (conversation == null) {
+ return;
+ }
+ final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_file), Toast.LENGTH_LONG);
+ prepareFileToast.show();
+ xmppConnectionService.attachFileToConversation(conversation, uri, new UiCallback<Message>() {
+ @Override
+ public void success(Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ xmppConnectionService.sendMessage(message);
+ }
+
+ @Override
+ public void error(final int errorCode, Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ replaceToast(getString(errorCode));
+ }
+ });
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ }
+ });
+ }
private void attachVideoToConversation(Conversation conversation, final Uri uri) {
if (conversation == null) {
return;
}
- final Toast prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_video), Toast.LENGTH_LONG);
+ final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_video), Toast.LENGTH_LONG);
prepareFileToast.show();
xmppConnectionService.attachVideoToConversation(conversation, uri, new UiCallback<Message>() {
@Override
@@ -1886,7 +1886,7 @@ public class ConversationActivity extends XmppActivity
if (conversation == null) {
return;
}
- final Toast prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_image), Toast.LENGTH_LONG);
+ final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
prepareFileToast.show();
xmppConnectionService.attachImageToConversation(conversation, uri,
new UiCallback<Message>() {
@@ -1915,327 +1915,327 @@ public class ConversationActivity extends XmppActivity
});
}
- private void attachImagesToConversation(Conversation conversation, Uri uri) {
- if (conversation == null) {
- return;
- }
- final Toast prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_image), Toast.LENGTH_LONG);
- prepareFileToast.show();
- xmppConnectionService.attachImageToConversation(conversation, uri,
- new UiCallback<Message>() {
-
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
- hidePrepareFileToast(prepareFileToast);
- }
-
- @Override
- public void success(Message message) {
- hidePrepareFileToast(prepareFileToast);
- xmppConnectionService.sendMessage(message);
- }
-
- @Override
- public void error(final int error, Message message) {
- hidePrepareFileToast(prepareFileToast);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- replaceToast(getString(error));
- }
- });
- }
- });
- }
-
- private void attachImageToConversation(Conversation conversation, Uri uri) {
- if (conversation == null) {
- return;
- }
- final Conversation conversation_preview = conversation;
- final Uri uri_preview = uri;
- Bitmap bitmap = BitmapFactory.decodeFile(FileUtils.getPath(this, uri));
- File file = new File(FileUtils.getPath(this, uri));
- ExifInterface exif = null;
- try {
- exif = new ExifInterface(file.toString());
- } catch (IOException e) {
- e.printStackTrace();
- }
- int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
- Log.d(Config.LOGTAG, "EXIF: " + orientation);
- Bitmap rotated_image = null;
- Log.d(Config.LOGTAG, "Rotate image");
- rotated_image = FileBackend.rotateBitmap(file, bitmap, orientation);
- if (rotated_image != null) {
- int scaleSize = 600;
- int originalWidth = rotated_image.getWidth();
- int originalHeight = rotated_image.getHeight();
- int newWidth = -1;
- int newHeight = -1;
- float multFactor;
- if (originalHeight > originalWidth) {
- newHeight = scaleSize;
- multFactor = (float) originalWidth / (float) originalHeight;
- newWidth = (int) (newHeight * multFactor);
- } else if (originalWidth > originalHeight) {
- newWidth = scaleSize;
- multFactor = (float) originalHeight / (float) originalWidth;
- newHeight = (int) (newWidth * multFactor);
- } else if (originalHeight == originalWidth) {
- newHeight = scaleSize;
- newWidth = scaleSize;
- }
- Log.d(Config.LOGTAG, "Scaling preview image from " + originalHeight + "px x " + originalWidth + "px to " + newHeight + "px x " + newWidth + "px");
- Bitmap preview = Bitmap.createScaledBitmap(rotated_image, newWidth, newHeight, false);
- ImageView ImagePreview = new ImageView(this);
- LinearLayout.LayoutParams vp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
- ImagePreview.setLayoutParams(vp);
- ImagePreview.setMaxWidth(newWidth);
- ImagePreview.setMaxHeight(newHeight);
- //ImagePreview.setScaleType(ImageView.ScaleType.FIT_XY);
- //ImagePreview.setAdjustViewBounds(true);
- ImagePreview.setPadding(5, 5, 5, 5);
- ImagePreview.setImageBitmap(preview);
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setView(ImagePreview);
- builder.setTitle(R.string.send_image);
- builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(final DialogInterface dialog, final int which) {
- final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
- prepareFileToast.show();
- xmppConnectionService.attachImageToConversation(conversation_preview, uri_preview,
- new UiCallback<Message>() {
-
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
- hidePrepareFileToast(prepareFileToast);
- }
-
- @Override
- public void success(Message message) {
- hidePrepareFileToast(prepareFileToast);
- xmppConnectionService.sendMessage(message);
- }
-
- @Override
- public void error(final int error, Message message) {
- hidePrepareFileToast(prepareFileToast);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- replaceToast(getString(error));
- }
- });
- }
- });
- }
- });
- builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- mPendingImageUris.clear();
- }
- });
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- mPendingImageUris.clear();
- }
- });
- }
- AlertDialog alertDialog = builder.create();
- alertDialog.show();
- } else {
- Toast.makeText(getApplicationContext(), getText(R.string.error_file_not_found), Toast.LENGTH_LONG).show();
- }
- }
-
- private void hidePrepareFileToast(final Toast prepareFileToast) {
- if (prepareFileToast != null) {
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- prepareFileToast.cancel();
- }
- });
- }
- }
-
- public void updateConversationList() {
- xmppConnectionService
- .populateWithOrderedConversations(conversationList);
- if (swipedConversation != null) {
- if (swipedConversation.isRead()) {
- conversationList.remove(swipedConversation);
- } else {
- listView.discardUndo();
- }
- }
- listAdapter.notifyDataSetChanged();
- }
-
- public void runIntent(PendingIntent pi, int requestCode) {
- try {
- this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
- null, 0, 0, 0);
- } catch (final SendIntentException ignored) {
- }
- }
-
- public void encryptTextMessage(Message message) {
- xmppConnectionService.getPgpEngine().encrypt(message,
- new UiCallback<Message>() {
-
- @Override
- public void userInputRequried(PendingIntent pi,Message message) {
- ConversationActivity.this.runIntent(pi,ConversationActivity.REQUEST_SEND_MESSAGE);
- }
-
- @Override
- public void success(Message message) {
- message.setEncryption(Message.ENCRYPTION_DECRYPTED);
- xmppConnectionService.sendMessage(message);
- if (mConversationFragment != null) {
- mConversationFragment.messageSent();
- }
- }
-
- @Override
- public void error(final int error, Message message) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ConversationActivity.this,
- R.string.unable_to_connect_to_keychain,
- Toast.LENGTH_SHORT
- ).show();
- }
- });
- }
- });
- }
-
- public boolean useSendButtonToIndicateStatus() {
- return getPreferences().getBoolean("send_button_status", true);
- }
-
- public boolean indicateReceived() {
- return getPreferences().getBoolean("indicate_received", true);
- }
-
- public boolean useWhiteBackground() {
- return getPreferences().getBoolean("use_white_background", false);
- }
-
- protected boolean trustKeysIfNeeded(int requestCode) {
- return trustKeysIfNeeded(requestCode, ATTACHMENT_CHOICE_INVALID);
- }
-
- protected boolean trustKeysIfNeeded(int requestCode, int attachmentChoice) {
- AxolotlService axolotlService = mSelectedConversation.getAccount().getAxolotlService();
- final List<Jid> targets = axolotlService.getCryptoTargets(mSelectedConversation);
- boolean hasUnaccepted = !mSelectedConversation.getAcceptedCryptoTargets().containsAll(targets);
- boolean hasUndecidedOwn = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided()).isEmpty();
- boolean hasUndecidedContacts = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), targets).isEmpty();
- boolean hasPendingKeys = !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty();
- boolean hasNoTrustedKeys = axolotlService.anyTargetHasNoTrustedKeys(targets);
- if(hasUndecidedOwn || hasUndecidedContacts || hasPendingKeys || hasNoTrustedKeys || hasUnaccepted) {
- axolotlService.createSessionsIfNeeded(mSelectedConversation);
- Intent intent = new Intent(getApplicationContext(), TrustKeysActivity.class);
- String[] contacts = new String[targets.size()];
- for(int i = 0; i < contacts.length; ++i) {
- contacts[i] = targets.get(i).toString();
- }
- intent.putExtra("contacts", contacts);
- intent.putExtra(EXTRA_ACCOUNT, mSelectedConversation.getAccount().getJid().toBareJid().toString());
- intent.putExtra("choice", attachmentChoice);
- intent.putExtra("conversation",mSelectedConversation.getUuid());
- startActivityForResult(intent, requestCode);
- return true;
- } else {
- return false;
- }
- }
-
- @Override
- protected void refreshUiReal() {
- updateConversationList();
- if (conversationList.size() > 0) {
- if (!this.mConversationFragment.isAdded()) {
- Log.d(Config.LOGTAG,"fragment NOT added to activity. detached="+Boolean.toString(mConversationFragment.isDetached()));
- }
- ConversationActivity.this.mConversationFragment.updateMessages();
- updateActionBarTitle();
- invalidateOptionsMenu();
- } else {
- Log.d(Config.LOGTAG,"not updating conversations fragment because conversations list size was 0");
- }
- }
-
- @Override
- public void onAccountUpdate() {
- this.refreshUi();
- }
-
- @Override
- public void onConversationUpdate() {
- this.refreshUi();
- }
-
- @Override
- public void onRosterUpdate() {
- this.refreshUi();
- }
-
- @Override
- public void OnUpdateBlocklist(Status status) {
- this.refreshUi();
- }
-
- public void unblockConversation(final Blockable conversation) {
- xmppConnectionService.sendUnblockRequest(conversation);
- }
-
- public boolean enterIsSend() {
- return getPreferences().getBoolean("enter_is_send",false);
- }
-
- @Override
- public void onShowErrorToast(final int resId) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ConversationActivity.this,resId,Toast.LENGTH_SHORT).show();
- }
- });
- }
-
- public boolean highlightSelectedConversations() {
- return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard;
- }
-
- @Override
- public void onClick(View view) {
- final Conversation conversation = getSelectedConversation();
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- switchToContactDetails(getSelectedConversation().getContact());
- } else if (conversation.getMode() == Conversation.MODE_MULTI) {
- Intent intent = new Intent(this,
- ConferenceDetailsActivity.class);
- intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
- intent.putExtra("uuid", getSelectedConversation().getUuid());
- startActivity(intent);
- }
- }
-
- public void setMessagesLoaded() {
- if (mConversationFragment != null) {
- mConversationFragment.setMessagesLoaded();
- mConversationFragment.updateMessages();
- }
- }
+ private void attachImagesToConversation(Conversation conversation, Uri uri) {
+ if (conversation == null) {
+ return;
+ }
+ final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
+ prepareFileToast.show();
+ xmppConnectionService.attachImageToConversation(conversation, uri,
+ new UiCallback<Message>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
+ hidePrepareFileToast(prepareFileToast);
+ }
+
+ @Override
+ public void success(Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ xmppConnectionService.sendMessage(message);
+ }
+
+ @Override
+ public void error(final int error, Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ replaceToast(getString(error));
+ }
+ });
+ }
+ });
+ }
+
+ private void attachImageToConversation(Conversation conversation, Uri uri) {
+ if (conversation == null) {
+ return;
+ }
+ final Conversation conversation_preview = conversation;
+ final Uri uri_preview = uri;
+ Bitmap bitmap = BitmapFactory.decodeFile(FileUtils.getPath(this, uri));
+ File file = new File(FileUtils.getPath(this, uri));
+ ExifInterface exif = null;
+ try {
+ exif = new ExifInterface(file.toString());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
+ Log.d(Config.LOGTAG, "EXIF: " + orientation);
+ Bitmap rotated_image = null;
+ Log.d(Config.LOGTAG, "Rotate image");
+ rotated_image = FileBackend.rotateBitmap(file, bitmap, orientation);
+ if (rotated_image != null) {
+ int scaleSize = 600;
+ int originalWidth = rotated_image.getWidth();
+ int originalHeight = rotated_image.getHeight();
+ int newWidth = -1;
+ int newHeight = -1;
+ float multFactor;
+ if (originalHeight > originalWidth) {
+ newHeight = scaleSize;
+ multFactor = (float) originalWidth / (float) originalHeight;
+ newWidth = (int) (newHeight * multFactor);
+ } else if (originalWidth > originalHeight) {
+ newWidth = scaleSize;
+ multFactor = (float) originalHeight / (float) originalWidth;
+ newHeight = (int) (newWidth * multFactor);
+ } else if (originalHeight == originalWidth) {
+ newHeight = scaleSize;
+ newWidth = scaleSize;
+ }
+ Log.d(Config.LOGTAG, "Scaling preview image from " + originalHeight + "px x " + originalWidth + "px to " + newHeight + "px x " + newWidth + "px");
+ Bitmap preview = Bitmap.createScaledBitmap(rotated_image, newWidth, newHeight, false);
+ ImageView ImagePreview = new ImageView(this);
+ LinearLayout.LayoutParams vp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
+ ImagePreview.setLayoutParams(vp);
+ ImagePreview.setMaxWidth(newWidth);
+ ImagePreview.setMaxHeight(newHeight);
+ //ImagePreview.setScaleType(ImageView.ScaleType.FIT_XY);
+ //ImagePreview.setAdjustViewBounds(true);
+ ImagePreview.setPadding(5, 5, 5, 5);
+ ImagePreview.setImageBitmap(preview);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setView(ImagePreview);
+ builder.setTitle(R.string.send_image);
+ builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ final Toast prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
+ prepareFileToast.show();
+ xmppConnectionService.attachImageToConversation(conversation_preview, uri_preview,
+ new UiCallback<Message>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
+ hidePrepareFileToast(prepareFileToast);
+ }
+
+ @Override
+ public void success(Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ xmppConnectionService.sendMessage(message);
+ }
+
+ @Override
+ public void error(final int error, Message message) {
+ hidePrepareFileToast(prepareFileToast);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ replaceToast(getString(error));
+ }
+ });
+ }
+ });
+ }
+ });
+ builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ mPendingImageUris.clear();
+ }
+ });
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mPendingImageUris.clear();
+ }
+ });
+ }
+ AlertDialog alertDialog = builder.create();
+ alertDialog.show();
+ } else {
+ Toast.makeText(getApplicationContext(), getText(R.string.error_file_not_found), Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void hidePrepareFileToast(final Toast prepareFileToast) {
+ if (prepareFileToast != null) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ prepareFileToast.cancel();
+ }
+ });
+ }
+ }
+
+ public void updateConversationList() {
+ xmppConnectionService
+ .populateWithOrderedConversations(conversationList);
+ if (swipedConversation != null) {
+ if (swipedConversation.isRead()) {
+ conversationList.remove(swipedConversation);
+ } else {
+ listView.discardUndo();
+ }
+ }
+ listAdapter.notifyDataSetChanged();
+ }
+
+ public void runIntent(PendingIntent pi, int requestCode) {
+ try {
+ this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
+ null, 0, 0, 0);
+ } catch (final SendIntentException ignored) {
+ }
+ }
+
+ public void encryptTextMessage(Message message) {
+ xmppConnectionService.getPgpEngine().encrypt(message,
+ new UiCallback<Message>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message message) {
+ ConversationActivity.this.runIntent(pi, ConversationActivity.REQUEST_SEND_MESSAGE);
+ }
+
+ @Override
+ public void success(Message message) {
+ message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ xmppConnectionService.sendMessage(message);
+ if (mConversationFragment != null) {
+ mConversationFragment.messageSent();
+ }
+ }
+
+ @Override
+ public void error(final int error, Message message) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ConversationActivity.this,
+ R.string.unable_to_connect_to_keychain,
+ Toast.LENGTH_SHORT
+ ).show();
+ }
+ });
+ }
+ });
+ }
+
+ public boolean useSendButtonToIndicateStatus() {
+ return getPreferences().getBoolean("send_button_status", true);
+ }
+
+ public boolean indicateReceived() {
+ return getPreferences().getBoolean("indicate_received", true);
+ }
+
+ public boolean useWhiteBackground() {
+ return getPreferences().getBoolean("use_white_background", false);
+ }
+
+ protected boolean trustKeysIfNeeded(int requestCode) {
+ return trustKeysIfNeeded(requestCode, ATTACHMENT_CHOICE_INVALID);
+ }
+
+ protected boolean trustKeysIfNeeded(int requestCode, int attachmentChoice) {
+ AxolotlService axolotlService = mSelectedConversation.getAccount().getAxolotlService();
+ final List<Jid> targets = axolotlService.getCryptoTargets(mSelectedConversation);
+ boolean hasUnaccepted = !mSelectedConversation.getAcceptedCryptoTargets().containsAll(targets);
+ boolean hasUndecidedOwn = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided()).isEmpty();
+ boolean hasUndecidedContacts = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), targets).isEmpty();
+ boolean hasPendingKeys = !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty();
+ boolean hasNoTrustedKeys = axolotlService.anyTargetHasNoTrustedKeys(targets);
+ if (hasUndecidedOwn || hasUndecidedContacts || hasPendingKeys || hasNoTrustedKeys || hasUnaccepted) {
+ axolotlService.createSessionsIfNeeded(mSelectedConversation);
+ Intent intent = new Intent(getApplicationContext(), TrustKeysActivity.class);
+ String[] contacts = new String[targets.size()];
+ for (int i = 0; i < contacts.length; ++i) {
+ contacts[i] = targets.get(i).toString();
+ }
+ intent.putExtra("contacts", contacts);
+ intent.putExtra(EXTRA_ACCOUNT, mSelectedConversation.getAccount().getJid().toBareJid().toString());
+ intent.putExtra("choice", attachmentChoice);
+ intent.putExtra("conversation", mSelectedConversation.getUuid());
+ startActivityForResult(intent, requestCode);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void refreshUiReal() {
+ updateConversationList();
+ if (conversationList.size() > 0) {
+ if (!this.mConversationFragment.isAdded()) {
+ Log.d(Config.LOGTAG, "fragment NOT added to activity. detached=" + Boolean.toString(mConversationFragment.isDetached()));
+ }
+ ConversationActivity.this.mConversationFragment.updateMessages();
+ updateActionBarTitle();
+ invalidateOptionsMenu();
+ } else {
+ Log.d(Config.LOGTAG, "not updating conversations fragment because conversations list size was 0");
+ }
+ }
+
+ @Override
+ public void onAccountUpdate() {
+ this.refreshUi();
+ }
+
+ @Override
+ public void onConversationUpdate() {
+ this.refreshUi();
+ }
+
+ @Override
+ public void onRosterUpdate() {
+ this.refreshUi();
+ }
+
+ @Override
+ public void OnUpdateBlocklist(Status status) {
+ this.refreshUi();
+ }
+
+ public void unblockConversation(final Blockable conversation) {
+ xmppConnectionService.sendUnblockRequest(conversation);
+ }
+
+ public boolean enterIsSend() {
+ return getPreferences().getBoolean("enter_is_send", false);
+ }
+
+ @Override
+ public void onShowErrorToast(final int resId) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ConversationActivity.this, resId, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ public boolean highlightSelectedConversations() {
+ return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard;
+ }
+
+ @Override
+ public void onClick(View view) {
+ final Conversation conversation = getSelectedConversation();
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ switchToContactDetails(getSelectedConversation().getContact());
+ } else if (conversation.getMode() == Conversation.MODE_MULTI) {
+ Intent intent = new Intent(this,
+ ConferenceDetailsActivity.class);
+ intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
+ intent.putExtra("uuid", getSelectedConversation().getUuid());
+ startActivity(intent);
+ }
+ }
+
+ public void setMessagesLoaded() {
+ if (mConversationFragment != null) {
+ mConversationFragment.setMessagesLoaded();
+ mConversationFragment.updateMessages();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
index c71be325a..01fc47afe 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
@@ -73,152 +73,152 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class ConversationFragment extends Fragment implements EditMessage.KeyboardListener {
- protected Conversation conversation;
- private OnClickListener leaveMuc = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.endConversation(conversation);
- }
- };
- private OnClickListener joinMuc = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.xmppConnectionService.joinMuc(conversation);
- }
- };
- private OnClickListener enterPassword = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- MucOptions muc = conversation.getMucOptions();
- String password = muc.getPassword();
- if (password == null) {
- password = "";
- }
- activity.quickPasswordEdit(password, new OnValueEdited() {
-
- @Override
- public void onValueEdited(String value) {
- activity.xmppConnectionService.providePasswordForMuc(
- conversation, value);
- }
- });
- }
- };
- protected ListView messagesView;
- final protected List<Message> messageList = new ArrayList<>();
- protected MessageAdapter messageListAdapter;
- private EditMessage mEditMessage;
- private ImageButton mSendButton;
- private RelativeLayout snackbar;
- private TextView snackbarMessage;
- private TextView snackbarAction;
- private boolean messagesLoaded = true;
- private Toast messageLoaderToast;
-
- private OnScrollListener mOnScrollListener = new OnScrollListener() {
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem,
- int visibleItemCount, int totalItemCount) {
- synchronized (ConversationFragment.this.messageList) {
- if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) {
- long timestamp;
- if (messageList.get(0).getType() == Message.TYPE_STATUS && messageList.size() >= 2) {
- timestamp = messageList.get(1).getTimeSent();
- } else {
- timestamp = messageList.get(0).getTimeSent();
- }
- messagesLoaded = false;
- activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() {
- @Override
- public void onMoreMessagesLoaded(final int c, Conversation conversation) {
- if (ConversationFragment.this.conversation != conversation) {
- return;
- }
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- final int oldPosition = messagesView.getFirstVisiblePosition();
- final Message message;
- if (oldPosition < messageList.size()) {
- message = messageList.get(oldPosition);
- } else {
- message = null;
- }
- String uuid = message != null ? message.getUuid() : null;
- View v = messagesView.getChildAt(0);
- final int pxOffset = (v == null) ? 0 : v.getTop();
- ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList);
+ protected Conversation conversation;
+ private OnClickListener leaveMuc = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.endConversation(conversation);
+ }
+ };
+ private OnClickListener joinMuc = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.xmppConnectionService.joinMuc(conversation);
+ }
+ };
+ private OnClickListener enterPassword = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ MucOptions muc = conversation.getMucOptions();
+ String password = muc.getPassword();
+ if (password == null) {
+ password = "";
+ }
+ activity.quickPasswordEdit(password, new OnValueEdited() {
+
+ @Override
+ public void onValueEdited(String value) {
+ activity.xmppConnectionService.providePasswordForMuc(
+ conversation, value);
+ }
+ });
+ }
+ };
+ protected ListView messagesView;
+ final protected List<Message> messageList = new ArrayList<>();
+ protected MessageAdapter messageListAdapter;
+ private EditMessage mEditMessage;
+ private ImageButton mSendButton;
+ private RelativeLayout snackbar;
+ private TextView snackbarMessage;
+ private TextView snackbarAction;
+ private boolean messagesLoaded = true;
+ private Toast messageLoaderToast;
+
+ private OnScrollListener mOnScrollListener = new OnScrollListener() {
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem,
+ int visibleItemCount, int totalItemCount) {
+ synchronized (ConversationFragment.this.messageList) {
+ if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) {
+ long timestamp;
+ if (messageList.get(0).getType() == Message.TYPE_STATUS && messageList.size() >= 2) {
+ timestamp = messageList.get(1).getTimeSent();
+ } else {
+ timestamp = messageList.get(0).getTimeSent();
+ }
+ messagesLoaded = false;
+ activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() {
+ @Override
+ public void onMoreMessagesLoaded(final int c, Conversation conversation) {
+ if (ConversationFragment.this.conversation != conversation) {
+ return;
+ }
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ final int oldPosition = messagesView.getFirstVisiblePosition();
+ final Message message;
+ if (oldPosition < messageList.size()) {
+ message = messageList.get(oldPosition);
+ } else {
+ message = null;
+ }
+ String uuid = message != null ? message.getUuid() : null;
+ View v = messagesView.getChildAt(0);
+ final int pxOffset = (v == null) ? 0 : v.getTop();
+ ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList);
try {
updateStatusMessages();
} catch (IllegalStateException e) {
Log.d(Config.LOGTAG, "caught illegal state exception while updating status messages");
}
messageListAdapter.notifyDataSetChanged();
- int pos = Math.max(getIndexOf(uuid,messageList),0);
- messagesView.setSelectionFromTop(pos, pxOffset);
- messagesLoaded = true;
- if (messageLoaderToast != null) {
- messageLoaderToast.cancel();
- }
- }
- });
- }
-
- @Override
- public void informUser(final int resId) {
-
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (messageLoaderToast != null) {
- messageLoaderToast.cancel();
- }
- if (ConversationFragment.this.conversation != conversation) {
- return;
- }
- messageLoaderToast = Toast.makeText(activity, resId, Toast.LENGTH_LONG);
- messageLoaderToast.show();
- }
- });
-
- }
- });
-
- }
- }
- }
- };
-
- private int getIndexOf(String uuid, List<Message> messages) {
- if (uuid == null) {
- return messages.size() - 1;
- }
- for(int i = 0; i < messages.size(); ++i) {
- if (uuid.equals(messages.get(i).getUuid())) {
- return i;
- } else {
- Message next = messages.get(i);
- while(next != null && next.wasMergedIntoPrevious()) {
- if (uuid.equals(next.getUuid())) {
- return i;
- }
- next = next.next();
- }
-
- }
- }
- return -1;
- }
+ int pos = Math.max(getIndexOf(uuid, messageList), 0);
+ messagesView.setSelectionFromTop(pos, pxOffset);
+ messagesLoaded = true;
+ if (messageLoaderToast != null) {
+ messageLoaderToast.cancel();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void informUser(final int resId) {
+
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (messageLoaderToast != null) {
+ messageLoaderToast.cancel();
+ }
+ if (ConversationFragment.this.conversation != conversation) {
+ return;
+ }
+ messageLoaderToast = Toast.makeText(activity, resId, Toast.LENGTH_LONG);
+ messageLoaderToast.show();
+ }
+ });
+
+ }
+ });
+
+ }
+ }
+ }
+ };
+
+ private int getIndexOf(String uuid, List<Message> messages) {
+ if (uuid == null) {
+ return messages.size() - 1;
+ }
+ for (int i = 0; i < messages.size(); ++i) {
+ if (uuid.equals(messages.get(i).getUuid())) {
+ return i;
+ } else {
+ Message next = messages.get(i);
+ while (next != null && next.wasMergedIntoPrevious()) {
+ if (uuid.equals(next.getUuid())) {
+ return i;
+ }
+ next = next.next();
+ }
+
+ }
+ }
+ return -1;
+ }
public Pair<Integer, Integer> getScrollPosition() {
if (this.messagesView.getCount() == 0 ||
@@ -243,334 +243,334 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
protected OnClickListener clickToDecryptListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- PendingIntent pendingIntent = conversation.getAccount().getPgpDecryptionService().getPendingIntent();
- if (pendingIntent != null) {
- try {
- activity.startIntentSenderForResult(pendingIntent.getIntentSender(),
+ @Override
+ public void onClick(View v) {
+ PendingIntent pendingIntent = conversation.getAccount().getPgpDecryptionService().getPendingIntent();
+ if (pendingIntent != null) {
+ try {
+ activity.startIntentSenderForResult(pendingIntent.getIntentSender(),
ConversationActivity.REQUEST_DECRYPT_PGP,
null,
0,
0,
0);
- } catch (SendIntentException e) {
- Toast.makeText(activity,R.string.unable_to_connect_to_keychain, Toast.LENGTH_SHORT).show();
- conversation.getAccount().getPgpDecryptionService().continueDecryption(true);
- }
- }
- updateSnackBar(conversation);
- }
- };
- protected OnClickListener clickToVerify = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.verifyOtrSessionDialog(conversation, v);
- }
- };
- private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() {
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (actionId == EditorInfo.IME_ACTION_SEND) {
- InputMethodManager imm = (InputMethodManager) v.getContext()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm.isFullscreenMode()) {
- imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
- }
- sendMessage();
- return true;
- } else {
- return false;
- }
- }
- };
- private OnClickListener mSendButtonListener = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Object tag = v.getTag();
- if (tag instanceof SendButtonAction) {
- SendButtonAction action = (SendButtonAction) tag;
- switch (action) {
- case TAKE_PHOTO:
- activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_TAKE_PHOTO);
- break;
- case SEND_LOCATION:
- activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_LOCATION);
- break;
- case RECORD_VOICE:
- activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_RECORD_VOICE);
- break;
- case CHOOSE_PICTURE:
- activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_CHOOSE_IMAGE);
- break;
- case CANCEL:
- if (conversation != null) {
- if (conversation.getCorrectingMessage() != null) {
- conversation.setCorrectingMessage(null);
- mEditMessage.getEditableText().clear();
- }
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- conversation.setNextCounterpart(null);
- }
- updateChatMsgHint();
- updateSendButton();
- }
- break;
- default:
- sendMessage();
- }
- } else {
- sendMessage();
- }
- }
- };
- private View.OnLongClickListener mSendButtonLongListener = new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- final String body = mEditMessage.getText().toString();
- if (body.length() == 0) {
- mEditMessage.getText().insert(0, "/me ");
- }
- return true;
- }
- };
- private OnClickListener clickToMuc = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(getActivity(), ConferenceDetailsActivity.class);
- intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
- intent.putExtra("uuid", conversation.getUuid());
- startActivity(intent);
- }
- };
- private ConversationActivity activity;
- private Message selectedMessage;
-
- public void setMessagesLoaded() {
- this.messagesLoaded = true;
- }
-
- private void sendMessage() {
- final String body = mEditMessage.getText().toString();
- if (body.length() == 0 || this.conversation == null) {
- return;
- }
- final Message message;
- if (conversation.getCorrectingMessage() == null) {
- message = new Message(conversation, body, conversation.getNextEncryption());
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- if (conversation.getNextCounterpart() != null) {
- message.setCounterpart(conversation.getNextCounterpart());
- message.setType(Message.TYPE_PRIVATE);
- }
- }
- } else {
- message = conversation.getCorrectingMessage();
- message.setBody(body);
- message.setEdited(message.getUuid());
- message.setUuid(UUID.randomUUID().toString());
- conversation.setCorrectingMessage(null);
- }
- switch (conversation.getNextEncryption()) {
- case Message.ENCRYPTION_OTR:
- sendOtrMessage(message);
- break;
- case Message.ENCRYPTION_PGP:
- sendPgpMessage(message);
- break;
- case Message.ENCRYPTION_AXOLOTL:
- if(!activity.trustKeysIfNeeded(ConversationActivity.REQUEST_TRUST_KEYS_TEXT)) {
- sendAxolotlMessage(message);
- }
- break;
- default:
- sendPlainTextMessage(message);
- }
- }
-
- public void updateChatMsgHint() {
- final boolean multi = conversation.getMode() == Conversation.MODE_MULTI;
- if (conversation.getCorrectingMessage() != null) {
- this.mEditMessage.setHint(R.string.send_corrected_message);
- } else if (multi && conversation.getNextCounterpart() != null) {
- this.mEditMessage.setHint(getString(
- R.string.send_private_message_to,
- conversation.getNextCounterpart().getResourcepart()));
- } else if (multi && !conversation.getMucOptions().participating()) {
- this.mEditMessage.setHint(R.string.you_are_not_participating);
- } else {
- this.mEditMessage.setHint(UIHelper.getMessageHint(activity,conversation));
- getActivity().invalidateOptionsMenu();
- }
- }
-
- public void setupIme() {
- if (activity == null) {
- return;
- } else if (activity.usingEnterKey() && activity.enterIsSend()) {
- mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
- mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
- } else if (activity.usingEnterKey()) {
- mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
- mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
- } else {
- mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
- mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
- }
- }
-
- @Override
- public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- final View view = inflater.inflate(R.layout.fragment_conversation, container, false);
- view.setOnClickListener(null);
- mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
- mEditMessage.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (activity != null) {
- activity.hideConversationsOverview();
- }
- }
- });
- mEditMessage.setOnEditorActionListener(mEditorActionListener);
-
- mSendButton = (ImageButton) view.findViewById(R.id.textSendButton);
- mSendButton.setOnClickListener(this.mSendButtonListener);
- mSendButton.setOnLongClickListener(this.mSendButtonLongListener);
-
- snackbar = (RelativeLayout) view.findViewById(R.id.snackbar);
- snackbarMessage = (TextView) view.findViewById(R.id.snackbar_message);
- snackbarAction = (TextView) view.findViewById(R.id.snackbar_action);
-
- messagesView = (ListView) view.findViewById(R.id.messages_view);
- messagesView.setOnScrollListener(mOnScrollListener);
- messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
- messageListAdapter = new MessageAdapter((ConversationActivity) getActivity(), this.messageList);
- messageListAdapter.setOnContactPictureClicked(new OnContactPictureClicked() {
-
- @Override
- public void onContactPictureClicked(Message message) {
- if (message.getStatus() <= Message.STATUS_RECEIVED) {
- if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- Jid user = message.getCounterpart();
- if (user != null && !user.isBareJid()) {
- if (!message.getConversation().getMucOptions().isUserInRoom(user)) {
- Toast.makeText(activity,activity.getString(R.string.user_has_left_conference,user.getResourcepart()),Toast.LENGTH_SHORT).show();
- }
- highlightInConference(user.getResourcepart());
- }
- } else {
- if (!message.getContact().isSelf()) {
- String fingerprint;
- if (message.getEncryption() == Message.ENCRYPTION_PGP
- || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- fingerprint = "pgp";
- } else {
- fingerprint = message.getFingerprint();
- }
- activity.switchToContactDetails(message.getContact(), fingerprint);
- }
- }
- } else {
- Account account = message.getConversation().getAccount();
- Intent intent;
- if (activity.manuallyChangePresence()) {
- intent = new Intent(activity, SetPresenceActivity.class);
- intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, account.getJid().toBareJid().toString());
- } else {
- intent = new Intent(activity, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().toBareJid().toString());
- String fingerprint;
- if (message.getEncryption() == Message.ENCRYPTION_PGP
- || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- fingerprint = "pgp";
- } else {
- fingerprint = message.getFingerprint();
- }
- intent.putExtra("fingerprint", fingerprint);
- }
- startActivity(intent);
- }
- }
- });
- messageListAdapter
- .setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
-
- @Override
- public void onContactPictureLongClicked(Message message) {
- if (message.getStatus() <= Message.STATUS_RECEIVED) {
- if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- Jid user = message.getCounterpart();
- if (user != null && !user.isBareJid()) {
- if (message.getConversation().getMucOptions().isUserInRoom(user)) {
- privateMessageWith(user);
- } else {
- Toast.makeText(activity, activity.getString(R.string.user_has_left_conference, user.getResourcepart()), Toast.LENGTH_SHORT).show();
- }
- }
- }
- } else {
- activity.showQrCode();
- }
- }
- });
- messagesView.setAdapter(messageListAdapter);
-
- registerForContextMenu(messagesView);
-
- return view;
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- synchronized (this.messageList) {
- super.onCreateContextMenu(menu, v, menuInfo);
- AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
- this.selectedMessage = this.messageList.get(acmi.position);
- populateContextMenu(menu);
- }
- }
-
- private void populateContextMenu(ContextMenu menu) {
- final Message m = this.selectedMessage;
- final Transferable t = m.getTransferable();
- Message relevantForCorrection = m;
- while(relevantForCorrection.mergeable(relevantForCorrection.next())) {
- relevantForCorrection = relevantForCorrection.next();
- }
- if (m.getType() != Message.TYPE_STATUS) {
- final boolean treatAsFile = m.getType() != Message.TYPE_TEXT
- && m.getType() != Message.TYPE_PRIVATE
- && t == null;
- activity.getMenuInflater().inflate(R.menu.message_context, menu);
- menu.setHeaderTitle(R.string.message_options);
- MenuItem copyText = menu.findItem(R.id.copy_text);
- MenuItem selectText = menu.findItem(R.id.select_text);
- MenuItem retryDecryption = menu.findItem(R.id.retry_decryption);
- MenuItem correctMessage = menu.findItem(R.id.correct_message);
- MenuItem shareWith = menu.findItem(R.id.share_with);
- MenuItem sendAgain = menu.findItem(R.id.send_again);
- MenuItem copyUrl = menu.findItem(R.id.copy_url);
- MenuItem downloadFile = menu.findItem(R.id.download_file);
- MenuItem cancelTransmission = menu.findItem(R.id.cancel_transmission);
- MenuItem deleteFile = menu.findItem(R.id.delete_file);
+ } catch (SendIntentException e) {
+ Toast.makeText(activity, R.string.unable_to_connect_to_keychain, Toast.LENGTH_SHORT).show();
+ conversation.getAccount().getPgpDecryptionService().continueDecryption(true);
+ }
+ }
+ updateSnackBar(conversation);
+ }
+ };
+ protected OnClickListener clickToVerify = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.verifyOtrSessionDialog(conversation, v);
+ }
+ };
+ private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() {
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_SEND) {
+ InputMethodManager imm = (InputMethodManager) v.getContext()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm.isFullscreenMode()) {
+ imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ }
+ sendMessage();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ };
+ private OnClickListener mSendButtonListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Object tag = v.getTag();
+ if (tag instanceof SendButtonAction) {
+ SendButtonAction action = (SendButtonAction) tag;
+ switch (action) {
+ case TAKE_PHOTO:
+ activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_TAKE_PHOTO);
+ break;
+ case SEND_LOCATION:
+ activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_LOCATION);
+ break;
+ case RECORD_VOICE:
+ activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_RECORD_VOICE);
+ break;
+ case CHOOSE_PICTURE:
+ activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_CHOOSE_IMAGE);
+ break;
+ case CANCEL:
+ if (conversation != null) {
+ if (conversation.getCorrectingMessage() != null) {
+ conversation.setCorrectingMessage(null);
+ mEditMessage.getEditableText().clear();
+ }
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ conversation.setNextCounterpart(null);
+ }
+ updateChatMsgHint();
+ updateSendButton();
+ }
+ break;
+ default:
+ sendMessage();
+ }
+ } else {
+ sendMessage();
+ }
+ }
+ };
+ private View.OnLongClickListener mSendButtonLongListener = new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ final String body = mEditMessage.getText().toString();
+ if (body.length() == 0) {
+ mEditMessage.getText().insert(0, "/me ");
+ }
+ return true;
+ }
+ };
+ private OnClickListener clickToMuc = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(getActivity(), ConferenceDetailsActivity.class);
+ intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
+ intent.putExtra("uuid", conversation.getUuid());
+ startActivity(intent);
+ }
+ };
+ private ConversationActivity activity;
+ private Message selectedMessage;
+
+ public void setMessagesLoaded() {
+ this.messagesLoaded = true;
+ }
+
+ private void sendMessage() {
+ final String body = mEditMessage.getText().toString();
+ if (body.length() == 0 || this.conversation == null) {
+ return;
+ }
+ final Message message;
+ if (conversation.getCorrectingMessage() == null) {
+ message = new Message(conversation, body, conversation.getNextEncryption());
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ if (conversation.getNextCounterpart() != null) {
+ message.setCounterpart(conversation.getNextCounterpart());
+ message.setType(Message.TYPE_PRIVATE);
+ }
+ }
+ } else {
+ message = conversation.getCorrectingMessage();
+ message.setBody(body);
+ message.setEdited(message.getUuid());
+ message.setUuid(UUID.randomUUID().toString());
+ conversation.setCorrectingMessage(null);
+ }
+ switch (conversation.getNextEncryption()) {
+ case Message.ENCRYPTION_OTR:
+ sendOtrMessage(message);
+ break;
+ case Message.ENCRYPTION_PGP:
+ sendPgpMessage(message);
+ break;
+ case Message.ENCRYPTION_AXOLOTL:
+ if (!activity.trustKeysIfNeeded(ConversationActivity.REQUEST_TRUST_KEYS_TEXT)) {
+ sendAxolotlMessage(message);
+ }
+ break;
+ default:
+ sendPlainTextMessage(message);
+ }
+ }
+
+ public void updateChatMsgHint() {
+ final boolean multi = conversation.getMode() == Conversation.MODE_MULTI;
+ if (conversation.getCorrectingMessage() != null) {
+ this.mEditMessage.setHint(R.string.send_corrected_message);
+ } else if (multi && conversation.getNextCounterpart() != null) {
+ this.mEditMessage.setHint(getString(
+ R.string.send_private_message_to,
+ conversation.getNextCounterpart().getResourcepart()));
+ } else if (multi && !conversation.getMucOptions().participating()) {
+ this.mEditMessage.setHint(R.string.you_are_not_participating);
+ } else {
+ this.mEditMessage.setHint(UIHelper.getMessageHint(activity, conversation));
+ getActivity().invalidateOptionsMenu();
+ }
+ }
+
+ public void setupIme() {
+ if (activity == null) {
+ return;
+ } else if (activity.usingEnterKey() && activity.enterIsSend()) {
+ mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
+ mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
+ } else if (activity.usingEnterKey()) {
+ mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
+ mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
+ } else {
+ mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
+ mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
+ }
+ }
+
+ @Override
+ public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.fragment_conversation, container, false);
+ view.setOnClickListener(null);
+ mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
+ mEditMessage.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (activity != null) {
+ activity.hideConversationsOverview();
+ }
+ }
+ });
+ mEditMessage.setOnEditorActionListener(mEditorActionListener);
+
+ mSendButton = (ImageButton) view.findViewById(R.id.textSendButton);
+ mSendButton.setOnClickListener(this.mSendButtonListener);
+ mSendButton.setOnLongClickListener(this.mSendButtonLongListener);
+
+ snackbar = (RelativeLayout) view.findViewById(R.id.snackbar);
+ snackbarMessage = (TextView) view.findViewById(R.id.snackbar_message);
+ snackbarAction = (TextView) view.findViewById(R.id.snackbar_action);
+
+ messagesView = (ListView) view.findViewById(R.id.messages_view);
+ messagesView.setOnScrollListener(mOnScrollListener);
+ messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
+ messageListAdapter = new MessageAdapter((ConversationActivity) getActivity(), this.messageList);
+ messageListAdapter.setOnContactPictureClicked(new OnContactPictureClicked() {
+
+ @Override
+ public void onContactPictureClicked(Message message) {
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ Jid user = message.getCounterpart();
+ if (user != null && !user.isBareJid()) {
+ if (!message.getConversation().getMucOptions().isUserInRoom(user)) {
+ Toast.makeText(activity, activity.getString(R.string.user_has_left_conference, user.getResourcepart()), Toast.LENGTH_SHORT).show();
+ }
+ highlightInConference(user.getResourcepart());
+ }
+ } else {
+ if (!message.getContact().isSelf()) {
+ String fingerprint;
+ if (message.getEncryption() == Message.ENCRYPTION_PGP
+ || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ fingerprint = "pgp";
+ } else {
+ fingerprint = message.getFingerprint();
+ }
+ activity.switchToContactDetails(message.getContact(), fingerprint);
+ }
+ }
+ } else {
+ Account account = message.getConversation().getAccount();
+ Intent intent;
+ if (activity.manuallyChangePresence()) {
+ intent = new Intent(activity, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, account.getJid().toBareJid().toString());
+ } else {
+ intent = new Intent(activity, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ String fingerprint;
+ if (message.getEncryption() == Message.ENCRYPTION_PGP
+ || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ fingerprint = "pgp";
+ } else {
+ fingerprint = message.getFingerprint();
+ }
+ intent.putExtra("fingerprint", fingerprint);
+ }
+ startActivity(intent);
+ }
+ }
+ });
+ messageListAdapter
+ .setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
+
+ @Override
+ public void onContactPictureLongClicked(Message message) {
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ Jid user = message.getCounterpart();
+ if (user != null && !user.isBareJid()) {
+ if (message.getConversation().getMucOptions().isUserInRoom(user)) {
+ privateMessageWith(user);
+ } else {
+ Toast.makeText(activity, activity.getString(R.string.user_has_left_conference, user.getResourcepart()), Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ } else {
+ activity.showQrCode();
+ }
+ }
+ });
+ messagesView.setAdapter(messageListAdapter);
+
+ registerForContextMenu(messagesView);
+
+ return view;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ synchronized (this.messageList) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
+ this.selectedMessage = this.messageList.get(acmi.position);
+ populateContextMenu(menu);
+ }
+ }
+
+ private void populateContextMenu(ContextMenu menu) {
+ final Message m = this.selectedMessage;
+ final Transferable t = m.getTransferable();
+ Message relevantForCorrection = m;
+ while (relevantForCorrection.mergeable(relevantForCorrection.next())) {
+ relevantForCorrection = relevantForCorrection.next();
+ }
+ if (m.getType() != Message.TYPE_STATUS) {
+ final boolean treatAsFile = m.getType() != Message.TYPE_TEXT
+ && m.getType() != Message.TYPE_PRIVATE
+ && t == null;
+ activity.getMenuInflater().inflate(R.menu.message_context, menu);
+ menu.setHeaderTitle(R.string.message_options);
+ MenuItem copyText = menu.findItem(R.id.copy_text);
+ MenuItem selectText = menu.findItem(R.id.select_text);
+ MenuItem retryDecryption = menu.findItem(R.id.retry_decryption);
+ MenuItem correctMessage = menu.findItem(R.id.correct_message);
+ MenuItem shareWith = menu.findItem(R.id.share_with);
+ MenuItem sendAgain = menu.findItem(R.id.send_again);
+ MenuItem copyUrl = menu.findItem(R.id.copy_url);
+ MenuItem downloadFile = menu.findItem(R.id.download_file);
+ MenuItem cancelTransmission = menu.findItem(R.id.cancel_transmission);
+ MenuItem deleteFile = menu.findItem(R.id.delete_file);
MenuItem showErrorMessage = menu.findItem(R.id.show_error_message);
- if (!treatAsFile
- && !GeoHelper.isGeoUri(m.getBody())
+ if (!treatAsFile
+ && !GeoHelper.isGeoUri(m.getBody())
&& !XmppUri.isXmppUri(m.getBody())
- && m.treatAsDownloadable() != Message.Decision.MUST) {
- copyText.setVisible(true);
+ && m.treatAsDownloadable() != Message.Decision.MUST) {
+ copyText.setVisible(true);
selectText.setVisible(ListSelectionManager.isSupported());
- }
- if (m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
- retryDecryption.setVisible(true);
- }
- if (((relevantForCorrection.getType() == Message.TYPE_TEXT
+ }
+ if (m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ retryDecryption.setVisible(true);
+ }
+ if (((relevantForCorrection.getType() == Message.TYPE_TEXT
&& relevantForCorrection.isLastCorrectableMessage()
&& conversation.getMode() == Conversation.MODE_SINGLE)
|| (relevantForCorrection.getType() == Message.TYPE_TEXT
@@ -578,129 +578,129 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
&& conversation.getMode() == Conversation.MODE_MULTI
&& m.getConversation().getMucOptions().nonanonymous()))
&& !(GeoHelper.isGeoUri(m.getBody()) || XmppUri.isXmppUri(m.getBody()))) {
- correctMessage.setVisible(true);
- }
- if (treatAsFile
+ correctMessage.setVisible(true);
+ }
+ if (treatAsFile
|| GeoHelper.isGeoUri(m.getBody())
|| XmppUri.isXmppUri(m.getBody())) {
- shareWith.setVisible(true);
- }
- if (m.getStatus() == Message.STATUS_SEND_FAILED) {
- sendAgain.setVisible(true);
- }
- if (m.hasFileOnRemoteHost()
- || GeoHelper.isGeoUri(m.getBody())
+ shareWith.setVisible(true);
+ }
+ if (m.getStatus() == Message.STATUS_SEND_FAILED) {
+ sendAgain.setVisible(true);
+ }
+ if (m.hasFileOnRemoteHost()
+ || GeoHelper.isGeoUri(m.getBody())
|| XmppUri.isXmppUri(m.getBody())
- || m.treatAsDownloadable() == Message.Decision.MUST
- || (t != null && t instanceof HttpDownloadConnection)) {
- copyUrl.setVisible(true);
- }
- if ((m.getType() == Message.TYPE_TEXT && t == null && m.treatAsDownloadable() != Message.Decision.NEVER)
- || (m.isFileOrImage() && t instanceof TransferablePlaceholder && m.hasFileOnRemoteHost())){
- downloadFile.setVisible(true);
- downloadFile.setTitle(activity.getString(R.string.download_x_file,UIHelper.getFileDescriptionString(activity, m)));
- }
+ || m.treatAsDownloadable() == Message.Decision.MUST
+ || (t != null && t instanceof HttpDownloadConnection)) {
+ copyUrl.setVisible(true);
+ }
+ if ((m.getType() == Message.TYPE_TEXT && t == null && m.treatAsDownloadable() != Message.Decision.NEVER)
+ || (m.isFileOrImage() && t instanceof TransferablePlaceholder && m.hasFileOnRemoteHost())) {
+ downloadFile.setVisible(true);
+ downloadFile.setTitle(activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, m)));
+ }
boolean waitingOfferedSending = m.getStatus() == Message.STATUS_WAITING
|| m.getStatus() == Message.STATUS_UNSEND
|| m.getStatus() == Message.STATUS_OFFERED;
if ((t != null && !(t instanceof TransferablePlaceholder)) || waitingOfferedSending && m.needsUploading()) {
- cancelTransmission.setVisible(true);
- }
- if (treatAsFile) {
+ cancelTransmission.setVisible(true);
+ }
+ if (treatAsFile) {
String path = m.getRelativeFilePath();
Log.d(Config.LOGTAG, "Path = " + path);
if (path == null || !path.startsWith("/") || path.contains(FileBackend.getConversationsDirectory())) {
deleteFile.setVisible(true);
deleteFile.setTitle(activity.getString(R.string.delete_x_file, UIHelper.getFileDescriptionString(activity, m)));
}
- }
+ }
if (m.getStatus() == Message.STATUS_SEND_FAILED && m.getErrorMessage() != null) {
showErrorMessage.setVisible(true);
}
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.share_with:
- shareWith(selectedMessage);
- return true;
- case R.id.copy_text:
- copyText(selectedMessage);
- return true;
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.share_with:
+ shareWith(selectedMessage);
+ return true;
+ case R.id.copy_text:
+ copyText(selectedMessage);
+ return true;
case R.id.select_text:
selectText(selectedMessage);
return true;
- case R.id.correct_message:
- correctMessage(selectedMessage);
- return true;
- case R.id.send_again:
- resendMessage(selectedMessage);
- return true;
- case R.id.copy_url:
- copyUrl(selectedMessage);
- return true;
- case R.id.download_file:
- downloadFile(selectedMessage);
- return true;
- case R.id.cancel_transmission:
- cancelTransmission(selectedMessage);
- return true;
- case R.id.retry_decryption:
- retryDecryption(selectedMessage);
- return true;
- case R.id.delete_file:
- deleteFile(selectedMessage);
- return true;
+ case R.id.correct_message:
+ correctMessage(selectedMessage);
+ return true;
+ case R.id.send_again:
+ resendMessage(selectedMessage);
+ return true;
+ case R.id.copy_url:
+ copyUrl(selectedMessage);
+ return true;
+ case R.id.download_file:
+ downloadFile(selectedMessage);
+ return true;
+ case R.id.cancel_transmission:
+ cancelTransmission(selectedMessage);
+ return true;
+ case R.id.retry_decryption:
+ retryDecryption(selectedMessage);
+ return true;
+ case R.id.delete_file:
+ deleteFile(selectedMessage);
+ return true;
case R.id.show_error_message:
showErrorMessage(selectedMessage);
return true;
- default:
- return super.onContextItemSelected(item);
- }
- }
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
private void showErrorMessage(final Message message) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(R.string.error_message);
builder.setMessage(message.getErrorMessage());
- builder.setPositiveButton(R.string.ok,null);
+ builder.setPositiveButton(R.string.ok, null);
builder.create().show();
}
- private void shareWith(Message message) {
- Intent shareIntent = new Intent();
- shareIntent.setAction(Intent.ACTION_SEND);
- if (GeoHelper.isGeoUri(message.getBody())) {
- shareIntent.putExtra(Intent.EXTRA_TEXT, message.getBody());
- shareIntent.setType("text/plain");
- } else {
- shareIntent.putExtra(Intent.EXTRA_STREAM,
- activity.xmppConnectionService.getFileBackend()
- .getJingleFileUri(message));
- shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- String mime = message.getMimeType();
- if (mime == null) {
- mime = "*/*";
- }
- shareIntent.setType(mime);
- }
- try {
- activity.startActivity(Intent.createChooser(shareIntent, getText(R.string.share_with)));
- } catch (ActivityNotFoundException e) {
- //This should happen only on faulty androids because normally chooser is always available
- Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show();
- }
- }
-
- private void copyText(Message message) {
+ private void shareWith(Message message) {
+ Intent shareIntent = new Intent();
+ shareIntent.setAction(Intent.ACTION_SEND);
+ if (GeoHelper.isGeoUri(message.getBody())) {
+ shareIntent.putExtra(Intent.EXTRA_TEXT, message.getBody());
+ shareIntent.setType("text/plain");
+ } else {
+ shareIntent.putExtra(Intent.EXTRA_STREAM,
+ activity.xmppConnectionService.getFileBackend()
+ .getJingleFileUri(message));
+ shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ String mime = message.getMimeType();
+ if (mime == null) {
+ mime = "*/*";
+ }
+ shareIntent.setType(mime);
+ }
+ try {
+ activity.startActivity(Intent.createChooser(shareIntent, getText(R.string.share_with)));
+ } catch (ActivityNotFoundException e) {
+ //This should happen only on faulty androids because normally chooser is always available
+ Toast.makeText(activity, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void copyText(Message message) {
if (activity.copyTextToClipboard(message.getMergedBody().toString(),
- R.string.message_text)) {
- Toast.makeText(activity, R.string.message_copied_to_clipboard,
- Toast.LENGTH_SHORT).show();
- }
- }
+ R.string.message_text)) {
+ Toast.makeText(activity, R.string.message_copied_to_clipboard,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
private void selectText(Message message) {
final int index;
@@ -721,779 +721,779 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
private void deleteFile(Message message) {
- if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
- message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- activity.updateConversationList();
- updateMessages();
- }
- }
-
- private void resendMessage(Message message) {
- if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
- DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
- if (!file.exists()) {
- Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
- message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- activity.updateConversationList();
- updateMessages();
- return;
- }
- }
- activity.xmppConnectionService.resendFailedMessages(message);
- }
-
- private void copyUrl(Message message) {
- final String url;
- final int resId;
- if (GeoHelper.isGeoUri(message.getBody())) {
+ if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
+ message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ activity.updateConversationList();
+ updateMessages();
+ }
+ }
+
+ private void resendMessage(Message message) {
+ if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
+ DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
+ if (!file.exists()) {
+ Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
+ message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ activity.updateConversationList();
+ updateMessages();
+ return;
+ }
+ }
+ activity.xmppConnectionService.resendFailedMessages(message);
+ }
+
+ private void copyUrl(Message message) {
+ final String url;
+ final int resId;
+ if (GeoHelper.isGeoUri(message.getBody())) {
resId = R.string.location;
url = message.getBody();
} else if (XmppUri.isXmppUri(message.getBody())) {
resId = R.string.contact;
url = message.getBody();
- } else if (message.hasFileOnRemoteHost()) {
- resId = R.string.file_url;
- url = message.getFileParams().url.toString();
- } else {
- url = message.getBody().trim();
- resId = R.string.file_url;
- }
- if (activity.copyTextToClipboard(url, resId)) {
- Toast.makeText(activity, R.string.url_copied_to_clipboard,
- Toast.LENGTH_SHORT).show();
- }
- }
-
- private void downloadFile(Message message) {
- activity.xmppConnectionService.getHttpConnectionManager()
- .createNewDownloadConnection(message,true);
- }
-
- private void cancelTransmission(Message message) {
- Transferable transferable = message.getTransferable();
- if (transferable != null) {
- transferable.cancel();
- } else {
- activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
- }
- }
-
- private void retryDecryption(Message message) {
- message.setEncryption(Message.ENCRYPTION_PGP);
- activity.updateConversationList();
- updateMessages();
- conversation.getAccount().getPgpDecryptionService().decrypt(message, false);
- }
-
- protected void privateMessageWith(final Jid counterpart) {
- this.mEditMessage.setText("");
- this.conversation.setNextCounterpart(counterpart);
- updateChatMsgHint();
- updateSendButton();
- }
-
- private void correctMessage(Message message) {
- while(message.mergeable(message.next())) {
- message = message.next();
- }
- this.conversation.setCorrectingMessage(message);
- this.mEditMessage.getEditableText().clear();
- this.mEditMessage.getEditableText().append(message.getBody());
-
- }
-
- protected void highlightInConference(String nick) {
- String oldString = mEditMessage.getText().toString().trim();
- if (oldString.isEmpty() || mEditMessage.getSelectionStart() == 0) {
- mEditMessage.getText().insert(0, nick + ": ");
- } else {
- if (mEditMessage.getText().charAt(
- mEditMessage.getSelectionStart() - 1) != ' ') {
- nick = " " + nick;
- }
- mEditMessage.getText().insert(mEditMessage.getSelectionStart(),
- nick + " ");
- }
- }
-
- @Override
- public void onStop() {
- super.onStop();
- if (this.conversation != null) {
- final String msg = mEditMessage.getText().toString();
- this.conversation.setNextMessage(msg);
- updateChatState(this.conversation, msg);
- }
- }
-
- private void updateChatState(final Conversation conversation, final String msg) {
- ChatState state = msg.length() == 0 ? Config.DEFAULT_CHATSTATE : ChatState.PAUSED;
- Account.State status = conversation.getAccount().getStatus();
- if (status == Account.State.ONLINE && conversation.setOutgoingChatState(state)) {
- activity.xmppConnectionService.sendChatState(conversation);
- }
- }
-
- public boolean reInit(Conversation conversation) {
- if (conversation == null) {
- return false;
- }
- this.activity = (ConversationActivity) getActivity();
- setupIme();
- if (this.conversation != null) {
- final String msg = mEditMessage.getText().toString();
- this.conversation.setNextMessage(msg);
- if (this.conversation != conversation) {
- updateChatState(this.conversation, msg);
- }
- this.conversation.trim();
- }
-
- this.conversation = conversation;
- boolean canWrite = this.conversation.getMode() == Conversation.MODE_SINGLE || this.conversation.getMucOptions().participating();
- this.mEditMessage.setEnabled(canWrite);
- this.mSendButton.setEnabled(canWrite);
- this.mEditMessage.setKeyboardListener(null);
- this.mEditMessage.setText("");
- this.mEditMessage.append(this.conversation.getNextMessage());
- this.mEditMessage.setKeyboardListener(this);
- messageListAdapter.updatePreferences();
- this.messagesView.setAdapter(messageListAdapter);
- updateMessages();
- this.messagesLoaded = true;
- synchronized (this.messageList) {
- final Message first = conversation.getFirstUnreadMessage();
- final int bottom = Math.max(0, this.messageList.size() - 1);
- final int pos;
- if (first == null) {
- pos = bottom;
- } else {
- int i = getIndexOf(first.getUuid(), this.messageList);
- pos = i < 0 ? bottom : i;
- }
- messagesView.setSelection(pos);
+ } else if (message.hasFileOnRemoteHost()) {
+ resId = R.string.file_url;
+ url = message.getFileParams().url.toString();
+ } else {
+ url = message.getBody().trim();
+ resId = R.string.file_url;
+ }
+ if (activity.copyTextToClipboard(url, resId)) {
+ Toast.makeText(activity, R.string.url_copied_to_clipboard,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void downloadFile(Message message) {
+ activity.xmppConnectionService.getHttpConnectionManager()
+ .createNewDownloadConnection(message, true);
+ }
+
+ private void cancelTransmission(Message message) {
+ Transferable transferable = message.getTransferable();
+ if (transferable != null) {
+ transferable.cancel();
+ } else {
+ activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
+ }
+ }
+
+ private void retryDecryption(Message message) {
+ message.setEncryption(Message.ENCRYPTION_PGP);
+ activity.updateConversationList();
+ updateMessages();
+ conversation.getAccount().getPgpDecryptionService().decrypt(message, false);
+ }
+
+ protected void privateMessageWith(final Jid counterpart) {
+ this.mEditMessage.setText("");
+ this.conversation.setNextCounterpart(counterpart);
+ updateChatMsgHint();
+ updateSendButton();
+ }
+
+ private void correctMessage(Message message) {
+ while (message.mergeable(message.next())) {
+ message = message.next();
+ }
+ this.conversation.setCorrectingMessage(message);
+ this.mEditMessage.getEditableText().clear();
+ this.mEditMessage.getEditableText().append(message.getBody());
+
+ }
+
+ protected void highlightInConference(String nick) {
+ String oldString = mEditMessage.getText().toString().trim();
+ if (oldString.isEmpty() || mEditMessage.getSelectionStart() == 0) {
+ mEditMessage.getText().insert(0, nick + ": ");
+ } else {
+ if (mEditMessage.getText().charAt(
+ mEditMessage.getSelectionStart() - 1) != ' ') {
+ nick = " " + nick;
+ }
+ mEditMessage.getText().insert(mEditMessage.getSelectionStart(),
+ nick + " ");
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (this.conversation != null) {
+ final String msg = mEditMessage.getText().toString();
+ this.conversation.setNextMessage(msg);
+ updateChatState(this.conversation, msg);
+ }
+ }
+
+ private void updateChatState(final Conversation conversation, final String msg) {
+ ChatState state = msg.length() == 0 ? Config.DEFAULT_CHATSTATE : ChatState.PAUSED;
+ Account.State status = conversation.getAccount().getStatus();
+ if (status == Account.State.ONLINE && conversation.setOutgoingChatState(state)) {
+ activity.xmppConnectionService.sendChatState(conversation);
+ }
+ }
+
+ public boolean reInit(Conversation conversation) {
+ if (conversation == null) {
+ return false;
+ }
+ this.activity = (ConversationActivity) getActivity();
+ setupIme();
+ if (this.conversation != null) {
+ final String msg = mEditMessage.getText().toString();
+ this.conversation.setNextMessage(msg);
+ if (this.conversation != conversation) {
+ updateChatState(this.conversation, msg);
+ }
+ this.conversation.trim();
+ }
+
+ this.conversation = conversation;
+ boolean canWrite = this.conversation.getMode() == Conversation.MODE_SINGLE || this.conversation.getMucOptions().participating();
+ this.mEditMessage.setEnabled(canWrite);
+ this.mSendButton.setEnabled(canWrite);
+ this.mEditMessage.setKeyboardListener(null);
+ this.mEditMessage.setText("");
+ this.mEditMessage.append(this.conversation.getNextMessage());
+ this.mEditMessage.setKeyboardListener(this);
+ messageListAdapter.updatePreferences();
+ this.messagesView.setAdapter(messageListAdapter);
+ updateMessages();
+ this.messagesLoaded = true;
+ synchronized (this.messageList) {
+ final Message first = conversation.getFirstUnreadMessage();
+ final int bottom = Math.max(0, this.messageList.size() - 1);
+ final int pos;
+ if (first == null) {
+ pos = bottom;
+ } else {
+ int i = getIndexOf(first.getUuid(), this.messageList);
+ pos = i < 0 ? bottom : i;
+ }
+ messagesView.setSelection(pos);
return pos == bottom;
- }
- }
-
- private OnClickListener mEnableAccountListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- final Account account = conversation == null ? null : conversation.getAccount();
- if (account != null) {
- account.setOption(Account.OPTION_DISABLED, false);
- activity.xmppConnectionService.updateAccount(account);
- }
- }
- };
-
- private OnClickListener mUnblockClickListener = new OnClickListener() {
- @Override
- public void onClick(final View v) {
- v.post(new Runnable() {
- @Override
- public void run() {
- v.setVisibility(View.INVISIBLE);
- }
- });
- if (conversation.isDomainBlocked()) {
- BlockContactDialog.show(activity, activity.xmppConnectionService, conversation);
- } else {
- activity.unblockConversation(conversation);
- }
- }
- };
-
- private OnClickListener mAddBackClickListener = new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- final Contact contact = conversation == null ? null : conversation.getContact();
- if (contact != null) {
- activity.xmppConnectionService.createContact(contact);
- activity.switchToContactDetails(contact);
- }
- }
- };
-
- private OnClickListener mAllowPresenceSubscription = new OnClickListener() {
- @Override
- public void onClick(View v) {
- final Contact contact = conversation == null ? null : conversation.getContact();
- if (contact != null) {
- activity.xmppConnectionService.sendPresencePacket(contact.getAccount(),
- activity.xmppConnectionService.getPresenceGenerator()
- .sendPresenceUpdatesTo(contact));
- hideSnackbar();
- }
- }
- };
-
- private OnClickListener mAnswerSmpClickListener = new OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(activity, VerifyOTRActivity.class);
- intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
- intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
- intent.putExtra(VerifyOTRActivity.EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
- intent.putExtra("mode", VerifyOTRActivity.MODE_ANSWER_QUESTION);
- startActivity(intent);
- }
- };
-
- private void updateSnackBar(final Conversation conversation) {
- final Account account = conversation.getAccount();
- final Contact contact = conversation.getContact();
- final int mode = conversation.getMode();
- if (account.getStatus() == Account.State.DISABLED) {
- showSnackbar(R.string.this_account_is_disabled, R.string.enable, this.mEnableAccountListener);
- } else if (conversation.isBlocked()) {
- showSnackbar(R.string.contact_blocked, R.string.unblock, this.mUnblockClickListener);
- } else if (!contact.showInRoster() && contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- showSnackbar(R.string.contact_added_you, R.string.add_back, this.mAddBackClickListener);
- } else if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- showSnackbar(R.string.contact_asks_for_presence_subscription, R.string.allow, this.mAllowPresenceSubscription);
- } else if (mode == Conversation.MODE_MULTI
- && !conversation.getMucOptions().online()
- && account.getStatus() == Account.State.ONLINE) {
- switch (conversation.getMucOptions().getError()) {
- case NICK_IN_USE:
- showSnackbar(R.string.nick_in_use, R.string.edit, clickToMuc);
- break;
- case NO_RESPONSE:
- showSnackbar(R.string.joining_conference, 0, null);
- break;
- case SERVER_NOT_FOUND:
- showSnackbar(R.string.remote_server_not_found,R.string.leave, leaveMuc);
- break;
- case PASSWORD_REQUIRED:
- showSnackbar(R.string.conference_requires_password, R.string.enter_password, enterPassword);
- break;
- case BANNED:
- showSnackbar(R.string.conference_banned, R.string.leave, leaveMuc);
- break;
- case MEMBERS_ONLY:
- showSnackbar(R.string.conference_members_only, R.string.leave, leaveMuc);
- break;
- case KICKED:
- showSnackbar(R.string.conference_kicked, R.string.join, joinMuc);
- break;
- case UNKNOWN:
- activity.xmppConnectionService.joinMuc(conversation);
- //showSnackbar(R.string.conference_unknown_error, R.string.join, joinMuc);
- break;
- case SHUTDOWN:
- showSnackbar(R.string.conference_shutdown, R.string.join, joinMuc);
- break;
- default:
- break;
- }
- } else if (account.hasPendingPgpIntent(conversation)) {
- showSnackbar(R.string.openpgp_messages_found, R.string.decrypt, clickToDecryptListener);
- } else if (mode == Conversation.MODE_SINGLE
- && conversation.smpRequested()) {
- showSnackbar(R.string.smp_requested, R.string.verify, this.mAnswerSmpClickListener);
- } else if (mode == Conversation.MODE_SINGLE
- && conversation.hasValidOtrSession()
- && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
- && (!conversation.isOtrFingerprintVerified())) {
- showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify);
- } else {
- hideSnackbar();
- }
- }
-
- public void updateMessages() {
- synchronized (this.messageList) {
- if (getView() == null) {
- return;
- }
- final ConversationActivity activity = (ConversationActivity) getActivity();
- if (this.conversation != null) {
- conversation.populateWithMessages(ConversationFragment.this.messageList);
- updateSnackBar(conversation);
- updateStatusMessages();
- this.messageListAdapter.notifyDataSetChanged();
- updateChatMsgHint();
- if (!activity.isConversationsOverviewVisable() || !activity.isConversationsOverviewHideable()) {
- activity.sendReadMarkerIfNecessary(conversation);
- }
- this.updateSendButton();
- }
- }
- }
-
- protected void messageSent() {
- mEditMessage.setText("");
- updateChatMsgHint();
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- int size = messageList.size();
- messagesView.setSelection(size - 1);
- }
- });
- }
-
- public void setFocusOnInputField() {
- mEditMessage.requestFocus();
- }
-
- enum SendButtonAction {TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL, CHOOSE_PICTURE}
-
- private int getSendButtonImageResource(SendButtonAction action, Presence.Status status) {
- switch (action) {
- case TEXT:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_text_online;
- case AWAY:
- return R.drawable.ic_send_text_away;
- case XA:
- case DND:
- return R.drawable.ic_send_text_dnd;
- default:
- return R.drawable.ic_send_text_offline;
- }
- case TAKE_PHOTO:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_photo_online;
- case AWAY:
- return R.drawable.ic_send_photo_away;
- case XA:
- case DND:
- return R.drawable.ic_send_photo_dnd;
- default:
- return R.drawable.ic_send_photo_offline;
- }
- case RECORD_VOICE:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_voice_online;
- case AWAY:
- return R.drawable.ic_send_voice_away;
- case XA:
- case DND:
- return R.drawable.ic_send_voice_dnd;
- default:
- return R.drawable.ic_send_voice_offline;
- }
- case SEND_LOCATION:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_location_online;
- case AWAY:
- return R.drawable.ic_send_location_away;
- case XA:
- case DND:
- return R.drawable.ic_send_location_dnd;
- default:
- return R.drawable.ic_send_location_offline;
- }
- case CANCEL:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_cancel_online;
- case AWAY:
- return R.drawable.ic_send_cancel_away;
- case XA:
- case DND:
- return R.drawable.ic_send_cancel_dnd;
- default:
- return R.drawable.ic_send_cancel_offline;
- }
- case CHOOSE_PICTURE:
- switch (status) {
- case CHAT:
- case ONLINE:
- return R.drawable.ic_send_picture_online;
- case AWAY:
- return R.drawable.ic_send_picture_away;
- case XA:
- case DND:
- return R.drawable.ic_send_picture_dnd;
- default:
- return R.drawable.ic_send_picture_offline;
- }
- }
- return R.drawable.ic_send_text_offline;
- }
-
- public void updateSendButton() {
- final Conversation c = this.conversation;
- final SendButtonAction action;
- final Presence.Status status;
- final String text = this.mEditMessage == null ? "" : this.mEditMessage.getText().toString();
- final boolean empty = text.length() == 0;
- final boolean conference = c.getMode() == Conversation.MODE_MULTI;
- if (c.getCorrectingMessage() != null && (empty || text.equals(c.getCorrectingMessage().getBody()))) {
- action = SendButtonAction.CANCEL;
- } else if (conference && !c.getAccount().httpUploadAvailable()) {
- if (empty && c.getNextCounterpart() != null) {
- action = SendButtonAction.CANCEL;
- } else {
- action = SendButtonAction.TEXT;
- }
- } else {
- if (empty) {
- if (conference && c.getNextCounterpart() != null) {
- action = SendButtonAction.CANCEL;
- } else {
- String setting = activity.getPreferences().getString("quick_action", "recent");
- if (!setting.equals("none") && UIHelper.receivedLocationQuestion(conversation.getLatestMessage())) {
- setting = "location";
- } else if (setting.equals("recent")) {
- setting = activity.getPreferences().getString("recently_used_quick_action", "text");
- }
- switch (setting) {
- case "photo":
- action = SendButtonAction.TAKE_PHOTO;
- break;
- case "location":
- action = SendButtonAction.SEND_LOCATION;
- break;
- case "voice":
- action = SendButtonAction.RECORD_VOICE;
- break;
- case "picture":
- action = SendButtonAction.CHOOSE_PICTURE;
- break;
- default:
- action = SendButtonAction.TEXT;
- break;
- }
- }
- } else {
- action = SendButtonAction.TEXT;
- }
- }
- if (activity.useSendButtonToIndicateStatus() && c != null
- && c.getAccount().getStatus() == Account.State.ONLINE) {
- if (c.getMode() == Conversation.MODE_SINGLE) {
- status = c.getContact().getShownStatus();
- } else {
- status = c.getMucOptions().online() ? Presence.Status.ONLINE : Presence.Status.OFFLINE;
- }
- } else {
- status = Presence.Status.OFFLINE;
- }
- this.mSendButton.setTag(action);
- this.mSendButton.setImageResource(getSendButtonImageResource(action, status));
- }
-
- protected void updateStatusMessages() {
- synchronized (this.messageList) {
- if (showLoadMoreMessages(conversation)) {
- this.messageList.add(0, Message.createLoadMoreMessage(conversation));
- }
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- ChatState state = conversation.getIncomingChatState();
- if (state == ChatState.COMPOSING) {
- //this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_is_typing, conversation.getName())));
- } else if (state == ChatState.PAUSED) {
- //this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_has_stopped_typing, conversation.getName())));
- } else {
- for (int i = this.messageList.size() - 1; i >= 0; --i) {
- if (this.messageList.get(i).getStatus() == Message.STATUS_RECEIVED) {
- return;
- } else {
- if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) {
+ }
+ }
+
+ private OnClickListener mEnableAccountListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Account account = conversation == null ? null : conversation.getAccount();
+ if (account != null) {
+ account.setOption(Account.OPTION_DISABLED, false);
+ activity.xmppConnectionService.updateAccount(account);
+ }
+ }
+ };
+
+ private OnClickListener mUnblockClickListener = new OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ v.post(new Runnable() {
+ @Override
+ public void run() {
+ v.setVisibility(View.INVISIBLE);
+ }
+ });
+ if (conversation.isDomainBlocked()) {
+ BlockContactDialog.show(activity, activity.xmppConnectionService, conversation);
+ } else {
+ activity.unblockConversation(conversation);
+ }
+ }
+ };
+
+ private OnClickListener mAddBackClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ final Contact contact = conversation == null ? null : conversation.getContact();
+ if (contact != null) {
+ activity.xmppConnectionService.createContact(contact);
+ activity.switchToContactDetails(contact);
+ }
+ }
+ };
+
+ private OnClickListener mAllowPresenceSubscription = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Contact contact = conversation == null ? null : conversation.getContact();
+ if (contact != null) {
+ activity.xmppConnectionService.sendPresencePacket(contact.getAccount(),
+ activity.xmppConnectionService.getPresenceGenerator()
+ .sendPresenceUpdatesTo(contact));
+ hideSnackbar();
+ }
+ }
+ };
+
+ private OnClickListener mAnswerSmpClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(activity, VerifyOTRActivity.class);
+ intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
+ intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
+ intent.putExtra(VerifyOTRActivity.EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
+ intent.putExtra("mode", VerifyOTRActivity.MODE_ANSWER_QUESTION);
+ startActivity(intent);
+ }
+ };
+
+ private void updateSnackBar(final Conversation conversation) {
+ final Account account = conversation.getAccount();
+ final Contact contact = conversation.getContact();
+ final int mode = conversation.getMode();
+ if (account.getStatus() == Account.State.DISABLED) {
+ showSnackbar(R.string.this_account_is_disabled, R.string.enable, this.mEnableAccountListener);
+ } else if (conversation.isBlocked()) {
+ showSnackbar(R.string.contact_blocked, R.string.unblock, this.mUnblockClickListener);
+ } else if (!contact.showInRoster() && contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ showSnackbar(R.string.contact_added_you, R.string.add_back, this.mAddBackClickListener);
+ } else if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ showSnackbar(R.string.contact_asks_for_presence_subscription, R.string.allow, this.mAllowPresenceSubscription);
+ } else if (mode == Conversation.MODE_MULTI
+ && !conversation.getMucOptions().online()
+ && account.getStatus() == Account.State.ONLINE) {
+ switch (conversation.getMucOptions().getError()) {
+ case NICK_IN_USE:
+ showSnackbar(R.string.nick_in_use, R.string.edit, clickToMuc);
+ break;
+ case NO_RESPONSE:
+ showSnackbar(R.string.joining_conference, 0, null);
+ break;
+ case SERVER_NOT_FOUND:
+ showSnackbar(R.string.remote_server_not_found, R.string.leave, leaveMuc);
+ break;
+ case PASSWORD_REQUIRED:
+ showSnackbar(R.string.conference_requires_password, R.string.enter_password, enterPassword);
+ break;
+ case BANNED:
+ showSnackbar(R.string.conference_banned, R.string.leave, leaveMuc);
+ break;
+ case MEMBERS_ONLY:
+ showSnackbar(R.string.conference_members_only, R.string.leave, leaveMuc);
+ break;
+ case KICKED:
+ showSnackbar(R.string.conference_kicked, R.string.join, joinMuc);
+ break;
+ case UNKNOWN:
+ activity.xmppConnectionService.joinMuc(conversation);
+ //showSnackbar(R.string.conference_unknown_error, R.string.join, joinMuc);
+ break;
+ case SHUTDOWN:
+ showSnackbar(R.string.conference_shutdown, R.string.join, joinMuc);
+ break;
+ default:
+ break;
+ }
+ } else if (account.hasPendingPgpIntent(conversation)) {
+ showSnackbar(R.string.openpgp_messages_found, R.string.decrypt, clickToDecryptListener);
+ } else if (mode == Conversation.MODE_SINGLE
+ && conversation.smpRequested()) {
+ showSnackbar(R.string.smp_requested, R.string.verify, this.mAnswerSmpClickListener);
+ } else if (mode == Conversation.MODE_SINGLE
+ && conversation.hasValidOtrSession()
+ && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
+ && (!conversation.isOtrFingerprintVerified())) {
+ showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify);
+ } else {
+ hideSnackbar();
+ }
+ }
+
+ public void updateMessages() {
+ synchronized (this.messageList) {
+ if (getView() == null) {
+ return;
+ }
+ final ConversationActivity activity = (ConversationActivity) getActivity();
+ if (this.conversation != null) {
+ conversation.populateWithMessages(ConversationFragment.this.messageList);
+ updateSnackBar(conversation);
+ updateStatusMessages();
+ this.messageListAdapter.notifyDataSetChanged();
+ updateChatMsgHint();
+ if (!activity.isConversationsOverviewVisable() || !activity.isConversationsOverviewHideable()) {
+ activity.sendReadMarkerIfNecessary(conversation);
+ }
+ this.updateSendButton();
+ }
+ }
+ }
+
+ protected void messageSent() {
+ mEditMessage.setText("");
+ updateChatMsgHint();
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ int size = messageList.size();
+ messagesView.setSelection(size - 1);
+ }
+ });
+ }
+
+ public void setFocusOnInputField() {
+ mEditMessage.requestFocus();
+ }
+
+ enum SendButtonAction {TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL, CHOOSE_PICTURE}
+
+ private int getSendButtonImageResource(SendButtonAction action, Presence.Status status) {
+ switch (action) {
+ case TEXT:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_text_online;
+ case AWAY:
+ return R.drawable.ic_send_text_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_text_dnd;
+ default:
+ return R.drawable.ic_send_text_offline;
+ }
+ case TAKE_PHOTO:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_photo_online;
+ case AWAY:
+ return R.drawable.ic_send_photo_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_photo_dnd;
+ default:
+ return R.drawable.ic_send_photo_offline;
+ }
+ case RECORD_VOICE:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_voice_online;
+ case AWAY:
+ return R.drawable.ic_send_voice_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_voice_dnd;
+ default:
+ return R.drawable.ic_send_voice_offline;
+ }
+ case SEND_LOCATION:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_location_online;
+ case AWAY:
+ return R.drawable.ic_send_location_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_location_dnd;
+ default:
+ return R.drawable.ic_send_location_offline;
+ }
+ case CANCEL:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_cancel_online;
+ case AWAY:
+ return R.drawable.ic_send_cancel_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_cancel_dnd;
+ default:
+ return R.drawable.ic_send_cancel_offline;
+ }
+ case CHOOSE_PICTURE:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_picture_online;
+ case AWAY:
+ return R.drawable.ic_send_picture_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_picture_dnd;
+ default:
+ return R.drawable.ic_send_picture_offline;
+ }
+ }
+ return R.drawable.ic_send_text_offline;
+ }
+
+ public void updateSendButton() {
+ final Conversation c = this.conversation;
+ final SendButtonAction action;
+ final Presence.Status status;
+ final String text = this.mEditMessage == null ? "" : this.mEditMessage.getText().toString();
+ final boolean empty = text.length() == 0;
+ final boolean conference = c.getMode() == Conversation.MODE_MULTI;
+ if (c.getCorrectingMessage() != null && (empty || text.equals(c.getCorrectingMessage().getBody()))) {
+ action = SendButtonAction.CANCEL;
+ } else if (conference && !c.getAccount().httpUploadAvailable()) {
+ if (empty && c.getNextCounterpart() != null) {
+ action = SendButtonAction.CANCEL;
+ } else {
+ action = SendButtonAction.TEXT;
+ }
+ } else {
+ if (empty) {
+ if (conference && c.getNextCounterpart() != null) {
+ action = SendButtonAction.CANCEL;
+ } else {
+ String setting = activity.getPreferences().getString("quick_action", "recent");
+ if (!setting.equals("none") && UIHelper.receivedLocationQuestion(conversation.getLatestMessage())) {
+ setting = "location";
+ } else if (setting.equals("recent")) {
+ setting = activity.getPreferences().getString("recently_used_quick_action", "text");
+ }
+ switch (setting) {
+ case "photo":
+ action = SendButtonAction.TAKE_PHOTO;
+ break;
+ case "location":
+ action = SendButtonAction.SEND_LOCATION;
+ break;
+ case "voice":
+ action = SendButtonAction.RECORD_VOICE;
+ break;
+ case "picture":
+ action = SendButtonAction.CHOOSE_PICTURE;
+ break;
+ default:
+ action = SendButtonAction.TEXT;
+ break;
+ }
+ }
+ } else {
+ action = SendButtonAction.TEXT;
+ }
+ }
+ if (activity.useSendButtonToIndicateStatus() && c != null
+ && c.getAccount().getStatus() == Account.State.ONLINE) {
+ if (c.getMode() == Conversation.MODE_SINGLE) {
+ status = c.getContact().getShownStatus();
+ } else {
+ status = c.getMucOptions().online() ? Presence.Status.ONLINE : Presence.Status.OFFLINE;
+ }
+ } else {
+ status = Presence.Status.OFFLINE;
+ }
+ this.mSendButton.setTag(action);
+ this.mSendButton.setImageResource(getSendButtonImageResource(action, status));
+ }
+
+ protected void updateStatusMessages() {
+ synchronized (this.messageList) {
+ if (showLoadMoreMessages(conversation)) {
+ this.messageList.add(0, Message.createLoadMoreMessage(conversation));
+ }
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ ChatState state = conversation.getIncomingChatState();
+ if (state == ChatState.COMPOSING) {
+ //this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_is_typing, conversation.getName())));
+ } else if (state == ChatState.PAUSED) {
+ //this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_has_stopped_typing, conversation.getName())));
+ } else {
+ for (int i = this.messageList.size() - 1; i >= 0; --i) {
+ if (this.messageList.get(i).getStatus() == Message.STATUS_RECEIVED) {
+ return;
+ } else {
+ if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) {
// this.messageList.add(i + 1,
// Message.createStatusMessage(conversation, getString(R.string.contact_has_read_up_to_this_point, conversation.getName())));
// return;
- }
- }
- }
- }
- }
- }
- }
-
- private boolean showLoadMoreMessages(final Conversation c) {
- final boolean mam = hasMamSupport(c);
- final MessageArchiveService service = activity.xmppConnectionService.getMessageArchiveService();
- return mam && (c.getLastClearHistory() != 0 || (c.countMessages() == 0 && c.hasMessagesLeftOnServer() && !service.queryInProgress(c)));
- }
-
- private boolean hasMamSupport(final Conversation c) {
- if (c.getMode() == Conversation.MODE_SINGLE) {
- final XmppConnection connection = c.getAccount().getXmppConnection();
- return connection != null && connection.getFeatures().mam();
- } else {
- return c.getMucOptions().mamSupport();
- }
- }
-
- protected void showSnackbar(final int message, final int action, final OnClickListener clickListener) {
- snackbar.setVisibility(View.VISIBLE);
- snackbar.setOnClickListener(null);
- snackbarMessage.setText(message);
- snackbarMessage.setOnClickListener(null);
- snackbarAction.setVisibility(clickListener == null ? View.GONE : View.VISIBLE);
- if (action != 0) {
- snackbarAction.setText(action);
- }
- snackbarAction.setOnClickListener(clickListener);
- }
-
- protected void hideSnackbar() {
- snackbar.setVisibility(View.GONE);
- }
-
- protected void sendPlainTextMessage(Message message) {
- ConversationActivity activity = (ConversationActivity) getActivity();
- activity.xmppConnectionService.sendMessage(message);
- messageSent();
- }
-
- protected void sendPgpMessage(final Message message) {
- final ConversationActivity activity = (ConversationActivity) getActivity();
- final XmppConnectionService xmppService = activity.xmppConnectionService;
- final Contact contact = message.getConversation().getContact();
- if (!activity.hasPgp()) {
- activity.showInstallPgpDialog();
- return;
- }
- if (conversation.getAccount().getPgpSignature() == null) {
- activity.announcePgp(conversation.getAccount(), conversation, activity.onOpenPGPKeyPublished);
- return;
- }
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- if (contact.getPgpKeyId() != 0) {
- xmppService.getPgpEngine().hasKey(contact,
- new UiCallback<Contact>() {
-
- @Override
- public void userInputRequried(PendingIntent pi,
- Contact contact) {
- activity.runIntent(
- pi,
- ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
- }
-
- @Override
- public void success(Contact contact) {
- activity.encryptTextMessage(message);
- }
-
- @Override
- public void error(int error, Contact contact) {
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(activity,
- R.string.unable_to_connect_to_keychain,
- Toast.LENGTH_SHORT
- ).show();
- }
- });
- }
- });
-
- } else {
- showNoPGPKeyDialog(false,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- conversation
- .setNextEncryption(Message.ENCRYPTION_NONE);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private boolean showLoadMoreMessages(final Conversation c) {
+ final boolean mam = hasMamSupport(c);
+ final MessageArchiveService service = activity.xmppConnectionService.getMessageArchiveService();
+ return mam && (c.getLastClearHistory() != 0 || (c.countMessages() == 0 && c.hasMessagesLeftOnServer() && !service.queryInProgress(c)));
+ }
+
+ private boolean hasMamSupport(final Conversation c) {
+ if (c.getMode() == Conversation.MODE_SINGLE) {
+ final XmppConnection connection = c.getAccount().getXmppConnection();
+ return connection != null && connection.getFeatures().mam();
+ } else {
+ return c.getMucOptions().mamSupport();
+ }
+ }
+
+ protected void showSnackbar(final int message, final int action, final OnClickListener clickListener) {
+ snackbar.setVisibility(View.VISIBLE);
+ snackbar.setOnClickListener(null);
+ snackbarMessage.setText(message);
+ snackbarMessage.setOnClickListener(null);
+ snackbarAction.setVisibility(clickListener == null ? View.GONE : View.VISIBLE);
+ if (action != 0) {
+ snackbarAction.setText(action);
+ }
+ snackbarAction.setOnClickListener(clickListener);
+ }
+
+ protected void hideSnackbar() {
+ snackbar.setVisibility(View.GONE);
+ }
+
+ protected void sendPlainTextMessage(Message message) {
+ ConversationActivity activity = (ConversationActivity) getActivity();
+ activity.xmppConnectionService.sendMessage(message);
+ messageSent();
+ }
+
+ protected void sendPgpMessage(final Message message) {
+ final ConversationActivity activity = (ConversationActivity) getActivity();
+ final XmppConnectionService xmppService = activity.xmppConnectionService;
+ final Contact contact = message.getConversation().getContact();
+ if (!activity.hasPgp()) {
+ activity.showInstallPgpDialog();
+ return;
+ }
+ if (conversation.getAccount().getPgpSignature() == null) {
+ activity.announcePgp(conversation.getAccount(), conversation, activity.onOpenPGPKeyPublished);
+ return;
+ }
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ if (contact.getPgpKeyId() != 0) {
+ xmppService.getPgpEngine().hasKey(contact,
+ new UiCallback<Contact>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi,
+ Contact contact) {
+ activity.runIntent(
+ pi,
+ ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
+ }
+
+ @Override
+ public void success(Contact contact) {
+ activity.encryptTextMessage(message);
+ }
+
+ @Override
+ public void error(int error, Contact contact) {
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(activity,
+ R.string.unable_to_connect_to_keychain,
+ Toast.LENGTH_SHORT
+ ).show();
+ }
+ });
+ }
+ });
+
+ } else {
+ showNoPGPKeyDialog(false,
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ conversation
+ .setNextEncryption(Message.ENCRYPTION_NONE);
xmppService.updateConversation(conversation);
- message.setEncryption(Message.ENCRYPTION_NONE);
- xmppService.sendMessage(message);
- messageSent();
- }
- });
- }
- } else {
- if (conversation.getMucOptions().pgpKeysInUse()) {
- if (!conversation.getMucOptions().everybodyHasKeys()) {
- Toast warning = Toast
- .makeText(getActivity(),
- R.string.missing_public_keys,
- Toast.LENGTH_LONG);
- warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
- warning.show();
- }
- activity.encryptTextMessage(message);
- } else {
- showNoPGPKeyDialog(true,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- conversation
- .setNextEncryption(Message.ENCRYPTION_NONE);
- message.setEncryption(Message.ENCRYPTION_NONE);
+ message.setEncryption(Message.ENCRYPTION_NONE);
+ xmppService.sendMessage(message);
+ messageSent();
+ }
+ });
+ }
+ } else {
+ if (conversation.getMucOptions().pgpKeysInUse()) {
+ if (!conversation.getMucOptions().everybodyHasKeys()) {
+ Toast warning = Toast
+ .makeText(getActivity(),
+ R.string.missing_public_keys,
+ Toast.LENGTH_LONG);
+ warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
+ warning.show();
+ }
+ activity.encryptTextMessage(message);
+ } else {
+ showNoPGPKeyDialog(true,
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ conversation
+ .setNextEncryption(Message.ENCRYPTION_NONE);
+ message.setEncryption(Message.ENCRYPTION_NONE);
xmppService.updateConversation(conversation);
- xmppService.sendMessage(message);
- messageSent();
- }
- });
- }
- }
- }
-
- public void showNoPGPKeyDialog(boolean plural,
- DialogInterface.OnClickListener listener) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- if (plural) {
- builder.setTitle(getString(R.string.no_pgp_keys));
- builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));
- } else {
- builder.setTitle(getString(R.string.no_pgp_key));
- builder.setMessage(getText(R.string.contact_has_no_pgp_key));
- }
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.send_unencrypted),
- listener);
- builder.create().show();
- }
-
- protected void sendAxolotlMessage(final Message message) {
- final ConversationActivity activity = (ConversationActivity) getActivity();
- final XmppConnectionService xmppService = activity.xmppConnectionService;
- xmppService.sendMessage(message);
- messageSent();
- }
-
- protected void sendOtrMessage(final Message message) {
- final ConversationActivity activity = (ConversationActivity) getActivity();
- final XmppConnectionService xmppService = activity.xmppConnectionService;
- activity.selectPresence(message.getConversation(),
- new OnPresenceSelected() {
-
- @Override
- public void onPresenceSelected() {
- message.setCounterpart(conversation.getNextCounterpart());
- xmppService.sendMessage(message);
- messageSent();
- }
- });
- }
-
- public void appendText(String text) {
- if (text == null) {
- return;
- }
- String previous = this.mEditMessage.getText().toString();
- if (previous.length() != 0 && !previous.endsWith(" ")) {
- text = " " + text;
- }
- this.mEditMessage.append(text);
- }
-
- @Override
- public boolean onEnterPressed() {
- if (activity.enterIsSend()) {
- sendMessage();
- return true;
- } else {
- return false;
- }
- }
-
- @Override
- public void onTypingStarted() {
- Account.State status = conversation.getAccount().getStatus();
- if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) {
- activity.xmppConnectionService.sendChatState(conversation);
- }
- activity.hideConversationsOverview();
- updateSendButton();
- }
-
- @Override
- public void onTypingStopped() {
- Account.State status = conversation.getAccount().getStatus();
- if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.PAUSED)) {
- activity.xmppConnectionService.sendChatState(conversation);
- }
- }
-
- @Override
- public void onTextDeleted() {
- Account.State status = conversation.getAccount().getStatus();
- if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
- activity.xmppConnectionService.sendChatState(conversation);
- }
- updateSendButton();
- }
-
- @Override
- public void onTextChanged() {
- if (conversation != null && conversation.getCorrectingMessage() != null) {
- updateSendButton();
- }
- }
-
- private int completionIndex = 0;
- private int lastCompletionLength = 0;
- private String incomplete;
- private int lastCompletionCursor;
- private boolean firstWord = false;
-
- @Override
- public boolean onTabPressed(boolean repeated) {
- if (conversation == null || conversation.getMode() == Conversation.MODE_SINGLE) {
- return false;
- }
- if (repeated) {
- completionIndex++;
- } else {
- lastCompletionLength = 0;
- completionIndex = 0;
- final String content = mEditMessage.getText().toString();
- lastCompletionCursor = mEditMessage.getSelectionEnd();
- int start = lastCompletionCursor > 0 ? content.lastIndexOf(" ",lastCompletionCursor-1) + 1 : 0;
- firstWord = start == 0;
- incomplete = content.substring(start,lastCompletionCursor);
- }
- List<String> completions = new ArrayList<>();
- for(MucOptions.User user : conversation.getMucOptions().getUsers()) {
- String name = user.getName();
- if (name != null && name.startsWith(incomplete)) {
- completions.add(name+(firstWord ? ": " : " "));
- }
- }
- Collections.sort(completions);
- if (completions.size() > completionIndex) {
- String completion = completions.get(completionIndex).substring(incomplete.length());
- mEditMessage.getEditableText().delete(lastCompletionCursor,lastCompletionCursor + lastCompletionLength);
- mEditMessage.getEditableText().insert(lastCompletionCursor, completion);
- lastCompletionLength = completion.length();
- } else {
- completionIndex = -1;
- mEditMessage.getEditableText().delete(lastCompletionCursor,lastCompletionCursor + lastCompletionLength);
- lastCompletionLength = 0;
- }
- return true;
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode,
- final Intent data) {
- if (resultCode == Activity.RESULT_OK) {
- if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
- activity.getSelectedConversation().getAccount().getPgpDecryptionService().continueDecryption(true);
- } else if (requestCode == ConversationActivity.REQUEST_TRUST_KEYS_TEXT) {
- final String body = mEditMessage.getText().toString();
- Message message = new Message(conversation, body, conversation.getNextEncryption());
- sendAxolotlMessage(message);
- } else if (requestCode == ConversationActivity.REQUEST_TRUST_KEYS_MENU) {
- int choice = data.getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID);
- activity.selectPresenceToAttachFile(choice, conversation.getNextEncryption());
- }
- }
- }
+ xmppService.sendMessage(message);
+ messageSent();
+ }
+ });
+ }
+ }
+ }
+
+ public void showNoPGPKeyDialog(boolean plural,
+ DialogInterface.OnClickListener listener) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ if (plural) {
+ builder.setTitle(getString(R.string.no_pgp_keys));
+ builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));
+ } else {
+ builder.setTitle(getString(R.string.no_pgp_key));
+ builder.setMessage(getText(R.string.contact_has_no_pgp_key));
+ }
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton(getString(R.string.send_unencrypted),
+ listener);
+ builder.create().show();
+ }
+
+ protected void sendAxolotlMessage(final Message message) {
+ final ConversationActivity activity = (ConversationActivity) getActivity();
+ final XmppConnectionService xmppService = activity.xmppConnectionService;
+ xmppService.sendMessage(message);
+ messageSent();
+ }
+
+ protected void sendOtrMessage(final Message message) {
+ final ConversationActivity activity = (ConversationActivity) getActivity();
+ final XmppConnectionService xmppService = activity.xmppConnectionService;
+ activity.selectPresence(message.getConversation(),
+ new OnPresenceSelected() {
+
+ @Override
+ public void onPresenceSelected() {
+ message.setCounterpart(conversation.getNextCounterpart());
+ xmppService.sendMessage(message);
+ messageSent();
+ }
+ });
+ }
+
+ public void appendText(String text) {
+ if (text == null) {
+ return;
+ }
+ String previous = this.mEditMessage.getText().toString();
+ if (previous.length() != 0 && !previous.endsWith(" ")) {
+ text = " " + text;
+ }
+ this.mEditMessage.append(text);
+ }
+
+ @Override
+ public boolean onEnterPressed() {
+ if (activity.enterIsSend()) {
+ sendMessage();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void onTypingStarted() {
+ Account.State status = conversation.getAccount().getStatus();
+ if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) {
+ activity.xmppConnectionService.sendChatState(conversation);
+ }
+ activity.hideConversationsOverview();
+ updateSendButton();
+ }
+
+ @Override
+ public void onTypingStopped() {
+ Account.State status = conversation.getAccount().getStatus();
+ if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.PAUSED)) {
+ activity.xmppConnectionService.sendChatState(conversation);
+ }
+ }
+
+ @Override
+ public void onTextDeleted() {
+ Account.State status = conversation.getAccount().getStatus();
+ if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+ activity.xmppConnectionService.sendChatState(conversation);
+ }
+ updateSendButton();
+ }
+
+ @Override
+ public void onTextChanged() {
+ if (conversation != null && conversation.getCorrectingMessage() != null) {
+ updateSendButton();
+ }
+ }
+
+ private int completionIndex = 0;
+ private int lastCompletionLength = 0;
+ private String incomplete;
+ private int lastCompletionCursor;
+ private boolean firstWord = false;
+
+ @Override
+ public boolean onTabPressed(boolean repeated) {
+ if (conversation == null || conversation.getMode() == Conversation.MODE_SINGLE) {
+ return false;
+ }
+ if (repeated) {
+ completionIndex++;
+ } else {
+ lastCompletionLength = 0;
+ completionIndex = 0;
+ final String content = mEditMessage.getText().toString();
+ lastCompletionCursor = mEditMessage.getSelectionEnd();
+ int start = lastCompletionCursor > 0 ? content.lastIndexOf(" ", lastCompletionCursor - 1) + 1 : 0;
+ firstWord = start == 0;
+ incomplete = content.substring(start, lastCompletionCursor);
+ }
+ List<String> completions = new ArrayList<>();
+ for (MucOptions.User user : conversation.getMucOptions().getUsers()) {
+ String name = user.getName();
+ if (name != null && name.startsWith(incomplete)) {
+ completions.add(name + (firstWord ? ": " : " "));
+ }
+ }
+ Collections.sort(completions);
+ if (completions.size() > completionIndex) {
+ String completion = completions.get(completionIndex).substring(incomplete.length());
+ mEditMessage.getEditableText().delete(lastCompletionCursor, lastCompletionCursor + lastCompletionLength);
+ mEditMessage.getEditableText().insert(lastCompletionCursor, completion);
+ lastCompletionLength = completion.length();
+ } else {
+ completionIndex = -1;
+ mEditMessage.getEditableText().delete(lastCompletionCursor, lastCompletionCursor + lastCompletionLength);
+ lastCompletionLength = 0;
+ }
+ return true;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode,
+ final Intent data) {
+ if (resultCode == Activity.RESULT_OK) {
+ if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) {
+ activity.getSelectedConversation().getAccount().getPgpDecryptionService().continueDecryption(true);
+ } else if (requestCode == ConversationActivity.REQUEST_TRUST_KEYS_TEXT) {
+ final String body = mEditMessage.getText().toString();
+ Message message = new Message(conversation, body, conversation.getNextEncryption());
+ sendAxolotlMessage(message);
+ } else if (requestCode == ConversationActivity.REQUEST_TRUST_KEYS_MENU) {
+ int choice = data.getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID);
+ activity.selectPresenceToAttachFile(choice, conversation.getNextEncryption());
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
index b5e3c8a1c..58453ba75 100644
--- a/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/EditAccountActivity.java
@@ -61,1104 +61,1104 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.pixart.messenger.xmpp.pep.Avatar;
public class EditAccountActivity extends OmemoActivity implements OnAccountUpdate,
- OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched {
-
- private static final int REQUEST_DATA_SAVER = 0x37af244;
- private AutoCompleteTextView mAccountJid;
- private EditText mPassword;
- private EditText mPasswordConfirm;
- private CheckBox mRegisterNew;
- private Button mCancelButton;
- private Button mSaveButton;
- private Button mDisableOsOptimizationsButton;
- private TextView mDisableOsOptimizationsHeadline;
- private TextView getmDisableOsOptimizationsBody;
- private TableLayout mMoreTable;
-
- private LinearLayout mStats;
- private RelativeLayout mOsOptimizations;
- private TextView mServerInfoSm;
- private TextView mServerInfoRosterVersion;
- private TextView mServerInfoCarbons;
- private TextView mServerInfoMam;
- private TextView mServerInfoCSI;
- private TextView mServerInfoBlocking;
- private TextView mServerInfoPep;
- private TextView mServerInfoHttpUpload;
- private TextView mServerInfoPush;
- private TextView mSessionEst;
- private TextView mOtrFingerprint;
- private TextView mAxolotlFingerprint;
- private TextView mOwnFingerprintDesc;
- private TextView mAccountJidLabel;
- private ImageView mAvatar;
- private RelativeLayout mOtrFingerprintBox;
- private RelativeLayout mAxolotlFingerprintBox;
- private ImageButton mOtrFingerprintToClipboardButton;
- private ImageButton mAxolotlFingerprintToClipboardButton;
- private ImageButton mRegenerateAxolotlKeyButton;
- private LinearLayout keys;
- private LinearLayout keysCard;
- private LinearLayout mNamePort;
- private EditText mHostname;
- private EditText mPort;
- private AlertDialog mCaptchaDialog = null;
-
- private Jid jidToEdit;
- private boolean mInitMode = false;
- private boolean mUsernameMode = Config.DOMAIN_LOCK != null;
- private boolean mShowOptions = false;
- private Account mAccount;
- private String messageFingerprint;
-
- private boolean mFetchingAvatar = false;
-
- private final OnClickListener mSaveButtonClickListener = new OnClickListener() {
-
- @Override
- public void onClick(final View v) {
- final String password = mPassword.getText().toString();
- final String passwordConfirm = mPasswordConfirm.getText().toString();
-
- if (!mInitMode && passwordChangedInMagicCreateMode()) {
- gotoChangePassword(password);
- return;
- }
- if (mInitMode && mAccount != null) {
- mAccount.setOption(Account.OPTION_DISABLED, false);
- }
- if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) {
- mAccount.setOption(Account.OPTION_DISABLED, false);
- if (!xmppConnectionService.updateAccount(mAccount)) {
- Toast.makeText(EditAccountActivity.this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
- }
- return;
- }
- final boolean registerNewAccount = mRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
- if (mUsernameMode && mAccountJid.getText().toString().contains("@")) {
- mAccountJid.setError(getString(R.string.invalid_username));
- mAccountJid.requestFocus();
- return;
- }
- final Jid jid;
- try {
- if (mUsernameMode) {
- jid = Jid.fromParts(mAccountJid.getText().toString(), getUserModeDomain(), null);
- } else {
- jid = Jid.fromString(mAccountJid.getText().toString());
- }
- } catch (final InvalidJidException e) {
- if (mUsernameMode) {
- mAccountJid.setError(getString(R.string.invalid_username));
- } else {
- mAccountJid.setError(getString(R.string.invalid_jid));
- }
- mAccountJid.requestFocus();
- return;
- }
- String hostname = null;
- int numericPort = 5222;
- if (mShowOptions) {
- hostname = mHostname.getText().toString().replaceAll("\\s","");
- final String port = mPort.getText().toString().replaceAll("\\s","");
- if (hostname.contains(" ")) {
- mHostname.setError(getString(R.string.not_valid_hostname));
- mHostname.requestFocus();
- return;
- }
- try {
- numericPort = Integer.parseInt(port);
- if (numericPort < 0 || numericPort > 65535) {
- mPort.setError(getString(R.string.not_a_valid_port));
- mPort.requestFocus();
- return;
- }
-
- } catch (NumberFormatException e) {
- mPort.setError(getString(R.string.not_a_valid_port));
- mPort.requestFocus();
- return;
- }
- }
-
- if (jid.isDomainJid()) {
- if (mUsernameMode) {
- mAccountJid.setError(getString(R.string.invalid_username));
- } else {
- mAccountJid.setError(getString(R.string.invalid_jid));
- }
- mAccountJid.requestFocus();
- return;
- }
- if (registerNewAccount) {
- if (XmppConnection.errorMessage != null) {
- Toast.makeText(EditAccountActivity.this,XmppConnection.errorMessage,Toast.LENGTH_LONG).show();
- }
- if (!password.equals(passwordConfirm)) {
- mPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
- mPasswordConfirm.requestFocus();
- return;
- }
- }
- if (mAccount != null) {
- if (mInitMode && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
- mAccount.setOption(Account.OPTION_MAGIC_CREATE, mAccount.getPassword().contains(password));
- }
- mAccount.setJid(jid);
- mAccount.setPort(numericPort);
- mAccount.setHostname(hostname);
- if (XmppConnection.errorMessage != null) {
- mAccountJid.setError(XmppConnection.errorMessage);
- } else {
- mAccountJid.setError(null);
- }
- mPasswordConfirm.setError(null);
- mAccount.setPassword(password);
- mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
- if (!xmppConnectionService.updateAccount(mAccount)) {
- Toast.makeText(EditAccountActivity.this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
- return;
- }
- } else {
- if (xmppConnectionService.findAccountByJid(jid) != null) {
- mAccountJid.setError(getString(R.string.account_already_exists));
- mAccountJid.requestFocus();
- return;
- }
- mAccount = new Account(jid.toBareJid(), password);
- mAccount.setPort(numericPort);
- mAccount.setHostname(hostname);
- mAccount.setOption(Account.OPTION_USETLS, true);
- mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
- mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
- xmppConnectionService.createAccount(mAccount);
- }
- mHostname.setError(null);
- mPort.setError(null);
- if (!mAccount.isOptionSet(Account.OPTION_DISABLED)
- && !registerNewAccount
- && !mInitMode) {
- finish();
- } else {
- updateSaveButton();
- updateAccountInformation(true);
- }
-
- }
- };
- private final OnClickListener mCancelButtonClickListener = new OnClickListener() {
-
- @Override
- public void onClick(final View v) {
- deleteMagicCreatedAccountAndReturnIfNecessary();
- finish();
- }
- };
- private Toast mFetchingMamPrefsToast;
- private TableRow mPushRow;
- private String mSavedInstanceAccount;
- private boolean mSavedInstanceInit = false;
-
- public void refreshUiReal() {
- invalidateOptionsMenu();
- if (mAccount != null
- && mAccount.getStatus() != Account.State.ONLINE
- && mFetchingAvatar) {
- startActivity(new Intent(getApplicationContext(),
- ManageAccountActivity.class));
- finish();
- } else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
- if (!mFetchingAvatar) {
- mFetchingAvatar = true;
- xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback);
- }
- }
- if (mAccount != null) {
- updateAccountInformation(false);
- }
- updateSaveButton();
- }
-
- @Override
- public boolean onNavigateUp() {
- deleteMagicCreatedAccountAndReturnIfNecessary();
- return super.onNavigateUp();
- }
-
- @Override
- public void onBackPressed() {
- deleteMagicCreatedAccountAndReturnIfNecessary();
- super.onBackPressed();
- }
-
- private void deleteMagicCreatedAccountAndReturnIfNecessary() {
- if (Config.MAGIC_CREATE_DOMAIN != null
- && mAccount != null
- && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
- && mAccount.isOptionSet(Account.OPTION_REGISTER)
- && xmppConnectionService.getAccounts().size() == 1) {
- xmppConnectionService.deleteAccount(mAccount);
- startActivity(new Intent(EditAccountActivity.this, WelcomeActivity.class));
- }
- }
-
- @Override
- public void onAccountUpdate() {
- refreshUi();
- }
-
- private final UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
-
- @Override
- public void userInputRequried(final PendingIntent pi, final Avatar avatar) {
- finishInitialSetup(avatar);
- }
-
- @Override
- public void success(final Avatar avatar) {
- finishInitialSetup(avatar);
- }
-
- @Override
- public void error(final int errorCode, final Avatar avatar) {
- finishInitialSetup(avatar);
- }
- };
- private final TextWatcher mTextWatcher = new TextWatcher() {
-
- @Override
- public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
- updateSaveButton();
- }
-
- @Override
- public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
- }
-
- @Override
- public void afterTextChanged(final Editable s) {
-
- }
- };
-
- private final OnClickListener mAvatarClickListener = new OnClickListener() {
- @Override
- public void onClick(final View view) {
- if (mAccount != null) {
- final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
- intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
- startActivity(intent);
- }
- }
- };
-
- protected void finishInitialSetup(final Avatar avatar) {
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- final Intent intent;
- final XmppConnection connection = mAccount.getXmppConnection();
- final boolean wasFirstAccount = xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1;
- if (avatar != null || (connection != null && !connection.getFeatures().pep())) {
- intent = new Intent(getApplicationContext(), StartConversationActivity.class);
- if (wasFirstAccount) {
- intent.putExtra("init", true);
- }
- } else {
- intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
- intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
- intent.putExtra("setup", true);
- }
- if (wasFirstAccount) {
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- }
- startActivity(intent);
- finish();
- }
- });
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_BATTERY_OP || requestCode == REQUEST_DATA_SAVER) {
- updateAccountInformation(mAccount == null);
- }
- }
-
- protected void updateSaveButton() {
- boolean accountInfoEdited = accountInfoEdited();
-
- if (!mInitMode && passwordChangedInMagicCreateMode()) {
- this.mSaveButton.setText(R.string.change_password);
- this.mSaveButton.setEnabled(true);
- this.mSaveButton.setTextColor(getPrimaryTextColor());
- } else if (accountInfoEdited && !mInitMode) {
- this.mSaveButton.setText(R.string.save);
- this.mSaveButton.setEnabled(true);
- this.mSaveButton.setTextColor(getPrimaryTextColor());
- } else if (mAccount != null
- && (mAccount.getStatus() == Account.State.CONNECTING || mAccount.getStatus() == Account.State.REGISTRATION_SUCCESSFUL|| mFetchingAvatar)) {
- this.mSaveButton.setEnabled(false);
- this.mSaveButton.setTextColor(getSecondaryTextColor());
- this.mSaveButton.setText(R.string.account_status_connecting);
- } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) {
- this.mSaveButton.setEnabled(true);
- this.mSaveButton.setTextColor(getPrimaryTextColor());
- this.mSaveButton.setText(R.string.enable);
- } else {
- this.mSaveButton.setEnabled(true);
- this.mSaveButton.setTextColor(getPrimaryTextColor());
- if (!mInitMode) {
- if (mAccount != null && mAccount.isOnlineAndConnected()) {
- this.mSaveButton.setText(R.string.save);
- if (!accountInfoEdited) {
- this.mSaveButton.setEnabled(false);
- this.mSaveButton.setTextColor(getSecondaryTextColor());
- }
- } else {
- this.mSaveButton.setText(R.string.connect);
- }
- } else {
- this.mSaveButton.setText(R.string.next);
- }
- }
- }
-
- protected boolean accountInfoEdited() {
- if (this.mAccount == null) {
- return false;
- }
- return jidEdited() ||
- !this.mAccount.getPassword().equals(this.mPassword.getText().toString()) ||
- !this.mAccount.getHostname().equals(this.mHostname.getText().toString()) ||
- !String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString());
- }
-
- protected boolean jidEdited() {
- final String unmodified;
- if (mUsernameMode) {
- unmodified = this.mAccount.getJid().getLocalpart();
- } else {
- unmodified = this.mAccount.getJid().toBareJid().toString();
- }
- return !unmodified.equals(this.mAccountJid.getText().toString());
- }
-
- protected boolean passwordChangedInMagicCreateMode() {
- return mAccount != null
- && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
- && !this.mAccount.getPassword().equals(this.mPassword.getText().toString())
- && !this.jidEdited()
- && mAccount.isOnlineAndConnected();
- }
-
- @Override
- protected String getShareableUri() {
- if (mAccount != null) {
- return mAccount.getShareableUri();
- } else {
- return "";
- }
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (savedInstanceState != null) {
- this.mSavedInstanceAccount = savedInstanceState.getString("account");
- this.mSavedInstanceInit = savedInstanceState.getBoolean("initMode", false);
- }
- setContentView(R.layout.activity_edit_account);
- this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
- this.mAccountJid.addTextChangedListener(this.mTextWatcher);
- this.mAccountJidLabel = (TextView) findViewById(R.id.account_jid_label);
- this.mPassword = (EditText) findViewById(R.id.account_password);
- this.mPassword.addTextChangedListener(this.mTextWatcher);
- this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
- this.mAvatar = (ImageView) findViewById(R.id.avater);
- this.mAvatar.setOnClickListener(this.mAvatarClickListener);
- this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
- this.mStats = (LinearLayout) findViewById(R.id.stats);
- this.mOsOptimizations = (RelativeLayout) findViewById(R.id.os_optimization);
- this.mDisableOsOptimizationsButton = (Button) findViewById(R.id.os_optimization_disable);
- this.mDisableOsOptimizationsHeadline = (TextView) findViewById(R.id.os_optimization_headline);
- this.getmDisableOsOptimizationsBody = (TextView) findViewById(R.id.os_optimization_body);
- this.mSessionEst = (TextView) findViewById(R.id.session_est);
- this.mServerInfoRosterVersion = (TextView) findViewById(R.id.server_info_roster_version);
- this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
- this.mServerInfoMam = (TextView) findViewById(R.id.server_info_mam);
- this.mServerInfoCSI = (TextView) findViewById(R.id.server_info_csi);
- this.mServerInfoBlocking = (TextView) findViewById(R.id.server_info_blocking);
- this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
- this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
- this.mServerInfoHttpUpload = (TextView) findViewById(R.id.server_info_http_upload);
- this.mPushRow = (TableRow) findViewById(R.id.push_row);
- this.mServerInfoPush = (TextView) findViewById(R.id.server_info_push);
- this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
- this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
- this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
- this.mAxolotlFingerprint = (TextView) findViewById(R.id.axolotl_fingerprint);
- this.mAxolotlFingerprintBox = (RelativeLayout) findViewById(R.id.axolotl_fingerprint_box);
- this.mAxolotlFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_axolotl_to_clipboard);
- this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_omemo_key);
- this.mOwnFingerprintDesc = (TextView) findViewById(R.id.own_fingerprint_desc);
- this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card);
- this.keys = (LinearLayout) findViewById(R.id.other_device_keys);
- this.mNamePort = (LinearLayout) findViewById(R.id.name_port);
- this.mHostname = (EditText) findViewById(R.id.hostname);
- this.mHostname.addTextChangedListener(mTextWatcher);
- this.mPort = (EditText) findViewById(R.id.port);
- this.mPort.setText("5222");
- this.mPort.addTextChangedListener(mTextWatcher);
- this.mSaveButton = (Button) findViewById(R.id.save_button);
- this.mCancelButton = (Button) findViewById(R.id.cancel_button);
- this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
- this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
- this.mMoreTable = (TableLayout) findViewById(R.id.server_info_more);
- if (savedInstanceState != null && savedInstanceState.getBoolean("showMoreTable")) {
- changeMoreTableVisibility(true);
- }
- final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(final CompoundButton buttonView,
- final boolean isChecked) {
- if (isChecked) {
- mPasswordConfirm.setVisibility(View.VISIBLE);
- } else {
- mPasswordConfirm.setVisibility(View.GONE);
- }
- updateSaveButton();
- }
- };
- this.mRegisterNew.setOnCheckedChangeListener(OnCheckedShowConfirmPassword);
- if (Config.DISALLOW_REGISTRATION_IN_UI) {
- this.mRegisterNew.setVisibility(View.GONE);
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.editaccount, menu);
- final MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code);
- final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
- final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
- final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
- final MenuItem showPassword = menu.findItem(R.id.action_show_password);
- final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
- final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
- final MenuItem mamPrefs = menu.findItem(R.id.action_mam_prefs);
- final MenuItem changePresence = menu.findItem(R.id.action_change_presence);
- renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
-
- if (mAccount != null && mAccount.isOnlineAndConnected()) {
- if (!mAccount.getXmppConnection().getFeatures().blocking()) {
- showBlocklist.setVisible(false);
- }
- if (!mAccount.getXmppConnection().getFeatures().register()) {
- changePassword.setVisible(false);
- }
- mamPrefs.setVisible(mAccount.getXmppConnection().getFeatures().mam());
- Set<Integer> otherDevices = mAccount.getAxolotlService().getOwnDeviceIds();
- if (otherDevices == null || otherDevices.isEmpty() || !Config.supportOmemo()) {
- clearDevices.setVisible(false);
- }
- changePresence.setVisible(manuallyChangePresence());
- } else {
- showQrCode.setVisible(false);
- showBlocklist.setVisible(false);
- showMoreInfo.setVisible(false);
- changePassword.setVisible(false);
- clearDevices.setVisible(false);
- mamPrefs.setVisible(false);
- changePresence.setVisible(false);
- }
-
- if (mAccount != null) {
- showPassword.setVisible(mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
- && !mAccount.isOptionSet(Account.OPTION_REGISTER));
- } else {
- showPassword.setVisible(false);
- }
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
- if (showMoreInfo.isVisible()) {
- showMoreInfo.setChecked(mMoreTable.getVisibility() == View.VISIBLE);
- }
- return super.onPrepareOptionsMenu(menu);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- final int theme = findTheme();
- if (this.mTheme != theme) {
- recreate();
- } else if (getIntent() != null) {
- try {
- this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid"));
- } catch (final InvalidJidException | NullPointerException ignored) {
- this.jidToEdit = null;
- }
- boolean init = getIntent().getBooleanExtra("init", false);
- this.mInitMode = init || this.jidToEdit == null;
- this.messageFingerprint = getIntent().getStringExtra("fingerprint");
- if (!mInitMode) {
- this.mRegisterNew.setVisibility(View.GONE);
- if (getActionBar() != null) {
- getActionBar().setTitle(getString(R.string.account_details));
- }
- } else {
- this.mAvatar.setVisibility(View.GONE);
- ActionBar ab = getActionBar();
- if (ab != null) {
- if (init && Config.MAGIC_CREATE_DOMAIN == null) {
- ab.setDisplayShowHomeEnabled(false);
- ab.setDisplayHomeAsUpEnabled(false);
- }
- ab.setTitle(R.string.action_add_account);
- }
- }
- }
- SharedPreferences preferences = getPreferences();
- boolean useTor = Config.FORCE_ORBOT || preferences.getBoolean("use_tor", false);
- this.mShowOptions = useTor || preferences.getBoolean("show_connection_options", false);
- mHostname.setHint(useTor ? R.string.hostname_or_onion : R.string.hostname_example);
- this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
- }
-
- @Override
- public void onSaveInstanceState(final Bundle savedInstanceState) {
- if (mAccount != null) {
- savedInstanceState.putString("account", mAccount.getJid().toBareJid().toString());
- savedInstanceState.putBoolean("initMode", mInitMode);
- savedInstanceState.putBoolean("showMoreTable", mMoreTable.getVisibility() == View.VISIBLE);
- }
- super.onSaveInstanceState(savedInstanceState);
- }
-
- protected void onBackendConnected() {
- boolean init = true;
- if (mSavedInstanceAccount != null) {
- try {
- this.mAccount = xmppConnectionService.findAccountByJid(Jid.fromString(mSavedInstanceAccount));
- this.mInitMode = mSavedInstanceInit;
- init = false;
- } catch (InvalidJidException e) {
- this.mAccount = null;
- }
-
- } else if (this.jidToEdit != null) {
- this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
- }
-
- if (mAccount != null) {
- this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
- this.mUsernameMode |= mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && mAccount.isOptionSet(Account.OPTION_REGISTER);
- if (this.mAccount.getPrivateKeyAlias() != null) {
- this.mPassword.setHint(R.string.authenticate_with_certificate);
- if (this.mInitMode) {
- this.mPassword.requestFocus();
- }
- }
- updateAccountInformation(init);
- }
-
-
- if (Config.MAGIC_CREATE_DOMAIN == null && this.xmppConnectionService.getAccounts().size() == 0) {
- this.mCancelButton.setEnabled(false);
- this.mCancelButton.setTextColor(getSecondaryTextColor());
- }
- if (mUsernameMode) {
- this.mAccountJidLabel.setText(R.string.username);
- this.mAccountJid.setHint(R.string.username_hint);
- } else {
- final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
- R.layout.simple_list_item,
- xmppConnectionService.getKnownHosts());
- this.mAccountJid.setAdapter(mKnownHostsAdapter);
- }
- updateSaveButton();
- invalidateOptionsMenu();
- }
-
- private String getUserModeDomain() {
- if (mAccount != null) {
- return mAccount.getJid().getDomainpart();
- } else {
- return Config.DOMAIN_LOCK;
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case R.id.mgmt_account_reconnect:
- if (xmppConnectionServiceBound) {
- unbindService(mConnection);
- xmppConnectionServiceBound = false;
- }
- stopService(new Intent(EditAccountActivity.this,
- XmppConnectionService.class));
- finish();
- break;
- case R.id.action_show_block_list:
- final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class);
- showBlocklistIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
- startActivity(showBlocklistIntent);
- break;
- case R.id.action_server_info_show_more:
- changeMoreTableVisibility(!item.isChecked());
- break;
- case R.id.action_change_password_on_server:
- gotoChangePassword(null);
- break;
- case R.id.action_mam_prefs:
- editMamPrefs();
- break;
- case R.id.action_clear_devices:
- showWipePepDialog();
- break;
- case R.id.action_renew_certificate:
- renewCertificate();
- break;
- case R.id.action_change_presence:
- changePresence();
- break;
- case R.id.action_show_password:
- showPassword();
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-
- private void changeMoreTableVisibility(boolean visible) {
- mMoreTable.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
-
- private void gotoChangePassword(String newPassword) {
- final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
- changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
- if (newPassword != null) {
- changePasswordIntent.putExtra("password", newPassword);
- }
- startActivity(changePasswordIntent);
- }
-
- private void renewCertificate() {
- KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
- }
-
- private void changePresence() {
- Intent intent = new Intent(this, SetPresenceActivity.class);
- intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT,mAccount.getJid().toBareJid().toString());
- startActivity(intent);
- }
-
- @Override
- public void alias(String alias) {
- if (alias != null) {
- xmppConnectionService.updateKeyInAccount(mAccount, alias);
- }
- }
-
- private void updateAccountInformation(boolean init) {
- if (init) {
- this.mAccountJid.getEditableText().clear();
- if (mUsernameMode) {
- this.mAccountJid.getEditableText().append(this.mAccount.getJid().getLocalpart());
- } else {
- this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString());
- }
- this.mPassword.setText(this.mAccount.getPassword());
- this.mHostname.setText("");
- this.mHostname.getEditableText().append(this.mAccount.getHostname());
- this.mPort.setText("");
- this.mPort.getEditableText().append(String.valueOf(this.mAccount.getPort()));
- this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
-
- }
-
- if (!mInitMode) {
- this.mAvatar.setVisibility(View.VISIBLE);
- this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(Config.AVATAR_SIZE)));
- this.mAccountJid.setEnabled(false);
- } else {
- this.mAvatar.setVisibility(View.GONE);
- }
- if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
- this.mRegisterNew.setVisibility(View.VISIBLE);
- this.mRegisterNew.setChecked(true);
- this.mPasswordConfirm.setText(this.mAccount.getPassword());
- } else {
- this.mRegisterNew.setVisibility(View.GONE);
- this.mRegisterNew.setChecked(false);
- }
- if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
- Features features = this.mAccount.getXmppConnection().getFeatures();
- this.mStats.setVisibility(View.VISIBLE);
- boolean showBatteryWarning = !xmppConnectionService.getPushManagementService().available(mAccount) && isOptimizingBattery();
- boolean showDataSaverWarning = isAffectedByDataSaver();
- showOsOptimizationWarning(showBatteryWarning,showDataSaverWarning);
- this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
- .getLastSessionEstablished()));
- if (features.rosterVersioning()) {
- this.mServerInfoRosterVersion.setText(R.string.server_info_available);
- } else {
- this.mServerInfoRosterVersion.setText(R.string.server_info_unavailable);
- }
- if (features.carbons()) {
- this.mServerInfoCarbons.setText(R.string.server_info_available);
- } else {
- this.mServerInfoCarbons
- .setText(R.string.server_info_unavailable);
- }
- if (features.mam()) {
- this.mServerInfoMam.setText(R.string.server_info_available);
- } else {
- this.mServerInfoMam.setText(R.string.server_info_unavailable);
- }
- if (features.csi()) {
- this.mServerInfoCSI.setText(R.string.server_info_available);
- } else {
- this.mServerInfoCSI.setText(R.string.server_info_unavailable);
- }
- if (features.blocking()) {
- this.mServerInfoBlocking.setText(R.string.server_info_available);
- } else {
- this.mServerInfoBlocking.setText(R.string.server_info_unavailable);
- }
- if (features.sm()) {
- this.mServerInfoSm.setText(R.string.server_info_available);
- } else {
- this.mServerInfoSm.setText(R.string.server_info_unavailable);
- }
- if (features.pep()) {
- AxolotlService axolotlService = this.mAccount.getAxolotlService();
- if (axolotlService != null && axolotlService.isPepBroken()) {
- this.mServerInfoPep.setText(R.string.server_info_broken);
- } else {
- this.mServerInfoPep.setText(R.string.server_info_available);
- }
- } else {
- this.mServerInfoPep.setText(R.string.server_info_unavailable);
- }
- if (features.httpUpload(0)) {
- this.mServerInfoHttpUpload.setText(R.string.server_info_available);
- } else {
- this.mServerInfoHttpUpload.setText(R.string.server_info_unavailable);
- }
-
- this.mPushRow.setVisibility(xmppConnectionService.getPushManagementService().isStub() ? View.GONE : View.VISIBLE);
-
- if (xmppConnectionService.getPushManagementService().available(mAccount)) {
- this.mServerInfoPush.setText(R.string.server_info_available);
- } else {
- this.mServerInfoPush.setText(R.string.server_info_unavailable);
- }
- final String otrFingerprint = this.mAccount.getOtrFingerprint();
- if (otrFingerprint != null && Config.supportOtr()) {
- this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
- this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
- this.mOtrFingerprintToClipboardButton
- .setVisibility(View.VISIBLE);
- this.mOtrFingerprintToClipboardButton
- .setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(final View v) {
-
- if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) {
- Toast.makeText(
- EditAccountActivity.this,
- R.string.toast_message_otr_fingerprint,
- Toast.LENGTH_SHORT).show();
- }
- }
- });
- } else {
- this.mOtrFingerprintBox.setVisibility(View.GONE);
- }
- final String ownAxolotlFingerprint = this.mAccount.getAxolotlService().getOwnFingerprint();
- if (ownAxolotlFingerprint != null && Config.supportOmemo()) {
- this.mAxolotlFingerprintBox.setVisibility(View.VISIBLE);
- if (ownAxolotlFingerprint.equals(messageFingerprint)) {
- this.mOwnFingerprintDesc.setTextColor(getResources().getColor(R.color.accent));
- } else {
- this.mOwnFingerprintDesc.setTextColor(getSecondaryTextColor());
- }
- this.mAxolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(ownAxolotlFingerprint.substring(2)));
- this.mAxolotlFingerprintToClipboardButton
- .setVisibility(View.VISIBLE);
- this.mAxolotlFingerprintToClipboardButton
- .setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(final View v) {
- copyOmemoFingerprint(ownAxolotlFingerprint);
- }
- });
- if (Config.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON) {
- this.mRegenerateAxolotlKeyButton
- .setVisibility(View.VISIBLE);
- this.mRegenerateAxolotlKeyButton
- .setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(final View v) {
- showRegenerateAxolotlKeyDialog();
- }
- });
- }
- } else {
- this.mAxolotlFingerprintBox.setVisibility(View.GONE);
- }
- boolean hasKeys = false;
- keys.removeAllViews();
- for (final String fingerprint : mAccount.getAxolotlService().getFingerprintsForOwnSessions()) {
- if (ownAxolotlFingerprint.equals(fingerprint)) {
- continue;
- }
- boolean highlight = fingerprint.equals(messageFingerprint);
- hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight);
- }
- if (hasKeys && Config.supportOmemo()) {
- keysCard.setVisibility(View.VISIBLE);
- } else {
- keysCard.setVisibility(View.GONE);
- }
- } else {
- if (this.mAccount.errorStatus()) {
- final EditText errorTextField;
- if (this.mAccount.getStatus() == Account.State.UNAUTHORIZED) {
- errorTextField = this.mPassword;
- } else if (mShowOptions
- && this.mAccount.getStatus() == Account.State.SERVER_NOT_FOUND
- && this.mHostname.getText().length() > 0) {
- errorTextField = this.mHostname;
- } else {
- errorTextField = this.mAccountJid;
- }
- errorTextField.setError(getString(this.mAccount.getStatus().getReadableId()));
- if (init || !accountInfoEdited()) {
- errorTextField.requestFocus();
- }
- } else {
- this.mAccountJid.setError(null);
- this.mPassword.setError(null);
- this.mHostname.setError(null);
- }
- this.mStats.setVisibility(View.GONE);
- }
- }
-
- private void showOsOptimizationWarning(boolean showBatteryWarning, boolean showDataSaverWarning) {
- this.mOsOptimizations.setVisibility(showBatteryWarning || showDataSaverWarning ? View.VISIBLE : View.GONE);
- if (showDataSaverWarning) {
- this.mDisableOsOptimizationsHeadline.setText(R.string.data_saver_enabled);
- this.getmDisableOsOptimizationsBody.setText(R.string.data_saver_enabled_explained);
- this.mDisableOsOptimizationsButton.setText(R.string.allow);
- this.mDisableOsOptimizationsButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS);
- Uri uri = Uri.parse("package:" + getPackageName());
- intent.setData(uri);
- try {
- startActivityForResult(intent, REQUEST_DATA_SAVER);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_data_saver, Toast.LENGTH_SHORT).show();
- }
- }
- });
- } else if (showBatteryWarning) {
- this.mDisableOsOptimizationsButton.setText(R.string.disable);
- this.mDisableOsOptimizationsHeadline.setText(R.string.battery_optimizations_enabled);
- this.getmDisableOsOptimizationsBody.setText(R.string.battery_optimizations_enabled_explained);
- this.mDisableOsOptimizationsButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
- Uri uri = Uri.parse("package:" + getPackageName());
- intent.setData(uri);
- try {
- startActivityForResult(intent, REQUEST_BATTERY_OP);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
- }
- }
- });
- }
- }
-
-
- public void showRegenerateAxolotlKeyDialog() {
- Builder builder = new Builder(this);
- builder.setTitle("Regenerate Key");
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setMessage("Are you sure you want to regenerate your Identity Key? (This will also wipe all established sessions and contact Identity Keys)");
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton("Yes",
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- mAccount.getAxolotlService().regenerateKeys(false);
- }
- });
- builder.create().show();
- }
-
- public void showWipePepDialog() {
- Builder builder = new Builder(this);
- builder.setTitle(getString(R.string.clear_other_devices));
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setMessage(getString(R.string.clear_other_devices_desc));
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.accept),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- mAccount.getAxolotlService().wipeOtherPepDevices();
- }
- });
- builder.create().show();
- }
-
- private void editMamPrefs() {
- this.mFetchingMamPrefsToast = Toast.makeText(this, R.string.fetching_mam_prefs, Toast.LENGTH_LONG);
- this.mFetchingMamPrefsToast.show();
- xmppConnectionService.fetchMamPreferences(mAccount, this);
- }
-
- private void showPassword() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- View view = getLayoutInflater().inflate(R.layout.dialog_show_password, null);
- TextView password = (TextView) view.findViewById(R.id.password);
- password.setText(mAccount.getPassword());
- builder.setTitle(R.string.password);
- builder.setView(view);
- builder.setPositiveButton(R.string.cancel, null);
- builder.create().show();
- }
-
- @Override
- public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
- refreshUi();
- }
-
- @Override
- public void onCaptchaRequested(final Account account, final String id, final Data data, final Bitmap captcha) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
- mCaptchaDialog.dismiss();
- }
- final AlertDialog.Builder builder = new AlertDialog.Builder(EditAccountActivity.this);
- final View view = getLayoutInflater().inflate(R.layout.captcha, null);
- final ImageView imageView = (ImageView) view.findViewById(R.id.captcha);
- final EditText input = (EditText) view.findViewById(R.id.input);
- imageView.setImageBitmap(captcha);
-
- builder.setTitle(getString(R.string.captcha_required));
- builder.setView(view);
-
- builder.setPositiveButton(getString(R.string.ok),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String rc = input.getText().toString();
- data.put("username", account.getUsername());
- data.put("password", account.getPassword());
- data.put("ocr", rc);
- data.submit();
-
- if (xmppConnectionServiceBound) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(
- account, id, data);
- }
- }
- });
- builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (xmppConnectionService != null) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
- }
- }
- });
-
- builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- if (xmppConnectionService != null) {
- xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
- }
- }
- });
- mCaptchaDialog = builder.create();
- mCaptchaDialog.show();
- }
- });
- }
-
- public void onShowErrorToast(final int resId) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show();
- }
- });
- }
-
- @Override
- public void onPreferencesFetched(final Element prefs) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (mFetchingMamPrefsToast != null) {
- mFetchingMamPrefsToast.cancel();
- }
- AlertDialog.Builder builder = new Builder(EditAccountActivity.this);
- builder.setTitle(R.string.server_side_mam_prefs);
- String defaultAttr = prefs.getAttribute("default");
- final List<String> defaults = Arrays.asList("never", "roster", "always");
- final AtomicInteger choice = new AtomicInteger(Math.max(0,defaults.indexOf(defaultAttr)));
- builder.setSingleChoiceItems(R.array.mam_prefs, choice.get(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- choice.set(which);
- }
- });
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- prefs.setAttribute("default",defaults.get(choice.get()));
- xmppConnectionService.pushMamPreferences(mAccount, prefs);
- }
- });
- builder.create().show();
- }
- });
- }
-
- @Override
- public void onPreferencesFetchFailed() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (mFetchingMamPrefsToast != null) {
- mFetchingMamPrefsToast.cancel();
- }
- Toast.makeText(EditAccountActivity.this,R.string.unable_to_fetch_mam_prefs,Toast.LENGTH_LONG).show();
- }
- });
- }
+ OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched {
+
+ private static final int REQUEST_DATA_SAVER = 0x37af244;
+ private AutoCompleteTextView mAccountJid;
+ private EditText mPassword;
+ private EditText mPasswordConfirm;
+ private CheckBox mRegisterNew;
+ private Button mCancelButton;
+ private Button mSaveButton;
+ private Button mDisableOsOptimizationsButton;
+ private TextView mDisableOsOptimizationsHeadline;
+ private TextView getmDisableOsOptimizationsBody;
+ private TableLayout mMoreTable;
+
+ private LinearLayout mStats;
+ private RelativeLayout mOsOptimizations;
+ private TextView mServerInfoSm;
+ private TextView mServerInfoRosterVersion;
+ private TextView mServerInfoCarbons;
+ private TextView mServerInfoMam;
+ private TextView mServerInfoCSI;
+ private TextView mServerInfoBlocking;
+ private TextView mServerInfoPep;
+ private TextView mServerInfoHttpUpload;
+ private TextView mServerInfoPush;
+ private TextView mSessionEst;
+ private TextView mOtrFingerprint;
+ private TextView mAxolotlFingerprint;
+ private TextView mOwnFingerprintDesc;
+ private TextView mAccountJidLabel;
+ private ImageView mAvatar;
+ private RelativeLayout mOtrFingerprintBox;
+ private RelativeLayout mAxolotlFingerprintBox;
+ private ImageButton mOtrFingerprintToClipboardButton;
+ private ImageButton mAxolotlFingerprintToClipboardButton;
+ private ImageButton mRegenerateAxolotlKeyButton;
+ private LinearLayout keys;
+ private LinearLayout keysCard;
+ private LinearLayout mNamePort;
+ private EditText mHostname;
+ private EditText mPort;
+ private AlertDialog mCaptchaDialog = null;
+
+ private Jid jidToEdit;
+ private boolean mInitMode = false;
+ private boolean mUsernameMode = Config.DOMAIN_LOCK != null;
+ private boolean mShowOptions = false;
+ private Account mAccount;
+ private String messageFingerprint;
+
+ private boolean mFetchingAvatar = false;
+
+ private final OnClickListener mSaveButtonClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(final View v) {
+ final String password = mPassword.getText().toString();
+ final String passwordConfirm = mPasswordConfirm.getText().toString();
+
+ if (!mInitMode && passwordChangedInMagicCreateMode()) {
+ gotoChangePassword(password);
+ return;
+ }
+ if (mInitMode && mAccount != null) {
+ mAccount.setOption(Account.OPTION_DISABLED, false);
+ }
+ if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) {
+ mAccount.setOption(Account.OPTION_DISABLED, false);
+ if (!xmppConnectionService.updateAccount(mAccount)) {
+ Toast.makeText(EditAccountActivity.this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show();
+ }
+ return;
+ }
+ final boolean registerNewAccount = mRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
+ if (mUsernameMode && mAccountJid.getText().toString().contains("@")) {
+ mAccountJid.setError(getString(R.string.invalid_username));
+ mAccountJid.requestFocus();
+ return;
+ }
+ final Jid jid;
+ try {
+ if (mUsernameMode) {
+ jid = Jid.fromParts(mAccountJid.getText().toString(), getUserModeDomain(), null);
+ } else {
+ jid = Jid.fromString(mAccountJid.getText().toString());
+ }
+ } catch (final InvalidJidException e) {
+ if (mUsernameMode) {
+ mAccountJid.setError(getString(R.string.invalid_username));
+ } else {
+ mAccountJid.setError(getString(R.string.invalid_jid));
+ }
+ mAccountJid.requestFocus();
+ return;
+ }
+ String hostname = null;
+ int numericPort = 5222;
+ if (mShowOptions) {
+ hostname = mHostname.getText().toString().replaceAll("\\s", "");
+ final String port = mPort.getText().toString().replaceAll("\\s", "");
+ if (hostname.contains(" ")) {
+ mHostname.setError(getString(R.string.not_valid_hostname));
+ mHostname.requestFocus();
+ return;
+ }
+ try {
+ numericPort = Integer.parseInt(port);
+ if (numericPort < 0 || numericPort > 65535) {
+ mPort.setError(getString(R.string.not_a_valid_port));
+ mPort.requestFocus();
+ return;
+ }
+
+ } catch (NumberFormatException e) {
+ mPort.setError(getString(R.string.not_a_valid_port));
+ mPort.requestFocus();
+ return;
+ }
+ }
+
+ if (jid.isDomainJid()) {
+ if (mUsernameMode) {
+ mAccountJid.setError(getString(R.string.invalid_username));
+ } else {
+ mAccountJid.setError(getString(R.string.invalid_jid));
+ }
+ mAccountJid.requestFocus();
+ return;
+ }
+ if (registerNewAccount) {
+ if (XmppConnection.errorMessage != null) {
+ Toast.makeText(EditAccountActivity.this, XmppConnection.errorMessage, Toast.LENGTH_LONG).show();
+ }
+ if (!password.equals(passwordConfirm)) {
+ mPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
+ mPasswordConfirm.requestFocus();
+ return;
+ }
+ }
+ if (mAccount != null) {
+ if (mInitMode && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
+ mAccount.setOption(Account.OPTION_MAGIC_CREATE, mAccount.getPassword().contains(password));
+ }
+ mAccount.setJid(jid);
+ mAccount.setPort(numericPort);
+ mAccount.setHostname(hostname);
+ if (XmppConnection.errorMessage != null) {
+ mAccountJid.setError(XmppConnection.errorMessage);
+ } else {
+ mAccountJid.setError(null);
+ }
+ mPasswordConfirm.setError(null);
+ mAccount.setPassword(password);
+ mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
+ if (!xmppConnectionService.updateAccount(mAccount)) {
+ Toast.makeText(EditAccountActivity.this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ } else {
+ if (xmppConnectionService.findAccountByJid(jid) != null) {
+ mAccountJid.setError(getString(R.string.account_already_exists));
+ mAccountJid.requestFocus();
+ return;
+ }
+ mAccount = new Account(jid.toBareJid(), password);
+ mAccount.setPort(numericPort);
+ mAccount.setHostname(hostname);
+ mAccount.setOption(Account.OPTION_USETLS, true);
+ mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
+ mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
+ xmppConnectionService.createAccount(mAccount);
+ }
+ mHostname.setError(null);
+ mPort.setError(null);
+ if (!mAccount.isOptionSet(Account.OPTION_DISABLED)
+ && !registerNewAccount
+ && !mInitMode) {
+ finish();
+ } else {
+ updateSaveButton();
+ updateAccountInformation(true);
+ }
+
+ }
+ };
+ private final OnClickListener mCancelButtonClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(final View v) {
+ deleteMagicCreatedAccountAndReturnIfNecessary();
+ finish();
+ }
+ };
+ private Toast mFetchingMamPrefsToast;
+ private TableRow mPushRow;
+ private String mSavedInstanceAccount;
+ private boolean mSavedInstanceInit = false;
+
+ public void refreshUiReal() {
+ invalidateOptionsMenu();
+ if (mAccount != null
+ && mAccount.getStatus() != Account.State.ONLINE
+ && mFetchingAvatar) {
+ startActivity(new Intent(getApplicationContext(),
+ ManageAccountActivity.class));
+ finish();
+ } else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
+ if (!mFetchingAvatar) {
+ mFetchingAvatar = true;
+ xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback);
+ }
+ }
+ if (mAccount != null) {
+ updateAccountInformation(false);
+ }
+ updateSaveButton();
+ }
+
+ @Override
+ public boolean onNavigateUp() {
+ deleteMagicCreatedAccountAndReturnIfNecessary();
+ return super.onNavigateUp();
+ }
+
+ @Override
+ public void onBackPressed() {
+ deleteMagicCreatedAccountAndReturnIfNecessary();
+ super.onBackPressed();
+ }
+
+ private void deleteMagicCreatedAccountAndReturnIfNecessary() {
+ if (Config.MAGIC_CREATE_DOMAIN != null
+ && mAccount != null
+ && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
+ && mAccount.isOptionSet(Account.OPTION_REGISTER)
+ && xmppConnectionService.getAccounts().size() == 1) {
+ xmppConnectionService.deleteAccount(mAccount);
+ startActivity(new Intent(EditAccountActivity.this, WelcomeActivity.class));
+ }
+ }
+
+ @Override
+ public void onAccountUpdate() {
+ refreshUi();
+ }
+
+ private final UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
+
+ @Override
+ public void userInputRequried(final PendingIntent pi, final Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+
+ @Override
+ public void success(final Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+
+ @Override
+ public void error(final int errorCode, final Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+ };
+ private final TextWatcher mTextWatcher = new TextWatcher() {
+
+ @Override
+ public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
+ updateSaveButton();
+ }
+
+ @Override
+ public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
+ }
+
+ @Override
+ public void afterTextChanged(final Editable s) {
+
+ }
+ };
+
+ private final OnClickListener mAvatarClickListener = new OnClickListener() {
+ @Override
+ public void onClick(final View view) {
+ if (mAccount != null) {
+ final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
+ intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+ }
+ };
+
+ protected void finishInitialSetup(final Avatar avatar) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ final Intent intent;
+ final XmppConnection connection = mAccount.getXmppConnection();
+ final boolean wasFirstAccount = xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1;
+ if (avatar != null || (connection != null && !connection.getFeatures().pep())) {
+ intent = new Intent(getApplicationContext(), StartConversationActivity.class);
+ if (wasFirstAccount) {
+ intent.putExtra("init", true);
+ }
+ } else {
+ intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
+ intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
+ intent.putExtra("setup", true);
+ }
+ if (wasFirstAccount) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+ startActivity(intent);
+ finish();
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_BATTERY_OP || requestCode == REQUEST_DATA_SAVER) {
+ updateAccountInformation(mAccount == null);
+ }
+ }
+
+ protected void updateSaveButton() {
+ boolean accountInfoEdited = accountInfoEdited();
+
+ if (!mInitMode && passwordChangedInMagicCreateMode()) {
+ this.mSaveButton.setText(R.string.change_password);
+ this.mSaveButton.setEnabled(true);
+ this.mSaveButton.setTextColor(getPrimaryTextColor());
+ } else if (accountInfoEdited && !mInitMode) {
+ this.mSaveButton.setText(R.string.save);
+ this.mSaveButton.setEnabled(true);
+ this.mSaveButton.setTextColor(getPrimaryTextColor());
+ } else if (mAccount != null
+ && (mAccount.getStatus() == Account.State.CONNECTING || mAccount.getStatus() == Account.State.REGISTRATION_SUCCESSFUL || mFetchingAvatar)) {
+ this.mSaveButton.setEnabled(false);
+ this.mSaveButton.setTextColor(getSecondaryTextColor());
+ this.mSaveButton.setText(R.string.account_status_connecting);
+ } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) {
+ this.mSaveButton.setEnabled(true);
+ this.mSaveButton.setTextColor(getPrimaryTextColor());
+ this.mSaveButton.setText(R.string.enable);
+ } else {
+ this.mSaveButton.setEnabled(true);
+ this.mSaveButton.setTextColor(getPrimaryTextColor());
+ if (!mInitMode) {
+ if (mAccount != null && mAccount.isOnlineAndConnected()) {
+ this.mSaveButton.setText(R.string.save);
+ if (!accountInfoEdited) {
+ this.mSaveButton.setEnabled(false);
+ this.mSaveButton.setTextColor(getSecondaryTextColor());
+ }
+ } else {
+ this.mSaveButton.setText(R.string.connect);
+ }
+ } else {
+ this.mSaveButton.setText(R.string.next);
+ }
+ }
+ }
+
+ protected boolean accountInfoEdited() {
+ if (this.mAccount == null) {
+ return false;
+ }
+ return jidEdited() ||
+ !this.mAccount.getPassword().equals(this.mPassword.getText().toString()) ||
+ !this.mAccount.getHostname().equals(this.mHostname.getText().toString()) ||
+ !String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString());
+ }
+
+ protected boolean jidEdited() {
+ final String unmodified;
+ if (mUsernameMode) {
+ unmodified = this.mAccount.getJid().getLocalpart();
+ } else {
+ unmodified = this.mAccount.getJid().toBareJid().toString();
+ }
+ return !unmodified.equals(this.mAccountJid.getText().toString());
+ }
+
+ protected boolean passwordChangedInMagicCreateMode() {
+ return mAccount != null
+ && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
+ && !this.mAccount.getPassword().equals(this.mPassword.getText().toString())
+ && !this.jidEdited()
+ && mAccount.isOnlineAndConnected();
+ }
+
+ @Override
+ protected String getShareableUri() {
+ if (mAccount != null) {
+ return mAccount.getShareableUri();
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ this.mSavedInstanceAccount = savedInstanceState.getString("account");
+ this.mSavedInstanceInit = savedInstanceState.getBoolean("initMode", false);
+ }
+ setContentView(R.layout.activity_edit_account);
+ this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
+ this.mAccountJid.addTextChangedListener(this.mTextWatcher);
+ this.mAccountJidLabel = (TextView) findViewById(R.id.account_jid_label);
+ this.mPassword = (EditText) findViewById(R.id.account_password);
+ this.mPassword.addTextChangedListener(this.mTextWatcher);
+ this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
+ this.mAvatar = (ImageView) findViewById(R.id.avater);
+ this.mAvatar.setOnClickListener(this.mAvatarClickListener);
+ this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
+ this.mStats = (LinearLayout) findViewById(R.id.stats);
+ this.mOsOptimizations = (RelativeLayout) findViewById(R.id.os_optimization);
+ this.mDisableOsOptimizationsButton = (Button) findViewById(R.id.os_optimization_disable);
+ this.mDisableOsOptimizationsHeadline = (TextView) findViewById(R.id.os_optimization_headline);
+ this.getmDisableOsOptimizationsBody = (TextView) findViewById(R.id.os_optimization_body);
+ this.mSessionEst = (TextView) findViewById(R.id.session_est);
+ this.mServerInfoRosterVersion = (TextView) findViewById(R.id.server_info_roster_version);
+ this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
+ this.mServerInfoMam = (TextView) findViewById(R.id.server_info_mam);
+ this.mServerInfoCSI = (TextView) findViewById(R.id.server_info_csi);
+ this.mServerInfoBlocking = (TextView) findViewById(R.id.server_info_blocking);
+ this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
+ this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
+ this.mServerInfoHttpUpload = (TextView) findViewById(R.id.server_info_http_upload);
+ this.mPushRow = (TableRow) findViewById(R.id.push_row);
+ this.mServerInfoPush = (TextView) findViewById(R.id.server_info_push);
+ this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
+ this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
+ this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
+ this.mAxolotlFingerprint = (TextView) findViewById(R.id.axolotl_fingerprint);
+ this.mAxolotlFingerprintBox = (RelativeLayout) findViewById(R.id.axolotl_fingerprint_box);
+ this.mAxolotlFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_axolotl_to_clipboard);
+ this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_omemo_key);
+ this.mOwnFingerprintDesc = (TextView) findViewById(R.id.own_fingerprint_desc);
+ this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card);
+ this.keys = (LinearLayout) findViewById(R.id.other_device_keys);
+ this.mNamePort = (LinearLayout) findViewById(R.id.name_port);
+ this.mHostname = (EditText) findViewById(R.id.hostname);
+ this.mHostname.addTextChangedListener(mTextWatcher);
+ this.mPort = (EditText) findViewById(R.id.port);
+ this.mPort.setText("5222");
+ this.mPort.addTextChangedListener(mTextWatcher);
+ this.mSaveButton = (Button) findViewById(R.id.save_button);
+ this.mCancelButton = (Button) findViewById(R.id.cancel_button);
+ this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
+ this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
+ this.mMoreTable = (TableLayout) findViewById(R.id.server_info_more);
+ if (savedInstanceState != null && savedInstanceState.getBoolean("showMoreTable")) {
+ changeMoreTableVisibility(true);
+ }
+ final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(final CompoundButton buttonView,
+ final boolean isChecked) {
+ if (isChecked) {
+ mPasswordConfirm.setVisibility(View.VISIBLE);
+ } else {
+ mPasswordConfirm.setVisibility(View.GONE);
+ }
+ updateSaveButton();
+ }
+ };
+ this.mRegisterNew.setOnCheckedChangeListener(OnCheckedShowConfirmPassword);
+ if (Config.DISALLOW_REGISTRATION_IN_UI) {
+ this.mRegisterNew.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.editaccount, menu);
+ final MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code);
+ final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
+ final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
+ final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
+ final MenuItem showPassword = menu.findItem(R.id.action_show_password);
+ final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
+ final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
+ final MenuItem mamPrefs = menu.findItem(R.id.action_mam_prefs);
+ final MenuItem changePresence = menu.findItem(R.id.action_change_presence);
+ renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
+
+ if (mAccount != null && mAccount.isOnlineAndConnected()) {
+ if (!mAccount.getXmppConnection().getFeatures().blocking()) {
+ showBlocklist.setVisible(false);
+ }
+ if (!mAccount.getXmppConnection().getFeatures().register()) {
+ changePassword.setVisible(false);
+ }
+ mamPrefs.setVisible(mAccount.getXmppConnection().getFeatures().mam());
+ Set<Integer> otherDevices = mAccount.getAxolotlService().getOwnDeviceIds();
+ if (otherDevices == null || otherDevices.isEmpty() || !Config.supportOmemo()) {
+ clearDevices.setVisible(false);
+ }
+ changePresence.setVisible(manuallyChangePresence());
+ } else {
+ showQrCode.setVisible(false);
+ showBlocklist.setVisible(false);
+ showMoreInfo.setVisible(false);
+ changePassword.setVisible(false);
+ clearDevices.setVisible(false);
+ mamPrefs.setVisible(false);
+ changePresence.setVisible(false);
+ }
+
+ if (mAccount != null) {
+ showPassword.setVisible(mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
+ && !mAccount.isOptionSet(Account.OPTION_REGISTER));
+ } else {
+ showPassword.setVisible(false);
+ }
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
+ if (showMoreInfo.isVisible()) {
+ showMoreInfo.setChecked(mMoreTable.getVisibility() == View.VISIBLE);
+ }
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ final int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ } else if (getIntent() != null) {
+ try {
+ this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid"));
+ } catch (final InvalidJidException | NullPointerException ignored) {
+ this.jidToEdit = null;
+ }
+ boolean init = getIntent().getBooleanExtra("init", false);
+ this.mInitMode = init || this.jidToEdit == null;
+ this.messageFingerprint = getIntent().getStringExtra("fingerprint");
+ if (!mInitMode) {
+ this.mRegisterNew.setVisibility(View.GONE);
+ if (getActionBar() != null) {
+ getActionBar().setTitle(getString(R.string.account_details));
+ }
+ } else {
+ this.mAvatar.setVisibility(View.GONE);
+ ActionBar ab = getActionBar();
+ if (ab != null) {
+ if (init && Config.MAGIC_CREATE_DOMAIN == null) {
+ ab.setDisplayShowHomeEnabled(false);
+ ab.setDisplayHomeAsUpEnabled(false);
+ }
+ ab.setTitle(R.string.action_add_account);
+ }
+ }
+ }
+ SharedPreferences preferences = getPreferences();
+ boolean useTor = Config.FORCE_ORBOT || preferences.getBoolean("use_tor", false);
+ this.mShowOptions = useTor || preferences.getBoolean("show_connection_options", false);
+ mHostname.setHint(useTor ? R.string.hostname_or_onion : R.string.hostname_example);
+ this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onSaveInstanceState(final Bundle savedInstanceState) {
+ if (mAccount != null) {
+ savedInstanceState.putString("account", mAccount.getJid().toBareJid().toString());
+ savedInstanceState.putBoolean("initMode", mInitMode);
+ savedInstanceState.putBoolean("showMoreTable", mMoreTable.getVisibility() == View.VISIBLE);
+ }
+ super.onSaveInstanceState(savedInstanceState);
+ }
+
+ protected void onBackendConnected() {
+ boolean init = true;
+ if (mSavedInstanceAccount != null) {
+ try {
+ this.mAccount = xmppConnectionService.findAccountByJid(Jid.fromString(mSavedInstanceAccount));
+ this.mInitMode = mSavedInstanceInit;
+ init = false;
+ } catch (InvalidJidException e) {
+ this.mAccount = null;
+ }
+
+ } else if (this.jidToEdit != null) {
+ this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
+ }
+
+ if (mAccount != null) {
+ this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
+ this.mUsernameMode |= mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && mAccount.isOptionSet(Account.OPTION_REGISTER);
+ if (this.mAccount.getPrivateKeyAlias() != null) {
+ this.mPassword.setHint(R.string.authenticate_with_certificate);
+ if (this.mInitMode) {
+ this.mPassword.requestFocus();
+ }
+ }
+ updateAccountInformation(init);
+ }
+
+
+ if (Config.MAGIC_CREATE_DOMAIN == null && this.xmppConnectionService.getAccounts().size() == 0) {
+ this.mCancelButton.setEnabled(false);
+ this.mCancelButton.setTextColor(getSecondaryTextColor());
+ }
+ if (mUsernameMode) {
+ this.mAccountJidLabel.setText(R.string.username);
+ this.mAccountJid.setHint(R.string.username_hint);
+ } else {
+ final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
+ R.layout.simple_list_item,
+ xmppConnectionService.getKnownHosts());
+ this.mAccountJid.setAdapter(mKnownHostsAdapter);
+ }
+ updateSaveButton();
+ invalidateOptionsMenu();
+ }
+
+ private String getUserModeDomain() {
+ if (mAccount != null) {
+ return mAccount.getJid().getDomainpart();
+ } else {
+ return Config.DOMAIN_LOCK;
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.mgmt_account_reconnect:
+ if (xmppConnectionServiceBound) {
+ unbindService(mConnection);
+ xmppConnectionServiceBound = false;
+ }
+ stopService(new Intent(EditAccountActivity.this,
+ XmppConnectionService.class));
+ finish();
+ break;
+ case R.id.action_show_block_list:
+ final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class);
+ showBlocklistIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
+ startActivity(showBlocklistIntent);
+ break;
+ case R.id.action_server_info_show_more:
+ changeMoreTableVisibility(!item.isChecked());
+ break;
+ case R.id.action_change_password_on_server:
+ gotoChangePassword(null);
+ break;
+ case R.id.action_mam_prefs:
+ editMamPrefs();
+ break;
+ case R.id.action_clear_devices:
+ showWipePepDialog();
+ break;
+ case R.id.action_renew_certificate:
+ renewCertificate();
+ break;
+ case R.id.action_change_presence:
+ changePresence();
+ break;
+ case R.id.action_show_password:
+ showPassword();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void changeMoreTableVisibility(boolean visible) {
+ mMoreTable.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
+ private void gotoChangePassword(String newPassword) {
+ final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
+ changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
+ if (newPassword != null) {
+ changePasswordIntent.putExtra("password", newPassword);
+ }
+ startActivity(changePasswordIntent);
+ }
+
+ private void renewCertificate() {
+ KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
+ }
+
+ private void changePresence() {
+ Intent intent = new Intent(this, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+
+ @Override
+ public void alias(String alias) {
+ if (alias != null) {
+ xmppConnectionService.updateKeyInAccount(mAccount, alias);
+ }
+ }
+
+ private void updateAccountInformation(boolean init) {
+ if (init) {
+ this.mAccountJid.getEditableText().clear();
+ if (mUsernameMode) {
+ this.mAccountJid.getEditableText().append(this.mAccount.getJid().getLocalpart());
+ } else {
+ this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString());
+ }
+ this.mPassword.setText(this.mAccount.getPassword());
+ this.mHostname.setText("");
+ this.mHostname.getEditableText().append(this.mAccount.getHostname());
+ this.mPort.setText("");
+ this.mPort.getEditableText().append(String.valueOf(this.mAccount.getPort()));
+ this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
+
+ }
+
+ if (!mInitMode) {
+ this.mAvatar.setVisibility(View.VISIBLE);
+ this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(Config.AVATAR_SIZE)));
+ this.mAccountJid.setEnabled(false);
+ } else {
+ this.mAvatar.setVisibility(View.GONE);
+ }
+ if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
+ this.mRegisterNew.setVisibility(View.VISIBLE);
+ this.mRegisterNew.setChecked(true);
+ this.mPasswordConfirm.setText(this.mAccount.getPassword());
+ } else {
+ this.mRegisterNew.setVisibility(View.GONE);
+ this.mRegisterNew.setChecked(false);
+ }
+ if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
+ Features features = this.mAccount.getXmppConnection().getFeatures();
+ this.mStats.setVisibility(View.VISIBLE);
+ boolean showBatteryWarning = !xmppConnectionService.getPushManagementService().available(mAccount) && isOptimizingBattery();
+ boolean showDataSaverWarning = isAffectedByDataSaver();
+ showOsOptimizationWarning(showBatteryWarning, showDataSaverWarning);
+ this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
+ .getLastSessionEstablished()));
+ if (features.rosterVersioning()) {
+ this.mServerInfoRosterVersion.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoRosterVersion.setText(R.string.server_info_unavailable);
+ }
+ if (features.carbons()) {
+ this.mServerInfoCarbons.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoCarbons
+ .setText(R.string.server_info_unavailable);
+ }
+ if (features.mam()) {
+ this.mServerInfoMam.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoMam.setText(R.string.server_info_unavailable);
+ }
+ if (features.csi()) {
+ this.mServerInfoCSI.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoCSI.setText(R.string.server_info_unavailable);
+ }
+ if (features.blocking()) {
+ this.mServerInfoBlocking.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoBlocking.setText(R.string.server_info_unavailable);
+ }
+ if (features.sm()) {
+ this.mServerInfoSm.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoSm.setText(R.string.server_info_unavailable);
+ }
+ if (features.pep()) {
+ AxolotlService axolotlService = this.mAccount.getAxolotlService();
+ if (axolotlService != null && axolotlService.isPepBroken()) {
+ this.mServerInfoPep.setText(R.string.server_info_broken);
+ } else {
+ this.mServerInfoPep.setText(R.string.server_info_available);
+ }
+ } else {
+ this.mServerInfoPep.setText(R.string.server_info_unavailable);
+ }
+ if (features.httpUpload(0)) {
+ this.mServerInfoHttpUpload.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoHttpUpload.setText(R.string.server_info_unavailable);
+ }
+
+ this.mPushRow.setVisibility(xmppConnectionService.getPushManagementService().isStub() ? View.GONE : View.VISIBLE);
+
+ if (xmppConnectionService.getPushManagementService().available(mAccount)) {
+ this.mServerInfoPush.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoPush.setText(R.string.server_info_unavailable);
+ }
+ final String otrFingerprint = this.mAccount.getOtrFingerprint();
+ if (otrFingerprint != null && Config.supportOtr()) {
+ this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
+ this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
+ this.mOtrFingerprintToClipboardButton
+ .setVisibility(View.VISIBLE);
+ this.mOtrFingerprintToClipboardButton
+ .setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(final View v) {
+
+ if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) {
+ Toast.makeText(
+ EditAccountActivity.this,
+ R.string.toast_message_otr_fingerprint,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ } else {
+ this.mOtrFingerprintBox.setVisibility(View.GONE);
+ }
+ final String ownAxolotlFingerprint = this.mAccount.getAxolotlService().getOwnFingerprint();
+ if (ownAxolotlFingerprint != null && Config.supportOmemo()) {
+ this.mAxolotlFingerprintBox.setVisibility(View.VISIBLE);
+ if (ownAxolotlFingerprint.equals(messageFingerprint)) {
+ this.mOwnFingerprintDesc.setTextColor(getResources().getColor(R.color.accent));
+ } else {
+ this.mOwnFingerprintDesc.setTextColor(getSecondaryTextColor());
+ }
+ this.mAxolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(ownAxolotlFingerprint.substring(2)));
+ this.mAxolotlFingerprintToClipboardButton
+ .setVisibility(View.VISIBLE);
+ this.mAxolotlFingerprintToClipboardButton
+ .setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(final View v) {
+ copyOmemoFingerprint(ownAxolotlFingerprint);
+ }
+ });
+ if (Config.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON) {
+ this.mRegenerateAxolotlKeyButton
+ .setVisibility(View.VISIBLE);
+ this.mRegenerateAxolotlKeyButton
+ .setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(final View v) {
+ showRegenerateAxolotlKeyDialog();
+ }
+ });
+ }
+ } else {
+ this.mAxolotlFingerprintBox.setVisibility(View.GONE);
+ }
+ boolean hasKeys = false;
+ keys.removeAllViews();
+ for (final String fingerprint : mAccount.getAxolotlService().getFingerprintsForOwnSessions()) {
+ if (ownAxolotlFingerprint.equals(fingerprint)) {
+ continue;
+ }
+ boolean highlight = fingerprint.equals(messageFingerprint);
+ hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight);
+ }
+ if (hasKeys && Config.supportOmemo()) {
+ keysCard.setVisibility(View.VISIBLE);
+ } else {
+ keysCard.setVisibility(View.GONE);
+ }
+ } else {
+ if (this.mAccount.errorStatus()) {
+ final EditText errorTextField;
+ if (this.mAccount.getStatus() == Account.State.UNAUTHORIZED) {
+ errorTextField = this.mPassword;
+ } else if (mShowOptions
+ && this.mAccount.getStatus() == Account.State.SERVER_NOT_FOUND
+ && this.mHostname.getText().length() > 0) {
+ errorTextField = this.mHostname;
+ } else {
+ errorTextField = this.mAccountJid;
+ }
+ errorTextField.setError(getString(this.mAccount.getStatus().getReadableId()));
+ if (init || !accountInfoEdited()) {
+ errorTextField.requestFocus();
+ }
+ } else {
+ this.mAccountJid.setError(null);
+ this.mPassword.setError(null);
+ this.mHostname.setError(null);
+ }
+ this.mStats.setVisibility(View.GONE);
+ }
+ }
+
+ private void showOsOptimizationWarning(boolean showBatteryWarning, boolean showDataSaverWarning) {
+ this.mOsOptimizations.setVisibility(showBatteryWarning || showDataSaverWarning ? View.VISIBLE : View.GONE);
+ if (showDataSaverWarning) {
+ this.mDisableOsOptimizationsHeadline.setText(R.string.data_saver_enabled);
+ this.getmDisableOsOptimizationsBody.setText(R.string.data_saver_enabled_explained);
+ this.mDisableOsOptimizationsButton.setText(R.string.allow);
+ this.mDisableOsOptimizationsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS);
+ Uri uri = Uri.parse("package:" + getPackageName());
+ intent.setData(uri);
+ try {
+ startActivityForResult(intent, REQUEST_DATA_SAVER);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_data_saver, Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ } else if (showBatteryWarning) {
+ this.mDisableOsOptimizationsButton.setText(R.string.disable);
+ this.mDisableOsOptimizationsHeadline.setText(R.string.battery_optimizations_enabled);
+ this.getmDisableOsOptimizationsBody.setText(R.string.battery_optimizations_enabled_explained);
+ this.mDisableOsOptimizationsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ Uri uri = Uri.parse("package:" + getPackageName());
+ intent.setData(uri);
+ try {
+ startActivityForResult(intent, REQUEST_BATTERY_OP);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+ }
+
+
+ public void showRegenerateAxolotlKeyDialog() {
+ Builder builder = new Builder(this);
+ builder.setTitle("Regenerate Key");
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage("Are you sure you want to regenerate your Identity Key? (This will also wipe all established sessions and contact Identity Keys)");
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton("Yes",
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mAccount.getAxolotlService().regenerateKeys(false);
+ }
+ });
+ builder.create().show();
+ }
+
+ public void showWipePepDialog() {
+ Builder builder = new Builder(this);
+ builder.setTitle(getString(R.string.clear_other_devices));
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage(getString(R.string.clear_other_devices_desc));
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton(getString(R.string.accept),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mAccount.getAxolotlService().wipeOtherPepDevices();
+ }
+ });
+ builder.create().show();
+ }
+
+ private void editMamPrefs() {
+ this.mFetchingMamPrefsToast = Toast.makeText(this, R.string.fetching_mam_prefs, Toast.LENGTH_LONG);
+ this.mFetchingMamPrefsToast.show();
+ xmppConnectionService.fetchMamPreferences(mAccount, this);
+ }
+
+ private void showPassword() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ View view = getLayoutInflater().inflate(R.layout.dialog_show_password, null);
+ TextView password = (TextView) view.findViewById(R.id.password);
+ password.setText(mAccount.getPassword());
+ builder.setTitle(R.string.password);
+ builder.setView(view);
+ builder.setPositiveButton(R.string.cancel, null);
+ builder.create().show();
+ }
+
+ @Override
+ public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
+ refreshUi();
+ }
+
+ @Override
+ public void onCaptchaRequested(final Account account, final String id, final Data data, final Bitmap captcha) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
+ mCaptchaDialog.dismiss();
+ }
+ final AlertDialog.Builder builder = new AlertDialog.Builder(EditAccountActivity.this);
+ final View view = getLayoutInflater().inflate(R.layout.captcha, null);
+ final ImageView imageView = (ImageView) view.findViewById(R.id.captcha);
+ final EditText input = (EditText) view.findViewById(R.id.input);
+ imageView.setImageBitmap(captcha);
+
+ builder.setTitle(getString(R.string.captcha_required));
+ builder.setView(view);
+
+ builder.setPositiveButton(getString(R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String rc = input.getText().toString();
+ data.put("username", account.getUsername());
+ data.put("password", account.getPassword());
+ data.put("ocr", rc);
+ data.submit();
+
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(
+ account, id, data);
+ }
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (xmppConnectionService != null) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
+ }
+ }
+ });
+
+ builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ if (xmppConnectionService != null) {
+ xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
+ }
+ }
+ });
+ mCaptchaDialog = builder.create();
+ mCaptchaDialog.show();
+ }
+ });
+ }
+
+ public void onShowErrorToast(final int resId) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ @Override
+ public void onPreferencesFetched(final Element prefs) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (mFetchingMamPrefsToast != null) {
+ mFetchingMamPrefsToast.cancel();
+ }
+ AlertDialog.Builder builder = new Builder(EditAccountActivity.this);
+ builder.setTitle(R.string.server_side_mam_prefs);
+ String defaultAttr = prefs.getAttribute("default");
+ final List<String> defaults = Arrays.asList("never", "roster", "always");
+ final AtomicInteger choice = new AtomicInteger(Math.max(0, defaults.indexOf(defaultAttr)));
+ builder.setSingleChoiceItems(R.array.mam_prefs, choice.get(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ choice.set(which);
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ prefs.setAttribute("default", defaults.get(choice.get()));
+ xmppConnectionService.pushMamPreferences(mAccount, prefs);
+ }
+ });
+ builder.create().show();
+ }
+ });
+ }
+
+ @Override
+ public void onPreferencesFetchFailed() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (mFetchingMamPrefsToast != null) {
+ mFetchingMamPrefsToast.cancel();
+ }
+ Toast.makeText(EditAccountActivity.this, R.string.unable_to_fetch_mam_prefs, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/EditMessage.java b/src/main/java/de/pixart/messenger/ui/EditMessage.java
index ea56e4d56..8c0a7f934 100644
--- a/src/main/java/de/pixart/messenger/ui/EditMessage.java
+++ b/src/main/java/de/pixart/messenger/ui/EditMessage.java
@@ -14,84 +14,89 @@ import github.ankushsachdeva.emojicon.EmojiconEditText;
public class EditMessage extends EmojiconEditText {
- public EditMessage(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public EditMessage(Context context) {
- super(context);
- }
-
- protected Handler mTypingHandler = new Handler();
-
- protected Runnable mTypingTimeout = new Runnable() {
- @Override
- public void run() {
- if (isUserTyping && keyboardListener != null) {
- keyboardListener.onTypingStopped();
- isUserTyping = false;
- }
- }
- };
-
- private boolean isUserTyping = false;
-
- private boolean lastInputWasTab = false;
-
- protected KeyboardListener keyboardListener;
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent e) {
- if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
- lastInputWasTab = false;
- if (keyboardListener != null && keyboardListener.onEnterPressed()) {
- return true;
- }
- } else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
- if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
- lastInputWasTab = true;
- return true;
- }
- } else {
- lastInputWasTab = false;
- }
- return super.onKeyDown(keyCode, e);
- }
-
- @Override
- public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
- super.onTextChanged(text,start,lengthBefore,lengthAfter);
- lastInputWasTab = false;
- if (this.mTypingHandler != null && this.keyboardListener != null) {
- this.mTypingHandler.removeCallbacks(mTypingTimeout);
- this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000);
- final int length = text.length();
- if (!isUserTyping && length > 0) {
- this.isUserTyping = true;
- this.keyboardListener.onTypingStarted();
- } else if (length == 0) {
- this.isUserTyping = false;
- this.keyboardListener.onTextDeleted();
- }
- this.keyboardListener.onTextChanged();
- }
- }
-
- public void setKeyboardListener(KeyboardListener listener) {
- this.keyboardListener = listener;
- if (listener != null) {
- this.isUserTyping = false;
- }
- }
-
- public interface KeyboardListener {
- boolean onEnterPressed();
- void onTypingStarted();
- void onTypingStopped();
- void onTextDeleted();
- void onTextChanged();
- boolean onTabPressed(boolean repeated);
- }
+ public EditMessage(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public EditMessage(Context context) {
+ super(context);
+ }
+
+ protected Handler mTypingHandler = new Handler();
+
+ protected Runnable mTypingTimeout = new Runnable() {
+ @Override
+ public void run() {
+ if (isUserTyping && keyboardListener != null) {
+ keyboardListener.onTypingStopped();
+ isUserTyping = false;
+ }
+ }
+ };
+
+ private boolean isUserTyping = false;
+
+ private boolean lastInputWasTab = false;
+
+ protected KeyboardListener keyboardListener;
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent e) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
+ lastInputWasTab = false;
+ if (keyboardListener != null && keyboardListener.onEnterPressed()) {
+ return true;
+ }
+ } else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
+ if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
+ lastInputWasTab = true;
+ return true;
+ }
+ } else {
+ lastInputWasTab = false;
+ }
+ return super.onKeyDown(keyCode, e);
+ }
+
+ @Override
+ public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter);
+ lastInputWasTab = false;
+ if (this.mTypingHandler != null && this.keyboardListener != null) {
+ this.mTypingHandler.removeCallbacks(mTypingTimeout);
+ this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000);
+ final int length = text.length();
+ if (!isUserTyping && length > 0) {
+ this.isUserTyping = true;
+ this.keyboardListener.onTypingStarted();
+ } else if (length == 0) {
+ this.isUserTyping = false;
+ this.keyboardListener.onTextDeleted();
+ }
+ this.keyboardListener.onTextChanged();
+ }
+ }
+
+ public void setKeyboardListener(KeyboardListener listener) {
+ this.keyboardListener = listener;
+ if (listener != null) {
+ this.isUserTyping = false;
+ }
+ }
+
+ public interface KeyboardListener {
+ boolean onEnterPressed();
+
+ void onTypingStarted();
+
+ void onTypingStopped();
+
+ void onTextDeleted();
+
+ void onTextChanged();
+
+ boolean onTabPressed(boolean repeated);
+ }
private static final InputFilter SPAN_FILTER = new InputFilter() {
diff --git a/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java b/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java
index 8f355e348..b26019fac 100644
--- a/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java
+++ b/src/main/java/de/pixart/messenger/ui/EnterJidDialog.java
@@ -19,111 +19,111 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class EnterJidDialog {
- public interface OnEnterJidDialogPositiveListener {
- boolean onEnterJidDialogPositive(Jid account, Jid contact) throws EnterJidDialog.JidError;
- }
-
- public static class JidError extends Exception {
- final String msg;
-
- public JidError(final String msg) {
- this.msg = msg;
- }
-
- public String toString() {
- return msg;
- }
- }
-
- protected final AlertDialog dialog;
- protected View.OnClickListener dialogOnClick;
- protected OnEnterJidDialogPositiveListener listener = null;
-
- public EnterJidDialog(
- final Context context, List<String> knownHosts, final List<String> activatedAccounts,
- final String title, final String positiveButton,
- final String prefilledJid, final String account, boolean allowEditJid
- ) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(title);
- View dialogView = LayoutInflater.from(context).inflate(R.layout.enter_jid_dialog, null);
- final TextView jabberIdDesc = (TextView) dialogView.findViewById(R.id.jabber_id);
- jabberIdDesc.setText(R.string.account_settings_jabber_id);
- final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
- final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView.findViewById(R.id.jid);
- jid.setAdapter(new KnownHostsAdapter(context, R.layout.simple_list_item, knownHosts));
- if (prefilledJid != null) {
- jid.append(prefilledJid);
- if (!allowEditJid) {
- jid.setFocusable(false);
- jid.setFocusableInTouchMode(false);
- jid.setClickable(false);
- jid.setCursorVisible(false);
- }
- }
-
- jid.setHint(R.string.account_settings_example_jabber_id);
-
- if (account == null) {
- StartConversationActivity.populateAccountSpinner(context, activatedAccounts, spinner);
- } else {
- ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
- R.layout.simple_list_item,
- new String[] { account });
- spinner.setEnabled(false);
- adapter.setDropDownViewResource(R.layout.simple_list_item);
- spinner.setAdapter(adapter);
- }
-
- builder.setView(dialogView);
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(positiveButton, null);
- this.dialog = builder.create();
-
- this.dialogOnClick = new View.OnClickListener() {
- @Override
- public void onClick(final View v) {
- final Jid accountJid;
- if (!spinner.isEnabled() && account == null) {
- return;
- }
- try {
- if (Config.DOMAIN_LOCK != null) {
- accountJid = Jid.fromParts((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null);
- } else {
- accountJid = Jid.fromString((String) spinner.getSelectedItem());
- }
- } catch (final InvalidJidException e) {
- return;
- }
- final Jid contactJid;
- try {
- contactJid = Jid.fromString(jid.getText().toString());
- } catch (final InvalidJidException e) {
- jid.setError(context.getString(R.string.invalid_jid));
- return;
- }
-
- if(listener != null) {
- try {
- if(listener.onEnterJidDialogPositive(accountJid, contactJid)) {
- dialog.dismiss();
- }
- } catch(JidError error) {
- jid.setError(error.toString());
- }
- }
- }
- };
- }
-
- public void setOnEnterJidDialogPositiveListener(OnEnterJidDialogPositiveListener listener) {
- this.listener = listener;
- }
-
- public Dialog show() {
- this.dialog.show();
- this.dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(this.dialogOnClick);
- return this.dialog;
- }
+ public interface OnEnterJidDialogPositiveListener {
+ boolean onEnterJidDialogPositive(Jid account, Jid contact) throws EnterJidDialog.JidError;
+ }
+
+ public static class JidError extends Exception {
+ final String msg;
+
+ public JidError(final String msg) {
+ this.msg = msg;
+ }
+
+ public String toString() {
+ return msg;
+ }
+ }
+
+ protected final AlertDialog dialog;
+ protected View.OnClickListener dialogOnClick;
+ protected OnEnterJidDialogPositiveListener listener = null;
+
+ public EnterJidDialog(
+ final Context context, List<String> knownHosts, final List<String> activatedAccounts,
+ final String title, final String positiveButton,
+ final String prefilledJid, final String account, boolean allowEditJid
+ ) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(title);
+ View dialogView = LayoutInflater.from(context).inflate(R.layout.enter_jid_dialog, null);
+ final TextView jabberIdDesc = (TextView) dialogView.findViewById(R.id.jabber_id);
+ jabberIdDesc.setText(R.string.account_settings_jabber_id);
+ final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
+ final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView.findViewById(R.id.jid);
+ jid.setAdapter(new KnownHostsAdapter(context, R.layout.simple_list_item, knownHosts));
+ if (prefilledJid != null) {
+ jid.append(prefilledJid);
+ if (!allowEditJid) {
+ jid.setFocusable(false);
+ jid.setFocusableInTouchMode(false);
+ jid.setClickable(false);
+ jid.setCursorVisible(false);
+ }
+ }
+
+ jid.setHint(R.string.account_settings_example_jabber_id);
+
+ if (account == null) {
+ StartConversationActivity.populateAccountSpinner(context, activatedAccounts, spinner);
+ } else {
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
+ R.layout.simple_list_item,
+ new String[]{account});
+ spinner.setEnabled(false);
+ adapter.setDropDownViewResource(R.layout.simple_list_item);
+ spinner.setAdapter(adapter);
+ }
+
+ builder.setView(dialogView);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(positiveButton, null);
+ this.dialog = builder.create();
+
+ this.dialogOnClick = new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ final Jid accountJid;
+ if (!spinner.isEnabled() && account == null) {
+ return;
+ }
+ try {
+ if (Config.DOMAIN_LOCK != null) {
+ accountJid = Jid.fromParts((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null);
+ } else {
+ accountJid = Jid.fromString((String) spinner.getSelectedItem());
+ }
+ } catch (final InvalidJidException e) {
+ return;
+ }
+ final Jid contactJid;
+ try {
+ contactJid = Jid.fromString(jid.getText().toString());
+ } catch (final InvalidJidException e) {
+ jid.setError(context.getString(R.string.invalid_jid));
+ return;
+ }
+
+ if (listener != null) {
+ try {
+ if (listener.onEnterJidDialogPositive(accountJid, contactJid)) {
+ dialog.dismiss();
+ }
+ } catch (JidError error) {
+ jid.setError(error.toString());
+ }
+ }
+ }
+ };
+ }
+
+ public void setOnEnterJidDialogPositiveListener(OnEnterJidDialogPositiveListener listener) {
+ this.listener = listener;
+ }
+
+ public Dialog show() {
+ this.dialog.show();
+ this.dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(this.dialogOnClick);
+ return this.dialog;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java b/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java
index c83318160..5e41ad326 100644
--- a/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/MagicCreateActivity.java
@@ -21,100 +21,100 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class MagicCreateActivity extends XmppActivity implements TextWatcher {
- private TextView mFullJidDisplay;
- private EditText mUsername;
- private SecureRandom mRandom;
-
- private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456780+-/#$!?";
- private static final int PW_LENGTH = 10;
-
- @Override
- protected void refreshUiReal() {
-
- }
-
- @Override
- void onBackendConnected() {
-
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- if (getResources().getBoolean(R.bool.portrait_only)) {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- }
- super.onCreate(savedInstanceState);
- setContentView(R.layout.magic_create);
- mFullJidDisplay = (TextView) findViewById(R.id.full_jid);
- mUsername = (EditText) findViewById(R.id.username);
- mRandom = new SecureRandom();
- Button next = (Button) findViewById(R.id.create_account);
- next.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- String username = mUsername.getText().toString();
- if (username.contains("@") || username.length() < 3) {
- mUsername.setError(getString(R.string.invalid_username));
- mUsername.requestFocus();
- } else {
- mUsername.setError(null);
- try {
- Jid jid = Jid.fromParts(username.toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
- Account account = xmppConnectionService.findAccountByJid(jid);
- if (account == null) {
- account = new Account(jid, createPassword());
- account.setOption(Account.OPTION_REGISTER, true);
- account.setOption(Account.OPTION_DISABLED, true);
- account.setOption(Account.OPTION_MAGIC_CREATE, true);
- xmppConnectionService.createAccount(account);
- }
- Intent intent = new Intent(MagicCreateActivity.this, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().toBareJid().toString());
- intent.putExtra("init", true);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- Toast.makeText(MagicCreateActivity.this, R.string.secure_password_generated, Toast.LENGTH_SHORT).show();
- startActivity(intent);
- } catch (InvalidJidException e) {
- mUsername.setError(getString(R.string.invalid_username));
- mUsername.requestFocus();
- }
- }
- }
- });
- mUsername.addTextChangedListener(this);
- }
-
- private String createPassword() {
- StringBuilder builder = new StringBuilder(PW_LENGTH);
- for(int i = 0; i < PW_LENGTH; ++i) {
- builder.append(CHARS.charAt(mRandom.nextInt(CHARS.length() - 1)));
- }
- return builder.toString();
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
-
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- if (s.toString().trim().length() > 0) {
- try {
- mFullJidDisplay.setVisibility(View.VISIBLE);
- Jid jid = Jid.fromParts(s.toString().toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
- mFullJidDisplay.setText(getString(R.string.your_full_jid_will_be, jid.toString()));
- } catch (InvalidJidException e) {
- mFullJidDisplay.setVisibility(View.INVISIBLE);
- }
-
- } else {
- mFullJidDisplay.setVisibility(View.INVISIBLE);
- }
- }
+ private TextView mFullJidDisplay;
+ private EditText mUsername;
+ private SecureRandom mRandom;
+
+ private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456780+-/#$!?";
+ private static final int PW_LENGTH = 10;
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ if (getResources().getBoolean(R.bool.portrait_only)) {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.magic_create);
+ mFullJidDisplay = (TextView) findViewById(R.id.full_jid);
+ mUsername = (EditText) findViewById(R.id.username);
+ mRandom = new SecureRandom();
+ Button next = (Button) findViewById(R.id.create_account);
+ next.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String username = mUsername.getText().toString();
+ if (username.contains("@") || username.length() < 3) {
+ mUsername.setError(getString(R.string.invalid_username));
+ mUsername.requestFocus();
+ } else {
+ mUsername.setError(null);
+ try {
+ Jid jid = Jid.fromParts(username.toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
+ Account account = xmppConnectionService.findAccountByJid(jid);
+ if (account == null) {
+ account = new Account(jid, createPassword());
+ account.setOption(Account.OPTION_REGISTER, true);
+ account.setOption(Account.OPTION_DISABLED, true);
+ account.setOption(Account.OPTION_MAGIC_CREATE, true);
+ xmppConnectionService.createAccount(account);
+ }
+ Intent intent = new Intent(MagicCreateActivity.this, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ intent.putExtra("init", true);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ Toast.makeText(MagicCreateActivity.this, R.string.secure_password_generated, Toast.LENGTH_SHORT).show();
+ startActivity(intent);
+ } catch (InvalidJidException e) {
+ mUsername.setError(getString(R.string.invalid_username));
+ mUsername.requestFocus();
+ }
+ }
+ }
+ });
+ mUsername.addTextChangedListener(this);
+ }
+
+ private String createPassword() {
+ StringBuilder builder = new StringBuilder(PW_LENGTH);
+ for (int i = 0; i < PW_LENGTH; ++i) {
+ builder.append(CHARS.charAt(mRandom.nextInt(CHARS.length() - 1)));
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.toString().trim().length() > 0) {
+ try {
+ mFullJidDisplay.setVisibility(View.VISIBLE);
+ Jid jid = Jid.fromParts(s.toString().toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
+ mFullJidDisplay.setText(getString(R.string.your_full_jid_will_be, jid.toString()));
+ } catch (InvalidJidException e) {
+ mFullJidDisplay.setVisibility(View.INVISIBLE);
+ }
+
+ } else {
+ mFullJidDisplay.setVisibility(View.INVISIBLE);
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java b/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java
index 0eb5eda21..66d1330ef 100644
--- a/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ManageAccountActivity.java
@@ -38,69 +38,69 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated {
- private final String STATE_SELECTED_ACCOUNT = "selected_account";
-
- protected Account selectedAccount = null;
- protected Jid selectedAccountJid = null;
-
- protected final List<Account> accountList = new ArrayList<>();
- protected ListView accountListView;
- protected AccountAdapter mAccountAdapter;
- protected AtomicBoolean mInvokedAddAccount = new AtomicBoolean(false);
-
- protected Pair<Integer, Intent> mPostponedActivityResult = null;
-
- @Override
- public void onAccountUpdate() {
- refreshUi();
- }
-
- @Override
- protected void refreshUiReal() {
- synchronized (this.accountList) {
- accountList.clear();
- accountList.addAll(xmppConnectionService.getAccounts());
- }
- ActionBar actionBar = getActionBar();
- if (actionBar != null) {
- actionBar.setHomeButtonEnabled(this.accountList.size() > 0);
- actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0);
- }
- invalidateOptionsMenu();
- mAccountAdapter.notifyDataSetChanged();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
-
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.manage_accounts);
-
- if (savedInstanceState != null) {
- String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT);
- if (jid != null) {
- try {
- this.selectedAccountJid = Jid.fromString(jid);
- } catch (InvalidJidException e) {
- this.selectedAccountJid = null;
- }
- }
- }
-
- accountListView = (ListView) findViewById(R.id.account_list);
- this.mAccountAdapter = new AccountAdapter(this, accountList);
- accountListView.setAdapter(this.mAccountAdapter);
- accountListView.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> arg0, View view,
- int position, long arg3) {
- switchToAccount(accountList.get(position));
- }
- });
- registerForContextMenu(accountListView);
- }
+ private final String STATE_SELECTED_ACCOUNT = "selected_account";
+
+ protected Account selectedAccount = null;
+ protected Jid selectedAccountJid = null;
+
+ protected final List<Account> accountList = new ArrayList<>();
+ protected ListView accountListView;
+ protected AccountAdapter mAccountAdapter;
+ protected AtomicBoolean mInvokedAddAccount = new AtomicBoolean(false);
+
+ protected Pair<Integer, Intent> mPostponedActivityResult = null;
+
+ @Override
+ public void onAccountUpdate() {
+ refreshUi();
+ }
+
+ @Override
+ protected void refreshUiReal() {
+ synchronized (this.accountList) {
+ accountList.clear();
+ accountList.addAll(xmppConnectionService.getAccounts());
+ }
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setHomeButtonEnabled(this.accountList.size() > 0);
+ actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0);
+ }
+ invalidateOptionsMenu();
+ mAccountAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.manage_accounts);
+
+ if (savedInstanceState != null) {
+ String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT);
+ if (jid != null) {
+ try {
+ this.selectedAccountJid = Jid.fromString(jid);
+ } catch (InvalidJidException e) {
+ this.selectedAccountJid = null;
+ }
+ }
+ }
+
+ accountListView = (ListView) findViewById(R.id.account_list);
+ this.mAccountAdapter = new AccountAdapter(this, accountList);
+ accountListView.setAdapter(this.mAccountAdapter);
+ accountListView.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View view,
+ int position, long arg3) {
+ switchToAccount(accountList.get(position));
+ }
+ });
+ registerForContextMenu(accountListView);
+ }
@Override
protected void onStart() {
@@ -111,286 +111,286 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
}
}
- @Override
- public void onSaveInstanceState(final Bundle savedInstanceState) {
- if (selectedAccount != null) {
- savedInstanceState.putString(STATE_SELECTED_ACCOUNT, selectedAccount.getJid().toBareJid().toString());
- }
- super.onSaveInstanceState(savedInstanceState);
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- ManageAccountActivity.this.getMenuInflater().inflate(
+ @Override
+ public void onSaveInstanceState(final Bundle savedInstanceState) {
+ if (selectedAccount != null) {
+ savedInstanceState.putString(STATE_SELECTED_ACCOUNT, selectedAccount.getJid().toBareJid().toString());
+ }
+ super.onSaveInstanceState(savedInstanceState);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ ManageAccountActivity.this.getMenuInflater().inflate(
R.menu.manageaccounts_context, menu);
- AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
- this.selectedAccount = accountList.get(acmi.position);
- if (this.selectedAccount.isOptionSet(Account.OPTION_DISABLED)) {
- menu.findItem(R.id.mgmt_account_reconnect).setVisible(false);
- menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
- menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
- menu.findItem(R.id.mgmt_account_change_presence).setVisible(false);
- } else {
- menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(Config.supportOpenPgp());
- menu.findItem(R.id.mgmt_account_change_presence).setVisible(manuallyChangePresence());
- }
- menu.setHeaderTitle(this.selectedAccount.getJid().toBareJid().toString());
- }
-
- @Override
- void onBackendConnected() {
- if (selectedAccountJid != null) {
- this.selectedAccount = xmppConnectionService.findAccountByJid(selectedAccountJid);
- }
- refreshUiReal();
- if (this.mPostponedActivityResult != null) {
- this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
- }
- if (Config.X509_VERIFICATION && this.accountList.size() == 0) {
- if (mInvokedAddAccount.compareAndSet(false, true)) {
- addAccountFromKey();
- }
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.manageaccounts, menu);
- MenuItem addAccount = menu.findItem(R.id.action_add_account);
- MenuItem addAccountWithCertificate = menu.findItem(R.id.action_add_account_with_cert);
-
- if (Config.X509_VERIFICATION) {
- addAccount.setVisible(false);
- addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- } else {
- addAccount.setVisible(!Config.SINGLE_ACCOUNT);
- }
- addAccountWithCertificate.setVisible(!Config.SINGLE_ACCOUNT);
- return true;
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.mgmt_account_publish_avatar:
- publishAvatar(selectedAccount);
- return true;
- case R.id.mgmt_account_reconnect:
- disableAccount(selectedAccount);
- enableAccount(selectedAccount);
- return true;
- case R.id.mgmt_account_delete:
- deleteAccount(selectedAccount);
- return true;
- case R.id.mgmt_account_announce_pgp:
- publishOpenPGPPublicKey(selectedAccount);
- return true;
- case R.id.mgmt_account_change_presence:
- changePresence(selectedAccount);
- return true;
- default:
- return super.onContextItemSelected(item);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_add_account:
- startActivity(new Intent(getApplicationContext(),
- EditAccountActivity.class));
- break;
- case R.id.action_add_account_with_cert:
- addAccountFromKey();
- break;
- default:
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public boolean onNavigateUp() {
- if (xmppConnectionService.getConversations().size() == 0) {
- Intent contactsIntent = new Intent(this,
- StartConversationActivity.class);
- contactsIntent.setFlags(
- // if activity exists in stack, pop the stack and go back to it
- Intent.FLAG_ACTIVITY_CLEAR_TOP |
- // otherwise, make a new task for it
- Intent.FLAG_ACTIVITY_NEW_TASK |
- // don't use the new activity animation; finish
- // animation runs instead
- Intent.FLAG_ACTIVITY_NO_ANIMATION);
- startActivity(contactsIntent);
- finish();
- return true;
- } else {
- return super.onNavigateUp();
- }
- }
-
- private void changePresence(Account account) {
- Intent intent = new Intent(this, SetPresenceActivity.class);
- intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT,account.getJid().toBareJid().toString());
- startActivity(intent);
- }
-
- public void onClickTglAccountState(Account account, boolean enable) {
- if (enable) {
- enableAccount(account);
- } else {
- disableAccount(account);
- }
- }
-
- private void addAccountFromKey() {
- try {
- KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show();
- }
- }
-
- private void publishAvatar(Account account) {
- Intent intent = new Intent(getApplicationContext(),
- PublishProfilePictureActivity.class);
- intent.putExtra(EXTRA_ACCOUNT, account.getJid().toString());
- startActivity(intent);
- }
-
- private void disableAllAccounts() {
- List<Account> list = new ArrayList<>();
- synchronized (this.accountList) {
- for (Account account : this.accountList) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- list.add(account);
- }
- }
- }
- for (Account account : list) {
- disableAccount(account);
- }
- }
-
- private boolean accountsLeftToDisable() {
- synchronized (this.accountList) {
- for (Account account : this.accountList) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- return true;
- }
- }
- return false;
- }
- }
-
- private boolean accountsLeftToEnable() {
- synchronized (this.accountList) {
- for (Account account : this.accountList) {
- if (account.isOptionSet(Account.OPTION_DISABLED)) {
- return true;
- }
- }
- return false;
- }
- }
-
- private void enableAllAccounts() {
- List<Account> list = new ArrayList<>();
- synchronized (this.accountList) {
- for (Account account : this.accountList) {
- if (account.isOptionSet(Account.OPTION_DISABLED)) {
- list.add(account);
- }
- }
- }
- for (Account account : list) {
- enableAccount(account);
- }
- }
-
- private void disableAccount(Account account) {
- account.setOption(Account.OPTION_DISABLED, true);
- if (!xmppConnectionService.updateAccount(account)) {
- Toast.makeText(this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
- }
- }
-
- private void enableAccount(Account account) {
- account.setOption(Account.OPTION_DISABLED, false);
- if (!xmppConnectionService.updateAccount(account)) {
- Toast.makeText(this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
- }
- }
-
- private void publishOpenPGPPublicKey(Account account) {
- if (ManageAccountActivity.this.hasPgp()) {
- announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
- } else {
- this.showInstallPgpDialog();
- }
- }
-
- private void deleteAccount(final Account account) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- ManageAccountActivity.this);
- builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
- builder.setPositiveButton(getString(R.string.delete),
- new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- xmppConnectionService.deleteAccount(account);
- selectedAccount = null;
- }
- });
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.create().show();
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK) {
- if (xmppConnectionServiceBound) {
- if (requestCode == REQUEST_CHOOSE_PGP_ID) {
- if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
- selectedAccount.setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
- announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
- } else {
- choosePgpSignId(selectedAccount);
- }
- } else if (requestCode == REQUEST_ANNOUNCE_PGP) {
- announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
- }
- this.mPostponedActivityResult = null;
- } else {
- this.mPostponedActivityResult = new Pair<>(requestCode, data);
- }
- }
- }
-
- @Override
- public void alias(String alias) {
- if (alias != null) {
- xmppConnectionService.createAccountFromKey(alias, this);
- }
- }
-
- @Override
- public void onAccountCreated(Account account) {
- switchToAccount(account, true);
- }
-
- @Override
- public void informUser(final int r) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show();
- }
- });
- }
+ AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
+ this.selectedAccount = accountList.get(acmi.position);
+ if (this.selectedAccount.isOptionSet(Account.OPTION_DISABLED)) {
+ menu.findItem(R.id.mgmt_account_reconnect).setVisible(false);
+ menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
+ menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
+ menu.findItem(R.id.mgmt_account_change_presence).setVisible(false);
+ } else {
+ menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(Config.supportOpenPgp());
+ menu.findItem(R.id.mgmt_account_change_presence).setVisible(manuallyChangePresence());
+ }
+ menu.setHeaderTitle(this.selectedAccount.getJid().toBareJid().toString());
+ }
+
+ @Override
+ void onBackendConnected() {
+ if (selectedAccountJid != null) {
+ this.selectedAccount = xmppConnectionService.findAccountByJid(selectedAccountJid);
+ }
+ refreshUiReal();
+ if (this.mPostponedActivityResult != null) {
+ this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
+ }
+ if (Config.X509_VERIFICATION && this.accountList.size() == 0) {
+ if (mInvokedAddAccount.compareAndSet(false, true)) {
+ addAccountFromKey();
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.manageaccounts, menu);
+ MenuItem addAccount = menu.findItem(R.id.action_add_account);
+ MenuItem addAccountWithCertificate = menu.findItem(R.id.action_add_account_with_cert);
+
+ if (Config.X509_VERIFICATION) {
+ addAccount.setVisible(false);
+ addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ } else {
+ addAccount.setVisible(!Config.SINGLE_ACCOUNT);
+ }
+ addAccountWithCertificate.setVisible(!Config.SINGLE_ACCOUNT);
+ return true;
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.mgmt_account_publish_avatar:
+ publishAvatar(selectedAccount);
+ return true;
+ case R.id.mgmt_account_reconnect:
+ disableAccount(selectedAccount);
+ enableAccount(selectedAccount);
+ return true;
+ case R.id.mgmt_account_delete:
+ deleteAccount(selectedAccount);
+ return true;
+ case R.id.mgmt_account_announce_pgp:
+ publishOpenPGPPublicKey(selectedAccount);
+ return true;
+ case R.id.mgmt_account_change_presence:
+ changePresence(selectedAccount);
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_add_account:
+ startActivity(new Intent(getApplicationContext(),
+ EditAccountActivity.class));
+ break;
+ case R.id.action_add_account_with_cert:
+ addAccountFromKey();
+ break;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onNavigateUp() {
+ if (xmppConnectionService.getConversations().size() == 0) {
+ Intent contactsIntent = new Intent(this,
+ StartConversationActivity.class);
+ contactsIntent.setFlags(
+ // if activity exists in stack, pop the stack and go back to it
+ Intent.FLAG_ACTIVITY_CLEAR_TOP |
+ // otherwise, make a new task for it
+ Intent.FLAG_ACTIVITY_NEW_TASK |
+ // don't use the new activity animation; finish
+ // animation runs instead
+ Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivity(contactsIntent);
+ finish();
+ return true;
+ } else {
+ return super.onNavigateUp();
+ }
+ }
+
+ private void changePresence(Account account) {
+ Intent intent = new Intent(this, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, account.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+
+ public void onClickTglAccountState(Account account, boolean enable) {
+ if (enable) {
+ enableAccount(account);
+ } else {
+ disableAccount(account);
+ }
+ }
+
+ private void addAccountFromKey() {
+ try {
+ KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void publishAvatar(Account account) {
+ Intent intent = new Intent(getApplicationContext(),
+ PublishProfilePictureActivity.class);
+ intent.putExtra(EXTRA_ACCOUNT, account.getJid().toString());
+ startActivity(intent);
+ }
+
+ private void disableAllAccounts() {
+ List<Account> list = new ArrayList<>();
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ list.add(account);
+ }
+ }
+ }
+ for (Account account : list) {
+ disableAccount(account);
+ }
+ }
+
+ private boolean accountsLeftToDisable() {
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private boolean accountsLeftToEnable() {
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (account.isOptionSet(Account.OPTION_DISABLED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private void enableAllAccounts() {
+ List<Account> list = new ArrayList<>();
+ synchronized (this.accountList) {
+ for (Account account : this.accountList) {
+ if (account.isOptionSet(Account.OPTION_DISABLED)) {
+ list.add(account);
+ }
+ }
+ }
+ for (Account account : list) {
+ enableAccount(account);
+ }
+ }
+
+ private void disableAccount(Account account) {
+ account.setOption(Account.OPTION_DISABLED, true);
+ if (!xmppConnectionService.updateAccount(account)) {
+ Toast.makeText(this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void enableAccount(Account account) {
+ account.setOption(Account.OPTION_DISABLED, false);
+ if (!xmppConnectionService.updateAccount(account)) {
+ Toast.makeText(this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void publishOpenPGPPublicKey(Account account) {
+ if (ManageAccountActivity.this.hasPgp()) {
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
+ } else {
+ this.showInstallPgpDialog();
+ }
+ }
+
+ private void deleteAccount(final Account account) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ ManageAccountActivity.this);
+ builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
+ builder.setPositiveButton(getString(R.string.delete),
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ xmppConnectionService.deleteAccount(account);
+ selectedAccount = null;
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.create().show();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (xmppConnectionServiceBound) {
+ if (requestCode == REQUEST_CHOOSE_PGP_ID) {
+ if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
+ selectedAccount.setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
+ } else {
+ choosePgpSignId(selectedAccount);
+ }
+ } else if (requestCode == REQUEST_ANNOUNCE_PGP) {
+ announcePgp(selectedAccount, null, onOpenPGPKeyPublished);
+ }
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+ }
+ }
+
+ @Override
+ public void alias(String alias) {
+ if (alias != null) {
+ xmppConnectionService.createAccountFromKey(alias, this);
+ }
+ }
+
+ @Override
+ public void onAccountCreated(Account account) {
+ switchToAccount(account, true);
+ }
+
+ @Override
+ public void informUser(final int r) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java b/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java
index 423ccef9e..ac981f722 100644
--- a/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/PublishProfilePictureActivity.java
@@ -1,4 +1,3 @@
-
package de.pixart.messenger.ui;
import android.app.PendingIntent;
@@ -31,311 +30,311 @@ import de.pixart.messenger.xmpp.pep.Avatar;
public class PublishProfilePictureActivity extends XmppActivity {
- private static final int REQUEST_CHOOSE_FILE_AND_CROP = 0xac23;
- private static final int REQUEST_CHOOSE_FILE = 0xac24;
- private ImageView avatar;
- private TextView accountTextView;
- private TextView hintOrWarning;
- private TextView secondaryHint;
- private Button cancelButton;
- private Button publishButton;
- private Uri avatarUri;
- private Uri defaultUri;
- private Account account;
- private boolean support = false;
- private OnLongClickListener backToDefaultListener = new OnLongClickListener() {
+ private static final int REQUEST_CHOOSE_FILE_AND_CROP = 0xac23;
+ private static final int REQUEST_CHOOSE_FILE = 0xac24;
+ private ImageView avatar;
+ private TextView accountTextView;
+ private TextView hintOrWarning;
+ private TextView secondaryHint;
+ private Button cancelButton;
+ private Button publishButton;
+ private Uri avatarUri;
+ private Uri defaultUri;
+ private Account account;
+ private boolean support = false;
+ private OnLongClickListener backToDefaultListener = new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- avatarUri = defaultUri;
- loadImageIntoPreview(defaultUri);
- return true;
- }
- };
- private boolean mInitialAccountSetup;
- private UiCallback<Avatar> avatarPublication = new UiCallback<Avatar>() {
+ @Override
+ public boolean onLongClick(View v) {
+ avatarUri = defaultUri;
+ loadImageIntoPreview(defaultUri);
+ return true;
+ }
+ };
+ private boolean mInitialAccountSetup;
+ private UiCallback<Avatar> avatarPublication = new UiCallback<Avatar>() {
- @Override
- public void success(Avatar object) {
- runOnUiThread(new Runnable() {
+ @Override
+ public void success(Avatar object) {
+ runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (mInitialAccountSetup) {
- Intent intent = new Intent(getApplicationContext(),
- StartConversationActivity.class);
- intent.putExtra("init", true);
- startActivity(intent);
- }
- Toast.makeText(PublishProfilePictureActivity.this,
- R.string.avatar_has_been_published,
- Toast.LENGTH_SHORT).show();
- finish();
- }
- });
- }
+ @Override
+ public void run() {
+ if (mInitialAccountSetup) {
+ Intent intent = new Intent(getApplicationContext(),
+ StartConversationActivity.class);
+ intent.putExtra("init", true);
+ startActivity(intent);
+ }
+ Toast.makeText(PublishProfilePictureActivity.this,
+ R.string.avatar_has_been_published,
+ Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ });
+ }
- @Override
- public void error(final int errorCode, Avatar object) {
- runOnUiThread(new Runnable() {
+ @Override
+ public void error(final int errorCode, Avatar object) {
+ runOnUiThread(new Runnable() {
- @Override
- public void run() {
- hintOrWarning.setText(errorCode);
- hintOrWarning.setTextColor(getWarningTextColor());
- publishButton.setText(R.string.publish);
- enablePublishButton();
- }
- });
+ @Override
+ public void run() {
+ hintOrWarning.setText(errorCode);
+ hintOrWarning.setTextColor(getWarningTextColor());
+ publishButton.setText(R.string.publish);
+ enablePublishButton();
+ }
+ });
- }
+ }
- @Override
- public void userInputRequried(PendingIntent pi, Avatar object) {
- }
- };
+ @Override
+ public void userInputRequried(PendingIntent pi, Avatar object) {
+ }
+ };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_publish_profile_picture);
- this.avatar = (ImageView) findViewById(R.id.account_image);
- this.cancelButton = (Button) findViewById(R.id.cancel_button);
- this.publishButton = (Button) findViewById(R.id.publish_button);
- this.accountTextView = (TextView) findViewById(R.id.account);
- this.hintOrWarning = (TextView) findViewById(R.id.hint_or_warning);
- this.secondaryHint = (TextView) findViewById(R.id.secondary_hint);
- this.publishButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_publish_profile_picture);
+ this.avatar = (ImageView) findViewById(R.id.account_image);
+ this.cancelButton = (Button) findViewById(R.id.cancel_button);
+ this.publishButton = (Button) findViewById(R.id.publish_button);
+ this.accountTextView = (TextView) findViewById(R.id.account);
+ this.hintOrWarning = (TextView) findViewById(R.id.hint_or_warning);
+ this.secondaryHint = (TextView) findViewById(R.id.secondary_hint);
+ this.publishButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (avatarUri != null) {
- publishButton.setText(R.string.publishing);
- disablePublishButton();
- xmppConnectionService.publishAvatar(account, avatarUri,
- avatarPublication);
- }
- }
- });
- this.cancelButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (avatarUri != null) {
+ publishButton.setText(R.string.publishing);
+ disablePublishButton();
+ xmppConnectionService.publishAvatar(account, avatarUri,
+ avatarPublication);
+ }
+ }
+ });
+ this.cancelButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mInitialAccountSetup) {
- Intent intent = new Intent(getApplicationContext(),
- StartConversationActivity.class);
- if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
- intent.putExtra("init", true);
- }
- startActivity(intent);
- }
- finish();
- }
- });
- this.avatar.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mInitialAccountSetup) {
+ Intent intent = new Intent(getApplicationContext(),
+ StartConversationActivity.class);
+ if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
+ intent.putExtra("init", true);
+ }
+ startActivity(intent);
+ }
+ finish();
+ }
+ });
+ this.avatar.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (hasStoragePermission(REQUEST_CHOOSE_FILE)) {
- chooseAvatar(false);
- }
+ @Override
+ public void onClick(View v) {
+ if (hasStoragePermission(REQUEST_CHOOSE_FILE)) {
+ chooseAvatar(false);
+ }
- }
- });
- this.defaultUri = PhoneHelper.getSelfiUri(getApplicationContext());
- }
+ }
+ });
+ this.defaultUri = PhoneHelper.getSelfiUri(getApplicationContext());
+ }
- private void chooseAvatar(boolean crop) {
- Intent attachFileIntent = new Intent();
- attachFileIntent.setType("image/*");
- attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
- Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file));
- startActivityForResult(chooser, crop ? REQUEST_CHOOSE_FILE_AND_CROP : REQUEST_CHOOSE_FILE);
- }
+ private void chooseAvatar(boolean crop) {
+ Intent attachFileIntent = new Intent();
+ attachFileIntent.setType("image/*");
+ attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
+ Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file));
+ startActivityForResult(chooser, crop ? REQUEST_CHOOSE_FILE_AND_CROP : REQUEST_CHOOSE_FILE);
+ }
- @Override
- public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
- if (grantResults.length > 0)
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- if (requestCode == REQUEST_CHOOSE_FILE_AND_CROP) {
- chooseAvatar(true);
- } else if (requestCode == REQUEST_CHOOSE_FILE) {
- chooseAvatar(false);
- }
- } else {
- Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
- }
- }
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+ if (grantResults.length > 0)
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (requestCode == REQUEST_CHOOSE_FILE_AND_CROP) {
+ chooseAvatar(true);
+ } else if (requestCode == REQUEST_CHOOSE_FILE) {
+ chooseAvatar(false);
+ }
+ } else {
+ Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
+ }
+ }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.publish_avatar, menu);
- return super.onCreateOptionsMenu(menu);
- }
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.publish_avatar, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- if (item.getItemId() == R.id.action_crop_image) {
- if (hasStoragePermission(REQUEST_CHOOSE_FILE_AND_CROP)) {
- chooseAvatar(true);
- }
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == R.id.action_crop_image) {
+ if (hasStoragePermission(REQUEST_CHOOSE_FILE_AND_CROP)) {
+ chooseAvatar(true);
+ }
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
- @Override
- protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK) {
- Uri source = data.getData();
- switch (requestCode) {
- case REQUEST_CHOOSE_FILE_AND_CROP:
- if (FileBackend.weOwnFile(this, source)) {
- Toast.makeText(this,R.string.security_error_invalid_file_access,Toast.LENGTH_SHORT).show();
- return;
- }
- String original = FileUtils.getPath(this, source);
- if (original != null) {
- source = Uri.parse("file://"+original);
- }
- Uri destination = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
- final int size = getPixel(Config.AVATAR_SIZE);
- Crop.of(source, destination).asSquare().withMaxSize(size, size).start(this);
- break;
- case REQUEST_CHOOSE_FILE:
- if (FileBackend.weOwnFile(this, source)) {
- Toast.makeText(this,R.string.security_error_invalid_file_access,Toast.LENGTH_SHORT).show();
- return;
- }
- this.avatarUri = source;
- if (xmppConnectionServiceBound) {
- loadImageIntoPreview(this.avatarUri);
- }
- break;
- case Crop.REQUEST_CROP:
- this.avatarUri = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
- if (xmppConnectionServiceBound) {
- loadImageIntoPreview(this.avatarUri);
- }
- break;
- }
- } else {
- if (requestCode == Crop.REQUEST_CROP && data != null) {
- Throwable throwable = Crop.getError(data);
- if (throwable != null && throwable instanceof OutOfMemoryError) {
- Toast.makeText(this,R.string.selection_too_large, Toast.LENGTH_SHORT).show();
- }
- }
- }
- }
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ Uri source = data.getData();
+ switch (requestCode) {
+ case REQUEST_CHOOSE_FILE_AND_CROP:
+ if (FileBackend.weOwnFile(this, source)) {
+ Toast.makeText(this, R.string.security_error_invalid_file_access, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ String original = FileUtils.getPath(this, source);
+ if (original != null) {
+ source = Uri.parse("file://" + original);
+ }
+ Uri destination = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
+ final int size = getPixel(Config.AVATAR_SIZE);
+ Crop.of(source, destination).asSquare().withMaxSize(size, size).start(this);
+ break;
+ case REQUEST_CHOOSE_FILE:
+ if (FileBackend.weOwnFile(this, source)) {
+ Toast.makeText(this, R.string.security_error_invalid_file_access, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ this.avatarUri = source;
+ if (xmppConnectionServiceBound) {
+ loadImageIntoPreview(this.avatarUri);
+ }
+ break;
+ case Crop.REQUEST_CROP:
+ this.avatarUri = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
+ if (xmppConnectionServiceBound) {
+ loadImageIntoPreview(this.avatarUri);
+ }
+ break;
+ }
+ } else {
+ if (requestCode == Crop.REQUEST_CROP && data != null) {
+ Throwable throwable = Crop.getError(data);
+ if (throwable != null && throwable instanceof OutOfMemoryError) {
+ Toast.makeText(this, R.string.selection_too_large, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ }
- @Override
- protected void onBackendConnected() {
- this.account = extractAccount(getIntent());
- if (this.account != null) {
- if (this.account.getXmppConnection() != null) {
- this.support = this.account.getXmppConnection().getFeatures().pep();
- }
- if (this.avatarUri == null) {
- if (this.account.getAvatar() != null
- || this.defaultUri == null) {
- this.avatar.setImageBitmap(avatarService().get(account, getPixel(Config.AVATAR_SIZE)));
- if (this.defaultUri != null) {
- this.avatar
- .setOnLongClickListener(this.backToDefaultListener);
- } else {
- this.secondaryHint.setVisibility(View.INVISIBLE);
- }
- if (!support) {
- this.hintOrWarning
- .setTextColor(getWarningTextColor());
+ @Override
+ protected void onBackendConnected() {
+ this.account = extractAccount(getIntent());
+ if (this.account != null) {
+ if (this.account.getXmppConnection() != null) {
+ this.support = this.account.getXmppConnection().getFeatures().pep();
+ }
+ if (this.avatarUri == null) {
+ if (this.account.getAvatar() != null
+ || this.defaultUri == null) {
+ this.avatar.setImageBitmap(avatarService().get(account, getPixel(Config.AVATAR_SIZE)));
+ if (this.defaultUri != null) {
+ this.avatar
+ .setOnLongClickListener(this.backToDefaultListener);
+ } else {
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ }
+ if (!support) {
+ this.hintOrWarning
+ .setTextColor(getWarningTextColor());
if (account.getStatus() == Account.State.ONLINE) {
this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
} else {
this.hintOrWarning.setText(R.string.error_publish_avatar_offline);
}
- }
- } else {
- this.avatarUri = this.defaultUri;
- loadImageIntoPreview(this.defaultUri);
- this.secondaryHint.setVisibility(View.INVISIBLE);
- }
- } else {
- loadImageIntoPreview(avatarUri);
- }
- String account;
- if (Config.DOMAIN_LOCK != null) {
- account = this.account.getJid().getLocalpart();
- } else {
- account = this.account.getJid().toBareJid().toString();
- }
- this.accountTextView.setText(account);
- }
- }
+ }
+ } else {
+ this.avatarUri = this.defaultUri;
+ loadImageIntoPreview(this.defaultUri);
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ loadImageIntoPreview(avatarUri);
+ }
+ String account;
+ if (Config.DOMAIN_LOCK != null) {
+ account = this.account.getJid().getLocalpart();
+ } else {
+ account = this.account.getJid().toBareJid().toString();
+ }
+ this.accountTextView.setText(account);
+ }
+ }
- @Override
- protected void onStart() {
- super.onStart();
- if (getIntent() != null) {
- this.mInitialAccountSetup = getIntent().getBooleanExtra("setup", false);
- }
- if (this.mInitialAccountSetup) {
- this.cancelButton.setText(R.string.skip);
- }
- }
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (getIntent() != null) {
+ this.mInitialAccountSetup = getIntent().getBooleanExtra("setup", false);
+ }
+ if (this.mInitialAccountSetup) {
+ this.cancelButton.setText(R.string.skip);
+ }
+ }
- protected void loadImageIntoPreview(Uri uri) {
- Bitmap bm = null;
- try {
- bm = xmppConnectionService.getFileBackend().cropCenterSquare(uri, getPixel(Config.AVATAR_SIZE));
- } catch (Exception e) {
- e.printStackTrace();
- }
+ protected void loadImageIntoPreview(Uri uri) {
+ Bitmap bm = null;
+ try {
+ bm = xmppConnectionService.getFileBackend().cropCenterSquare(uri, getPixel(Config.AVATAR_SIZE));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
- if (bm == null) {
- disablePublishButton();
- this.hintOrWarning.setTextColor(getWarningTextColor());
- this.hintOrWarning
- .setText(R.string.error_publish_avatar_converting);
- return;
- }
- this.avatar.setImageBitmap(bm);
- if (support) {
- enablePublishButton();
- this.publishButton.setText(R.string.publish);
- this.hintOrWarning.setText(R.string.publish_avatar_explanation);
- this.hintOrWarning.setTextColor(getPrimaryTextColor());
- } else {
- disablePublishButton();
- this.hintOrWarning.setTextColor(getWarningTextColor());
+ if (bm == null) {
+ disablePublishButton();
+ this.hintOrWarning.setTextColor(getWarningTextColor());
+ this.hintOrWarning
+ .setText(R.string.error_publish_avatar_converting);
+ return;
+ }
+ this.avatar.setImageBitmap(bm);
+ if (support) {
+ enablePublishButton();
+ this.publishButton.setText(R.string.publish);
+ this.hintOrWarning.setText(R.string.publish_avatar_explanation);
+ this.hintOrWarning.setTextColor(getPrimaryTextColor());
+ } else {
+ disablePublishButton();
+ this.hintOrWarning.setTextColor(getWarningTextColor());
if (account.getStatus() == Account.State.ONLINE) {
this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
} else {
this.hintOrWarning.setText(R.string.error_publish_avatar_offline);
}
- }
- if (this.defaultUri != null && uri.equals(this.defaultUri)) {
- this.secondaryHint.setVisibility(View.INVISIBLE);
- this.avatar.setOnLongClickListener(null);
- } else if (this.defaultUri != null) {
- this.secondaryHint.setVisibility(View.VISIBLE);
- this.avatar.setOnLongClickListener(this.backToDefaultListener);
- }
- }
+ }
+ if (this.defaultUri != null && uri.equals(this.defaultUri)) {
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ this.avatar.setOnLongClickListener(null);
+ } else if (this.defaultUri != null) {
+ this.secondaryHint.setVisibility(View.VISIBLE);
+ this.avatar.setOnLongClickListener(this.backToDefaultListener);
+ }
+ }
- protected void enablePublishButton() {
- this.publishButton.setEnabled(true);
- this.publishButton.setTextColor(getPrimaryTextColor());
- }
+ protected void enablePublishButton() {
+ this.publishButton.setEnabled(true);
+ this.publishButton.setTextColor(getPrimaryTextColor());
+ }
- protected void disablePublishButton() {
- this.publishButton.setEnabled(false);
- this.publishButton.setTextColor(getSecondaryTextColor());
- }
+ protected void disablePublishButton() {
+ this.publishButton.setEnabled(false);
+ this.publishButton.setTextColor(getSecondaryTextColor());
+ }
- public void refreshUiReal() {
- //nothing to do. This Activity doesn't implement any listeners
- }
+ public void refreshUiReal() {
+ //nothing to do. This Activity doesn't implement any listeners
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/RecordingActivity.java b/src/main/java/de/pixart/messenger/ui/RecordingActivity.java
index ed753022b..e5e43f290 100644
--- a/src/main/java/de/pixart/messenger/ui/RecordingActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/RecordingActivity.java
@@ -26,139 +26,139 @@ import de.pixart.messenger.persistance.FileBackend;
public class RecordingActivity extends Activity implements View.OnClickListener {
- private TextView mTimerTextView;
- private Button mCancelButton;
- private Button mStopButton;
-
- private MediaRecorder mRecorder;
- private long mStartTime = 0;
-
- private int[] amplitudes = new int[100];
- private int i = 0;
-
- private Handler mHandler = new Handler();
- private Runnable mTickExecutor = new Runnable() {
- @Override
- public void run() {
- tick();
- mHandler.postDelayed(mTickExecutor,100);
- }
- };
- private File mOutputFile;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_recording);
- this.mTimerTextView = (TextView) this.findViewById(R.id.timer);
- this.mCancelButton = (Button) this.findViewById(R.id.cancel_button);
- this.mCancelButton.setOnClickListener(this);
- this.mStopButton = (Button) this.findViewById(R.id.share_button);
- this.mStopButton.setOnClickListener(this);
+ private TextView mTimerTextView;
+ private Button mCancelButton;
+ private Button mStopButton;
+
+ private MediaRecorder mRecorder;
+ private long mStartTime = 0;
+
+ private int[] amplitudes = new int[100];
+ private int i = 0;
+
+ private Handler mHandler = new Handler();
+ private Runnable mTickExecutor = new Runnable() {
+ @Override
+ public void run() {
+ tick();
+ mHandler.postDelayed(mTickExecutor, 100);
+ }
+ };
+ private File mOutputFile;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_recording);
+ this.mTimerTextView = (TextView) this.findViewById(R.id.timer);
+ this.mCancelButton = (Button) this.findViewById(R.id.cancel_button);
+ this.mCancelButton.setOnClickListener(this);
+ this.mStopButton = (Button) this.findViewById(R.id.share_button);
+ this.mStopButton.setOnClickListener(this);
this.setFinishOnTouchOutside(false);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
- @Override
- protected void onStart() {
- super.onStart();
- Log.d(Config.LOGTAG, "output: " + getOutputFile());
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(Config.LOGTAG, "output: " + getOutputFile());
if (!startRecording()) {
mStopButton.setEnabled(false);
mStopButton.setTextColor(0x8a000000);
- Toast.makeText(this,R.string.unable_to_start_recording,Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.unable_to_start_recording, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mRecorder != null) {
+ stopRecording(false);
+ }
+ }
+
+ private boolean startRecording() {
+ mRecorder = new MediaRecorder();
+ mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ mRecorder.setAudioEncodingBitRate(48000);
+ } else {
+ mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ mRecorder.setAudioEncodingBitRate(48000);
}
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- if (mRecorder != null) {
- stopRecording(false);
- }
- }
-
- private boolean startRecording() {
- mRecorder = new MediaRecorder();
- mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
- mRecorder.setAudioEncodingBitRate(48000);
- } else {
- mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
- mRecorder.setAudioEncodingBitRate(48000);
- }
- mRecorder.setAudioSamplingRate(48000);
- mOutputFile = getOutputFile();
- mOutputFile.getParentFile().mkdirs();
- mRecorder.setOutputFile(mOutputFile.getAbsolutePath());
-
- try {
- mRecorder.prepare();
- mRecorder.start();
- mStartTime = SystemClock.elapsedRealtime();
- mHandler.postDelayed(mTickExecutor, 100);
- Log.d(Config.LOGTAG,"started recording to "+mOutputFile.getAbsolutePath());
+ mRecorder.setAudioSamplingRate(48000);
+ mOutputFile = getOutputFile();
+ mOutputFile.getParentFile().mkdirs();
+ mRecorder.setOutputFile(mOutputFile.getAbsolutePath());
+
+ try {
+ mRecorder.prepare();
+ mRecorder.start();
+ mStartTime = SystemClock.elapsedRealtime();
+ mHandler.postDelayed(mTickExecutor, 100);
+ Log.d(Config.LOGTAG, "started recording to " + mOutputFile.getAbsolutePath());
return true;
- } catch (Exception e) {
- Log.e(Config.LOGTAG, "prepare() failed "+e.getMessage());
+ } catch (Exception e) {
+ Log.e(Config.LOGTAG, "prepare() failed " + e.getMessage());
return false;
- }
- }
-
- protected void stopRecording(boolean saveFile) {
- mRecorder.stop();
- mRecorder.release();
- mRecorder = null;
- mStartTime = 0;
- mHandler.removeCallbacks(mTickExecutor);
- if (!saveFile && mOutputFile != null) {
- mOutputFile.delete();
- }
- }
-
- private File getOutputFile() {
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
- return new File(FileBackend.getConversationsAudioDirectory() + "/"
- + dateFormat.format(new Date())
- + ".m4a");
- }
-
- private void tick() {
- long time = (mStartTime < 0) ? 0 : (SystemClock.elapsedRealtime() - mStartTime);
- int minutes = (int) (time / 60000);
- int seconds = (int) (time / 1000) % 60;
- int milliseconds = (int) (time / 100) % 10;
- mTimerTextView.setText(minutes+":"+(seconds < 10 ? "0"+seconds : seconds)+"."+milliseconds);
- if (mRecorder != null) {
- amplitudes[i] = mRecorder.getMaxAmplitude();
- //Log.d(Config.LOGTAG,"amplitude: "+(amplitudes[i] * 100 / 32767));
- if (i >= amplitudes.length -1) {
- i = 0;
- } else {
- ++i;
- }
- }
- }
-
- @Override
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.cancel_button:
- stopRecording(false);
- setResult(RESULT_CANCELED);
- finish();
- break;
- case R.id.share_button:
- stopRecording(true);
- Uri uri = Uri.parse("file://"+mOutputFile.getAbsolutePath());
- Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
- scanIntent.setData(uri);
- sendBroadcast(scanIntent);
- setResult(Activity.RESULT_OK, new Intent().setData(uri));
- finish();
- break;
- }
- }
+ }
+ }
+
+ protected void stopRecording(boolean saveFile) {
+ mRecorder.stop();
+ mRecorder.release();
+ mRecorder = null;
+ mStartTime = 0;
+ mHandler.removeCallbacks(mTickExecutor);
+ if (!saveFile && mOutputFile != null) {
+ mOutputFile.delete();
+ }
+ }
+
+ private File getOutputFile() {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+ return new File(FileBackend.getConversationsAudioDirectory() + "/"
+ + dateFormat.format(new Date())
+ + ".m4a");
+ }
+
+ private void tick() {
+ long time = (mStartTime < 0) ? 0 : (SystemClock.elapsedRealtime() - mStartTime);
+ int minutes = (int) (time / 60000);
+ int seconds = (int) (time / 1000) % 60;
+ int milliseconds = (int) (time / 100) % 10;
+ mTimerTextView.setText(minutes + ":" + (seconds < 10 ? "0" + seconds : seconds) + "." + milliseconds);
+ if (mRecorder != null) {
+ amplitudes[i] = mRecorder.getMaxAmplitude();
+ //Log.d(Config.LOGTAG,"amplitude: "+(amplitudes[i] * 100 / 32767));
+ if (i >= amplitudes.length - 1) {
+ i = 0;
+ } else {
+ ++i;
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.cancel_button:
+ stopRecording(false);
+ setResult(RESULT_CANCELED);
+ finish();
+ break;
+ case R.id.share_button:
+ stopRecording(true);
+ Uri uri = Uri.parse("file://" + mOutputFile.getAbsolutePath());
+ Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ scanIntent.setData(uri);
+ sendBroadcast(scanIntent);
+ setResult(Activity.RESULT_OK, new Intent().setData(uri));
+ finish();
+ break;
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/SetPresenceActivity.java b/src/main/java/de/pixart/messenger/ui/SetPresenceActivity.java
index 7214e560b..b4e88e5cc 100644
--- a/src/main/java/de/pixart/messenger/ui/SetPresenceActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/SetPresenceActivity.java
@@ -29,204 +29,204 @@ import de.pixart.messenger.utils.UIHelper;
public class SetPresenceActivity extends XmppActivity implements View.OnClickListener {
- //data
- protected Account mAccount;
- private List<PresenceTemplate> mTemplates;
-
- //UI Elements
- protected ScrollView mScrollView;
- protected EditText mStatusMessage;
- protected Spinner mShowSpinner;
- protected CheckBox mAllAccounts;
- protected LinearLayout mTemplatesView;
- private Pair<Integer, Intent> mPostponedActivityResult;
-
- private Runnable onPresenceChanged = new Runnable() {
- @Override
- public void run() {
- finish();
- }
- };
-
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_set_presence);
- mScrollView = (ScrollView) findViewById(R.id.scroll_view);
- mShowSpinner = (Spinner) findViewById(R.id.presence_show);
- ArrayAdapter adapter = ArrayAdapter.createFromResource(this,
- R.array.presence_show_options,
- R.layout.simple_list_item);
- mShowSpinner.setAdapter(adapter);
- mShowSpinner.setSelection(1);
- mStatusMessage = (EditText) findViewById(R.id.presence_status_message);
- mAllAccounts = (CheckBox) findViewById(R.id.all_accounts);
- mTemplatesView = (LinearLayout) findViewById(R.id.templates);
- final Button changePresence = (Button) findViewById(R.id.change_presence);
- changePresence.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- executeChangePresence();
- }
- });
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.change_presence, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- if (item.getItemId() == R.id.action_account_details) {
- if (mAccount != null) {
- switchToAccount(mAccount);
- }
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK) {
- if (xmppConnectionServiceBound && mAccount != null) {
- if (requestCode == REQUEST_ANNOUNCE_PGP) {
- announcePgp(mAccount, null, onPresenceChanged);
- }
- this.mPostponedActivityResult = null;
- } else {
- this.mPostponedActivityResult = new Pair<>(requestCode, data);
- }
- }
- }
-
- private void executeChangePresence() {
- Presence.Status status = getStatusFromSpinner();
- boolean allAccounts = mAllAccounts.isChecked();
- String statusMessage = mStatusMessage.getText().toString().trim();
- if (allAccounts && noAccountUsesPgp()) {
- xmppConnectionService.changeStatus(status, statusMessage);
- finish();
- } else if (mAccount != null) {
- if (mAccount.getPgpId() == 0 || !hasPgp()) {
- xmppConnectionService.changeStatus(mAccount, status, statusMessage, true);
- finish();
- } else {
- xmppConnectionService.changeStatus(mAccount, status, statusMessage, false);
- announcePgp(mAccount, null, onPresenceChanged);
- }
- }
- }
-
- private Presence.Status getStatusFromSpinner() {
- switch (mShowSpinner.getSelectedItemPosition()) {
- case 0:
- return Presence.Status.CHAT;
- case 2:
- return Presence.Status.AWAY;
- case 3:
- return Presence.Status.XA;
- case 4:
- return Presence.Status.DND;
- default:
- return Presence.Status.ONLINE;
- }
- }
-
- private void setStatusInSpinner(Presence.Status status) {
- switch(status) {
- case AWAY:
- mShowSpinner.setSelection(2);
- break;
- case XA:
- mShowSpinner.setSelection(3);
- break;
- case CHAT:
- mShowSpinner.setSelection(0);
- break;
- case DND:
- mShowSpinner.setSelection(4);
- break;
- default:
- mShowSpinner.setSelection(1);
- break;
- }
- }
-
- @Override
- protected void refreshUiReal() {
-
- }
-
- @Override
- void onBackendConnected() {
- mAccount = extractAccount(getIntent());
- if (mAccount != null) {
- setStatusInSpinner(mAccount.getPresenceStatus());
- String message = mAccount.getPresenceStatusMessage();
- if (mStatusMessage.getText().length() == 0 && message != null) {
- mStatusMessage.append(message);
- }
- mTemplates = xmppConnectionService.getPresenceTemplates(mAccount);
- if (this.mPostponedActivityResult != null) {
- this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
- }
- boolean e = noAccountUsesPgp();
- mAllAccounts.setEnabled(e);
- mAllAccounts.setTextColor(e ? getPrimaryTextColor() : getSecondaryTextColor());
- }
- redrawTemplates();
- }
-
- private void redrawTemplates() {
- if (mTemplates == null || mTemplates.size() == 0) {
- mTemplatesView.setVisibility(View.GONE);
- } else {
- mTemplatesView.removeAllViews();
- mTemplatesView.setVisibility(View.VISIBLE);
- LayoutInflater inflater = getLayoutInflater();
- for (PresenceTemplate template : mTemplates) {
- View templateLayout = inflater.inflate(R.layout.presence_template, mTemplatesView, false);
- templateLayout.setTag(template);
- setListItemBackgroundOnView(templateLayout);
- templateLayout.setOnClickListener(this);
- TextView message = (TextView) templateLayout.findViewById(R.id.presence_status_message);
- TextView status = (TextView) templateLayout.findViewById(R.id.status);
- ImageButton button = (ImageButton) templateLayout.findViewById(R.id.delete_button);
- button.setTag(template);
- button.setOnClickListener(this);
- ListItem.Tag tag = UIHelper.getTagForStatus(this, template.getStatus());
- status.setText(tag.getName());
- status.setBackgroundColor(tag.getColor());
- message.setText(template.getStatusMessage());
- mTemplatesView.addView(templateLayout);
- }
- }
- }
-
- @Override
- public void onClick(View v) {
- PresenceTemplate template = (PresenceTemplate) v.getTag();
- if (template == null) {
- return;
- }
- if (v.getId() == R.id.presence_template) {
- setStatusInSpinner(template.getStatus());
- mStatusMessage.getEditableText().clear();
- mStatusMessage.getEditableText().append(template.getStatusMessage());
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- mScrollView.smoothScrollTo(0,0);
- }
- });
- } else if (v.getId() == R.id.delete_button) {
- xmppConnectionService.databaseBackend.deletePresenceTemplate(template);
- mTemplates.remove(template);
- redrawTemplates();
- }
- }
+ //data
+ protected Account mAccount;
+ private List<PresenceTemplate> mTemplates;
+
+ //UI Elements
+ protected ScrollView mScrollView;
+ protected EditText mStatusMessage;
+ protected Spinner mShowSpinner;
+ protected CheckBox mAllAccounts;
+ protected LinearLayout mTemplatesView;
+ private Pair<Integer, Intent> mPostponedActivityResult;
+
+ private Runnable onPresenceChanged = new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ };
+
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_set_presence);
+ mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+ mShowSpinner = (Spinner) findViewById(R.id.presence_show);
+ ArrayAdapter adapter = ArrayAdapter.createFromResource(this,
+ R.array.presence_show_options,
+ R.layout.simple_list_item);
+ mShowSpinner.setAdapter(adapter);
+ mShowSpinner.setSelection(1);
+ mStatusMessage = (EditText) findViewById(R.id.presence_status_message);
+ mAllAccounts = (CheckBox) findViewById(R.id.all_accounts);
+ mTemplatesView = (LinearLayout) findViewById(R.id.templates);
+ final Button changePresence = (Button) findViewById(R.id.change_presence);
+ changePresence.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ executeChangePresence();
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.change_presence, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == R.id.action_account_details) {
+ if (mAccount != null) {
+ switchToAccount(mAccount);
+ }
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (xmppConnectionServiceBound && mAccount != null) {
+ if (requestCode == REQUEST_ANNOUNCE_PGP) {
+ announcePgp(mAccount, null, onPresenceChanged);
+ }
+ this.mPostponedActivityResult = null;
+ } else {
+ this.mPostponedActivityResult = new Pair<>(requestCode, data);
+ }
+ }
+ }
+
+ private void executeChangePresence() {
+ Presence.Status status = getStatusFromSpinner();
+ boolean allAccounts = mAllAccounts.isChecked();
+ String statusMessage = mStatusMessage.getText().toString().trim();
+ if (allAccounts && noAccountUsesPgp()) {
+ xmppConnectionService.changeStatus(status, statusMessage);
+ finish();
+ } else if (mAccount != null) {
+ if (mAccount.getPgpId() == 0 || !hasPgp()) {
+ xmppConnectionService.changeStatus(mAccount, status, statusMessage, true);
+ finish();
+ } else {
+ xmppConnectionService.changeStatus(mAccount, status, statusMessage, false);
+ announcePgp(mAccount, null, onPresenceChanged);
+ }
+ }
+ }
+
+ private Presence.Status getStatusFromSpinner() {
+ switch (mShowSpinner.getSelectedItemPosition()) {
+ case 0:
+ return Presence.Status.CHAT;
+ case 2:
+ return Presence.Status.AWAY;
+ case 3:
+ return Presence.Status.XA;
+ case 4:
+ return Presence.Status.DND;
+ default:
+ return Presence.Status.ONLINE;
+ }
+ }
+
+ private void setStatusInSpinner(Presence.Status status) {
+ switch (status) {
+ case AWAY:
+ mShowSpinner.setSelection(2);
+ break;
+ case XA:
+ mShowSpinner.setSelection(3);
+ break;
+ case CHAT:
+ mShowSpinner.setSelection(0);
+ break;
+ case DND:
+ mShowSpinner.setSelection(4);
+ break;
+ default:
+ mShowSpinner.setSelection(1);
+ break;
+ }
+ }
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+ mAccount = extractAccount(getIntent());
+ if (mAccount != null) {
+ setStatusInSpinner(mAccount.getPresenceStatus());
+ String message = mAccount.getPresenceStatusMessage();
+ if (mStatusMessage.getText().length() == 0 && message != null) {
+ mStatusMessage.append(message);
+ }
+ mTemplates = xmppConnectionService.getPresenceTemplates(mAccount);
+ if (this.mPostponedActivityResult != null) {
+ this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
+ }
+ boolean e = noAccountUsesPgp();
+ mAllAccounts.setEnabled(e);
+ mAllAccounts.setTextColor(e ? getPrimaryTextColor() : getSecondaryTextColor());
+ }
+ redrawTemplates();
+ }
+
+ private void redrawTemplates() {
+ if (mTemplates == null || mTemplates.size() == 0) {
+ mTemplatesView.setVisibility(View.GONE);
+ } else {
+ mTemplatesView.removeAllViews();
+ mTemplatesView.setVisibility(View.VISIBLE);
+ LayoutInflater inflater = getLayoutInflater();
+ for (PresenceTemplate template : mTemplates) {
+ View templateLayout = inflater.inflate(R.layout.presence_template, mTemplatesView, false);
+ templateLayout.setTag(template);
+ setListItemBackgroundOnView(templateLayout);
+ templateLayout.setOnClickListener(this);
+ TextView message = (TextView) templateLayout.findViewById(R.id.presence_status_message);
+ TextView status = (TextView) templateLayout.findViewById(R.id.status);
+ ImageButton button = (ImageButton) templateLayout.findViewById(R.id.delete_button);
+ button.setTag(template);
+ button.setOnClickListener(this);
+ ListItem.Tag tag = UIHelper.getTagForStatus(this, template.getStatus());
+ status.setText(tag.getName());
+ status.setBackgroundColor(tag.getColor());
+ message.setText(template.getStatusMessage());
+ mTemplatesView.addView(templateLayout);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ PresenceTemplate template = (PresenceTemplate) v.getTag();
+ if (template == null) {
+ return;
+ }
+ if (v.getId() == R.id.presence_template) {
+ setStatusInSpinner(template.getStatus());
+ mStatusMessage.getEditableText().clear();
+ mStatusMessage.getEditableText().append(template.getStatusMessage());
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ mScrollView.smoothScrollTo(0, 0);
+ }
+ });
+ } else if (v.getId() == R.id.delete_button) {
+ xmppConnectionService.databaseBackend.deletePresenceTemplate(template);
+ mTemplates.remove(template);
+ redrawTemplates();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java
index 05b14d215..51c41c2ea 100644
--- a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java
@@ -33,269 +33,269 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class SettingsActivity extends XmppActivity implements
- OnSharedPreferenceChangeListener {
+ OnSharedPreferenceChangeListener {
- public static final int REQUEST_WRITE_LOGS = 0xbf8701;
- private SettingsFragment mSettingsFragment;
+ public static final int REQUEST_WRITE_LOGS = 0xbf8701;
+ private SettingsFragment mSettingsFragment;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- FragmentManager fm = getFragmentManager();
- mSettingsFragment = (SettingsFragment) fm.findFragmentById(android.R.id.content);
- if (mSettingsFragment == null || !mSettingsFragment.getClass().equals(SettingsFragment.class)) {
- mSettingsFragment = new SettingsFragment();
- fm.beginTransaction().replace(android.R.id.content, mSettingsFragment).commit();
- }
- }
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ FragmentManager fm = getFragmentManager();
+ mSettingsFragment = (SettingsFragment) fm.findFragmentById(android.R.id.content);
+ if (mSettingsFragment == null || !mSettingsFragment.getClass().equals(SettingsFragment.class)) {
+ mSettingsFragment = new SettingsFragment();
+ fm.beginTransaction().replace(android.R.id.content, mSettingsFragment).commit();
+ }
+ }
- @Override
- void onBackendConnected() {
+ @Override
+ void onBackendConnected() {
- }
+ }
- @Override
- public void onStart() {
- super.onStart();
- PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
- ListPreference resources = (ListPreference) mSettingsFragment.findPreference("resource");
- if (resources != null) {
- ArrayList<CharSequence> entries = new ArrayList<>(Arrays.asList(resources.getEntries()));
- if (!entries.contains(Build.MODEL)) {
- entries.add(0, Build.MODEL);
- resources.setEntries(entries.toArray(new CharSequence[entries.size()]));
- resources.setEntryValues(entries.toArray(new CharSequence[entries.size()]));
- }
- }
+ @Override
+ public void onStart() {
+ super.onStart();
+ PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
+ ListPreference resources = (ListPreference) mSettingsFragment.findPreference("resource");
+ if (resources != null) {
+ ArrayList<CharSequence> entries = new ArrayList<>(Arrays.asList(resources.getEntries()));
+ if (!entries.contains(Build.MODEL)) {
+ entries.add(0, Build.MODEL);
+ resources.setEntries(entries.toArray(new CharSequence[entries.size()]));
+ resources.setEntryValues(entries.toArray(new CharSequence[entries.size()]));
+ }
+ }
- if (Config.FORCE_ORBOT) {
- PreferenceCategory connectionOptions = (PreferenceCategory) mSettingsFragment.findPreference("connection_options");
- PreferenceScreen expert = (PreferenceScreen) mSettingsFragment.findPreference("expert");
- if (connectionOptions != null) {
- expert.removePreference(connectionOptions);
- }
- }
+ if (Config.FORCE_ORBOT) {
+ PreferenceCategory connectionOptions = (PreferenceCategory) mSettingsFragment.findPreference("connection_options");
+ PreferenceScreen expert = (PreferenceScreen) mSettingsFragment.findPreference("expert");
+ if (connectionOptions != null) {
+ expert.removePreference(connectionOptions);
+ }
+ }
- final Preference removeCertsPreference = mSettingsFragment.findPreference("remove_trusted_certificates");
- removeCertsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager();
- final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
- if (aliases.size() == 0) {
- displayToast(getString(R.string.toast_no_trusted_certs));
- return true;
- }
+ final Preference removeCertsPreference = mSettingsFragment.findPreference("remove_trusted_certificates");
+ removeCertsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager();
+ final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
+ if (aliases.size() == 0) {
+ displayToast(getString(R.string.toast_no_trusted_certs));
+ return true;
+ }
final ArrayList selectedItems = new ArrayList<>();
- final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this);
- dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title));
- dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null,
- new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int indexSelected,
- boolean isChecked) {
- if (isChecked) {
- selectedItems.add(indexSelected);
- } else if (selectedItems.contains(indexSelected)) {
- selectedItems.remove(Integer.valueOf(indexSelected));
- }
- if (selectedItems.size() > 0)
- ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
- else {
- ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
- }
- }
- });
+ final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this);
+ dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title));
+ dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null,
+ new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int indexSelected,
+ boolean isChecked) {
+ if (isChecked) {
+ selectedItems.add(indexSelected);
+ } else if (selectedItems.contains(indexSelected)) {
+ selectedItems.remove(Integer.valueOf(indexSelected));
+ }
+ if (selectedItems.size() > 0)
+ ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+ else {
+ ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+ }
+ }
+ });
- dialogBuilder.setPositiveButton(
- getResources().getString(R.string.dialog_manage_certs_positivebutton), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- int count = selectedItems.size();
- if (count > 0) {
- for (int i = 0; i < count; i++) {
- try {
- Integer item = Integer.valueOf(selectedItems.get(i).toString());
- String alias = aliases.get(item);
- mtm.deleteCertificate(alias);
- } catch (KeyStoreException e) {
- e.printStackTrace();
- displayToast("Error: " + e.getLocalizedMessage());
- }
- }
- if (xmppConnectionServiceBound) {
- reconnectAccounts();
- }
- displayToast(getResources().getQuantityString(R.plurals.toast_delete_certificates, count, count));
- }
- }
- });
- dialogBuilder.setNegativeButton(getResources().getString(R.string.dialog_manage_certs_negativebutton), null);
- AlertDialog removeCertsDialog = dialogBuilder.create();
- removeCertsDialog.show();
- removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
- return true;
- }
- });
+ dialogBuilder.setPositiveButton(
+ getResources().getString(R.string.dialog_manage_certs_positivebutton), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int count = selectedItems.size();
+ if (count > 0) {
+ for (int i = 0; i < count; i++) {
+ try {
+ Integer item = Integer.valueOf(selectedItems.get(i).toString());
+ String alias = aliases.get(item);
+ mtm.deleteCertificate(alias);
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ displayToast("Error: " + e.getLocalizedMessage());
+ }
+ }
+ if (xmppConnectionServiceBound) {
+ reconnectAccounts();
+ }
+ displayToast(getResources().getQuantityString(R.plurals.toast_delete_certificates, count, count));
+ }
+ }
+ });
+ dialogBuilder.setNegativeButton(getResources().getString(R.string.dialog_manage_certs_negativebutton), null);
+ AlertDialog removeCertsDialog = dialogBuilder.create();
+ removeCertsDialog.show();
+ removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
+ return true;
+ }
+ });
- final Preference exportLogsPreference = mSettingsFragment.findPreference("export_logs");
- exportLogsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
+ final Preference exportLogsPreference = mSettingsFragment.findPreference("export_logs");
+ exportLogsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
if (hasStoragePermission(REQUEST_WRITE_LOGS)) {
startExport();
}
- return true;
- }
- });
+ return true;
+ }
+ });
final Preference deleteOmemoPreference = mSettingsFragment.findPreference("delete_omemo_identities");
- deleteOmemoPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- deleteOmemoIdentities();
- return true;
- }
- });
- }
+ deleteOmemoPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ deleteOmemoIdentities();
+ return true;
+ }
+ });
+ }
- private void deleteOmemoIdentities() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.pref_delete_omemo_identities);
- final List<CharSequence> accounts = new ArrayList<>();
- for(Account account : xmppConnectionService.getAccounts()) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- accounts.add(account.getJid().toBareJid().toString());
- }
- }
- final boolean[] checkedItems = new boolean[accounts.size()];
- builder.setMultiChoiceItems(accounts.toArray(new CharSequence[accounts.size()]), checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- checkedItems[which] = isChecked;
- final AlertDialog alertDialog = (AlertDialog) dialog;
- for(boolean item : checkedItems) {
- if (item) {
- alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
- return;
- }
- }
- alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
- }
- });
- builder.setNegativeButton(R.string.cancel,null);
- builder.setPositiveButton(R.string.delete_selected_keys, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- for(int i = 0; i < checkedItems.length; ++i) {
- if (checkedItems[i]) {
- try {
- Jid jid = Jid.fromString(accounts.get(i).toString());
- Account account = xmppConnectionService.findAccountByJid(jid);
- if (account != null) {
- account.getAxolotlService().regenerateKeys(true);
- }
- } catch (InvalidJidException e) {
- //
- }
+ private void deleteOmemoIdentities() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.pref_delete_omemo_identities);
+ final List<CharSequence> accounts = new ArrayList<>();
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ accounts.add(account.getJid().toBareJid().toString());
+ }
+ }
+ final boolean[] checkedItems = new boolean[accounts.size()];
+ builder.setMultiChoiceItems(accounts.toArray(new CharSequence[accounts.size()]), checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ checkedItems[which] = isChecked;
+ final AlertDialog alertDialog = (AlertDialog) dialog;
+ for (boolean item : checkedItems) {
+ if (item) {
+ alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+ return;
+ }
+ }
+ alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.delete_selected_keys, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ for (int i = 0; i < checkedItems.length; ++i) {
+ if (checkedItems[i]) {
+ try {
+ Jid jid = Jid.fromString(accounts.get(i).toString());
+ Account account = xmppConnectionService.findAccountByJid(jid);
+ if (account != null) {
+ account.getAxolotlService().regenerateKeys(true);
+ }
+ } catch (InvalidJidException e) {
+ //
+ }
- }
- }
- }
- });
- AlertDialog dialog = builder.create();
- dialog.show();
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
- }
+ }
+ }
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
+ }
- @Override
- public void onStop() {
- super.onStop();
- PreferenceManager.getDefaultSharedPreferences(this)
- .unregisterOnSharedPreferenceChangeListener(this);
- }
+ @Override
+ public void onStop() {
+ super.onStop();
+ PreferenceManager.getDefaultSharedPreferences(this)
+ .unregisterOnSharedPreferenceChangeListener(this);
+ }
- @Override
- public void onSharedPreferenceChanged(SharedPreferences preferences, String name) {
- final List<String> resendPresence = Arrays.asList(
- "confirm_messages",
- "xa_on_silent_mode",
- "away_when_screen_off",
- "allow_message_correction",
- "treat_vibrate_as_silent",
- "manually_change_presence",
- "last_activity");
- if (name.equals("resource")) {
- String resource = preferences.getString("resource", "mobile")
- .toLowerCase(Locale.US);
- if (xmppConnectionServiceBound) {
- for (Account account : xmppConnectionService.getAccounts()) {
- if (account.setResource(resource)) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- connection.resetStreamId();
- }
- xmppConnectionService.reconnectAccountInBackground(account);
- }
- }
- }
- }
- } else if (resendPresence.contains(name)) {
- if (xmppConnectionServiceBound) {
- if (name.equals("away_when_screen_off")
- || name.equals("manually_change_presence")) {
- xmppConnectionService.toggleScreenEventReceiver();
- }
- if (name.equals("manually_change_presence") && !noAccountUsesPgp()) {
- Toast.makeText(this, R.string.republish_pgp_keys, Toast.LENGTH_LONG).show();
- }
- xmppConnectionService.refreshAllPresences();
- }
- } else if (name.equals("dont_trust_system_cas")) {
- xmppConnectionService.updateMemorizingTrustmanager();
- reconnectAccounts();
- } else if (name.equals("use_tor")) {
- reconnectAccounts();
- }
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences preferences, String name) {
+ final List<String> resendPresence = Arrays.asList(
+ "confirm_messages",
+ "xa_on_silent_mode",
+ "away_when_screen_off",
+ "allow_message_correction",
+ "treat_vibrate_as_silent",
+ "manually_change_presence",
+ "last_activity");
+ if (name.equals("resource")) {
+ String resource = preferences.getString("resource", "mobile")
+ .toLowerCase(Locale.US);
+ if (xmppConnectionServiceBound) {
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.setResource(resource)) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.resetStreamId();
+ }
+ xmppConnectionService.reconnectAccountInBackground(account);
+ }
+ }
+ }
+ }
+ } else if (resendPresence.contains(name)) {
+ if (xmppConnectionServiceBound) {
+ if (name.equals("away_when_screen_off")
+ || name.equals("manually_change_presence")) {
+ xmppConnectionService.toggleScreenEventReceiver();
+ }
+ if (name.equals("manually_change_presence") && !noAccountUsesPgp()) {
+ Toast.makeText(this, R.string.republish_pgp_keys, Toast.LENGTH_LONG).show();
+ }
+ xmppConnectionService.refreshAllPresences();
+ }
+ } else if (name.equals("dont_trust_system_cas")) {
+ xmppConnectionService.updateMemorizingTrustmanager();
+ reconnectAccounts();
+ } else if (name.equals("use_tor")) {
+ reconnectAccounts();
+ }
- }
+ }
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- if (grantResults.length > 0)
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- if (requestCode == REQUEST_WRITE_LOGS) {
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ if (grantResults.length > 0)
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (requestCode == REQUEST_WRITE_LOGS) {
startExport();
- }
- } else {
- Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
- }
- }
+ }
+ } else {
+ Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
+ }
+ }
private void startExport() {
startService(new Intent(getApplicationContext(), ExportLogsService.class));
}
private void displayToast(final String msg) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show();
- }
- });
- }
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
- private void reconnectAccounts() {
- for (Account account : xmppConnectionService.getAccounts()) {
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- xmppConnectionService.reconnectAccountInBackground(account);
- }
- }
- }
+ private void reconnectAccounts() {
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ xmppConnectionService.reconnectAccountInBackground(account);
+ }
+ }
+ }
- public void refreshUiReal() {
- //nothing to do. This Activity doesn't implement any listeners
- }
+ public void refreshUiReal() {
+ //nothing to do. This Activity doesn't implement any listeners
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/SettingsFragment.java b/src/main/java/de/pixart/messenger/ui/SettingsFragment.java
index c9e6dc51a..119e79fb7 100644
--- a/src/main/java/de/pixart/messenger/ui/SettingsFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/SettingsFragment.java
@@ -15,51 +15,51 @@ import de.pixart.messenger.R;
public class SettingsFragment extends PreferenceFragment {
- //http://stackoverflow.com/questions/16374820/action-bar-home-button-not-functional-with-nested-preferencescreen/16800527#16800527
- private void initializeActionBar(PreferenceScreen preferenceScreen) {
- final Dialog dialog = preferenceScreen.getDialog();
+ //http://stackoverflow.com/questions/16374820/action-bar-home-button-not-functional-with-nested-preferencescreen/16800527#16800527
+ private void initializeActionBar(PreferenceScreen preferenceScreen) {
+ final Dialog dialog = preferenceScreen.getDialog();
- if (dialog != null) {
- View homeBtn = dialog.findViewById(android.R.id.home);
+ if (dialog != null) {
+ View homeBtn = dialog.findViewById(android.R.id.home);
- if (homeBtn != null) {
- View.OnClickListener dismissDialogClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- dialog.dismiss();
- }
- };
+ if (homeBtn != null) {
+ View.OnClickListener dismissDialogClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dialog.dismiss();
+ }
+ };
- ViewParent homeBtnContainer = homeBtn.getParent();
+ ViewParent homeBtnContainer = homeBtn.getParent();
- if (homeBtnContainer instanceof FrameLayout) {
- ViewGroup containerParent = (ViewGroup) homeBtnContainer.getParent();
- if (containerParent instanceof LinearLayout) {
- ((LinearLayout) containerParent).setOnClickListener(dismissDialogClickListener);
- } else {
- ((FrameLayout) homeBtnContainer).setOnClickListener(dismissDialogClickListener);
- }
- } else {
- homeBtn.setOnClickListener(dismissDialogClickListener);
- }
- }
- }
- }
+ if (homeBtnContainer instanceof FrameLayout) {
+ ViewGroup containerParent = (ViewGroup) homeBtnContainer.getParent();
+ if (containerParent instanceof LinearLayout) {
+ ((LinearLayout) containerParent).setOnClickListener(dismissDialogClickListener);
+ } else {
+ ((FrameLayout) homeBtnContainer).setOnClickListener(dismissDialogClickListener);
+ }
+ } else {
+ homeBtn.setOnClickListener(dismissDialogClickListener);
+ }
+ }
+ }
+ }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- // Load the preferences from an XML resource
- addPreferencesFromResource(R.xml.preferences);
- }
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.preferences);
+ }
- @Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
- super.onPreferenceTreeClick(preferenceScreen, preference);
- if (preference instanceof PreferenceScreen) {
- initializeActionBar((PreferenceScreen) preference);
- }
- return false;
- }
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ super.onPreferenceTreeClick(preferenceScreen, preference);
+ if (preference instanceof PreferenceScreen) {
+ initializeActionBar((PreferenceScreen) preference);
+ }
+ return false;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ShareLocationActivity.java b/src/main/java/de/pixart/messenger/ui/ShareLocationActivity.java
index 2bb9dfd9d..e742b6590 100644
--- a/src/main/java/de/pixart/messenger/ui/ShareLocationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ShareLocationActivity.java
@@ -160,62 +160,62 @@ public class ShareLocationActivity extends Activity implements OnMapReadyCallbac
}
- @Override
- public void onConnectionSuspended(int i) {
+ @Override
+ public void onConnectionSuspended(int i) {
- }
+ }
- @Override
- public void onConnectionFailed(ConnectionResult connectionResult) {
+ @Override
+ public void onConnectionFailed(ConnectionResult connectionResult) {
- }
+ }
- @Override
- public void onLocationChanged(Location location) {
+ @Override
+ public void onLocationChanged(Location location) {
double longitude = location.getLongitude();
double latitude = location.getLatitude();
- if (this.mLastLocation == null) {
- centerOnLocation(new LatLng(location.getLatitude(), location.getLongitude()));
- this.mShareButton.setEnabled(true);
- this.mShareButton.setTextColor(0xde000000);
- this.mShareButton.setText(R.string.share);
- }
- this.mLastLocation = location;
+ if (this.mLastLocation == null) {
+ centerOnLocation(new LatLng(location.getLatitude(), location.getLongitude()));
+ this.mShareButton.setEnabled(true);
+ this.mShareButton.setTextColor(0xde000000);
+ this.mShareButton.setText(R.string.share);
+ }
+ this.mLastLocation = location;
if (latitude != 0 && longitude != 0) {
Double[] lat_long = new Double[]{latitude, longitude};
new ReverseGeocodingTask(getBaseContext()).execute(lat_long);
}
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- private boolean isLocationEnabledKitkat() {
- try {
- int locationMode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE);
- return locationMode != Settings.Secure.LOCATION_MODE_OFF;
- } catch (Settings.SettingNotFoundException e) {
- return false;
- }
- }
-
- @SuppressWarnings("deprecation")
- private boolean isLocationEnabledLegacy() {
- String locationProviders = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
- return !TextUtils.isEmpty(locationProviders);
- }
-
- private boolean isLocationEnabled() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
- return isLocationEnabledKitkat();
- }else{
- return isLocationEnabledLegacy();
- }
- }
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private boolean isLocationEnabledKitkat() {
+ try {
+ int locationMode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE);
+ return locationMode != Settings.Secure.LOCATION_MODE_OFF;
+ } catch (Settings.SettingNotFoundException e) {
+ return false;
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private boolean isLocationEnabledLegacy() {
+ String locationProviders = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
+ return !TextUtils.isEmpty(locationProviders);
+ }
+
+ private boolean isLocationEnabled() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ return isLocationEnabledKitkat();
+ } else {
+ return isLocationEnabledLegacy();
+ }
+ }
private class ReverseGeocodingTask extends AsyncTask<Double, Void, String> {
Context mContext;
- public ReverseGeocodingTask(Context context){
+ public ReverseGeocodingTask(Context context) {
super();
mContext = context;
}
@@ -228,10 +228,10 @@ public class ShareLocationActivity extends Activity implements OnMapReadyCallbac
double longitude = params[1];
List<Address> addresses = null;
- String address="";
+ String address = "";
try {
- addresses = geocoder.getFromLocation(latitude, longitude,1);
+ addresses = geocoder.getFromLocation(latitude, longitude, 1);
} catch (IOException e) {
e.printStackTrace();
}
@@ -244,7 +244,7 @@ public class ShareLocationActivity extends Activity implements OnMapReadyCallbac
strAddress.append(Address.getAddressLine(i)).append("\n");
}
address = strAddress.toString();
- address = address.substring(0, address.length()-1); //trim last \n
+ address = address.substring(0, address.length() - 1); //trim last \n
}
return address;
@@ -256,7 +256,7 @@ public class ShareLocationActivity extends Activity implements OnMapReadyCallbac
// Setting address of the touched Position
mLocationInfo.setVisibility(View.VISIBLE);
mSnackbarLocation.setText(address);
- Log.d(Config.LOGTAG,"Location: Address = "+ address);
+ Log.d(Config.LOGTAG, "Location: Address = " + address);
}
}
}
diff --git a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java
index d9b0695ef..5df34457b 100644
--- a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java
@@ -35,214 +35,214 @@ import static java.lang.String.format;
public class ShareWithActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate {
- private boolean mReturnToPrevious = false;
+ private boolean mReturnToPrevious = false;
private static final String STATE_SHARING_IS_RUNNING = "state_sharing_is_running";
static boolean ContactChosen = false;
static boolean IntentReceived = false;
boolean SharingIsRunning = false;
- @Override
- public void onConversationUpdate() {
- refreshUi();
- }
+ @Override
+ public void onConversationUpdate() {
+ refreshUi();
+ }
- private class Share {
- public List<Uri> uris = new ArrayList<>();
- public boolean image;
+ private class Share {
+ public List<Uri> uris = new ArrayList<>();
+ public boolean image;
public boolean video;
- public String account;
- public String contact;
- public String text;
- public String uuid;
- public boolean multiple = false;
- }
-
- private Share share;
-
- private static final int REQUEST_START_NEW_CONVERSATION = 0x0501;
- private ListView mListView;
- private ConversationAdapter mAdapter;
- private List<Conversation> mConversations = new ArrayList<>();
- private Toast mToast;
- private AtomicInteger attachmentCounter = new AtomicInteger(0);
-
- private UiCallback<Message> attachFileCallback = new UiCallback<Message>() {
-
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void success(final Message message) {
- xmppConnectionService.sendMessage(message);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (attachmentCounter.decrementAndGet() <=0 ) {
- int resId;
- if (share.image && share.multiple) {
- resId = R.string.shared_images_with_x;
- } else if (share.image) {
- resId = R.string.shared_image_with_x;
+ public String account;
+ public String contact;
+ public String text;
+ public String uuid;
+ public boolean multiple = false;
+ }
+
+ private Share share;
+
+ private static final int REQUEST_START_NEW_CONVERSATION = 0x0501;
+ private ListView mListView;
+ private ConversationAdapter mAdapter;
+ private List<Conversation> mConversations = new ArrayList<>();
+ private Toast mToast;
+ private AtomicInteger attachmentCounter = new AtomicInteger(0);
+
+ private UiCallback<Message> attachFileCallback = new UiCallback<Message>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void success(final Message message) {
+ xmppConnectionService.sendMessage(message);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (attachmentCounter.decrementAndGet() <= 0) {
+ int resId;
+ if (share.image && share.multiple) {
+ resId = R.string.shared_images_with_x;
+ } else if (share.image) {
+ resId = R.string.shared_image_with_x;
} else if (share.video) {
resId = R.string.shared_video_with_x;
- } else {
- resId = R.string.shared_file_with_x;
- }
- replaceToast(getString(resId, message.getConversation().getName()));
- if (mReturnToPrevious) {
- finish();
- } else {
- switchToConversation(message.getConversation());
- }
- }
- }
- });
- }
-
- @Override
- public void error(final int errorCode, Message object) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- replaceToast(getString(errorCode));
- if (attachmentCounter.decrementAndGet() <=0 ) {
- finish();
- }
- }
- });
- }
- };
-
- protected void hideToast() {
- if (mToast != null) {
- mToast.cancel();
- }
- }
-
- protected void replaceToast(String msg) {
- hideToast();
- mToast = Toast.makeText(this, msg ,Toast.LENGTH_LONG);
- mToast.show();
- }
-
- protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_START_NEW_CONVERSATION
- && resultCode == RESULT_OK) {
- share.contact = data.getStringExtra("contact");
- share.account = data.getStringExtra(EXTRA_ACCOUNT);
- }
- if (xmppConnectionServiceBound
- && share != null
- && share.contact != null
- && share.account != null) {
- share();
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (getActionBar() != null) {
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
- }
-
- setContentView(R.layout.share_with);
- setTitle(getString(R.string.title_activity_sharewith));
-
- mListView = (ListView) findViewById(R.id.choose_conversation_list);
- mAdapter = new ConversationAdapter(this, this.mConversations);
- mListView.setAdapter(mAdapter);
- mListView.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
- share(mConversations.get(position));
- }
- });
+ } else {
+ resId = R.string.shared_file_with_x;
+ }
+ replaceToast(getString(resId, message.getConversation().getName()));
+ if (mReturnToPrevious) {
+ finish();
+ } else {
+ switchToConversation(message.getConversation());
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void error(final int errorCode, Message object) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ replaceToast(getString(errorCode));
+ if (attachmentCounter.decrementAndGet() <= 0) {
+ finish();
+ }
+ }
+ });
+ }
+ };
+
+ protected void hideToast() {
+ if (mToast != null) {
+ mToast.cancel();
+ }
+ }
+
+ protected void replaceToast(String msg) {
+ hideToast();
+ mToast = Toast.makeText(this, msg, Toast.LENGTH_LONG);
+ mToast.show();
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_START_NEW_CONVERSATION
+ && resultCode == RESULT_OK) {
+ share.contact = data.getStringExtra("contact");
+ share.account = data.getStringExtra(EXTRA_ACCOUNT);
+ }
+ if (xmppConnectionServiceBound
+ && share != null
+ && share.contact != null
+ && share.account != null) {
+ share();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getActionBar() != null) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setHomeButtonEnabled(false);
+ }
+
+ setContentView(R.layout.share_with);
+ setTitle(getString(R.string.title_activity_sharewith));
+
+ mListView = (ListView) findViewById(R.id.choose_conversation_list);
+ mAdapter = new ConversationAdapter(this, this.mConversations);
+ mListView.setAdapter(mAdapter);
+ mListView.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
+ share(mConversations.get(position));
+ }
+ });
if (savedInstanceState != null) {
SharingIsRunning = savedInstanceState.getBoolean(STATE_SHARING_IS_RUNNING, false);
}
- if (!SharingIsRunning) {
+ if (!SharingIsRunning) {
Log.d(Config.LOGTAG, "ShareWithActivity onCreate: state restored");
this.share = new Share();
} else {
Log.d(Config.LOGTAG, "ShareWithActivity onCreate: shring running, finish()");
this.finish();
}
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.share_with, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_add:
- final Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class);
- startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION);
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public void onStart() {
- super.onStart();
- Intent intent = getIntent();
- if (intent == null) {
- return;
- } else {
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.share_with, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_add:
+ final Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class);
+ startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Intent intent = getIntent();
+ if (intent == null) {
+ return;
+ } else {
IntentReceived = true;
}
Log.d(Config.LOGTAG, "ShareWithActivity onStart() getIntent " + intent.toString());
- this.mReturnToPrevious = getPreferences().getBoolean("return_to_previous", false);
- final String type = intent.getType();
- final String action = intent.getAction();
- Log.d(Config.LOGTAG, "action: "+action+ ", type:"+type);
- share.uuid = intent.getStringExtra("uuid");
- if (Intent.ACTION_SEND.equals(action)) {
+ this.mReturnToPrevious = getPreferences().getBoolean("return_to_previous", false);
+ final String type = intent.getType();
+ final String action = intent.getAction();
+ Log.d(Config.LOGTAG, "action: " + action + ", type:" + type);
+ share.uuid = intent.getStringExtra("uuid");
+ if (Intent.ACTION_SEND.equals(action)) {
final String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
- final String text = intent.getStringExtra(Intent.EXTRA_TEXT);
- final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
+ final String text = intent.getStringExtra(Intent.EXTRA_TEXT);
+ final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
Log.d(Config.LOGTAG, "ShareWithActivity onStart() Uri: " + uri);
- if (type != null && uri != null && (text == null || !type.equals("text/plain"))) {
- this.share.uris.clear();
- this.share.uris.add(uri);
- this.share.image = type.startsWith("image/") || isImage(uri);
+ if (type != null && uri != null && (text == null || !type.equals("text/plain"))) {
+ this.share.uris.clear();
+ this.share.uris.add(uri);
+ this.share.image = type.startsWith("image/") || isImage(uri);
this.share.video = type.startsWith("video/") || isVideo(uri);
- } else {
+ } else {
if (subject != null) {
this.share.text = format("[%s]%n%s", subject, text);
} else {
this.share.text = text;
}
- }
- } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
- this.share.image = type != null && type.startsWith("image/");
- if (!this.share.image) {
- return;
- }
- this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
- }
- if (xmppConnectionServiceBound) {
- if (share.uuid != null) {
- share();
- } else {
- xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0);
- }
- }
-
- }
+ }
+ } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
+ this.share.image = type != null && type.startsWith("image/");
+ if (!this.share.image) {
+ return;
+ }
+ this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+ }
+ if (xmppConnectionServiceBound) {
+ if (share.uuid != null) {
+ share();
+ } else {
+ xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0);
+ }
+ }
+
+ }
@Override
public void onSaveInstanceState(final Bundle savedInstanceState) {
@@ -256,14 +256,14 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
super.onSaveInstanceState(savedInstanceState);
}
- protected boolean isImage(Uri uri) {
- try {
- String guess = URLConnection.guessContentTypeFromName(uri.toString());
- return (guess != null && guess.startsWith("image/"));
- } catch (final StringIndexOutOfBoundsException ignored) {
- return false;
- }
- }
+ protected boolean isImage(Uri uri) {
+ try {
+ String guess = URLConnection.guessContentTypeFromName(uri.toString());
+ return (guess != null && guess.startsWith("image/"));
+ } catch (final StringIndexOutOfBoundsException ignored) {
+ return false;
+ }
+ }
protected boolean isVideo(Uri uri) {
try {
@@ -274,65 +274,65 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
}
}
- @Override
- void onBackendConnected() {
- if (xmppConnectionServiceBound && share != null
- && ((share.contact != null && share.account != null) || share.uuid != null)) {
- share();
- return;
- }
- refreshUiReal();
- }
-
- private void share() {
- final Conversation conversation;
- if (share.uuid != null) {
- conversation = xmppConnectionService.findConversationByUuid(share.uuid);
- if (conversation == null) {
- return;
- }
- }else{
- Account account;
- try {
- account = xmppConnectionService.findAccountByJid(Jid.fromString(share.account));
- } catch (final InvalidJidException e) {
- account = null;
- }
- if (account == null) {
- return;
- }
-
- try {
- conversation = xmppConnectionService
- .findOrCreateConversation(account, Jid.fromString(share.contact), false);
- } catch (final InvalidJidException e) {
- return;
- }
- }
+ @Override
+ void onBackendConnected() {
+ if (xmppConnectionServiceBound && share != null
+ && ((share.contact != null && share.account != null) || share.uuid != null)) {
+ share();
+ return;
+ }
+ refreshUiReal();
+ }
+
+ private void share() {
+ final Conversation conversation;
+ if (share.uuid != null) {
+ conversation = xmppConnectionService.findConversationByUuid(share.uuid);
+ if (conversation == null) {
+ return;
+ }
+ } else {
+ Account account;
+ try {
+ account = xmppConnectionService.findAccountByJid(Jid.fromString(share.account));
+ } catch (final InvalidJidException e) {
+ account = null;
+ }
+ if (account == null) {
+ return;
+ }
+
+ try {
+ conversation = xmppConnectionService
+ .findOrCreateConversation(account, Jid.fromString(share.contact), false);
+ } catch (final InvalidJidException e) {
+ return;
+ }
+ }
ContactChosen = true;
- share(conversation);
- }
-
- private void share(final Conversation conversation) {
- final Account account = conversation.getAccount();
- final XmppConnection connection = account.getXmppConnection();
- final long max = connection == null ? -1 : connection.getFeatures().getMaxHttpUploadSize();
- mListView.setEnabled(false);
- if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP && !hasPgp()) {
- if (share.uuid == null) {
- showInstallPgpDialog();
- } else {
- Toast.makeText(this,R.string.openkeychain_not_installed,Toast.LENGTH_SHORT).show();
- finish();
- }
- return;
- }
- if (share.uris.size() != 0) {
- OnPresenceSelected callback = new OnPresenceSelected() {
- @Override
- public void onPresenceSelected() {
- attachmentCounter.set(share.uris.size());
- if (share.image) {
+ share(conversation);
+ }
+
+ private void share(final Conversation conversation) {
+ final Account account = conversation.getAccount();
+ final XmppConnection connection = account.getXmppConnection();
+ final long max = connection == null ? -1 : connection.getFeatures().getMaxHttpUploadSize();
+ mListView.setEnabled(false);
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP && !hasPgp()) {
+ if (share.uuid == null) {
+ showInstallPgpDialog();
+ } else {
+ Toast.makeText(this, R.string.openkeychain_not_installed, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ return;
+ }
+ if (share.uris.size() != 0) {
+ OnPresenceSelected callback = new OnPresenceSelected() {
+ @Override
+ public void onPresenceSelected() {
+ attachmentCounter.set(share.uris.size());
+ if (share.image) {
Log.d(Config.LOGTAG, "ShareWithActivity share() image " + share.uris.size() + " uri(s) " + share.uris.toString());
share.multiple = share.uris.size() > 1;
replaceToast(getString(share.multiple ? R.string.preparing_images : R.string.preparing_image));
@@ -347,49 +347,49 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
ShareWithActivity.this.xmppConnectionService
.attachVideoToConversation(conversation, share.uris.get(0),
attachFileCallback);
- } else {
+ } else {
Log.d(Config.LOGTAG, "ShareWithActivity share() file " + share.uris.size() + " uri(s) " + share.uris.toString());
- replaceToast(getString(R.string.preparing_file));
- ShareWithActivity.this.xmppConnectionService
- .attachFileToConversation(conversation, share.uris.get(0),
- attachFileCallback);
- }
- }
- };
- if (account.httpUploadAvailable()
- && ((share.image && !neverCompressPictures())
- || share.video
- || conversation.getMode() == Conversation.MODE_MULTI
- || FileBackend.allFilesUnderSize(this, share.uris, max))
- && conversation.getNextEncryption() != Message.ENCRYPTION_OTR) {
- callback.onPresenceSelected();
- } else {
- selectPresence(conversation, callback);
- }
- } else {
- if (mReturnToPrevious && this.share.text != null && !this.share.text.isEmpty() ) {
- final OnPresenceSelected callback = new OnPresenceSelected() {
- @Override
- public void onPresenceSelected() {
- Message message = new Message(conversation,share.text, conversation.getNextEncryption());
- if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
- message.setCounterpart(conversation.getNextCounterpart());
- }
- xmppConnectionService.sendMessage(message);
- replaceToast(getString(R.string.shared_text_with_x, conversation.getName()));
- finish();
- }
- };
- if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
- selectPresence(conversation, callback);
- } else {
- callback.onPresenceSelected();
- }
- } else {
+ replaceToast(getString(R.string.preparing_file));
+ ShareWithActivity.this.xmppConnectionService
+ .attachFileToConversation(conversation, share.uris.get(0),
+ attachFileCallback);
+ }
+ }
+ };
+ if (account.httpUploadAvailable()
+ && ((share.image && !neverCompressPictures())
+ || share.video
+ || conversation.getMode() == Conversation.MODE_MULTI
+ || FileBackend.allFilesUnderSize(this, share.uris, max))
+ && conversation.getNextEncryption() != Message.ENCRYPTION_OTR) {
+ callback.onPresenceSelected();
+ } else {
+ selectPresence(conversation, callback);
+ }
+ } else {
+ if (mReturnToPrevious && this.share.text != null && !this.share.text.isEmpty()) {
final OnPresenceSelected callback = new OnPresenceSelected() {
@Override
public void onPresenceSelected() {
- Message message = new Message(conversation,share.text, conversation.getNextEncryption());
+ Message message = new Message(conversation, share.text, conversation.getNextEncryption());
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
+ message.setCounterpart(conversation.getNextCounterpart());
+ }
+ xmppConnectionService.sendMessage(message);
+ replaceToast(getString(R.string.shared_text_with_x, conversation.getName()));
+ finish();
+ }
+ };
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
+ selectPresence(conversation, callback);
+ } else {
+ callback.onPresenceSelected();
+ }
+ } else {
+ final OnPresenceSelected callback = new OnPresenceSelected() {
+ @Override
+ public void onPresenceSelected() {
+ Message message = new Message(conversation, share.text, conversation.getNextEncryption());
if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
message.setCounterpart(conversation.getNextCounterpart());
}
@@ -403,22 +403,22 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
} else {
callback.onPresenceSelected();
}
- }
- }
-
- }
-
- public void refreshUiReal() {
- xmppConnectionService.populateWithOrderedConversations(mConversations, this.share != null && this.share.uris.size() == 0);
- mAdapter.notifyDataSetChanged();
- }
-
- @Override
- public void onBackPressed() {
- if (attachmentCounter.get() >= 1) {
- replaceToast(getString(R.string.sharing_files_please_wait));
- } else {
- super.onBackPressed();
- }
- }
+ }
+ }
+
+ }
+
+ public void refreshUiReal() {
+ xmppConnectionService.populateWithOrderedConversations(mConversations, this.share != null && this.share.uris.size() == 0);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (attachmentCounter.get() >= 1) {
+ replaceToast(getString(R.string.sharing_files_please_wait));
+ } else {
+ super.onBackPressed();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java b/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java
index ffa9018ec..faddebb86 100644
--- a/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ShowFullscreenMessageActivity.java
@@ -162,7 +162,7 @@ public class ShowFullscreenMessageActivity extends Activity {
}
private void DisplayVideo(Uri uri) {
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(uri.getPath());
height = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
width = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
@@ -217,7 +217,7 @@ public class ShowFullscreenMessageActivity extends Activity {
}
@Override
- public void onStop () {
+ public void onStop() {
mVideo.reset();
WindowManager.LayoutParams layout = getWindow().getAttributes();
layout.screenBrightness = -1;
diff --git a/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java b/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java
index 40ff08d80..bb950a21e 100644
--- a/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ShowLocationActivity.java
@@ -35,9 +35,9 @@ import de.pixart.messenger.R;
public class ShowLocationActivity extends Activity implements OnMapReadyCallback {
- private GoogleMap mGoogleMap;
- private LatLng mLocation;
- private String mLocationName;
+ private GoogleMap mGoogleMap;
+ private LatLng mLocation;
+ private String mLocationName;
private MarkerOptions options;
private Marker marker;
@@ -66,26 +66,26 @@ public class ShowLocationActivity extends Activity implements OnMapReadyCallback
}
}
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
if (getActionBar() != null) {
getActionBar().setHomeButtonEnabled(true);
getActionBar().setDisplayHomeAsUpEnabled(true);
}
- setContentView(R.layout.activity_show_locaction);
- MapFragment fragment = (MapFragment) getFragmentManager().findFragmentById(R.id.map_fragment);
- fragment.getMapAsync(this);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- finish();
- return true;
+ setContentView(R.layout.activity_show_locaction);
+ MapFragment fragment = (MapFragment) getFragmentManager().findFragmentById(R.id.map_fragment);
+ fragment.getMapAsync(this);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
case R.id.action_navigate:
double longitude = mLocation.longitude;
double latitude = mLocation.latitude;
@@ -96,9 +96,9 @@ public class ShowLocationActivity extends Activity implements OnMapReadyCallback
Toast.makeText(this, R.string.no_application_found_to_display_location, Toast.LENGTH_SHORT).show();
}
return true;
- }
- return super.onOptionsItemSelected(item);
- }
+ }
+ return super.onOptionsItemSelected(item);
+ }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
@@ -107,31 +107,31 @@ public class ShowLocationActivity extends Activity implements OnMapReadyCallback
return true;
}
- @Override
- protected void onResume() {
- super.onResume();
- Intent intent = getIntent();
-
- this.mLocationName = intent != null ? intent.getStringExtra("name") : null;
-
- if (intent != null && intent.hasExtra("longitude") && intent.hasExtra("latitude")) {
- double longitude = intent.getDoubleExtra("longitude",0);
- double latitude = intent.getDoubleExtra("latitude",0);
- this.mLocation = new LatLng(latitude,longitude);
- if (this.mGoogleMap != null) {
- markAndCenterOnLocation(this.mLocation, this.mLocationName);
- }
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- @Override
- public void onMapReady(GoogleMap googleMap) {
- this.mGoogleMap = googleMap;
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Intent intent = getIntent();
+
+ this.mLocationName = intent != null ? intent.getStringExtra("name") : null;
+
+ if (intent != null && intent.hasExtra("longitude") && intent.hasExtra("latitude")) {
+ double longitude = intent.getDoubleExtra("longitude", 0);
+ double latitude = intent.getDoubleExtra("latitude", 0);
+ this.mLocation = new LatLng(latitude, longitude);
+ if (this.mGoogleMap != null) {
+ markAndCenterOnLocation(this.mLocation, this.mLocationName);
+ }
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public void onMapReady(GoogleMap googleMap) {
+ this.mGoogleMap = googleMap;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
@@ -142,10 +142,10 @@ public class ShowLocationActivity extends Activity implements OnMapReadyCallback
this.mGoogleMap.setBuildingsEnabled(true);
this.mGoogleMap.setMyLocationEnabled(true);
}
- if (this.mLocation != null) {
- this.markAndCenterOnLocation(this.mLocation,this.mLocationName);
- }
- }
+ if (this.mLocation != null) {
+ this.markAndCenterOnLocation(this.mLocation, this.mLocationName);
+ }
+ }
private static String getAddress(Context context, LatLng location) {
double longitude = location.longitude;
@@ -162,7 +162,7 @@ public class ShowLocationActivity extends Activity implements OnMapReadyCallback
strAddress.append(Address.getAddressLine(i)).append("\n");
}
address = strAddress.toString();
- address = address.substring(0, address.length()-1); //trim last \n
+ address = address.substring(0, address.length() - 1); //trim last \n
}
} catch (Exception e) {
diff --git a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
index cd64149cd..46beddcde 100644
--- a/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/StartConversationActivity.java
@@ -843,7 +843,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
private boolean handleJid(Invite invite) {
Account account = xmppConnectionService.findAccountByJid(invite.getJid());
if (account != null && invite.hasFingerprints()) {
- if (xmppConnectionService.verifyFingerprints(account,invite.getFingerprints())) {
+ if (xmppConnectionService.verifyFingerprints(account, invite.getFingerprints())) {
switchToAccount(account);
finish();
return true;
@@ -865,7 +865,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
} else if (contacts.size() == 1) {
Contact contact = contacts.get(0);
if (invite.hasFingerprints()) {
- xmppConnectionService.verifyFingerprints(contact,invite.getFingerprints());
+ xmppConnectionService.verifyFingerprints(contact, invite.getFingerprints());
}
switchToConversation(contact);
return true;
diff --git a/src/main/java/de/pixart/messenger/ui/StartUI.java b/src/main/java/de/pixart/messenger/ui/StartUI.java
index 5c9e4c324..18a5d0f0d 100644
--- a/src/main/java/de/pixart/messenger/ui/StartUI.java
+++ b/src/main/java/de/pixart/messenger/ui/StartUI.java
@@ -43,8 +43,8 @@ public class StartUI extends AppCompatActivity
long FirstStartTime = FirstStart.getLong("FirstStart", 0);
if (EasyPermissions.hasPermissions(this, perms)) {
// Already have permission, start ConversationsActivity
- Log.d(Config.LOGTAG, "All permissions granted, starting "+getString(R.string.app_name) + "(" +FirstStartTime + ")");
- Intent intent = new Intent (this, ConversationActivity.class);
+ Log.d(Config.LOGTAG, "All permissions granted, starting " + getString(R.string.app_name) + "(" + FirstStartTime + ")");
+ Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra("FirstStart", FirstStartTime);
startActivity(intent);
finish();
diff --git a/src/main/java/de/pixart/messenger/ui/TimePreference.java b/src/main/java/de/pixart/messenger/ui/TimePreference.java
index 1c8c8c0f1..731a3bb50 100644
--- a/src/main/java/de/pixart/messenger/ui/TimePreference.java
+++ b/src/main/java/de/pixart/messenger/ui/TimePreference.java
@@ -13,93 +13,93 @@ import java.util.Calendar;
import java.util.Date;
public class TimePreference extends DialogPreference implements Preference.OnPreferenceChangeListener {
- private TimePicker picker = null;
- public final static long DEFAULT_VALUE = 0;
-
- public TimePreference(final Context context, final AttributeSet attrs) {
- super(context, attrs, 0);
- this.setOnPreferenceChangeListener(this);
- }
-
- protected void setTime(final long time) {
- persistLong(time);
- notifyDependencyChange(shouldDisableDependents());
- notifyChanged();
- }
-
- protected void updateSummary(final long time) {
- final DateFormat dateFormat = android.text.format.DateFormat.getTimeFormat(getContext());
- final Date date = new Date(time);
- setSummary(dateFormat.format(date.getTime()));
- }
-
- @Override
- protected View onCreateDialogView() {
- picker = new TimePicker(getContext());
- picker.setIs24HourView(android.text.format.DateFormat.is24HourFormat(getContext()));
- return picker;
- }
-
- protected Calendar getPersistedTime() {
- final Calendar c = Calendar.getInstance();
- c.setTimeInMillis(getPersistedLong(DEFAULT_VALUE));
-
- return c;
- }
-
- @SuppressWarnings("NullableProblems")
- @Override
- protected void onBindDialogView(final View v) {
- super.onBindDialogView(v);
- final Calendar c = getPersistedTime();
-
- picker.setCurrentHour(c.get(Calendar.HOUR_OF_DAY));
- picker.setCurrentMinute(c.get(Calendar.MINUTE));
- }
-
- @Override
- protected void onDialogClosed(final boolean positiveResult) {
- super.onDialogClosed(positiveResult);
-
- if (positiveResult) {
- final Calendar c = Calendar.getInstance();
- c.set(Calendar.MINUTE, picker.getCurrentMinute());
- c.set(Calendar.HOUR_OF_DAY, picker.getCurrentHour());
-
-
- if (!callChangeListener(c.getTimeInMillis())) {
- return;
- }
-
- setTime(c.getTimeInMillis());
- }
- }
-
- @Override
- protected Object onGetDefaultValue(final TypedArray a, final int index) {
- return a.getInteger(index, 0);
- }
-
- @Override
- protected void onSetInitialValue(final boolean restorePersistedValue, final Object defaultValue) {
- long time;
- if (defaultValue == null) {
- time = restorePersistedValue ? getPersistedLong(DEFAULT_VALUE) : DEFAULT_VALUE;
- } else if (defaultValue instanceof Long) {
- time = restorePersistedValue ? getPersistedLong((Long) defaultValue) : (Long) defaultValue;
- } else if (defaultValue instanceof Calendar) {
- time = restorePersistedValue ? getPersistedLong(((Calendar)defaultValue).getTimeInMillis()) : ((Calendar)defaultValue).getTimeInMillis();
- } else {
- time = restorePersistedValue ? getPersistedLong(DEFAULT_VALUE) : DEFAULT_VALUE;
- }
-
- setTime(time);
- updateSummary(time);
- }
-
- @Override
- public boolean onPreferenceChange(final Preference preference, final Object newValue) {
- ((TimePreference) preference).updateSummary((Long)newValue);
- return true;
- }
+ private TimePicker picker = null;
+ public final static long DEFAULT_VALUE = 0;
+
+ public TimePreference(final Context context, final AttributeSet attrs) {
+ super(context, attrs, 0);
+ this.setOnPreferenceChangeListener(this);
+ }
+
+ protected void setTime(final long time) {
+ persistLong(time);
+ notifyDependencyChange(shouldDisableDependents());
+ notifyChanged();
+ }
+
+ protected void updateSummary(final long time) {
+ final DateFormat dateFormat = android.text.format.DateFormat.getTimeFormat(getContext());
+ final Date date = new Date(time);
+ setSummary(dateFormat.format(date.getTime()));
+ }
+
+ @Override
+ protected View onCreateDialogView() {
+ picker = new TimePicker(getContext());
+ picker.setIs24HourView(android.text.format.DateFormat.is24HourFormat(getContext()));
+ return picker;
+ }
+
+ protected Calendar getPersistedTime() {
+ final Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(getPersistedLong(DEFAULT_VALUE));
+
+ return c;
+ }
+
+ @SuppressWarnings("NullableProblems")
+ @Override
+ protected void onBindDialogView(final View v) {
+ super.onBindDialogView(v);
+ final Calendar c = getPersistedTime();
+
+ picker.setCurrentHour(c.get(Calendar.HOUR_OF_DAY));
+ picker.setCurrentMinute(c.get(Calendar.MINUTE));
+ }
+
+ @Override
+ protected void onDialogClosed(final boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+
+ if (positiveResult) {
+ final Calendar c = Calendar.getInstance();
+ c.set(Calendar.MINUTE, picker.getCurrentMinute());
+ c.set(Calendar.HOUR_OF_DAY, picker.getCurrentHour());
+
+
+ if (!callChangeListener(c.getTimeInMillis())) {
+ return;
+ }
+
+ setTime(c.getTimeInMillis());
+ }
+ }
+
+ @Override
+ protected Object onGetDefaultValue(final TypedArray a, final int index) {
+ return a.getInteger(index, 0);
+ }
+
+ @Override
+ protected void onSetInitialValue(final boolean restorePersistedValue, final Object defaultValue) {
+ long time;
+ if (defaultValue == null) {
+ time = restorePersistedValue ? getPersistedLong(DEFAULT_VALUE) : DEFAULT_VALUE;
+ } else if (defaultValue instanceof Long) {
+ time = restorePersistedValue ? getPersistedLong((Long) defaultValue) : (Long) defaultValue;
+ } else if (defaultValue instanceof Calendar) {
+ time = restorePersistedValue ? getPersistedLong(((Calendar) defaultValue).getTimeInMillis()) : ((Calendar) defaultValue).getTimeInMillis();
+ } else {
+ time = restorePersistedValue ? getPersistedLong(DEFAULT_VALUE) : DEFAULT_VALUE;
+ }
+
+ setTime(time);
+ updateSummary(time);
+ }
+
+ @Override
+ public boolean onPreferenceChange(final Preference preference, final Object newValue) {
+ ((TimePreference) preference).updateSummary((Long) newValue);
+ return true;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java b/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
index 2f3951c3a..893317970 100644
--- a/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
@@ -30,307 +30,307 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdated {
- private List<Jid> contactJids;
-
- private Account mAccount;
- private Conversation mConversation;
- private TextView keyErrorMessage;
- private LinearLayout keyErrorMessageCard;
- private TextView ownKeysTitle;
- private LinearLayout ownKeys;
- private LinearLayout ownKeysCard;
- private LinearLayout foreignKeys;
- private Button mSaveButton;
- private Button mCancelButton;
-
- private AxolotlService.FetchStatus lastFetchReport = AxolotlService.FetchStatus.SUCCESS;
-
- private final Map<String, Boolean> ownKeysToTrust = new HashMap<>();
- private final Map<Jid,Map<String, Boolean>> foreignKeysToTrust = new HashMap<>();
-
- private final OnClickListener mSaveButtonListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- commitTrusts();
- finishOk();
- }
- };
-
- private final OnClickListener mCancelButtonListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- setResult(RESULT_CANCELED);
- finish();
- }
- };
-
- @Override
- protected void refreshUiReal() {
- invalidateOptionsMenu();
- populateView();
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_trust_keys);
- this.contactJids = new ArrayList<>();
- for(String jid : getIntent().getStringArrayExtra("contacts")) {
- try {
- this.contactJids.add(Jid.fromString(jid));
- } catch (InvalidJidException e) {
- e.printStackTrace();
- }
- }
-
- keyErrorMessageCard = (LinearLayout) findViewById(R.id.key_error_message_card);
- keyErrorMessage = (TextView) findViewById(R.id.key_error_message);
- ownKeysTitle = (TextView) findViewById(R.id.own_keys_title);
- ownKeys = (LinearLayout) findViewById(R.id.own_keys_details);
- ownKeysCard = (LinearLayout) findViewById(R.id.own_keys_card);
- foreignKeys = (LinearLayout) findViewById(R.id.foreign_keys);
- mCancelButton = (Button) findViewById(R.id.cancel_button);
- mCancelButton.setOnClickListener(mCancelButtonListener);
- mSaveButton = (Button) findViewById(R.id.save_button);
- mSaveButton.setOnClickListener(mSaveButtonListener);
-
-
- if (getActionBar() != null) {
- getActionBar().setHomeButtonEnabled(true);
- getActionBar().setDisplayHomeAsUpEnabled(true);
- }
- }
-
- private void populateView() {
- setTitle(getString(R.string.trust_omemo_fingerprints));
- ownKeys.removeAllViews();
- foreignKeys.removeAllViews();
- boolean hasOwnKeys = false;
- boolean hasForeignKeys = false;
- for(final String fingerprint : ownKeysToTrust.keySet()) {
- hasOwnKeys = true;
- addFingerprintRowWithListeners(ownKeys, mAccount, fingerprint, false,
- FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)), false, false,
- new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- ownKeysToTrust.put(fingerprint, isChecked);
- // own fingerprints have no impact on locked status.
- }
- }
- );
- }
-
- synchronized (this.foreignKeysToTrust) {
- for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
- hasForeignKeys = true;
- final LinearLayout layout = (LinearLayout) getLayoutInflater().inflate(R.layout.keys_card, foreignKeys, false);
- final Jid jid = entry.getKey();
- final TextView header = (TextView) layout.findViewById(R.id.foreign_keys_title);
- final LinearLayout keysContainer = (LinearLayout) layout.findViewById(R.id.foreign_keys_details);
- final TextView informNoKeys = (TextView) layout.findViewById(R.id.no_keys_to_accept);
- header.setText(jid.toString());
- final Map<String, Boolean> fingerprints = entry.getValue();
- for (final String fingerprint : fingerprints.keySet()) {
- addFingerprintRowWithListeners(keysContainer, mAccount, fingerprint, false,
- FingerprintStatus.createActive(fingerprints.get(fingerprint)), false, false,
- new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- fingerprints.put(fingerprint, isChecked);
- lockOrUnlockAsNeeded();
- }
- }
- );
- }
- if (fingerprints.size() == 0) {
- informNoKeys.setVisibility(View.VISIBLE);
- informNoKeys.setText(getString(R.string.no_keys_just_confirm,mAccount.getRoster().getContact(jid).getDisplayName()));
- } else {
- informNoKeys.setVisibility(View.GONE);
- }
- foreignKeys.addView(layout);
- }
- }
-
- ownKeysTitle.setText(mAccount.getJid().toBareJid().toString());
- ownKeysCard.setVisibility(hasOwnKeys ? View.VISIBLE : View.GONE);
- foreignKeys.setVisibility(hasForeignKeys ? View.VISIBLE : View.GONE);
- if(hasPendingKeyFetches()) {
- setFetching();
- lock();
- } else {
- if (!hasForeignKeys && hasNoOtherTrustedKeys()) {
- keyErrorMessageCard.setVisibility(View.VISIBLE);
- if (lastFetchReport == AxolotlService.FetchStatus.ERROR
- || mAccount.getAxolotlService().fetchMapHasErrors(contactJids)) {
- keyErrorMessage.setText(R.string.error_no_keys_to_trust_server_error);
- } else {
- keyErrorMessage.setText(R.string.error_no_keys_to_trust);
- }
- ownKeys.removeAllViews();
- ownKeysCard.setVisibility(View.GONE);
- foreignKeys.removeAllViews();
- foreignKeys.setVisibility(View.GONE);
- }
- lockOrUnlockAsNeeded();
- setDone();
- }
- }
-
- private boolean reloadFingerprints() {
- List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
- ownKeysToTrust.clear();
- AxolotlService service = this.mAccount.getAxolotlService();
- Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided());
- for(final IdentityKey identityKey : ownKeysSet) {
- if(!ownKeysToTrust.containsKey(identityKey)) {
- ownKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
- }
- }
- synchronized (this.foreignKeysToTrust) {
- foreignKeysToTrust.clear();
- for (Jid jid : contactJids) {
- Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), jid);
- if (hasNoOtherTrustedKeys(jid) && ownKeysSet.size() == 0) {
- foreignKeysSet.addAll(service.getKeysWithTrust(FingerprintStatus.createActive(false), jid));
- }
- Map<String, Boolean> foreignFingerprints = new HashMap<>();
- for (final IdentityKey identityKey : foreignKeysSet) {
- if (!foreignFingerprints.containsKey(identityKey)) {
- foreignFingerprints.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
- }
- }
- if (foreignFingerprints.size() > 0 || !acceptedTargets.contains(jid)) {
- foreignKeysToTrust.put(jid, foreignFingerprints);
- }
- }
- }
- return ownKeysSet.size() + foreignKeysToTrust.size() > 0;
- }
-
- public void onBackendConnected() {
- Intent intent = getIntent();
- this.mAccount = extractAccount(intent);
- if (this.mAccount != null && intent != null) {
- String uuid = intent.getStringExtra("conversation");
- this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
- reloadFingerprints();
- populateView();
- }
- }
-
- private boolean hasNoOtherTrustedKeys() {
- return mAccount == null || mAccount.getAxolotlService().anyTargetHasNoTrustedKeys(contactJids);
- }
-
- private boolean hasNoOtherTrustedKeys(Jid contact) {
- return mAccount == null || mAccount.getAxolotlService().getNumTrustedKeys(contact) == 0;
- }
-
- private boolean hasPendingKeyFetches() {
- return mAccount != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount, contactJids);
- }
-
-
- @Override
- public void onKeyStatusUpdated(final AxolotlService.FetchStatus report) {
- if (report != null) {
- lastFetchReport = report;
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- switch (report) {
- case ERROR:
- Toast.makeText(TrustKeysActivity.this,R.string.error_fetching_omemo_key,Toast.LENGTH_SHORT).show();
- break;
- case SUCCESS_VERIFIED:
- Toast.makeText(TrustKeysActivity.this,
- Config.X509_VERIFICATION ? R.string.verified_omemo_key_with_certificate : R.string.all_omemo_keys_have_been_verified,
- Toast.LENGTH_LONG).show();
- break;
- }
- }
- });
-
- }
- boolean keysToTrust = reloadFingerprints();
- if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) {
- refreshUi();
- } else {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- finishOk();
- }
- });
-
- }
- }
-
- private void finishOk() {
- Intent data = new Intent();
- data.putExtra("choice", getIntent().getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID));
- setResult(RESULT_OK, data);
- finish();
- }
-
- private void commitTrusts() {
- for(final String fingerprint :ownKeysToTrust.keySet()) {
- mAccount.getAxolotlService().setFingerprintTrust(
- fingerprint,
- FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)));
- }
- List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
- synchronized (this.foreignKeysToTrust) {
- for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
- Jid jid = entry.getKey();
- Map<String, Boolean> value = entry.getValue();
- if (!acceptedTargets.contains(jid)) {
- acceptedTargets.add(jid);
- }
- for (final String fingerprint : value.keySet()) {
- mAccount.getAxolotlService().setFingerprintTrust(
- fingerprint,
- FingerprintStatus.createActive(value.get(fingerprint)));
- }
- }
- }
- if (mConversation != null && mConversation.getMode() == Conversation.MODE_MULTI) {
- mConversation.setAcceptedCryptoTargets(acceptedTargets);
- xmppConnectionService.updateConversation(mConversation);
- }
- }
-
- private void unlock() {
- mSaveButton.setEnabled(true);
- mSaveButton.setTextColor(getPrimaryTextColor());
- }
-
- private void lock() {
- mSaveButton.setEnabled(false);
- mSaveButton.setTextColor(getSecondaryTextColor());
- }
-
- private void lockOrUnlockAsNeeded() {
- synchronized (this.foreignKeysToTrust) {
- for (Jid jid : contactJids) {
- Map<String, Boolean> fingerprints = foreignKeysToTrust.get(jid);
- if (hasNoOtherTrustedKeys(jid) && (fingerprints == null || !fingerprints.values().contains(true))) {
- lock();
- return;
- }
- }
- }
- unlock();
-
- }
-
- private void setDone() {
- mSaveButton.setText(getString(R.string.done));
- }
-
- private void setFetching() {
- mSaveButton.setText(getString(R.string.fetching_keys));
- }
+ private List<Jid> contactJids;
+
+ private Account mAccount;
+ private Conversation mConversation;
+ private TextView keyErrorMessage;
+ private LinearLayout keyErrorMessageCard;
+ private TextView ownKeysTitle;
+ private LinearLayout ownKeys;
+ private LinearLayout ownKeysCard;
+ private LinearLayout foreignKeys;
+ private Button mSaveButton;
+ private Button mCancelButton;
+
+ private AxolotlService.FetchStatus lastFetchReport = AxolotlService.FetchStatus.SUCCESS;
+
+ private final Map<String, Boolean> ownKeysToTrust = new HashMap<>();
+ private final Map<Jid, Map<String, Boolean>> foreignKeysToTrust = new HashMap<>();
+
+ private final OnClickListener mSaveButtonListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ commitTrusts();
+ finishOk();
+ }
+ };
+
+ private final OnClickListener mCancelButtonListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ };
+
+ @Override
+ protected void refreshUiReal() {
+ invalidateOptionsMenu();
+ populateView();
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_trust_keys);
+ this.contactJids = new ArrayList<>();
+ for (String jid : getIntent().getStringArrayExtra("contacts")) {
+ try {
+ this.contactJids.add(Jid.fromString(jid));
+ } catch (InvalidJidException e) {
+ e.printStackTrace();
+ }
+ }
+
+ keyErrorMessageCard = (LinearLayout) findViewById(R.id.key_error_message_card);
+ keyErrorMessage = (TextView) findViewById(R.id.key_error_message);
+ ownKeysTitle = (TextView) findViewById(R.id.own_keys_title);
+ ownKeys = (LinearLayout) findViewById(R.id.own_keys_details);
+ ownKeysCard = (LinearLayout) findViewById(R.id.own_keys_card);
+ foreignKeys = (LinearLayout) findViewById(R.id.foreign_keys);
+ mCancelButton = (Button) findViewById(R.id.cancel_button);
+ mCancelButton.setOnClickListener(mCancelButtonListener);
+ mSaveButton = (Button) findViewById(R.id.save_button);
+ mSaveButton.setOnClickListener(mSaveButtonListener);
+
+
+ if (getActionBar() != null) {
+ getActionBar().setHomeButtonEnabled(true);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ private void populateView() {
+ setTitle(getString(R.string.trust_omemo_fingerprints));
+ ownKeys.removeAllViews();
+ foreignKeys.removeAllViews();
+ boolean hasOwnKeys = false;
+ boolean hasForeignKeys = false;
+ for (final String fingerprint : ownKeysToTrust.keySet()) {
+ hasOwnKeys = true;
+ addFingerprintRowWithListeners(ownKeys, mAccount, fingerprint, false,
+ FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)), false, false,
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ ownKeysToTrust.put(fingerprint, isChecked);
+ // own fingerprints have no impact on locked status.
+ }
+ }
+ );
+ }
+
+ synchronized (this.foreignKeysToTrust) {
+ for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
+ hasForeignKeys = true;
+ final LinearLayout layout = (LinearLayout) getLayoutInflater().inflate(R.layout.keys_card, foreignKeys, false);
+ final Jid jid = entry.getKey();
+ final TextView header = (TextView) layout.findViewById(R.id.foreign_keys_title);
+ final LinearLayout keysContainer = (LinearLayout) layout.findViewById(R.id.foreign_keys_details);
+ final TextView informNoKeys = (TextView) layout.findViewById(R.id.no_keys_to_accept);
+ header.setText(jid.toString());
+ final Map<String, Boolean> fingerprints = entry.getValue();
+ for (final String fingerprint : fingerprints.keySet()) {
+ addFingerprintRowWithListeners(keysContainer, mAccount, fingerprint, false,
+ FingerprintStatus.createActive(fingerprints.get(fingerprint)), false, false,
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ fingerprints.put(fingerprint, isChecked);
+ lockOrUnlockAsNeeded();
+ }
+ }
+ );
+ }
+ if (fingerprints.size() == 0) {
+ informNoKeys.setVisibility(View.VISIBLE);
+ informNoKeys.setText(getString(R.string.no_keys_just_confirm, mAccount.getRoster().getContact(jid).getDisplayName()));
+ } else {
+ informNoKeys.setVisibility(View.GONE);
+ }
+ foreignKeys.addView(layout);
+ }
+ }
+
+ ownKeysTitle.setText(mAccount.getJid().toBareJid().toString());
+ ownKeysCard.setVisibility(hasOwnKeys ? View.VISIBLE : View.GONE);
+ foreignKeys.setVisibility(hasForeignKeys ? View.VISIBLE : View.GONE);
+ if (hasPendingKeyFetches()) {
+ setFetching();
+ lock();
+ } else {
+ if (!hasForeignKeys && hasNoOtherTrustedKeys()) {
+ keyErrorMessageCard.setVisibility(View.VISIBLE);
+ if (lastFetchReport == AxolotlService.FetchStatus.ERROR
+ || mAccount.getAxolotlService().fetchMapHasErrors(contactJids)) {
+ keyErrorMessage.setText(R.string.error_no_keys_to_trust_server_error);
+ } else {
+ keyErrorMessage.setText(R.string.error_no_keys_to_trust);
+ }
+ ownKeys.removeAllViews();
+ ownKeysCard.setVisibility(View.GONE);
+ foreignKeys.removeAllViews();
+ foreignKeys.setVisibility(View.GONE);
+ }
+ lockOrUnlockAsNeeded();
+ setDone();
+ }
+ }
+
+ private boolean reloadFingerprints() {
+ List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
+ ownKeysToTrust.clear();
+ AxolotlService service = this.mAccount.getAxolotlService();
+ Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided());
+ for (final IdentityKey identityKey : ownKeysSet) {
+ if (!ownKeysToTrust.containsKey(identityKey)) {
+ ownKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
+ }
+ }
+ synchronized (this.foreignKeysToTrust) {
+ foreignKeysToTrust.clear();
+ for (Jid jid : contactJids) {
+ Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), jid);
+ if (hasNoOtherTrustedKeys(jid) && ownKeysSet.size() == 0) {
+ foreignKeysSet.addAll(service.getKeysWithTrust(FingerprintStatus.createActive(false), jid));
+ }
+ Map<String, Boolean> foreignFingerprints = new HashMap<>();
+ for (final IdentityKey identityKey : foreignKeysSet) {
+ if (!foreignFingerprints.containsKey(identityKey)) {
+ foreignFingerprints.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
+ }
+ }
+ if (foreignFingerprints.size() > 0 || !acceptedTargets.contains(jid)) {
+ foreignKeysToTrust.put(jid, foreignFingerprints);
+ }
+ }
+ }
+ return ownKeysSet.size() + foreignKeysToTrust.size() > 0;
+ }
+
+ public void onBackendConnected() {
+ Intent intent = getIntent();
+ this.mAccount = extractAccount(intent);
+ if (this.mAccount != null && intent != null) {
+ String uuid = intent.getStringExtra("conversation");
+ this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
+ reloadFingerprints();
+ populateView();
+ }
+ }
+
+ private boolean hasNoOtherTrustedKeys() {
+ return mAccount == null || mAccount.getAxolotlService().anyTargetHasNoTrustedKeys(contactJids);
+ }
+
+ private boolean hasNoOtherTrustedKeys(Jid contact) {
+ return mAccount == null || mAccount.getAxolotlService().getNumTrustedKeys(contact) == 0;
+ }
+
+ private boolean hasPendingKeyFetches() {
+ return mAccount != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount, contactJids);
+ }
+
+
+ @Override
+ public void onKeyStatusUpdated(final AxolotlService.FetchStatus report) {
+ if (report != null) {
+ lastFetchReport = report;
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ switch (report) {
+ case ERROR:
+ Toast.makeText(TrustKeysActivity.this, R.string.error_fetching_omemo_key, Toast.LENGTH_SHORT).show();
+ break;
+ case SUCCESS_VERIFIED:
+ Toast.makeText(TrustKeysActivity.this,
+ Config.X509_VERIFICATION ? R.string.verified_omemo_key_with_certificate : R.string.all_omemo_keys_have_been_verified,
+ Toast.LENGTH_LONG).show();
+ break;
+ }
+ }
+ });
+
+ }
+ boolean keysToTrust = reloadFingerprints();
+ if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) {
+ refreshUi();
+ } else {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ finishOk();
+ }
+ });
+
+ }
+ }
+
+ private void finishOk() {
+ Intent data = new Intent();
+ data.putExtra("choice", getIntent().getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID));
+ setResult(RESULT_OK, data);
+ finish();
+ }
+
+ private void commitTrusts() {
+ for (final String fingerprint : ownKeysToTrust.keySet()) {
+ mAccount.getAxolotlService().setFingerprintTrust(
+ fingerprint,
+ FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)));
+ }
+ List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
+ synchronized (this.foreignKeysToTrust) {
+ for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
+ Jid jid = entry.getKey();
+ Map<String, Boolean> value = entry.getValue();
+ if (!acceptedTargets.contains(jid)) {
+ acceptedTargets.add(jid);
+ }
+ for (final String fingerprint : value.keySet()) {
+ mAccount.getAxolotlService().setFingerprintTrust(
+ fingerprint,
+ FingerprintStatus.createActive(value.get(fingerprint)));
+ }
+ }
+ }
+ if (mConversation != null && mConversation.getMode() == Conversation.MODE_MULTI) {
+ mConversation.setAcceptedCryptoTargets(acceptedTargets);
+ xmppConnectionService.updateConversation(mConversation);
+ }
+ }
+
+ private void unlock() {
+ mSaveButton.setEnabled(true);
+ mSaveButton.setTextColor(getPrimaryTextColor());
+ }
+
+ private void lock() {
+ mSaveButton.setEnabled(false);
+ mSaveButton.setTextColor(getSecondaryTextColor());
+ }
+
+ private void lockOrUnlockAsNeeded() {
+ synchronized (this.foreignKeysToTrust) {
+ for (Jid jid : contactJids) {
+ Map<String, Boolean> fingerprints = foreignKeysToTrust.get(jid);
+ if (hasNoOtherTrustedKeys(jid) && (fingerprints == null || !fingerprints.values().contains(true))) {
+ lock();
+ return;
+ }
+ }
+ }
+ unlock();
+
+ }
+
+ private void setDone() {
+ mSaveButton.setText(getString(R.string.done));
+ }
+
+ private void setFetching() {
+ mSaveButton.setText(getString(R.string.fetching_keys));
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/UiCallback.java b/src/main/java/de/pixart/messenger/ui/UiCallback.java
index 07377c879..310a49255 100644
--- a/src/main/java/de/pixart/messenger/ui/UiCallback.java
+++ b/src/main/java/de/pixart/messenger/ui/UiCallback.java
@@ -3,9 +3,9 @@ package de.pixart.messenger.ui;
import android.app.PendingIntent;
public interface UiCallback<T> {
- void success(T object);
+ void success(T object);
- void error(int errorCode, T object);
+ void error(int errorCode, T object);
- void userInputRequried(PendingIntent pi, T object);
+ void userInputRequried(PendingIntent pi, T object);
}
diff --git a/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java b/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java
index bee5ab692..1bd8e20f7 100644
--- a/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java
@@ -31,414 +31,414 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class VerifyOTRActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate {
- public static final String ACTION_VERIFY_CONTACT = "verify_contact";
- public static final int MODE_SCAN_FINGERPRINT = - 0x0502;
- public static final int MODE_ASK_QUESTION = 0x0503;
- public static final int MODE_ANSWER_QUESTION = 0x0504;
- public static final int MODE_MANUAL_VERIFICATION = 0x0505;
+ public static final String ACTION_VERIFY_CONTACT = "verify_contact";
+ public static final int MODE_SCAN_FINGERPRINT = -0x0502;
+ public static final int MODE_ASK_QUESTION = 0x0503;
+ public static final int MODE_ANSWER_QUESTION = 0x0504;
+ public static final int MODE_MANUAL_VERIFICATION = 0x0505;
- private LinearLayout mManualVerificationArea;
- private LinearLayout mSmpVerificationArea;
- private TextView mRemoteFingerprint;
- private TextView mYourFingerprint;
- private TextView mVerificationExplain;
- private TextView mStatusMessage;
- private TextView mSharedSecretHint;
- private EditText mSharedSecretHintEditable;
- private EditText mSharedSecretSecret;
- private Button mLeftButton;
- private Button mRightButton;
- private Account mAccount;
- private Conversation mConversation;
- private int mode = MODE_MANUAL_VERIFICATION;
- private XmppUri mPendingUri = null;
+ private LinearLayout mManualVerificationArea;
+ private LinearLayout mSmpVerificationArea;
+ private TextView mRemoteFingerprint;
+ private TextView mYourFingerprint;
+ private TextView mVerificationExplain;
+ private TextView mStatusMessage;
+ private TextView mSharedSecretHint;
+ private EditText mSharedSecretHintEditable;
+ private EditText mSharedSecretSecret;
+ private Button mLeftButton;
+ private Button mRightButton;
+ private Account mAccount;
+ private Conversation mConversation;
+ private int mode = MODE_MANUAL_VERIFICATION;
+ private XmppUri mPendingUri = null;
- private DialogInterface.OnClickListener mVerifyFingerprintListener = new DialogInterface.OnClickListener() {
+ private DialogInterface.OnClickListener mVerifyFingerprintListener = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int click) {
- mConversation.verifyOtrFingerprint();
- xmppConnectionService.syncRosterToDisk(mConversation.getAccount());
- Toast.makeText(VerifyOTRActivity.this,R.string.verified,Toast.LENGTH_SHORT).show();
- finish();
- }
- };
+ @Override
+ public void onClick(DialogInterface dialogInterface, int click) {
+ mConversation.verifyOtrFingerprint();
+ xmppConnectionService.syncRosterToDisk(mConversation.getAccount());
+ Toast.makeText(VerifyOTRActivity.this, R.string.verified, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ };
- private View.OnClickListener mCreateSharedSecretListener = new View.OnClickListener() {
- @Override
- public void onClick(final View view) {
- if (isAccountOnline()) {
- final String question = mSharedSecretHintEditable.getText().toString();
- final String secret = mSharedSecretSecret.getText().toString();
- if (question.trim().isEmpty()) {
- mSharedSecretHintEditable.requestFocus();
- mSharedSecretHintEditable.setError(getString(R.string.shared_secret_hint_should_not_be_empty));
- } else if (secret.trim().isEmpty()) {
- mSharedSecretSecret.requestFocus();
- mSharedSecretSecret.setError(getString(R.string.shared_secret_can_not_be_empty));
- } else {
- mSharedSecretSecret.setError(null);
- mSharedSecretHintEditable.setError(null);
- initSmp(question, secret);
- updateView();
- }
- }
- }
- };
- private View.OnClickListener mCancelSharedSecretListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (isAccountOnline()) {
- abortSmp();
- updateView();
- }
- }
- };
- private View.OnClickListener mRespondSharedSecretListener = new View.OnClickListener() {
+ private View.OnClickListener mCreateSharedSecretListener = new View.OnClickListener() {
+ @Override
+ public void onClick(final View view) {
+ if (isAccountOnline()) {
+ final String question = mSharedSecretHintEditable.getText().toString();
+ final String secret = mSharedSecretSecret.getText().toString();
+ if (question.trim().isEmpty()) {
+ mSharedSecretHintEditable.requestFocus();
+ mSharedSecretHintEditable.setError(getString(R.string.shared_secret_hint_should_not_be_empty));
+ } else if (secret.trim().isEmpty()) {
+ mSharedSecretSecret.requestFocus();
+ mSharedSecretSecret.setError(getString(R.string.shared_secret_can_not_be_empty));
+ } else {
+ mSharedSecretSecret.setError(null);
+ mSharedSecretHintEditable.setError(null);
+ initSmp(question, secret);
+ updateView();
+ }
+ }
+ }
+ };
+ private View.OnClickListener mCancelSharedSecretListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (isAccountOnline()) {
+ abortSmp();
+ updateView();
+ }
+ }
+ };
+ private View.OnClickListener mRespondSharedSecretListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (isAccountOnline()) {
- final String question = mSharedSecretHintEditable.getText().toString();
- final String secret = mSharedSecretSecret.getText().toString();
- respondSmp(question, secret);
- updateView();
- }
- }
- };
- private View.OnClickListener mRetrySharedSecretListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mConversation.smp().status = Conversation.Smp.STATUS_NONE;
- mConversation.smp().hint = null;
- mConversation.smp().secret = null;
- updateView();
- }
- };
- private View.OnClickListener mFinishListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mConversation.smp().status = Conversation.Smp.STATUS_NONE;
- finish();
- }
- };
+ @Override
+ public void onClick(View view) {
+ if (isAccountOnline()) {
+ final String question = mSharedSecretHintEditable.getText().toString();
+ final String secret = mSharedSecretSecret.getText().toString();
+ respondSmp(question, secret);
+ updateView();
+ }
+ }
+ };
+ private View.OnClickListener mRetrySharedSecretListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mConversation.smp().status = Conversation.Smp.STATUS_NONE;
+ mConversation.smp().hint = null;
+ mConversation.smp().secret = null;
+ updateView();
+ }
+ };
+ private View.OnClickListener mFinishListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mConversation.smp().status = Conversation.Smp.STATUS_NONE;
+ finish();
+ }
+ };
- protected boolean initSmp(final String question, final String secret) {
- final Session session = mConversation.getOtrSession();
- if (session!=null) {
- try {
- session.initSmp(question, secret);
- mConversation.smp().status = Conversation.Smp.STATUS_WE_REQUESTED;
- mConversation.smp().secret = secret;
- mConversation.smp().hint = question;
- return true;
- } catch (OtrException e) {
- return false;
- }
- } else {
- return false;
- }
- }
+ protected boolean initSmp(final String question, final String secret) {
+ final Session session = mConversation.getOtrSession();
+ if (session != null) {
+ try {
+ session.initSmp(question, secret);
+ mConversation.smp().status = Conversation.Smp.STATUS_WE_REQUESTED;
+ mConversation.smp().secret = secret;
+ mConversation.smp().hint = question;
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
- protected boolean abortSmp() {
- final Session session = mConversation.getOtrSession();
- if (session!=null) {
- try {
- session.abortSmp();
- mConversation.smp().status = Conversation.Smp.STATUS_NONE;
- mConversation.smp().hint = null;
- mConversation.smp().secret = null;
- return true;
- } catch (OtrException e) {
- return false;
- }
- } else {
- return false;
- }
- }
+ protected boolean abortSmp() {
+ final Session session = mConversation.getOtrSession();
+ if (session != null) {
+ try {
+ session.abortSmp();
+ mConversation.smp().status = Conversation.Smp.STATUS_NONE;
+ mConversation.smp().hint = null;
+ mConversation.smp().secret = null;
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
- protected boolean respondSmp(final String question, final String secret) {
- final Session session = mConversation.getOtrSession();
- if (session!=null) {
- try {
- session.respondSmp(question,secret);
- return true;
- } catch (OtrException e) {
- return false;
- }
- } else {
- return false;
- }
- }
+ protected boolean respondSmp(final String question, final String secret) {
+ final Session session = mConversation.getOtrSession();
+ if (session != null) {
+ try {
+ session.respondSmp(question, secret);
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
- protected boolean verifyWithUri(XmppUri uri) {
- Contact contact = mConversation.getContact();
- if (this.mConversation.getContact().getJid().equals(uri.getJid()) && uri.hasFingerprints()) {
- xmppConnectionService.verifyFingerprints(contact,uri.getFingerprints());
- Toast.makeText(this,R.string.verified,Toast.LENGTH_SHORT).show();
- updateView();
- return true;
- } else {
- Toast.makeText(this,R.string.could_not_verify_fingerprint,Toast.LENGTH_SHORT).show();
- return false;
- }
- }
+ protected boolean verifyWithUri(XmppUri uri) {
+ Contact contact = mConversation.getContact();
+ if (this.mConversation.getContact().getJid().equals(uri.getJid()) && uri.hasFingerprints()) {
+ xmppConnectionService.verifyFingerprints(contact, uri.getFingerprints());
+ Toast.makeText(this, R.string.verified, Toast.LENGTH_SHORT).show();
+ updateView();
+ return true;
+ } else {
+ Toast.makeText(this, R.string.could_not_verify_fingerprint, Toast.LENGTH_SHORT).show();
+ return false;
+ }
+ }
- protected boolean isAccountOnline() {
- if (this.mAccount.getStatus() != Account.State.ONLINE) {
- Toast.makeText(this,R.string.not_connected_try_again,Toast.LENGTH_SHORT).show();
- return false;
- } else {
- return true;
- }
- }
+ protected boolean isAccountOnline() {
+ if (this.mAccount.getStatus() != Account.State.ONLINE) {
+ Toast.makeText(this, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show();
+ return false;
+ } else {
+ return true;
+ }
+ }
- protected boolean handleIntent(Intent intent) {
- if (intent != null && intent.getAction().equals(ACTION_VERIFY_CONTACT)) {
- this.mAccount = extractAccount(intent);
- if (this.mAccount == null) {
- return false;
- }
- try {
- this.mConversation = this.xmppConnectionService.find(this.mAccount,Jid.fromString(intent.getExtras().getString("contact")));
- if (this.mConversation == null) {
- return false;
- }
- } catch (final InvalidJidException ignored) {
- return false;
- }
- this.mode = intent.getIntExtra("mode", MODE_MANUAL_VERIFICATION);
- if (this.mode == MODE_SCAN_FINGERPRINT) {
- new IntentIntegrator(this).initiateScan();
- return false;
- }
- return true;
- } else {
- return false;
- }
- }
+ protected boolean handleIntent(Intent intent) {
+ if (intent != null && intent.getAction().equals(ACTION_VERIFY_CONTACT)) {
+ this.mAccount = extractAccount(intent);
+ if (this.mAccount == null) {
+ return false;
+ }
+ try {
+ this.mConversation = this.xmppConnectionService.find(this.mAccount, Jid.fromString(intent.getExtras().getString("contact")));
+ if (this.mConversation == null) {
+ return false;
+ }
+ } catch (final InvalidJidException ignored) {
+ return false;
+ }
+ this.mode = intent.getIntExtra("mode", MODE_MANUAL_VERIFICATION);
+ if (this.mode == MODE_SCAN_FINGERPRINT) {
+ new IntentIntegrator(this).initiateScan();
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
- IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
- if (scanResult != null && scanResult.getFormatName() != null) {
- String data = scanResult.getContents();
- XmppUri uri = new XmppUri(data);
- if (xmppConnectionServiceBound) {
- verifyWithUri(uri);
- finish();
- } else {
- this.mPendingUri = uri;
- }
- } else {
- finish();
- }
- }
- super.onActivityResult(requestCode, requestCode, intent);
- }
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
+ IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
+ if (scanResult != null && scanResult.getFormatName() != null) {
+ String data = scanResult.getContents();
+ XmppUri uri = new XmppUri(data);
+ if (xmppConnectionServiceBound) {
+ verifyWithUri(uri);
+ finish();
+ } else {
+ this.mPendingUri = uri;
+ }
+ } else {
+ finish();
+ }
+ }
+ super.onActivityResult(requestCode, requestCode, intent);
+ }
- @Override
- protected void onBackendConnected() {
- if (handleIntent(getIntent())) {
- updateView();
- } else if (mPendingUri!=null) {
- verifyWithUri(mPendingUri);
- finish();
- mPendingUri = null;
- }
- setIntent(null);
- }
+ @Override
+ protected void onBackendConnected() {
+ if (handleIntent(getIntent())) {
+ updateView();
+ } else if (mPendingUri != null) {
+ verifyWithUri(mPendingUri);
+ finish();
+ mPendingUri = null;
+ }
+ setIntent(null);
+ }
- protected void updateView() {
- if (this.mConversation != null && this.mConversation.hasValidOtrSession()) {
- final ActionBar actionBar = getActionBar();
- this.mVerificationExplain.setText(R.string.no_otr_session_found);
- invalidateOptionsMenu();
- switch(this.mode) {
- case MODE_ASK_QUESTION:
- if (actionBar != null ) {
- actionBar.setTitle(R.string.ask_question);
- }
- this.updateViewAskQuestion();
- break;
- case MODE_ANSWER_QUESTION:
- if (actionBar != null ) {
- actionBar.setTitle(R.string.smp_requested);
- }
- this.updateViewAnswerQuestion();
- break;
- case MODE_MANUAL_VERIFICATION:
- default:
- if (actionBar != null ) {
- actionBar.setTitle(R.string.manually_verify);
- }
- this.updateViewManualVerification();
- break;
- }
- } else {
- this.mManualVerificationArea.setVisibility(View.GONE);
- this.mSmpVerificationArea.setVisibility(View.GONE);
- }
- }
+ protected void updateView() {
+ if (this.mConversation != null && this.mConversation.hasValidOtrSession()) {
+ final ActionBar actionBar = getActionBar();
+ this.mVerificationExplain.setText(R.string.no_otr_session_found);
+ invalidateOptionsMenu();
+ switch (this.mode) {
+ case MODE_ASK_QUESTION:
+ if (actionBar != null) {
+ actionBar.setTitle(R.string.ask_question);
+ }
+ this.updateViewAskQuestion();
+ break;
+ case MODE_ANSWER_QUESTION:
+ if (actionBar != null) {
+ actionBar.setTitle(R.string.smp_requested);
+ }
+ this.updateViewAnswerQuestion();
+ break;
+ case MODE_MANUAL_VERIFICATION:
+ default:
+ if (actionBar != null) {
+ actionBar.setTitle(R.string.manually_verify);
+ }
+ this.updateViewManualVerification();
+ break;
+ }
+ } else {
+ this.mManualVerificationArea.setVisibility(View.GONE);
+ this.mSmpVerificationArea.setVisibility(View.GONE);
+ }
+ }
- protected void updateViewManualVerification() {
- this.mVerificationExplain.setText(R.string.manual_verification_explanation);
- this.mManualVerificationArea.setVisibility(View.VISIBLE);
- this.mSmpVerificationArea.setVisibility(View.GONE);
- this.mYourFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mAccount.getOtrFingerprint()));
- this.mRemoteFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mConversation.getOtrFingerprint()));
- if (this.mConversation.isOtrFingerprintVerified()) {
- deactivateButton(this.mRightButton,R.string.verified);
- activateButton(this.mLeftButton,R.string.cancel,this.mFinishListener);
- } else {
- activateButton(this.mLeftButton,R.string.cancel,this.mFinishListener);
- activateButton(this.mRightButton,R.string.verify, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- showManuallyVerifyDialog();
- }
- });
- }
- }
+ protected void updateViewManualVerification() {
+ this.mVerificationExplain.setText(R.string.manual_verification_explanation);
+ this.mManualVerificationArea.setVisibility(View.VISIBLE);
+ this.mSmpVerificationArea.setVisibility(View.GONE);
+ this.mYourFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mAccount.getOtrFingerprint()));
+ this.mRemoteFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mConversation.getOtrFingerprint()));
+ if (this.mConversation.isOtrFingerprintVerified()) {
+ deactivateButton(this.mRightButton, R.string.verified);
+ activateButton(this.mLeftButton, R.string.cancel, this.mFinishListener);
+ } else {
+ activateButton(this.mLeftButton, R.string.cancel, this.mFinishListener);
+ activateButton(this.mRightButton, R.string.verify, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ showManuallyVerifyDialog();
+ }
+ });
+ }
+ }
- protected void updateViewAskQuestion() {
- this.mManualVerificationArea.setVisibility(View.GONE);
- this.mSmpVerificationArea.setVisibility(View.VISIBLE);
- this.mVerificationExplain.setText(R.string.smp_explain_question);
- final int smpStatus = this.mConversation.smp().status;
- switch (smpStatus) {
- case Conversation.Smp.STATUS_WE_REQUESTED:
- this.mStatusMessage.setVisibility(View.GONE);
- this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
- this.mSharedSecretSecret.setVisibility(View.VISIBLE);
- this.mSharedSecretHintEditable.setText(this.mConversation.smp().hint);
- this.mSharedSecretSecret.setText(this.mConversation.smp().secret);
- this.activateButton(this.mLeftButton, R.string.cancel, this.mCancelSharedSecretListener);
- this.deactivateButton(this.mRightButton, R.string.in_progress);
- break;
- case Conversation.Smp.STATUS_FAILED:
- this.mStatusMessage.setVisibility(View.GONE);
- this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
- this.mSharedSecretSecret.setVisibility(View.VISIBLE);
- this.mSharedSecretSecret.requestFocus();
- this.mSharedSecretSecret.setError(getString(R.string.secrets_do_not_match));
- this.deactivateButton(this.mLeftButton, R.string.cancel);
- this.activateButton(this.mRightButton, R.string.try_again, this.mRetrySharedSecretListener);
- break;
- case Conversation.Smp.STATUS_VERIFIED:
- this.mSharedSecretHintEditable.setText("");
- this.mSharedSecretHintEditable.setVisibility(View.GONE);
- this.mSharedSecretSecret.setText("");
- this.mSharedSecretSecret.setVisibility(View.GONE);
- this.mStatusMessage.setVisibility(View.VISIBLE);
- this.deactivateButton(this.mLeftButton, R.string.cancel);
- this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
- break;
- default:
- this.mStatusMessage.setVisibility(View.GONE);
- this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
- this.mSharedSecretSecret.setVisibility(View.VISIBLE);
- this.activateButton(this.mLeftButton,R.string.cancel,this.mFinishListener);
- this.activateButton(this.mRightButton, R.string.ask_question, this.mCreateSharedSecretListener);
- break;
- }
- }
+ protected void updateViewAskQuestion() {
+ this.mManualVerificationArea.setVisibility(View.GONE);
+ this.mSmpVerificationArea.setVisibility(View.VISIBLE);
+ this.mVerificationExplain.setText(R.string.smp_explain_question);
+ final int smpStatus = this.mConversation.smp().status;
+ switch (smpStatus) {
+ case Conversation.Smp.STATUS_WE_REQUESTED:
+ this.mStatusMessage.setVisibility(View.GONE);
+ this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.mSharedSecretHintEditable.setText(this.mConversation.smp().hint);
+ this.mSharedSecretSecret.setText(this.mConversation.smp().secret);
+ this.activateButton(this.mLeftButton, R.string.cancel, this.mCancelSharedSecretListener);
+ this.deactivateButton(this.mRightButton, R.string.in_progress);
+ break;
+ case Conversation.Smp.STATUS_FAILED:
+ this.mStatusMessage.setVisibility(View.GONE);
+ this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.requestFocus();
+ this.mSharedSecretSecret.setError(getString(R.string.secrets_do_not_match));
+ this.deactivateButton(this.mLeftButton, R.string.cancel);
+ this.activateButton(this.mRightButton, R.string.try_again, this.mRetrySharedSecretListener);
+ break;
+ case Conversation.Smp.STATUS_VERIFIED:
+ this.mSharedSecretHintEditable.setText("");
+ this.mSharedSecretHintEditable.setVisibility(View.GONE);
+ this.mSharedSecretSecret.setText("");
+ this.mSharedSecretSecret.setVisibility(View.GONE);
+ this.mStatusMessage.setVisibility(View.VISIBLE);
+ this.deactivateButton(this.mLeftButton, R.string.cancel);
+ this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
+ break;
+ default:
+ this.mStatusMessage.setVisibility(View.GONE);
+ this.mSharedSecretHintEditable.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.activateButton(this.mLeftButton, R.string.cancel, this.mFinishListener);
+ this.activateButton(this.mRightButton, R.string.ask_question, this.mCreateSharedSecretListener);
+ break;
+ }
+ }
- protected void updateViewAnswerQuestion() {
- this.mManualVerificationArea.setVisibility(View.GONE);
- this.mSmpVerificationArea.setVisibility(View.VISIBLE);
- this.mVerificationExplain.setText(R.string.smp_explain_answer);
- this.mSharedSecretHintEditable.setVisibility(View.GONE);
- this.mSharedSecretHint.setVisibility(View.VISIBLE);
- this.deactivateButton(this.mLeftButton, R.string.cancel);
- final int smpStatus = this.mConversation.smp().status;
- switch (smpStatus) {
- case Conversation.Smp.STATUS_CONTACT_REQUESTED:
- this.mStatusMessage.setVisibility(View.GONE);
- this.mSharedSecretHint.setText(this.mConversation.smp().hint);
- this.activateButton(this.mRightButton,R.string.respond,this.mRespondSharedSecretListener);
- break;
- case Conversation.Smp.STATUS_VERIFIED:
- this.mSharedSecretHintEditable.setText("");
- this.mSharedSecretHintEditable.setVisibility(View.GONE);
- this.mSharedSecretHint.setVisibility(View.GONE);
- this.mSharedSecretSecret.setText("");
- this.mSharedSecretSecret.setVisibility(View.GONE);
- this.mStatusMessage.setVisibility(View.VISIBLE);
- this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
- break;
- case Conversation.Smp.STATUS_FAILED:
- default:
- this.mSharedSecretSecret.requestFocus();
- this.mSharedSecretSecret.setError(getString(R.string.secrets_do_not_match));
- this.activateButton(this.mRightButton,R.string.finish,this.mFinishListener);
- break;
- }
- }
+ protected void updateViewAnswerQuestion() {
+ this.mManualVerificationArea.setVisibility(View.GONE);
+ this.mSmpVerificationArea.setVisibility(View.VISIBLE);
+ this.mVerificationExplain.setText(R.string.smp_explain_answer);
+ this.mSharedSecretHintEditable.setVisibility(View.GONE);
+ this.mSharedSecretHint.setVisibility(View.VISIBLE);
+ this.deactivateButton(this.mLeftButton, R.string.cancel);
+ final int smpStatus = this.mConversation.smp().status;
+ switch (smpStatus) {
+ case Conversation.Smp.STATUS_CONTACT_REQUESTED:
+ this.mStatusMessage.setVisibility(View.GONE);
+ this.mSharedSecretHint.setText(this.mConversation.smp().hint);
+ this.activateButton(this.mRightButton, R.string.respond, this.mRespondSharedSecretListener);
+ break;
+ case Conversation.Smp.STATUS_VERIFIED:
+ this.mSharedSecretHintEditable.setText("");
+ this.mSharedSecretHintEditable.setVisibility(View.GONE);
+ this.mSharedSecretHint.setVisibility(View.GONE);
+ this.mSharedSecretSecret.setText("");
+ this.mSharedSecretSecret.setVisibility(View.GONE);
+ this.mStatusMessage.setVisibility(View.VISIBLE);
+ this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
+ break;
+ case Conversation.Smp.STATUS_FAILED:
+ default:
+ this.mSharedSecretSecret.requestFocus();
+ this.mSharedSecretSecret.setError(getString(R.string.secrets_do_not_match));
+ this.activateButton(this.mRightButton, R.string.finish, this.mFinishListener);
+ break;
+ }
+ }
- protected void activateButton(Button button, int text, View.OnClickListener listener) {
- button.setEnabled(true);
- button.setTextColor(getPrimaryTextColor());
- button.setText(text);
- button.setOnClickListener(listener);
- }
+ protected void activateButton(Button button, int text, View.OnClickListener listener) {
+ button.setEnabled(true);
+ button.setTextColor(getPrimaryTextColor());
+ button.setText(text);
+ button.setOnClickListener(listener);
+ }
- protected void deactivateButton(Button button, int text) {
- button.setEnabled(false);
- button.setTextColor(getSecondaryTextColor());
- button.setText(text);
- button.setOnClickListener(null);
- }
+ protected void deactivateButton(Button button, int text) {
+ button.setEnabled(false);
+ button.setTextColor(getSecondaryTextColor());
+ button.setText(text);
+ button.setOnClickListener(null);
+ }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_verify_otr);
- this.mRemoteFingerprint = (TextView) findViewById(R.id.remote_fingerprint);
- this.mYourFingerprint = (TextView) findViewById(R.id.your_fingerprint);
- this.mLeftButton = (Button) findViewById(R.id.left_button);
- this.mRightButton = (Button) findViewById(R.id.right_button);
- this.mVerificationExplain = (TextView) findViewById(R.id.verification_explanation);
- this.mStatusMessage = (TextView) findViewById(R.id.status_message);
- this.mSharedSecretSecret = (EditText) findViewById(R.id.shared_secret_secret);
- this.mSharedSecretHintEditable = (EditText) findViewById(R.id.shared_secret_hint_editable);
- this.mSharedSecretHint = (TextView) findViewById(R.id.shared_secret_hint);
- this.mManualVerificationArea = (LinearLayout) findViewById(R.id.manual_verification_area);
- this.mSmpVerificationArea = (LinearLayout) findViewById(R.id.smp_verification_area);
- }
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_verify_otr);
+ this.mRemoteFingerprint = (TextView) findViewById(R.id.remote_fingerprint);
+ this.mYourFingerprint = (TextView) findViewById(R.id.your_fingerprint);
+ this.mLeftButton = (Button) findViewById(R.id.left_button);
+ this.mRightButton = (Button) findViewById(R.id.right_button);
+ this.mVerificationExplain = (TextView) findViewById(R.id.verification_explanation);
+ this.mStatusMessage = (TextView) findViewById(R.id.status_message);
+ this.mSharedSecretSecret = (EditText) findViewById(R.id.shared_secret_secret);
+ this.mSharedSecretHintEditable = (EditText) findViewById(R.id.shared_secret_hint_editable);
+ this.mSharedSecretHint = (TextView) findViewById(R.id.shared_secret_hint);
+ this.mManualVerificationArea = (LinearLayout) findViewById(R.id.manual_verification_area);
+ this.mSmpVerificationArea = (LinearLayout) findViewById(R.id.smp_verification_area);
+ }
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.verify_otr, menu);
- return true;
- }
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.verify_otr, menu);
+ return true;
+ }
- private void showManuallyVerifyDialog() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.manually_verify);
- builder.setMessage(R.string.are_you_sure_verify_fingerprint);
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.verify, mVerifyFingerprintListener);
- builder.create().show();
- }
+ private void showManuallyVerifyDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.manually_verify);
+ builder.setMessage(R.string.are_you_sure_verify_fingerprint);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.verify, mVerifyFingerprintListener);
+ builder.create().show();
+ }
- @Override
- protected String getShareableUri() {
- if (mAccount!=null) {
- return mAccount.getShareableUri();
- } else {
- return "";
- }
- }
+ @Override
+ protected String getShareableUri() {
+ if (mAccount != null) {
+ return mAccount.getShareableUri();
+ } else {
+ return "";
+ }
+ }
- public void onConversationUpdate() {
- refreshUi();
- }
+ public void onConversationUpdate() {
+ refreshUi();
+ }
- @Override
- protected void refreshUiReal() {
- updateView();
- }
+ @Override
+ protected void refreshUiReal() {
+ updateView();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java b/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
index f0235ce97..7573e40c7 100644
--- a/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
@@ -101,8 +101,8 @@ public class WelcomeActivity extends XmppActivity {
List<Account> accounts = xmppConnectionService.getAccounts();
Intent intent = new Intent(WelcomeActivity.this, EditAccountActivity.class);
if (accounts.size() == 1) {
- intent.putExtra("jid",accounts.get(0).getJid().toBareJid().toString());
- intent.putExtra("init",true);
+ intent.putExtra("jid", accounts.get(0).getJid().toBareJid().toString());
+ intent.putExtra("init", true);
} else if (accounts.size() >= 1) {
intent = new Intent(WelcomeActivity.this, ManageAccountActivity.class);
}
@@ -125,7 +125,7 @@ public class WelcomeActivity extends XmppActivity {
.setCancelable(false)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,int id) {
+ public void onClick(DialogInterface dialog, int id) {
final String password = userInput.getText().toString();
final ProgressDialog pd = ProgressDialog.show(WelcomeActivity.this, getString(R.string.please_wait), getString(R.string.databaseimport_started), true);
if (!password.isEmpty()) {
@@ -157,7 +157,7 @@ public class WelcomeActivity extends XmppActivity {
})
.setNegativeButton(R.string.cancel,
new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,int id) {
+ public void onClick(DialogInterface dialog, int id) {
Toast.makeText(WelcomeActivity.this, R.string.import_canceled, Toast.LENGTH_LONG).show();
dialog.dismiss();
}
@@ -176,12 +176,12 @@ public class WelcomeActivity extends XmppActivity {
private boolean BackupAvailable() {
// Set the folder on the SDcard
File filePath = new File(FileBackend.getConversationsDirectory() + "/database/database.db.crypt");
- Log.d(Config.LOGTAG,"DB Path: " + filePath.toString());
- if(filePath.exists()) {
- Log.d(Config.LOGTAG,"DB Path existing");
+ Log.d(Config.LOGTAG, "DB Path: " + filePath.toString());
+ if (filePath.exists()) {
+ Log.d(Config.LOGTAG, "DB Path existing");
return true;
} else {
- Log.d(Config.LOGTAG,"DB Path not existing");
+ Log.d(Config.LOGTAG, "DB Path not existing");
return false;
}
}
@@ -198,16 +198,16 @@ public class WelcomeActivity extends XmppActivity {
try {
EncryptDecryptFile.decrypt(InputFile, OutputTemp, DecryptionKey);
} catch (NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG,"Database importer: decryption failed with " + e);
+ Log.d(Config.LOGTAG, "Database importer: decryption failed with " + e);
e.printStackTrace();
} catch (NoSuchPaddingException e) {
- Log.d(Config.LOGTAG,"Database importer: decryption failed with " + e);
+ Log.d(Config.LOGTAG, "Database importer: decryption failed with " + e);
e.printStackTrace();
} catch (InvalidKeyException e) {
- Log.d(Config.LOGTAG,"Database importer: decryption failed (invalid key) with " + e);
+ Log.d(Config.LOGTAG, "Database importer: decryption failed (invalid key) with " + e);
e.printStackTrace();
} catch (IOException e) {
- Log.d(Config.LOGTAG,"Database importer: decryption failed (IO) with " + e);
+ Log.d(Config.LOGTAG, "Database importer: decryption failed (IO) with " + e);
e.printStackTrace();
}
diff --git a/src/main/java/de/pixart/messenger/ui/XmppActivity.java b/src/main/java/de/pixart/messenger/ui/XmppActivity.java
index 26f185294..8b3d26179 100644
--- a/src/main/java/de/pixart/messenger/ui/XmppActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/XmppActivity.java
@@ -92,1209 +92,1209 @@ import de.pixart.messenger.xmpp.jid.Jid;
public abstract class XmppActivity extends Activity {
- protected static final int REQUEST_ANNOUNCE_PGP = 0x0101;
- protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x0102;
- protected static final int REQUEST_CHOOSE_PGP_ID = 0x0103;
- protected static final int REQUEST_BATTERY_OP = 0x13849ff;
-
- public static final String EXTRA_ACCOUNT = "account";
-
- public XmppConnectionService xmppConnectionService;
- public boolean xmppConnectionServiceBound = false;
- protected boolean registeredListeners = false;
-
- protected int mPrimaryTextColor;
- protected int mSecondaryTextColor;
- protected int mTertiaryTextColor;
- protected int mPrimaryBackgroundColor;
- protected int mSecondaryBackgroundColor;
- protected int mColorRed;
- protected int mColorWhite;
- protected int mColorOrange;
- protected int mColorGreen;
- protected int mPrimaryColor;
-
- protected boolean mUseSubject = true;
-
- private DisplayMetrics metrics;
- protected int mTheme;
- protected boolean mUsingEnterKey = false;
-
- protected Toast mToast;
-
- protected void hideToast() {
- if (mToast != null) {
- mToast.cancel();
- }
- }
-
- protected void replaceToast(String msg) {
- replaceToast(msg, true);
- }
-
- protected void replaceToast(String msg, boolean showlong) {
- hideToast();
- mToast = Toast.makeText(this, msg ,showlong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
- mToast.show();
- }
-
- public void showProgress() {
-
- }
-
- public void closeProgress() {
-
- }
-
- protected Runnable onOpenPGPKeyPublished = new Runnable() {
- @Override
- public void run() {
- Toast.makeText(XmppActivity.this,R.string.openpgp_has_been_published, Toast.LENGTH_SHORT).show();
- }
- };
-
- private long mLastUiRefresh = 0;
- private Handler mRefreshUiHandler = new Handler();
- private Runnable mRefreshUiRunnable = new Runnable() {
- @Override
- public void run() {
- mLastUiRefresh = SystemClock.elapsedRealtime();
- refreshUiReal();
- }
- };
-
- protected ConferenceInvite mPendingConferenceInvite = null;
-
-
- protected final void refreshUi() {
- final long diff = SystemClock.elapsedRealtime() - mLastUiRefresh;
- if (diff > Config.REFRESH_UI_INTERVAL) {
- mRefreshUiHandler.removeCallbacks(mRefreshUiRunnable);
- runOnUiThread(mRefreshUiRunnable);
- } else {
- final long next = Config.REFRESH_UI_INTERVAL - diff;
- mRefreshUiHandler.removeCallbacks(mRefreshUiRunnable);
- mRefreshUiHandler.postDelayed(mRefreshUiRunnable,next);
- }
- }
-
- abstract protected void refreshUiReal();
-
- protected interface OnValueEdited {
- public void onValueEdited(String value);
- }
-
- public interface OnPresenceSelected {
- public void onPresenceSelected();
- }
-
- protected ServiceConnection mConnection = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- XmppConnectionBinder binder = (XmppConnectionBinder) service;
- xmppConnectionService = binder.getService();
- xmppConnectionServiceBound = true;
- if (!registeredListeners && shouldRegisterListeners()) {
- registerListeners();
- registeredListeners = true;
- }
- onBackendConnected();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName arg0) {
- xmppConnectionServiceBound = false;
- }
- };
-
- @Override
- protected void onStart() {
- super.onStart();
- if (!xmppConnectionServiceBound) {
- connectToBackend();
- } else {
- if (!registeredListeners) {
- this.registerListeners();
- this.registeredListeners = true;
- }
- this.onBackendConnected();
- }
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- protected boolean shouldRegisterListeners() {
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- return !isDestroyed() && !isFinishing();
- } else {
- return !isFinishing();
- }
- }
-
- public void connectToBackend() {
- Intent intent = new Intent(this, XmppConnectionService.class);
- intent.setAction("ui");
- startService(intent);
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- if (xmppConnectionServiceBound) {
- if (registeredListeners) {
- this.unregisterListeners();
- this.registeredListeners = false;
- }
- unbindService(mConnection);
- xmppConnectionServiceBound = false;
- }
- }
-
- protected void hideKeyboard() {
- InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
-
- View focus = getCurrentFocus();
-
- if (focus != null) {
-
- inputManager.hideSoftInputFromWindow(focus.getWindowToken(),
- InputMethodManager.HIDE_NOT_ALWAYS);
- }
- }
-
- public boolean hasPgp() {
- return xmppConnectionService.getPgpEngine() != null;
- }
-
- public void showInstallPgpDialog() {
- Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(getString(R.string.openkeychain_required));
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setMessage(getText(R.string.openkeychain_required_long));
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setNeutralButton(getString(R.string.restart),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (xmppConnectionServiceBound) {
- unbindService(mConnection);
- xmppConnectionServiceBound = false;
- }
- stopService(new Intent(XmppActivity.this,
- XmppConnectionService.class));
- finish();
- }
- });
- builder.setPositiveButton(getString(R.string.install),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Uri uri = Uri
- .parse("market://details?id=org.sufficientlysecure.keychain");
- Intent marketIntent = new Intent(Intent.ACTION_VIEW,
- uri);
- PackageManager manager = getApplicationContext()
- .getPackageManager();
- List<ResolveInfo> infos = manager
- .queryIntentActivities(marketIntent, 0);
- if (infos.size() > 0) {
- startActivity(marketIntent);
- } else {
- uri = Uri.parse("http://www.openkeychain.org/");
- Intent browserIntent = new Intent(
- Intent.ACTION_VIEW, uri);
- startActivity(browserIntent);
- }
- finish();
- }
- });
- builder.create().show();
- }
-
- abstract void onBackendConnected();
-
- protected void registerListeners() {
- if (this instanceof XmppConnectionService.OnConversationUpdate) {
- this.xmppConnectionService.setOnConversationListChangedListener((XmppConnectionService.OnConversationUpdate) this);
- }
- if (this instanceof XmppConnectionService.OnAccountUpdate) {
- this.xmppConnectionService.setOnAccountListChangedListener((XmppConnectionService.OnAccountUpdate) this);
- }
- if (this instanceof XmppConnectionService.OnCaptchaRequested) {
- this.xmppConnectionService.setOnCaptchaRequestedListener((XmppConnectionService.OnCaptchaRequested) this);
- }
- if (this instanceof XmppConnectionService.OnRosterUpdate) {
- this.xmppConnectionService.setOnRosterUpdateListener((XmppConnectionService.OnRosterUpdate) this);
- }
- if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
- this.xmppConnectionService.setOnMucRosterUpdateListener((XmppConnectionService.OnMucRosterUpdate) this);
- }
- if (this instanceof OnUpdateBlocklist) {
- this.xmppConnectionService.setOnUpdateBlocklistListener((OnUpdateBlocklist) this);
- }
- if (this instanceof XmppConnectionService.OnShowErrorToast) {
- this.xmppConnectionService.setOnShowErrorToastListener((XmppConnectionService.OnShowErrorToast) this);
- }
- if (this instanceof OnKeyStatusUpdated) {
- this.xmppConnectionService.setOnKeyStatusUpdatedListener((OnKeyStatusUpdated) this);
- }
- }
-
- protected void unregisterListeners() {
- if (this instanceof XmppConnectionService.OnConversationUpdate) {
- this.xmppConnectionService.removeOnConversationListChangedListener();
- }
- if (this instanceof XmppConnectionService.OnAccountUpdate) {
- this.xmppConnectionService.removeOnAccountListChangedListener();
- }
- if (this instanceof XmppConnectionService.OnCaptchaRequested) {
- this.xmppConnectionService.removeOnCaptchaRequestedListener();
- }
- if (this instanceof XmppConnectionService.OnRosterUpdate) {
- this.xmppConnectionService.removeOnRosterUpdateListener();
- }
- if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
- this.xmppConnectionService.removeOnMucRosterUpdateListener();
- }
- if (this instanceof OnUpdateBlocklist) {
- this.xmppConnectionService.removeOnUpdateBlocklistListener();
- }
- if (this instanceof XmppConnectionService.OnShowErrorToast) {
- this.xmppConnectionService.removeOnShowErrorToastListener();
- }
- if (this instanceof OnKeyStatusUpdated) {
- this.xmppConnectionService.removeOnNewKeysAvailableListener();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_invite_user:
- inviteUser();
- break;
- case R.id.action_create_issue:
- createIssue();
- break;
- case R.id.action_settings:
- startActivity(new Intent(this, SettingsActivity.class));
- break;
- case R.id.action_check_updates:
- if (xmppConnectionService.hasInternetConnection()) {
- startActivity(new Intent(this, UpdaterActivity.class));
- } else {
- Toast.makeText(this, R.string.account_status_no_internet, Toast.LENGTH_LONG).show();
- }
- break;
- case R.id.action_accounts:
- final Intent intent = new Intent(getApplicationContext(), EditAccountActivity.class);
- Account mAccount = xmppConnectionService.getAccounts().get(0);
- intent.putExtra("jid", mAccount.getJid().toBareJid().toString());
- intent.putExtra("init", false);
- startActivity(intent);
- break;
- case android.R.id.home:
- finish();
- break;
- case R.id.action_show_qr_code:
- showQrCode();
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- metrics = getResources().getDisplayMetrics();
- ExceptionHelper.init(getApplicationContext());
- mPrimaryTextColor = getResources().getColor(R.color.black87);
- mSecondaryTextColor = getResources().getColor(R.color.black54);
- mTertiaryTextColor = getResources().getColor(R.color.black12);
- mColorRed = getResources().getColor(R.color.red800);
- mColorWhite = getResources().getColor(R.color.white70);
- mColorOrange = getResources().getColor(R.color.orange500);
- mColorGreen = getResources().getColor(R.color.realgreen);
- mPrimaryColor = getResources().getColor(R.color.primary);
- mPrimaryBackgroundColor = getResources().getColor(R.color.grey50);
- mSecondaryBackgroundColor = getResources().getColor(R.color.grey200);
- this.mTheme = findTheme();
- setTheme(this.mTheme);
- this.mUsingEnterKey = usingEnterKey();
- mUseSubject = getPreferences().getBoolean("use_subject", true);
- final ActionBar ab = getActionBar();
- if (ab!=null) {
- ab.setDisplayHomeAsUpEnabled(true);
- }
- }
-
- protected boolean isOptimizingBattery() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
- return !pm.isIgnoringBatteryOptimizations(getPackageName());
- } else {
- return false;
- }
- }
-
- protected boolean isAffectedByDataSaver() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
- return cm.isActiveNetworkMetered()
- && cm.getRestrictBackgroundStatus() == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
- } else {
- return false;
- }
- }
-
- protected boolean usingEnterKey() {
- return getPreferences().getBoolean("display_enter_key", false);
- }
-
- protected SharedPreferences getPreferences() {
- return PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext());
- }
-
- public boolean useSubjectToIdentifyConference() {
- return mUseSubject;
- }
-
- public void switchToConversation(Conversation conversation) {
- switchToConversation(conversation, null, false);
- }
-
- public void switchToConversation(Conversation conversation, String text,
- boolean newTask) {
- switchToConversation(conversation,text,null,false,newTask);
- }
-
- public void highlightInMuc(Conversation conversation, String nick) {
- switchToConversation(conversation, null, nick, false, false);
- }
-
- public void privateMsgInMuc(Conversation conversation, String nick) {
- switchToConversation(conversation, null, nick, true, false);
- }
-
- private void switchToConversation(Conversation conversation, String text, String nick, boolean pm, boolean newTask) {
- Intent viewConversationIntent = new Intent(this,
- ConversationActivity.class);
- viewConversationIntent.setAction(ConversationActivity.ACTION_VIEW_CONVERSATION);
- viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
- conversation.getUuid());
- if (text != null) {
- viewConversationIntent.putExtra(ConversationActivity.TEXT, text);
- }
- if (nick != null) {
- viewConversationIntent.putExtra(ConversationActivity.NICK, nick);
- viewConversationIntent.putExtra(ConversationActivity.PRIVATE_MESSAGE,pm);
- }
- if (newTask) {
- viewConversationIntent.setFlags(viewConversationIntent.getFlags()
- | Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- } else {
- viewConversationIntent.setFlags(viewConversationIntent.getFlags()
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- }
- startActivity(viewConversationIntent);
- finish();
- }
-
- public void switchToContactDetails(Contact contact) {
- switchToContactDetails(contact, null);
- }
-
- public void switchToContactDetails(Contact contact, String messageFingerprint) {
- Intent intent = new Intent(this, ContactDetailsActivity.class);
- intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
- intent.putExtra(EXTRA_ACCOUNT, contact.getAccount().getJid().toBareJid().toString());
- intent.putExtra("contact", contact.getJid().toString());
- intent.putExtra("fingerprint", messageFingerprint);
- startActivity(intent);
- }
-
- public void switchToAccount(Account account) {
- switchToAccount(account, false);
- }
-
- public void switchToAccount(Account account, boolean init) {
- Intent intent = new Intent(this, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().toBareJid().toString());
- intent.putExtra("init", init);
- startActivity(intent);
- }
-
- protected void inviteToConversation(Conversation conversation) {
- Intent intent = new Intent(getApplicationContext(),
- ChooseContactActivity.class);
- List<String> contacts = new ArrayList<>();
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- for (MucOptions.User user : conversation.getMucOptions().getUsers(false)) {
- Jid jid = user.getRealJid();
- if (jid != null) {
- contacts.add(jid.toBareJid().toString());
- }
- }
- } else {
- contacts.add(conversation.getJid().toBareJid().toString());
- }
- intent.putExtra("filter_contacts", contacts.toArray(new String[contacts.size()]));
- intent.putExtra("conversation", conversation.getUuid());
- intent.putExtra("multiple", true);
- intent.putExtra("show_enter_jid", true);
- intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
- startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION);
- }
-
- protected void announcePgp(Account account, final Conversation conversation, final Runnable onSuccess) {
- if (account.getPgpId() == 0) {
- choosePgpSignId(account);
- } else {
- String status = null;
- if (manuallyChangePresence()) {
- status = account.getPresenceStatusMessage();
- }
- if (status == null) {
- status = "";
- }
- xmppConnectionService.getPgpEngine().generateSignature(account, status, new UiCallback<Account>() {
-
- @Override
- public void userInputRequried(PendingIntent pi, Account account) {
- try {
- startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
- } catch (final SendIntentException ignored) {
- }
- }
-
- @Override
- public void success(Account account) {
- xmppConnectionService.databaseBackend.updateAccount(account);
- xmppConnectionService.sendPresence(account);
- if (conversation != null) {
- conversation.setNextEncryption(Message.ENCRYPTION_PGP);
- xmppConnectionService.updateConversation(conversation);
- refreshUi();
- }
- if (onSuccess != null) {
- runOnUiThread(onSuccess);
- }
- }
-
- @Override
- public void error(int error, Account account) {
- if (error == 0 && account != null) {
- account.setPgpSignId(0);
- account.unsetPgpSignature();
- xmppConnectionService.databaseBackend.updateAccount(account);
- choosePgpSignId(account);
- } else {
- displayErrorDialog(error);
- }
- }
- });
- }
- }
-
- protected boolean noAccountUsesPgp() {
- if (!hasPgp()) {
- return true;
- }
- for(Account account : xmppConnectionService.getAccounts()) {
- if (account.getPgpId() != 0) {
- return false;
- }
- }
- return true;
- }
-
- @SuppressWarnings("deprecation")
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- protected void setListItemBackgroundOnView(View view) {
- int sdk = android.os.Build.VERSION.SDK_INT;
- if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
- view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
- } else {
- view.setBackground(getResources().getDrawable(R.drawable.greybackground));
- }
- }
-
- protected void choosePgpSignId(Account account) {
- xmppConnectionService.getPgpEngine().chooseKey(account, new UiCallback<Account>() {
- @Override
- public void success(Account account1) {
- }
-
- @Override
- public void error(int errorCode, Account object) {
-
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Account object) {
- try {
- startIntentSenderForResult(pi.getIntentSender(),
- REQUEST_CHOOSE_PGP_ID, null, 0, 0, 0);
- } catch (final SendIntentException ignored) {
- }
- }
- });
- }
-
- protected void displayErrorDialog(final int errorCode) {
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- XmppActivity.this);
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setTitle(getString(R.string.error));
- builder.setMessage(errorCode);
- builder.setNeutralButton(R.string.accept, null);
- builder.create().show();
- }
- });
-
- }
-
- protected void showAddToRosterDialog(final Conversation conversation) {
- showAddToRosterDialog(conversation.getContact());
- }
-
- protected void showAddToRosterDialog(final Contact contact) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(contact.getJid().toString());
- builder.setMessage(getString(R.string.not_in_roster));
- builder.setNegativeButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.add_contact),
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final Jid jid = contact.getJid();
- Account account = contact.getAccount();
- Contact contact = account.getRoster().getContact(jid);
- xmppConnectionService.createContact(contact);
- }
- });
- builder.create().show();
- }
-
- private void showAskForPresenceDialog(final Contact contact) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(contact.getJid().toString());
- builder.setMessage(R.string.request_presence_updates);
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.request_now,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (xmppConnectionServiceBound) {
- xmppConnectionService.sendPresencePacket(contact
- .getAccount(), xmppConnectionService
- .getPresenceGenerator()
- .requestPresenceUpdatesFrom(contact));
- }
- }
- });
- builder.create().show();
- }
-
- private void warnMutalPresenceSubscription(final Conversation conversation,
- final OnPresenceSelected listener) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(conversation.getContact().getJid().toString());
- builder.setMessage(R.string.without_mutual_presence_updates);
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.ignore, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- conversation.setNextCounterpart(null);
- if (listener != null) {
- listener.onPresenceSelected();
- }
- }
- });
- builder.create().show();
- }
-
- protected void quickEdit(String previousValue, int hint, OnValueEdited callback) {
- quickEdit(previousValue, callback, hint, false);
- }
-
- protected void quickPasswordEdit(String previousValue, OnValueEdited callback) {
- quickEdit(previousValue, callback, R.string.password, true);
- }
-
- @SuppressLint("InflateParams")
- private void quickEdit(final String previousValue,
- final OnValueEdited callback,
- final int hint,
- boolean password) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- View view = getLayoutInflater().inflate(R.layout.quickedit, null);
- final EditText editor = (EditText) view.findViewById(R.id.editor);
- OnClickListener mClickListener = new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String value = editor.getText().toString();
- if (!value.equals(previousValue) && value.trim().length() > 0) {
- callback.onValueEdited(value);
- }
- }
- };
- if (password) {
- editor.setInputType(InputType.TYPE_CLASS_TEXT
- | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- builder.setPositiveButton(R.string.accept, mClickListener);
- } else {
- builder.setPositiveButton(R.string.edit, mClickListener);
- }
- if (hint != 0) {
- editor.setHint(hint);
- }
- editor.requestFocus();
- editor.setText("");
- if (previousValue != null) {
- editor.getText().append(previousValue);
- }
- builder.setView(view);
- builder.setNegativeButton(R.string.cancel, null);
- builder.create().show();
- }
-
- public boolean hasStoragePermission(int requestCode) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
- return false;
- } else {
- return true;
- }
- } else {
- return true;
- }
- }
-
- public boolean hasMicPermission(int requestCode) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, requestCode);
- return false;
- } else {
- return true;
- }
- } else {
- return true;
- }
- }
-
- public boolean hasLocationPermission(int requestCode) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, requestCode);
- requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, requestCode);
- return false;
- } else {
- return true;
- }
- } else {
- return true;
- }
- }
-
- public void selectPresence(final Conversation conversation,
- final OnPresenceSelected listener) {
- final Contact contact = conversation.getContact();
- if (conversation.hasValidOtrSession()) {
- SessionID id = conversation.getOtrSession().getSessionID();
- Jid jid;
- try {
- jid = Jid.fromString(id.getAccountID() + "/" + id.getUserID());
- } catch (InvalidJidException e) {
- jid = null;
- }
- conversation.setNextCounterpart(jid);
- listener.onPresenceSelected();
- } else if (!contact.showInRoster()) {
- showAddToRosterDialog(conversation);
- } else {
- final Presences presences = contact.getPresences();
- if (presences.size() == 0) {
- if (!contact.getOption(Contact.Options.TO)
- && !contact.getOption(Contact.Options.ASKING)
- && contact.getAccount().getStatus() == Account.State.ONLINE) {
- showAskForPresenceDialog(contact);
- } else if (!contact.getOption(Contact.Options.TO)
- || !contact.getOption(Contact.Options.FROM)) {
- warnMutalPresenceSubscription(conversation, listener);
- } else {
- conversation.setNextCounterpart(null);
- listener.onPresenceSelected();
- }
- } else if (presences.size() == 1) {
- String presence = presences.toResourceArray()[0];
- try {
- conversation.setNextCounterpart(Jid.fromParts(contact.getJid().getLocalpart(),contact.getJid().getDomainpart(),presence));
- } catch (InvalidJidException e) {
- conversation.setNextCounterpart(null);
- }
- listener.onPresenceSelected();
- } else {
- showPresenceSelectionDialog(presences,conversation,listener);
- }
- }
- }
-
- private void showPresenceSelectionDialog(Presences presences, final Conversation conversation, final OnPresenceSelected listener) {
- final Contact contact = conversation.getContact();
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(getString(R.string.choose_presence));
- final String[] resourceArray = presences.toResourceArray();
- Pair<Map<String, String>, Map<String, String>> typeAndName = presences.toTypeAndNameMap();
- final Map<String,String> resourceTypeMap = typeAndName.first;
- final Map<String,String> resourceNameMap = typeAndName.second;
- final String[] readableIdentities = new String[resourceArray.length];
- final AtomicInteger selectedResource = new AtomicInteger(0);
- for (int i = 0; i < resourceArray.length; ++i) {
- String resource = resourceArray[i];
- if (resource.equals(contact.getLastResource())) {
- selectedResource.set(i);
- }
- String type = resourceTypeMap.get(resource);
- String name = resourceNameMap.get(resource);
- if (type != null) {
- if (Collections.frequency(resourceTypeMap.values(),type) == 1) {
- readableIdentities[i] = UIHelper.tranlasteType(this,type);
- } else if (name != null) {
- if (Collections.frequency(resourceNameMap.values(), name) == 1
- || CryptoHelper.UUID_PATTERN.matcher(resource).matches()) {
- readableIdentities[i] = UIHelper.tranlasteType(this,type) + " (" + name+")";
- } else {
- readableIdentities[i] = UIHelper.tranlasteType(this,type) + " (" + name +" / " + resource+")";
- }
- } else {
- readableIdentities[i] = UIHelper.tranlasteType(this,type) + " (" + resource+")";
- }
- } else {
- readableIdentities[i] = resource;
- }
- }
- builder.setSingleChoiceItems(readableIdentities,
- selectedResource.get(),
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- selectedResource.set(which);
- }
- });
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(R.string.ok, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- try {
- Jid next = Jid.fromParts(contact.getJid().getLocalpart(),contact.getJid().getDomainpart(),resourceArray[selectedResource.get()]);
- conversation.setNextCounterpart(next);
- } catch (InvalidJidException e) {
- conversation.setNextCounterpart(null);
- }
- listener.onPresenceSelected();
- }
- });
- builder.create().show();
- }
-
- protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) {
- mPendingConferenceInvite = ConferenceInvite.parse(data);
- if (xmppConnectionServiceBound && mPendingConferenceInvite != null) {
- if (mPendingConferenceInvite.execute(this)) {
- mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
- mToast.show();
- }
- mPendingConferenceInvite = null;
- }
- }
- }
-
-
- private UiCallback<Conversation> adhocCallback = new UiCallback<Conversation>() {
- @Override
- public void success(final Conversation conversation) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- switchToConversation(conversation);
- hideToast();
- }
- });
- }
-
- @Override
- public void error(final int errorCode, Conversation object) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- replaceToast(getString(errorCode));
- }
- });
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Conversation object) {
-
- }
- };
-
- public int getTertiaryTextColor() {
- return this.mTertiaryTextColor;
- }
-
- public int getSecondaryTextColor() {
- return this.mSecondaryTextColor;
- }
-
- public int getPrimaryTextColor() {
- return this.mPrimaryTextColor;
- }
-
- public int getWarningTextColor() {
- return this.mColorRed;
- }
-
- public int getUnencryptedTextColor() {
- return this.mColorWhite;
- }
-
- public int getOnlineColor() {
- return this.mColorGreen;
- }
-
- public int getPrimaryBackgroundColor() {
- return this.mPrimaryBackgroundColor;
- }
-
- public int getSecondaryBackgroundColor() {
- return this.mSecondaryBackgroundColor;
- }
-
- public int getPixel(int dp) {
- DisplayMetrics metrics = getResources().getDisplayMetrics();
- return ((int) (dp * metrics.density));
- }
-
- public boolean copyTextToClipboard(String text, int labelResId) {
- ClipboardManager mClipBoardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
- String label = getResources().getString(labelResId);
- if (mClipBoardManager != null) {
- ClipData mClipData = ClipData.newPlainText(label, text);
- mClipBoardManager.setPrimaryClip(mClipData);
- return true;
- }
- return false;
- }
-
- protected void registerNdefPushMessageCallback() {
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
- if (nfcAdapter != null && nfcAdapter.isEnabled()) {
- nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
- @Override
- public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
- return new NdefMessage(new NdefRecord[]{
- NdefRecord.createUri(getShareableUri()),
- NdefRecord.createApplicationRecord("de.pixart.messenger")
- });
- }
- }, this);
- }
- }
-
- protected boolean neverCompressPictures() {
- return getPreferences().getString("picture_compression", "auto").equals("never");
- }
-
- protected boolean manuallyChangePresence() {
- return getPreferences().getBoolean("manually_change_presence", true);
- }
-
- protected void unregisterNdefPushMessageCallback() {
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
- if (nfcAdapter != null && nfcAdapter.isEnabled()) {
- nfcAdapter.setNdefPushMessageCallback(null,this);
- }
- }
-
- protected String getShareableUri() {
- return null;
- }
-
- private void inviteUser() {
- Account mAccount = xmppConnectionService.getAccounts().get(0);
- String user = mAccount.getJid().getLocalpart().toString();
- String domain = mAccount.getJid().getDomainpart().toString();
- String inviteURL = Config.inviteUserURL + user + "/" + domain;
- String inviteText = getString(R.string.InviteText, user);
- Intent intent = new Intent(android.content.Intent.ACTION_SEND);
- intent.setType("text/plain");
- intent.putExtra(Intent.EXTRA_SUBJECT, user + " " + getString(R.string.inviteUser_Subject) + " " + getString(R.string.app_name));
- intent.putExtra(Intent.EXTRA_TEXT, inviteText + "\n\n" + inviteURL);
- startActivity(Intent.createChooser(intent, getString(R.string.invite_contact)));
- }
-
- private void createIssue() {
- String IssueURL = Config.ISSUE_URL;
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(IssueURL));
- startActivity(intent);
- }
-
- protected void shareUri() {
- String uri = getShareableUri();
- if (uri == null || uri.isEmpty()) {
- return;
- }
- Intent shareIntent = new Intent();
- shareIntent.setAction(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_TEXT, getShareableUri());
- shareIntent.setType("text/plain");
- try {
- startActivity(Intent.createChooser(shareIntent, getText(R.string.share_uri_with)));
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.no_application_to_share_uri, Toast.LENGTH_SHORT).show();
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- if (this.getShareableUri()!=null) {
- this.registerNdefPushMessageCallback();
- }
- }
-
- protected int findTheme() {
- if (getPreferences().getBoolean("use_larger_font", false)) {
- return R.style.ConversationsTheme_LargerText;
- } else {
- return R.style.ConversationsTheme;
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- this.unregisterNdefPushMessageCallback();
- }
-
- protected void showQrCode() {
- String uri = getShareableUri();
- if (uri!=null) {
- Point size = new Point();
- getWindowManager().getDefaultDisplay().getSize(size);
- final int width = (size.x < size.y ? size.x : size.y);
- Bitmap bitmap = createQrCodeBitmap(uri, width);
- ImageView view = new ImageView(this);
- view.setImageBitmap(bitmap);
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setView(view);
- builder.create().show();
- }
- }
-
- protected Bitmap createQrCodeBitmap(String input, int size) {
- Log.d(Config.LOGTAG,"qr code requested size: "+size);
- try {
- final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
- final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
- hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
- final BitMatrix result = QR_CODE_WRITER.encode(input, BarcodeFormat.QR_CODE, size, size, hints);
- final int width = result.getWidth();
- final int height = result.getHeight();
- final int[] pixels = new int[width * height];
- for (int y = 0; y < height; y++) {
- final int offset = y * width;
- for (int x = 0; x < width; x++) {
- pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT;
- }
- }
- final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Log.d(Config.LOGTAG,"output size: "+width+"x"+height);
- bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
- return bitmap;
- } catch (final WriterException e) {
- return null;
- }
- }
-
- protected Account extractAccount(Intent intent) {
- String jid = intent != null ? intent.getStringExtra(EXTRA_ACCOUNT) : null;
- try {
- return jid != null ? xmppConnectionService.findAccountByJid(Jid.fromString(jid)) : null;
- } catch (InvalidJidException e) {
- return null;
- }
- }
-
- public static class ConferenceInvite {
- private String uuid;
- private List<Jid> jids = new ArrayList<>();
-
- public static ConferenceInvite parse(Intent data) {
- ConferenceInvite invite = new ConferenceInvite();
- invite.uuid = data.getStringExtra("conversation");
- if (invite.uuid == null) {
- return null;
- }
- try {
- if (data.getBooleanExtra("multiple", false)) {
- String[] toAdd = data.getStringArrayExtra("contacts");
- for (String item : toAdd) {
- invite.jids.add(Jid.fromString(item));
- }
- } else {
- invite.jids.add(Jid.fromString(data.getStringExtra("contact")));
- }
- } catch (final InvalidJidException ignored) {
- return null;
- }
- return invite;
- }
-
- public boolean execute(XmppActivity activity) {
- XmppConnectionService service = activity.xmppConnectionService;
- Conversation conversation = service.findConversationByUuid(this.uuid);
- if (conversation == null) {
- return false;
- }
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- for (Jid jid : jids) {
- service.invite(conversation, jid);
- }
- return false;
- } else {
- jids.add(conversation.getJid().toBareJid());
- service.createAdhocConference(conversation.getAccount(), null, jids, activity.adhocCallback);
- return true;
- }
- }
- }
-
- public AvatarService avatarService() {
- return xmppConnectionService.getAvatarService();
- }
-
- class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
- private final WeakReference<ImageView> imageViewReference;
- private Message message = null;
-
- public BitmapWorkerTask(ImageView imageView) {
- imageViewReference = new WeakReference<>(imageView);
- }
-
- @Override
- protected Bitmap doInBackground(Message... params) {
- if (isCancelled()) {
- return null;
- }
- message = params[0];
- try {
- return xmppConnectionService.getFileBackend().getThumbnail(
- message, (int) (metrics.density * 288), false);
- } catch (FileNotFoundException e) {
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null && !isCancelled()) {
- final ImageView imageView = imageViewReference.get();
- if (imageView != null) {
- imageView.setImageBitmap(bitmap);
- imageView.setBackgroundColor(0x00000000);
- }
- }
- }
- }
-
- public void loadBitmap(Message message, ImageView imageView) {
- Bitmap bm;
- try {
- bm = xmppConnectionService.getFileBackend().getThumbnail(message,
- (int) (metrics.density * 288), true);
- } catch (FileNotFoundException e) {
- bm = null;
- }
- if (bm != null) {
- cancelPotentialWork(message, imageView);
- imageView.setImageBitmap(bm);
- imageView.setBackgroundColor(0x00000000);
- } else {
- if (cancelPotentialWork(message, imageView)) {
- imageView.setBackgroundColor(0xff333333);
- imageView.setImageDrawable(null);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- final AsyncDrawable asyncDrawable = new AsyncDrawable(
- getResources(), null, task);
- imageView.setImageDrawable(asyncDrawable);
- try {
- task.execute(message);
- } catch (final RejectedExecutionException ignored) {
- ignored.printStackTrace();
- }
- }
- }
- }
-
- public static boolean cancelPotentialWork(Message message, ImageView imageView) {
- final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
-
- if (bitmapWorkerTask != null) {
- final Message oldMessage = bitmapWorkerTask.message;
- if (oldMessage == null || message != oldMessage) {
- bitmapWorkerTask.cancel(true);
- } else {
- return false;
- }
- }
- return true;
- }
-
- private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
- if (imageView != null) {
- final Drawable drawable = imageView.getDrawable();
- if (drawable instanceof AsyncDrawable) {
- final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
- return asyncDrawable.getBitmapWorkerTask();
- }
- }
- return null;
- }
-
- static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
-
- public AsyncDrawable(Resources res, Bitmap bitmap,
- BitmapWorkerTask bitmapWorkerTask) {
- super(res, bitmap);
- bitmapWorkerTaskReference = new WeakReference<>(
- bitmapWorkerTask);
- }
-
- public BitmapWorkerTask getBitmapWorkerTask() {
- return bitmapWorkerTaskReference.get();
- }
- }
+ protected static final int REQUEST_ANNOUNCE_PGP = 0x0101;
+ protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x0102;
+ protected static final int REQUEST_CHOOSE_PGP_ID = 0x0103;
+ protected static final int REQUEST_BATTERY_OP = 0x13849ff;
+
+ public static final String EXTRA_ACCOUNT = "account";
+
+ public XmppConnectionService xmppConnectionService;
+ public boolean xmppConnectionServiceBound = false;
+ protected boolean registeredListeners = false;
+
+ protected int mPrimaryTextColor;
+ protected int mSecondaryTextColor;
+ protected int mTertiaryTextColor;
+ protected int mPrimaryBackgroundColor;
+ protected int mSecondaryBackgroundColor;
+ protected int mColorRed;
+ protected int mColorWhite;
+ protected int mColorOrange;
+ protected int mColorGreen;
+ protected int mPrimaryColor;
+
+ protected boolean mUseSubject = true;
+
+ private DisplayMetrics metrics;
+ protected int mTheme;
+ protected boolean mUsingEnterKey = false;
+
+ protected Toast mToast;
+
+ protected void hideToast() {
+ if (mToast != null) {
+ mToast.cancel();
+ }
+ }
+
+ protected void replaceToast(String msg) {
+ replaceToast(msg, true);
+ }
+
+ protected void replaceToast(String msg, boolean showlong) {
+ hideToast();
+ mToast = Toast.makeText(this, msg, showlong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
+ mToast.show();
+ }
+
+ public void showProgress() {
+
+ }
+
+ public void closeProgress() {
+
+ }
+
+ protected Runnable onOpenPGPKeyPublished = new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(XmppActivity.this, R.string.openpgp_has_been_published, Toast.LENGTH_SHORT).show();
+ }
+ };
+
+ private long mLastUiRefresh = 0;
+ private Handler mRefreshUiHandler = new Handler();
+ private Runnable mRefreshUiRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLastUiRefresh = SystemClock.elapsedRealtime();
+ refreshUiReal();
+ }
+ };
+
+ protected ConferenceInvite mPendingConferenceInvite = null;
+
+
+ protected final void refreshUi() {
+ final long diff = SystemClock.elapsedRealtime() - mLastUiRefresh;
+ if (diff > Config.REFRESH_UI_INTERVAL) {
+ mRefreshUiHandler.removeCallbacks(mRefreshUiRunnable);
+ runOnUiThread(mRefreshUiRunnable);
+ } else {
+ final long next = Config.REFRESH_UI_INTERVAL - diff;
+ mRefreshUiHandler.removeCallbacks(mRefreshUiRunnable);
+ mRefreshUiHandler.postDelayed(mRefreshUiRunnable, next);
+ }
+ }
+
+ abstract protected void refreshUiReal();
+
+ protected interface OnValueEdited {
+ public void onValueEdited(String value);
+ }
+
+ public interface OnPresenceSelected {
+ public void onPresenceSelected();
+ }
+
+ protected ServiceConnection mConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ XmppConnectionBinder binder = (XmppConnectionBinder) service;
+ xmppConnectionService = binder.getService();
+ xmppConnectionServiceBound = true;
+ if (!registeredListeners && shouldRegisterListeners()) {
+ registerListeners();
+ registeredListeners = true;
+ }
+ onBackendConnected();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ xmppConnectionServiceBound = false;
+ }
+ };
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (!xmppConnectionServiceBound) {
+ connectToBackend();
+ } else {
+ if (!registeredListeners) {
+ this.registerListeners();
+ this.registeredListeners = true;
+ }
+ this.onBackendConnected();
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+ protected boolean shouldRegisterListeners() {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return !isDestroyed() && !isFinishing();
+ } else {
+ return !isFinishing();
+ }
+ }
+
+ public void connectToBackend() {
+ Intent intent = new Intent(this, XmppConnectionService.class);
+ intent.setAction("ui");
+ startService(intent);
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (xmppConnectionServiceBound) {
+ if (registeredListeners) {
+ this.unregisterListeners();
+ this.registeredListeners = false;
+ }
+ unbindService(mConnection);
+ xmppConnectionServiceBound = false;
+ }
+ }
+
+ protected void hideKeyboard() {
+ InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ View focus = getCurrentFocus();
+
+ if (focus != null) {
+
+ inputManager.hideSoftInputFromWindow(focus.getWindowToken(),
+ InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ }
+
+ public boolean hasPgp() {
+ return xmppConnectionService.getPgpEngine() != null;
+ }
+
+ public void showInstallPgpDialog() {
+ Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.openkeychain_required));
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage(getText(R.string.openkeychain_required_long));
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setNeutralButton(getString(R.string.restart),
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (xmppConnectionServiceBound) {
+ unbindService(mConnection);
+ xmppConnectionServiceBound = false;
+ }
+ stopService(new Intent(XmppActivity.this,
+ XmppConnectionService.class));
+ finish();
+ }
+ });
+ builder.setPositiveButton(getString(R.string.install),
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Uri uri = Uri
+ .parse("market://details?id=org.sufficientlysecure.keychain");
+ Intent marketIntent = new Intent(Intent.ACTION_VIEW,
+ uri);
+ PackageManager manager = getApplicationContext()
+ .getPackageManager();
+ List<ResolveInfo> infos = manager
+ .queryIntentActivities(marketIntent, 0);
+ if (infos.size() > 0) {
+ startActivity(marketIntent);
+ } else {
+ uri = Uri.parse("http://www.openkeychain.org/");
+ Intent browserIntent = new Intent(
+ Intent.ACTION_VIEW, uri);
+ startActivity(browserIntent);
+ }
+ finish();
+ }
+ });
+ builder.create().show();
+ }
+
+ abstract void onBackendConnected();
+
+ protected void registerListeners() {
+ if (this instanceof XmppConnectionService.OnConversationUpdate) {
+ this.xmppConnectionService.setOnConversationListChangedListener((XmppConnectionService.OnConversationUpdate) this);
+ }
+ if (this instanceof XmppConnectionService.OnAccountUpdate) {
+ this.xmppConnectionService.setOnAccountListChangedListener((XmppConnectionService.OnAccountUpdate) this);
+ }
+ if (this instanceof XmppConnectionService.OnCaptchaRequested) {
+ this.xmppConnectionService.setOnCaptchaRequestedListener((XmppConnectionService.OnCaptchaRequested) this);
+ }
+ if (this instanceof XmppConnectionService.OnRosterUpdate) {
+ this.xmppConnectionService.setOnRosterUpdateListener((XmppConnectionService.OnRosterUpdate) this);
+ }
+ if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
+ this.xmppConnectionService.setOnMucRosterUpdateListener((XmppConnectionService.OnMucRosterUpdate) this);
+ }
+ if (this instanceof OnUpdateBlocklist) {
+ this.xmppConnectionService.setOnUpdateBlocklistListener((OnUpdateBlocklist) this);
+ }
+ if (this instanceof XmppConnectionService.OnShowErrorToast) {
+ this.xmppConnectionService.setOnShowErrorToastListener((XmppConnectionService.OnShowErrorToast) this);
+ }
+ if (this instanceof OnKeyStatusUpdated) {
+ this.xmppConnectionService.setOnKeyStatusUpdatedListener((OnKeyStatusUpdated) this);
+ }
+ }
+
+ protected void unregisterListeners() {
+ if (this instanceof XmppConnectionService.OnConversationUpdate) {
+ this.xmppConnectionService.removeOnConversationListChangedListener();
+ }
+ if (this instanceof XmppConnectionService.OnAccountUpdate) {
+ this.xmppConnectionService.removeOnAccountListChangedListener();
+ }
+ if (this instanceof XmppConnectionService.OnCaptchaRequested) {
+ this.xmppConnectionService.removeOnCaptchaRequestedListener();
+ }
+ if (this instanceof XmppConnectionService.OnRosterUpdate) {
+ this.xmppConnectionService.removeOnRosterUpdateListener();
+ }
+ if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
+ this.xmppConnectionService.removeOnMucRosterUpdateListener();
+ }
+ if (this instanceof OnUpdateBlocklist) {
+ this.xmppConnectionService.removeOnUpdateBlocklistListener();
+ }
+ if (this instanceof XmppConnectionService.OnShowErrorToast) {
+ this.xmppConnectionService.removeOnShowErrorToastListener();
+ }
+ if (this instanceof OnKeyStatusUpdated) {
+ this.xmppConnectionService.removeOnNewKeysAvailableListener();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_invite_user:
+ inviteUser();
+ break;
+ case R.id.action_create_issue:
+ createIssue();
+ break;
+ case R.id.action_settings:
+ startActivity(new Intent(this, SettingsActivity.class));
+ break;
+ case R.id.action_check_updates:
+ if (xmppConnectionService.hasInternetConnection()) {
+ startActivity(new Intent(this, UpdaterActivity.class));
+ } else {
+ Toast.makeText(this, R.string.account_status_no_internet, Toast.LENGTH_LONG).show();
+ }
+ break;
+ case R.id.action_accounts:
+ final Intent intent = new Intent(getApplicationContext(), EditAccountActivity.class);
+ Account mAccount = xmppConnectionService.getAccounts().get(0);
+ intent.putExtra("jid", mAccount.getJid().toBareJid().toString());
+ intent.putExtra("init", false);
+ startActivity(intent);
+ break;
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.action_show_qr_code:
+ showQrCode();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ metrics = getResources().getDisplayMetrics();
+ ExceptionHelper.init(getApplicationContext());
+ mPrimaryTextColor = getResources().getColor(R.color.black87);
+ mSecondaryTextColor = getResources().getColor(R.color.black54);
+ mTertiaryTextColor = getResources().getColor(R.color.black12);
+ mColorRed = getResources().getColor(R.color.red800);
+ mColorWhite = getResources().getColor(R.color.white70);
+ mColorOrange = getResources().getColor(R.color.orange500);
+ mColorGreen = getResources().getColor(R.color.realgreen);
+ mPrimaryColor = getResources().getColor(R.color.primary);
+ mPrimaryBackgroundColor = getResources().getColor(R.color.grey50);
+ mSecondaryBackgroundColor = getResources().getColor(R.color.grey200);
+ this.mTheme = findTheme();
+ setTheme(this.mTheme);
+ this.mUsingEnterKey = usingEnterKey();
+ mUseSubject = getPreferences().getBoolean("use_subject", true);
+ final ActionBar ab = getActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ protected boolean isOptimizingBattery() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
+ return !pm.isIgnoringBatteryOptimizations(getPackageName());
+ } else {
+ return false;
+ }
+ }
+
+ protected boolean isAffectedByDataSaver() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+ return cm.isActiveNetworkMetered()
+ && cm.getRestrictBackgroundStatus() == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+ } else {
+ return false;
+ }
+ }
+
+ protected boolean usingEnterKey() {
+ return getPreferences().getBoolean("display_enter_key", false);
+ }
+
+ protected SharedPreferences getPreferences() {
+ return PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+ }
+
+ public boolean useSubjectToIdentifyConference() {
+ return mUseSubject;
+ }
+
+ public void switchToConversation(Conversation conversation) {
+ switchToConversation(conversation, null, false);
+ }
+
+ public void switchToConversation(Conversation conversation, String text,
+ boolean newTask) {
+ switchToConversation(conversation, text, null, false, newTask);
+ }
+
+ public void highlightInMuc(Conversation conversation, String nick) {
+ switchToConversation(conversation, null, nick, false, false);
+ }
+
+ public void privateMsgInMuc(Conversation conversation, String nick) {
+ switchToConversation(conversation, null, nick, true, false);
+ }
+
+ private void switchToConversation(Conversation conversation, String text, String nick, boolean pm, boolean newTask) {
+ Intent viewConversationIntent = new Intent(this,
+ ConversationActivity.class);
+ viewConversationIntent.setAction(ConversationActivity.ACTION_VIEW_CONVERSATION);
+ viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
+ conversation.getUuid());
+ if (text != null) {
+ viewConversationIntent.putExtra(ConversationActivity.TEXT, text);
+ }
+ if (nick != null) {
+ viewConversationIntent.putExtra(ConversationActivity.NICK, nick);
+ viewConversationIntent.putExtra(ConversationActivity.PRIVATE_MESSAGE, pm);
+ }
+ if (newTask) {
+ viewConversationIntent.setFlags(viewConversationIntent.getFlags()
+ | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ } else {
+ viewConversationIntent.setFlags(viewConversationIntent.getFlags()
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ }
+ startActivity(viewConversationIntent);
+ finish();
+ }
+
+ public void switchToContactDetails(Contact contact) {
+ switchToContactDetails(contact, null);
+ }
+
+ public void switchToContactDetails(Contact contact, String messageFingerprint) {
+ Intent intent = new Intent(this, ContactDetailsActivity.class);
+ intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
+ intent.putExtra(EXTRA_ACCOUNT, contact.getAccount().getJid().toBareJid().toString());
+ intent.putExtra("contact", contact.getJid().toString());
+ intent.putExtra("fingerprint", messageFingerprint);
+ startActivity(intent);
+ }
+
+ public void switchToAccount(Account account) {
+ switchToAccount(account, false);
+ }
+
+ public void switchToAccount(Account account, boolean init) {
+ Intent intent = new Intent(this, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ intent.putExtra("init", init);
+ startActivity(intent);
+ }
+
+ protected void inviteToConversation(Conversation conversation) {
+ Intent intent = new Intent(getApplicationContext(),
+ ChooseContactActivity.class);
+ List<String> contacts = new ArrayList<>();
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ for (MucOptions.User user : conversation.getMucOptions().getUsers(false)) {
+ Jid jid = user.getRealJid();
+ if (jid != null) {
+ contacts.add(jid.toBareJid().toString());
+ }
+ }
+ } else {
+ contacts.add(conversation.getJid().toBareJid().toString());
+ }
+ intent.putExtra("filter_contacts", contacts.toArray(new String[contacts.size()]));
+ intent.putExtra("conversation", conversation.getUuid());
+ intent.putExtra("multiple", true);
+ intent.putExtra("show_enter_jid", true);
+ intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().toBareJid().toString());
+ startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION);
+ }
+
+ protected void announcePgp(Account account, final Conversation conversation, final Runnable onSuccess) {
+ if (account.getPgpId() == 0) {
+ choosePgpSignId(account);
+ } else {
+ String status = null;
+ if (manuallyChangePresence()) {
+ status = account.getPresenceStatusMessage();
+ }
+ if (status == null) {
+ status = "";
+ }
+ xmppConnectionService.getPgpEngine().generateSignature(account, status, new UiCallback<Account>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Account account) {
+ try {
+ startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
+ } catch (final SendIntentException ignored) {
+ }
+ }
+
+ @Override
+ public void success(Account account) {
+ xmppConnectionService.databaseBackend.updateAccount(account);
+ xmppConnectionService.sendPresence(account);
+ if (conversation != null) {
+ conversation.setNextEncryption(Message.ENCRYPTION_PGP);
+ xmppConnectionService.updateConversation(conversation);
+ refreshUi();
+ }
+ if (onSuccess != null) {
+ runOnUiThread(onSuccess);
+ }
+ }
+
+ @Override
+ public void error(int error, Account account) {
+ if (error == 0 && account != null) {
+ account.setPgpSignId(0);
+ account.unsetPgpSignature();
+ xmppConnectionService.databaseBackend.updateAccount(account);
+ choosePgpSignId(account);
+ } else {
+ displayErrorDialog(error);
+ }
+ }
+ });
+ }
+ }
+
+ protected boolean noAccountUsesPgp() {
+ if (!hasPgp()) {
+ return true;
+ }
+ for (Account account : xmppConnectionService.getAccounts()) {
+ if (account.getPgpId() != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @SuppressWarnings("deprecation")
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ protected void setListItemBackgroundOnView(View view) {
+ int sdk = android.os.Build.VERSION.SDK_INT;
+ if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
+ } else {
+ view.setBackground(getResources().getDrawable(R.drawable.greybackground));
+ }
+ }
+
+ protected void choosePgpSignId(Account account) {
+ xmppConnectionService.getPgpEngine().chooseKey(account, new UiCallback<Account>() {
+ @Override
+ public void success(Account account1) {
+ }
+
+ @Override
+ public void error(int errorCode, Account object) {
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Account object) {
+ try {
+ startIntentSenderForResult(pi.getIntentSender(),
+ REQUEST_CHOOSE_PGP_ID, null, 0, 0, 0);
+ } catch (final SendIntentException ignored) {
+ }
+ }
+ });
+ }
+
+ protected void displayErrorDialog(final int errorCode) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ XmppActivity.this);
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setTitle(getString(R.string.error));
+ builder.setMessage(errorCode);
+ builder.setNeutralButton(R.string.accept, null);
+ builder.create().show();
+ }
+ });
+
+ }
+
+ protected void showAddToRosterDialog(final Conversation conversation) {
+ showAddToRosterDialog(conversation.getContact());
+ }
+
+ protected void showAddToRosterDialog(final Contact contact) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(contact.getJid().toString());
+ builder.setMessage(getString(R.string.not_in_roster));
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.setPositiveButton(getString(R.string.add_contact),
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Jid jid = contact.getJid();
+ Account account = contact.getAccount();
+ Contact contact = account.getRoster().getContact(jid);
+ xmppConnectionService.createContact(contact);
+ }
+ });
+ builder.create().show();
+ }
+
+ private void showAskForPresenceDialog(final Contact contact) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(contact.getJid().toString());
+ builder.setMessage(R.string.request_presence_updates);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.request_now,
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.sendPresencePacket(contact
+ .getAccount(), xmppConnectionService
+ .getPresenceGenerator()
+ .requestPresenceUpdatesFrom(contact));
+ }
+ }
+ });
+ builder.create().show();
+ }
+
+ private void warnMutalPresenceSubscription(final Conversation conversation,
+ final OnPresenceSelected listener) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(conversation.getContact().getJid().toString());
+ builder.setMessage(R.string.without_mutual_presence_updates);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.ignore, new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ conversation.setNextCounterpart(null);
+ if (listener != null) {
+ listener.onPresenceSelected();
+ }
+ }
+ });
+ builder.create().show();
+ }
+
+ protected void quickEdit(String previousValue, int hint, OnValueEdited callback) {
+ quickEdit(previousValue, callback, hint, false);
+ }
+
+ protected void quickPasswordEdit(String previousValue, OnValueEdited callback) {
+ quickEdit(previousValue, callback, R.string.password, true);
+ }
+
+ @SuppressLint("InflateParams")
+ private void quickEdit(final String previousValue,
+ final OnValueEdited callback,
+ final int hint,
+ boolean password) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ View view = getLayoutInflater().inflate(R.layout.quickedit, null);
+ final EditText editor = (EditText) view.findViewById(R.id.editor);
+ OnClickListener mClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String value = editor.getText().toString();
+ if (!value.equals(previousValue) && value.trim().length() > 0) {
+ callback.onValueEdited(value);
+ }
+ }
+ };
+ if (password) {
+ editor.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ builder.setPositiveButton(R.string.accept, mClickListener);
+ } else {
+ builder.setPositiveButton(R.string.edit, mClickListener);
+ }
+ if (hint != 0) {
+ editor.setHint(hint);
+ }
+ editor.requestFocus();
+ editor.setText("");
+ if (previousValue != null) {
+ editor.getText().append(previousValue);
+ }
+ builder.setView(view);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.create().show();
+ }
+
+ public boolean hasStoragePermission(int requestCode) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public boolean hasMicPermission(int requestCode) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, requestCode);
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public boolean hasLocationPermission(int requestCode) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, requestCode);
+ requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, requestCode);
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public void selectPresence(final Conversation conversation,
+ final OnPresenceSelected listener) {
+ final Contact contact = conversation.getContact();
+ if (conversation.hasValidOtrSession()) {
+ SessionID id = conversation.getOtrSession().getSessionID();
+ Jid jid;
+ try {
+ jid = Jid.fromString(id.getAccountID() + "/" + id.getUserID());
+ } catch (InvalidJidException e) {
+ jid = null;
+ }
+ conversation.setNextCounterpart(jid);
+ listener.onPresenceSelected();
+ } else if (!contact.showInRoster()) {
+ showAddToRosterDialog(conversation);
+ } else {
+ final Presences presences = contact.getPresences();
+ if (presences.size() == 0) {
+ if (!contact.getOption(Contact.Options.TO)
+ && !contact.getOption(Contact.Options.ASKING)
+ && contact.getAccount().getStatus() == Account.State.ONLINE) {
+ showAskForPresenceDialog(contact);
+ } else if (!contact.getOption(Contact.Options.TO)
+ || !contact.getOption(Contact.Options.FROM)) {
+ warnMutalPresenceSubscription(conversation, listener);
+ } else {
+ conversation.setNextCounterpart(null);
+ listener.onPresenceSelected();
+ }
+ } else if (presences.size() == 1) {
+ String presence = presences.toResourceArray()[0];
+ try {
+ conversation.setNextCounterpart(Jid.fromParts(contact.getJid().getLocalpart(), contact.getJid().getDomainpart(), presence));
+ } catch (InvalidJidException e) {
+ conversation.setNextCounterpart(null);
+ }
+ listener.onPresenceSelected();
+ } else {
+ showPresenceSelectionDialog(presences, conversation, listener);
+ }
+ }
+ }
+
+ private void showPresenceSelectionDialog(Presences presences, final Conversation conversation, final OnPresenceSelected listener) {
+ final Contact contact = conversation.getContact();
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.choose_presence));
+ final String[] resourceArray = presences.toResourceArray();
+ Pair<Map<String, String>, Map<String, String>> typeAndName = presences.toTypeAndNameMap();
+ final Map<String, String> resourceTypeMap = typeAndName.first;
+ final Map<String, String> resourceNameMap = typeAndName.second;
+ final String[] readableIdentities = new String[resourceArray.length];
+ final AtomicInteger selectedResource = new AtomicInteger(0);
+ for (int i = 0; i < resourceArray.length; ++i) {
+ String resource = resourceArray[i];
+ if (resource.equals(contact.getLastResource())) {
+ selectedResource.set(i);
+ }
+ String type = resourceTypeMap.get(resource);
+ String name = resourceNameMap.get(resource);
+ if (type != null) {
+ if (Collections.frequency(resourceTypeMap.values(), type) == 1) {
+ readableIdentities[i] = UIHelper.tranlasteType(this, type);
+ } else if (name != null) {
+ if (Collections.frequency(resourceNameMap.values(), name) == 1
+ || CryptoHelper.UUID_PATTERN.matcher(resource).matches()) {
+ readableIdentities[i] = UIHelper.tranlasteType(this, type) + " (" + name + ")";
+ } else {
+ readableIdentities[i] = UIHelper.tranlasteType(this, type) + " (" + name + " / " + resource + ")";
+ }
+ } else {
+ readableIdentities[i] = UIHelper.tranlasteType(this, type) + " (" + resource + ")";
+ }
+ } else {
+ readableIdentities[i] = resource;
+ }
+ }
+ builder.setSingleChoiceItems(readableIdentities,
+ selectedResource.get(),
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ selectedResource.set(which);
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.ok, new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ try {
+ Jid next = Jid.fromParts(contact.getJid().getLocalpart(), contact.getJid().getDomainpart(), resourceArray[selectedResource.get()]);
+ conversation.setNextCounterpart(next);
+ } catch (InvalidJidException e) {
+ conversation.setNextCounterpart(null);
+ }
+ listener.onPresenceSelected();
+ }
+ });
+ builder.create().show();
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) {
+ mPendingConferenceInvite = ConferenceInvite.parse(data);
+ if (xmppConnectionServiceBound && mPendingConferenceInvite != null) {
+ if (mPendingConferenceInvite.execute(this)) {
+ mToast = Toast.makeText(this, R.string.creating_conference, Toast.LENGTH_LONG);
+ mToast.show();
+ }
+ mPendingConferenceInvite = null;
+ }
+ }
+ }
+
+
+ private UiCallback<Conversation> adhocCallback = new UiCallback<Conversation>() {
+ @Override
+ public void success(final Conversation conversation) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ switchToConversation(conversation);
+ hideToast();
+ }
+ });
+ }
+
+ @Override
+ public void error(final int errorCode, Conversation object) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ replaceToast(getString(errorCode));
+ }
+ });
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Conversation object) {
+
+ }
+ };
+
+ public int getTertiaryTextColor() {
+ return this.mTertiaryTextColor;
+ }
+
+ public int getSecondaryTextColor() {
+ return this.mSecondaryTextColor;
+ }
+
+ public int getPrimaryTextColor() {
+ return this.mPrimaryTextColor;
+ }
+
+ public int getWarningTextColor() {
+ return this.mColorRed;
+ }
+
+ public int getUnencryptedTextColor() {
+ return this.mColorWhite;
+ }
+
+ public int getOnlineColor() {
+ return this.mColorGreen;
+ }
+
+ public int getPrimaryBackgroundColor() {
+ return this.mPrimaryBackgroundColor;
+ }
+
+ public int getSecondaryBackgroundColor() {
+ return this.mSecondaryBackgroundColor;
+ }
+
+ public int getPixel(int dp) {
+ DisplayMetrics metrics = getResources().getDisplayMetrics();
+ return ((int) (dp * metrics.density));
+ }
+
+ public boolean copyTextToClipboard(String text, int labelResId) {
+ ClipboardManager mClipBoardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ String label = getResources().getString(labelResId);
+ if (mClipBoardManager != null) {
+ ClipData mClipData = ClipData.newPlainText(label, text);
+ mClipBoardManager.setPrimaryClip(mClipData);
+ return true;
+ }
+ return false;
+ }
+
+ protected void registerNdefPushMessageCallback() {
+ NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (nfcAdapter != null && nfcAdapter.isEnabled()) {
+ nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
+ @Override
+ public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
+ return new NdefMessage(new NdefRecord[]{
+ NdefRecord.createUri(getShareableUri()),
+ NdefRecord.createApplicationRecord("de.pixart.messenger")
+ });
+ }
+ }, this);
+ }
+ }
+
+ protected boolean neverCompressPictures() {
+ return getPreferences().getString("picture_compression", "auto").equals("never");
+ }
+
+ protected boolean manuallyChangePresence() {
+ return getPreferences().getBoolean("manually_change_presence", true);
+ }
+
+ protected void unregisterNdefPushMessageCallback() {
+ NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (nfcAdapter != null && nfcAdapter.isEnabled()) {
+ nfcAdapter.setNdefPushMessageCallback(null, this);
+ }
+ }
+
+ protected String getShareableUri() {
+ return null;
+ }
+
+ private void inviteUser() {
+ Account mAccount = xmppConnectionService.getAccounts().get(0);
+ String user = mAccount.getJid().getLocalpart().toString();
+ String domain = mAccount.getJid().getDomainpart().toString();
+ String inviteURL = Config.inviteUserURL + user + "/" + domain;
+ String inviteText = getString(R.string.InviteText, user);
+ Intent intent = new Intent(android.content.Intent.ACTION_SEND);
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_SUBJECT, user + " " + getString(R.string.inviteUser_Subject) + " " + getString(R.string.app_name));
+ intent.putExtra(Intent.EXTRA_TEXT, inviteText + "\n\n" + inviteURL);
+ startActivity(Intent.createChooser(intent, getString(R.string.invite_contact)));
+ }
+
+ private void createIssue() {
+ String IssueURL = Config.ISSUE_URL;
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(IssueURL));
+ startActivity(intent);
+ }
+
+ protected void shareUri() {
+ String uri = getShareableUri();
+ if (uri == null || uri.isEmpty()) {
+ return;
+ }
+ Intent shareIntent = new Intent();
+ shareIntent.setAction(Intent.ACTION_SEND);
+ shareIntent.putExtra(Intent.EXTRA_TEXT, getShareableUri());
+ shareIntent.setType("text/plain");
+ try {
+ startActivity(Intent.createChooser(shareIntent, getText(R.string.share_uri_with)));
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, R.string.no_application_to_share_uri, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (this.getShareableUri() != null) {
+ this.registerNdefPushMessageCallback();
+ }
+ }
+
+ protected int findTheme() {
+ if (getPreferences().getBoolean("use_larger_font", false)) {
+ return R.style.ConversationsTheme_LargerText;
+ } else {
+ return R.style.ConversationsTheme;
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ this.unregisterNdefPushMessageCallback();
+ }
+
+ protected void showQrCode() {
+ String uri = getShareableUri();
+ if (uri != null) {
+ Point size = new Point();
+ getWindowManager().getDefaultDisplay().getSize(size);
+ final int width = (size.x < size.y ? size.x : size.y);
+ Bitmap bitmap = createQrCodeBitmap(uri, width);
+ ImageView view = new ImageView(this);
+ view.setImageBitmap(bitmap);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setView(view);
+ builder.create().show();
+ }
+ }
+
+ protected Bitmap createQrCodeBitmap(String input, int size) {
+ Log.d(Config.LOGTAG, "qr code requested size: " + size);
+ try {
+ final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
+ final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
+ final BitMatrix result = QR_CODE_WRITER.encode(input, BarcodeFormat.QR_CODE, size, size, hints);
+ final int width = result.getWidth();
+ final int height = result.getHeight();
+ final int[] pixels = new int[width * height];
+ for (int y = 0; y < height; y++) {
+ final int offset = y * width;
+ for (int x = 0; x < width; x++) {
+ pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT;
+ }
+ }
+ final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Log.d(Config.LOGTAG, "output size: " + width + "x" + height);
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ return bitmap;
+ } catch (final WriterException e) {
+ return null;
+ }
+ }
+
+ protected Account extractAccount(Intent intent) {
+ String jid = intent != null ? intent.getStringExtra(EXTRA_ACCOUNT) : null;
+ try {
+ return jid != null ? xmppConnectionService.findAccountByJid(Jid.fromString(jid)) : null;
+ } catch (InvalidJidException e) {
+ return null;
+ }
+ }
+
+ public static class ConferenceInvite {
+ private String uuid;
+ private List<Jid> jids = new ArrayList<>();
+
+ public static ConferenceInvite parse(Intent data) {
+ ConferenceInvite invite = new ConferenceInvite();
+ invite.uuid = data.getStringExtra("conversation");
+ if (invite.uuid == null) {
+ return null;
+ }
+ try {
+ if (data.getBooleanExtra("multiple", false)) {
+ String[] toAdd = data.getStringArrayExtra("contacts");
+ for (String item : toAdd) {
+ invite.jids.add(Jid.fromString(item));
+ }
+ } else {
+ invite.jids.add(Jid.fromString(data.getStringExtra("contact")));
+ }
+ } catch (final InvalidJidException ignored) {
+ return null;
+ }
+ return invite;
+ }
+
+ public boolean execute(XmppActivity activity) {
+ XmppConnectionService service = activity.xmppConnectionService;
+ Conversation conversation = service.findConversationByUuid(this.uuid);
+ if (conversation == null) {
+ return false;
+ }
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ for (Jid jid : jids) {
+ service.invite(conversation, jid);
+ }
+ return false;
+ } else {
+ jids.add(conversation.getJid().toBareJid());
+ service.createAdhocConference(conversation.getAccount(), null, jids, activity.adhocCallback);
+ return true;
+ }
+ }
+ }
+
+ public AvatarService avatarService() {
+ return xmppConnectionService.getAvatarService();
+ }
+
+ class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
+ private final WeakReference<ImageView> imageViewReference;
+ private Message message = null;
+
+ public BitmapWorkerTask(ImageView imageView) {
+ imageViewReference = new WeakReference<>(imageView);
+ }
+
+ @Override
+ protected Bitmap doInBackground(Message... params) {
+ if (isCancelled()) {
+ return null;
+ }
+ message = params[0];
+ try {
+ return xmppConnectionService.getFileBackend().getThumbnail(
+ message, (int) (metrics.density * 288), false);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (bitmap != null && !isCancelled()) {
+ final ImageView imageView = imageViewReference.get();
+ if (imageView != null) {
+ imageView.setImageBitmap(bitmap);
+ imageView.setBackgroundColor(0x00000000);
+ }
+ }
+ }
+ }
+
+ public void loadBitmap(Message message, ImageView imageView) {
+ Bitmap bm;
+ try {
+ bm = xmppConnectionService.getFileBackend().getThumbnail(message,
+ (int) (metrics.density * 288), true);
+ } catch (FileNotFoundException e) {
+ bm = null;
+ }
+ if (bm != null) {
+ cancelPotentialWork(message, imageView);
+ imageView.setImageBitmap(bm);
+ imageView.setBackgroundColor(0x00000000);
+ } else {
+ if (cancelPotentialWork(message, imageView)) {
+ imageView.setBackgroundColor(0xff333333);
+ imageView.setImageDrawable(null);
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable = new AsyncDrawable(
+ getResources(), null, task);
+ imageView.setImageDrawable(asyncDrawable);
+ try {
+ task.execute(message);
+ } catch (final RejectedExecutionException ignored) {
+ ignored.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public static boolean cancelPotentialWork(Message message, ImageView imageView) {
+ final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+ if (bitmapWorkerTask != null) {
+ final Message oldMessage = bitmapWorkerTask.message;
+ if (oldMessage == null || message != oldMessage) {
+ bitmapWorkerTask.cancel(true);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+ if (imageView != null) {
+ final Drawable drawable = imageView.getDrawable();
+ if (drawable instanceof AsyncDrawable) {
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+ return asyncDrawable.getBitmapWorkerTask();
+ }
+ }
+ return null;
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap,
+ BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference = new WeakReference<>(
+ bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java
index 812c41ace..f8eb6f4f0 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java
@@ -17,43 +17,43 @@ import de.pixart.messenger.ui.XmppActivity;
public class AccountAdapter extends ArrayAdapter<Account> {
- private XmppActivity activity;
+ private XmppActivity activity;
- public AccountAdapter(XmppActivity activity, List<Account> objects) {
- super(activity, 0, objects);
- this.activity = activity;
- }
+ public AccountAdapter(XmppActivity activity, List<Account> objects) {
+ super(activity, 0, objects);
+ this.activity = activity;
+ }
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- final Account account = getItem(position);
- if (view == null) {
- LayoutInflater inflater = (LayoutInflater) getContext()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = inflater.inflate(R.layout.account_row, parent, false);
- }
- TextView jid = (TextView) view.findViewById(R.id.account_jid);
- if (Config.DOMAIN_LOCK != null) {
- jid.setText(account.getJid().getLocalpart());
- } else {
- jid.setText(account.getJid().toBareJid().toString());
- }
- TextView statusView = (TextView) view.findViewById(R.id.account_status);
- ImageView imageView = (ImageView) view.findViewById(R.id.account_image);
- imageView.setImageBitmap(activity.avatarService().get(account, activity.getPixel(56)));
- statusView.setText(getContext().getString(account.getStatus().getReadableId()));
- switch (account.getStatus()) {
- case ONLINE:
- statusView.setTextColor(activity.getOnlineColor());
- break;
- case DISABLED:
- case CONNECTING:
- statusView.setTextColor(activity.getSecondaryTextColor());
- break;
- default:
- statusView.setTextColor(activity.getWarningTextColor());
- break;
- }
- return view;
- }
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ final Account account = getItem(position);
+ if (view == null) {
+ LayoutInflater inflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = inflater.inflate(R.layout.account_row, parent, false);
+ }
+ TextView jid = (TextView) view.findViewById(R.id.account_jid);
+ if (Config.DOMAIN_LOCK != null) {
+ jid.setText(account.getJid().getLocalpart());
+ } else {
+ jid.setText(account.getJid().toBareJid().toString());
+ }
+ TextView statusView = (TextView) view.findViewById(R.id.account_status);
+ ImageView imageView = (ImageView) view.findViewById(R.id.account_image);
+ imageView.setImageBitmap(activity.avatarService().get(account, activity.getPixel(56)));
+ statusView.setText(getContext().getString(account.getStatus().getReadableId()));
+ switch (account.getStatus()) {
+ case ONLINE:
+ statusView.setTextColor(activity.getOnlineColor());
+ break;
+ case DISABLED:
+ case CONNECTING:
+ statusView.setTextColor(activity.getSecondaryTextColor());
+ break;
+ default:
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ }
+ return view;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
index f19dfa43b..d78c82cb2 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java
@@ -29,13 +29,13 @@ import de.pixart.messenger.xmpp.chatstate.ChatState;
public class ConversationAdapter extends ArrayAdapter<Conversation> {
- private XmppActivity activity;
+ private XmppActivity activity;
- public ConversationAdapter(XmppActivity activity,
- List<Conversation> conversations) {
- super(activity, 0, conversations);
- this.activity = activity;
- }
+ public ConversationAdapter(XmppActivity activity,
+ List<Conversation> conversations) {
+ super(activity, 0, conversations);
+ this.activity = activity;
+ }
public static boolean cancelPotentialWork(Conversation conversation, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
@@ -62,8 +62,8 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
return null;
}
- @Override
- public View getView(int position, View view, ViewGroup parent) {
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.conversation_list_row, parent, false);
@@ -126,23 +126,23 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
}
}
} else {*/
- Pair<String, Boolean> preview = UIHelper.getMessagePreview(activity, message);
- mLastMessage.setVisibility(View.VISIBLE);
- imagePreview.setVisibility(View.GONE);
- mLastMessage.setText(preview.first);
- if (preview.second) {
- if (conversation.isRead()) {
- mLastMessage.setTypeface(null, Typeface.ITALIC);
- } else {
- mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC);
- }
+ Pair<String, Boolean> preview = UIHelper.getMessagePreview(activity, message);
+ mLastMessage.setVisibility(View.VISIBLE);
+ imagePreview.setVisibility(View.GONE);
+ mLastMessage.setText(preview.first);
+ if (preview.second) {
+ if (conversation.isRead()) {
+ mLastMessage.setTypeface(null, Typeface.ITALIC);
} else {
- if (conversation.isRead()) {
- mLastMessage.setTypeface(null, Typeface.NORMAL);
- } else {
- mLastMessage.setTypeface(null, Typeface.BOLD);
- }
+ mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC);
}
+ } else {
+ if (conversation.isRead()) {
+ mLastMessage.setTypeface(null, Typeface.NORMAL);
+ } else {
+ mLastMessage.setTypeface(null, Typeface.BOLD);
+ }
+ }
//}
long muted_till = conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
@@ -168,41 +168,41 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC);
}
return view;
- }
-
- public void loadAvatar(Conversation conversation, ImageView imageView) {
- if (cancelPotentialWork(conversation, imageView)) {
- final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true);
- if (bm != null) {
- cancelPotentialWork(conversation, imageView);
- imageView.setImageBitmap(bm);
- imageView.setBackgroundColor(0x00000000);
- } else {
- imageView.setBackgroundColor(UIHelper.getColorForName(conversation.getName()));
- imageView.setImageDrawable(null);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
- imageView.setImageDrawable(asyncDrawable);
- try {
- task.execute(conversation);
- } catch (final RejectedExecutionException ignored) {
- }
- }
- }
- }
-
- static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
-
- public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
- super(res, bitmap);
- bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
- }
-
- public BitmapWorkerTask getBitmapWorkerTask() {
- return bitmapWorkerTaskReference.get();
- }
- }
+ }
+
+ public void loadAvatar(Conversation conversation, ImageView imageView) {
+ if (cancelPotentialWork(conversation, imageView)) {
+ final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true);
+ if (bm != null) {
+ cancelPotentialWork(conversation, imageView);
+ imageView.setImageBitmap(bm);
+ imageView.setBackgroundColor(0x00000000);
+ } else {
+ imageView.setBackgroundColor(UIHelper.getColorForName(conversation.getName()));
+ imageView.setImageDrawable(null);
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+ imageView.setImageDrawable(asyncDrawable);
+ try {
+ task.execute(conversation);
+ } catch (final RejectedExecutionException ignored) {
+ }
+ }
+ }
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
class BitmapWorkerTask extends AsyncTask<Conversation, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java
index 2906f16ba..6096a7196 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java
@@ -9,62 +9,62 @@ import java.util.List;
import java.util.Locale;
public class KnownHostsAdapter extends ArrayAdapter<String> {
- private ArrayList<String> domains;
- private Filter domainFilter = new Filter() {
+ private ArrayList<String> domains;
+ private Filter domainFilter = new Filter() {
- @Override
- protected FilterResults performFiltering(CharSequence constraint) {
- if (constraint != null) {
- ArrayList<String> suggestions = new ArrayList<>();
- final String[] split = constraint.toString().split("@");
- if (split.length == 1) {
- for (String domain : domains) {
- suggestions.add(split[0].toLowerCase(Locale
- .getDefault()) + "@" + domain);
- }
- } else if (split.length == 2) {
- for (String domain : domains) {
- if (domain.contentEquals(split[1])) {
- suggestions.clear();
- break;
- } else if (domain.contains(split[1])) {
- suggestions.add(split[0].toLowerCase(Locale
- .getDefault()) + "@" + domain);
- }
- }
- } else {
- return new FilterResults();
- }
- FilterResults filterResults = new FilterResults();
- filterResults.values = suggestions;
- filterResults.count = suggestions.size();
- return filterResults;
- } else {
- return new FilterResults();
- }
- }
+ @Override
+ protected FilterResults performFiltering(CharSequence constraint) {
+ if (constraint != null) {
+ ArrayList<String> suggestions = new ArrayList<>();
+ final String[] split = constraint.toString().split("@");
+ if (split.length == 1) {
+ for (String domain : domains) {
+ suggestions.add(split[0].toLowerCase(Locale
+ .getDefault()) + "@" + domain);
+ }
+ } else if (split.length == 2) {
+ for (String domain : domains) {
+ if (domain.contentEquals(split[1])) {
+ suggestions.clear();
+ break;
+ } else if (domain.contains(split[1])) {
+ suggestions.add(split[0].toLowerCase(Locale
+ .getDefault()) + "@" + domain);
+ }
+ }
+ } else {
+ return new FilterResults();
+ }
+ FilterResults filterResults = new FilterResults();
+ filterResults.values = suggestions;
+ filterResults.count = suggestions.size();
+ return filterResults;
+ } else {
+ return new FilterResults();
+ }
+ }
- @Override
- protected void publishResults(CharSequence constraint,
- FilterResults results) {
- ArrayList filteredList = (ArrayList) results.values;
- if (results != null && results.count > 0) {
- clear();
- for (Object c : filteredList) {
- add((String) c);
- }
- notifyDataSetChanged();
- }
- }
- };
+ @Override
+ protected void publishResults(CharSequence constraint,
+ FilterResults results) {
+ ArrayList filteredList = (ArrayList) results.values;
+ if (results != null && results.count > 0) {
+ clear();
+ for (Object c : filteredList) {
+ add((String) c);
+ }
+ notifyDataSetChanged();
+ }
+ }
+ };
- public KnownHostsAdapter(Context context, int viewResourceId, List<String> mKnownHosts) {
- super(context, viewResourceId, new ArrayList<String>());
- domains = new ArrayList<>(mKnownHosts);
- }
+ public KnownHostsAdapter(Context context, int viewResourceId, List<String> mKnownHosts) {
+ super(context, viewResourceId, new ArrayList<String>());
+ domains = new ArrayList<>(mKnownHosts);
+ }
- @Override
- public Filter getFilter() {
- return domainFilter;
- }
+ @Override
+ public Filter getFilter() {
+ return domainFilter;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
index 691b901db..1502b187d 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
@@ -28,156 +28,156 @@ import de.pixart.messenger.utils.UIHelper;
public class ListItemAdapter extends ArrayAdapter<ListItem> {
- protected XmppActivity activity;
- protected boolean showDynamicTags = false;
- private View.OnClickListener onTagTvClick = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (view instanceof TextView && mOnTagClickedListener != null) {
- TextView tv = (TextView) view;
- final String tag = tv.getText().toString();
- mOnTagClickedListener.onTagClicked(tag);
- }
- }
- };
- private OnTagClickedListener mOnTagClickedListener = null;
-
- public ListItemAdapter(XmppActivity activity, List<ListItem> objects) {
- super(activity, 0, objects);
- this.activity = activity;
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
- this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false);
- }
-
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- LayoutInflater inflater = (LayoutInflater) getContext()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- ListItem item = getItem(position);
- if (view == null) {
- view = inflater.inflate(R.layout.contact, parent, false);
- }
- TextView tvName = (TextView) view.findViewById(R.id.contact_display_name);
- TextView tvJid = (TextView) view.findViewById(R.id.contact_jid);
- ImageView picture = (ImageView) view.findViewById(R.id.contact_photo);
- FlowLayout tagLayout = (FlowLayout) view.findViewById(R.id.tags);
-
- List<ListItem.Tag> tags = item.getTags(activity);
- if (tags.size() == 0 || !this.showDynamicTags) {
- tagLayout.setVisibility(View.GONE);
- } else {
- tagLayout.setVisibility(View.VISIBLE);
- tagLayout.removeAllViewsInLayout();
- for(ListItem.Tag tag : tags) {
- TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,tagLayout,false);
- tv.setText(tag.getName());
- tv.setBackgroundColor(tag.getColor());
- tv.setOnClickListener(this.onTagTvClick);
- tagLayout.addView(tv);
- }
- }
- final String jid = item.getDisplayJid();
- if (jid != null) {
- tvJid.setVisibility(View.VISIBLE);
- tvJid.setText(jid);
- } else {
- tvJid.setVisibility(View.GONE);
- }
- tvName.setText(item.getDisplayName());
- loadAvatar(item,picture);
- return view;
- }
-
- public void setOnTagClickedListener(OnTagClickedListener listener) {
- this.mOnTagClickedListener = listener;
- }
-
- public interface OnTagClickedListener {
- void onTagClicked(String tag);
- }
-
- class BitmapWorkerTask extends AsyncTask<ListItem, Void, Bitmap> {
- private final WeakReference<ImageView> imageViewReference;
- private ListItem item = null;
-
- public BitmapWorkerTask(ImageView imageView) {
- imageViewReference = new WeakReference<>(imageView);
- }
-
- @Override
- protected Bitmap doInBackground(ListItem... params) {
- return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled());
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null && !isCancelled()) {
- final ImageView imageView = imageViewReference.get();
- if (imageView != null) {
- imageView.setImageBitmap(bitmap);
- imageView.setBackgroundColor(0x00000000);
- }
- }
- }
- }
-
- public void loadAvatar(ListItem item, ImageView imageView) {
- if (cancelPotentialWork(item, imageView)) {
- final Bitmap bm = activity.avatarService().get(item,activity.getPixel(48),true);
- if (bm != null) {
- cancelPotentialWork(item, imageView);
- imageView.setImageBitmap(bm);
- imageView.setBackgroundColor(0x00000000);
- } else {
- imageView.setBackgroundColor(UIHelper.getColorForName(item.getDisplayName()));
- imageView.setImageDrawable(null);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
- imageView.setImageDrawable(asyncDrawable);
- try {
- task.execute(item);
- } catch (final RejectedExecutionException ignored) {
- }
- }
- }
- }
-
- public static boolean cancelPotentialWork(ListItem item, ImageView imageView) {
- final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
-
- if (bitmapWorkerTask != null) {
- final ListItem oldItem = bitmapWorkerTask.item;
- if (oldItem == null || item != oldItem) {
- bitmapWorkerTask.cancel(true);
- } else {
- return false;
- }
- }
- return true;
- }
-
- private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
- if (imageView != null) {
- final Drawable drawable = imageView.getDrawable();
- if (drawable instanceof AsyncDrawable) {
- final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
- return asyncDrawable.getBitmapWorkerTask();
- }
- }
- return null;
- }
-
- static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
-
- public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
- super(res, bitmap);
- bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
- }
-
- public BitmapWorkerTask getBitmapWorkerTask() {
- return bitmapWorkerTaskReference.get();
- }
- }
+ protected XmppActivity activity;
+ protected boolean showDynamicTags = false;
+ private View.OnClickListener onTagTvClick = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (view instanceof TextView && mOnTagClickedListener != null) {
+ TextView tv = (TextView) view;
+ final String tag = tv.getText().toString();
+ mOnTagClickedListener.onTagClicked(tag);
+ }
+ }
+ };
+ private OnTagClickedListener mOnTagClickedListener = null;
+
+ public ListItemAdapter(XmppActivity activity, List<ListItem> objects) {
+ super(activity, 0, objects);
+ this.activity = activity;
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
+ this.showDynamicTags = preferences.getBoolean("show_dynamic_tags", false);
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ LayoutInflater inflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ ListItem item = getItem(position);
+ if (view == null) {
+ view = inflater.inflate(R.layout.contact, parent, false);
+ }
+ TextView tvName = (TextView) view.findViewById(R.id.contact_display_name);
+ TextView tvJid = (TextView) view.findViewById(R.id.contact_jid);
+ ImageView picture = (ImageView) view.findViewById(R.id.contact_photo);
+ FlowLayout tagLayout = (FlowLayout) view.findViewById(R.id.tags);
+
+ List<ListItem.Tag> tags = item.getTags(activity);
+ if (tags.size() == 0 || !this.showDynamicTags) {
+ tagLayout.setVisibility(View.GONE);
+ } else {
+ tagLayout.setVisibility(View.VISIBLE);
+ tagLayout.removeAllViewsInLayout();
+ for (ListItem.Tag tag : tags) {
+ TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, tagLayout, false);
+ tv.setText(tag.getName());
+ tv.setBackgroundColor(tag.getColor());
+ tv.setOnClickListener(this.onTagTvClick);
+ tagLayout.addView(tv);
+ }
+ }
+ final String jid = item.getDisplayJid();
+ if (jid != null) {
+ tvJid.setVisibility(View.VISIBLE);
+ tvJid.setText(jid);
+ } else {
+ tvJid.setVisibility(View.GONE);
+ }
+ tvName.setText(item.getDisplayName());
+ loadAvatar(item, picture);
+ return view;
+ }
+
+ public void setOnTagClickedListener(OnTagClickedListener listener) {
+ this.mOnTagClickedListener = listener;
+ }
+
+ public interface OnTagClickedListener {
+ void onTagClicked(String tag);
+ }
+
+ class BitmapWorkerTask extends AsyncTask<ListItem, Void, Bitmap> {
+ private final WeakReference<ImageView> imageViewReference;
+ private ListItem item = null;
+
+ public BitmapWorkerTask(ImageView imageView) {
+ imageViewReference = new WeakReference<>(imageView);
+ }
+
+ @Override
+ protected Bitmap doInBackground(ListItem... params) {
+ return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled());
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (bitmap != null && !isCancelled()) {
+ final ImageView imageView = imageViewReference.get();
+ if (imageView != null) {
+ imageView.setImageBitmap(bitmap);
+ imageView.setBackgroundColor(0x00000000);
+ }
+ }
+ }
+ }
+
+ public void loadAvatar(ListItem item, ImageView imageView) {
+ if (cancelPotentialWork(item, imageView)) {
+ final Bitmap bm = activity.avatarService().get(item, activity.getPixel(48), true);
+ if (bm != null) {
+ cancelPotentialWork(item, imageView);
+ imageView.setImageBitmap(bm);
+ imageView.setBackgroundColor(0x00000000);
+ } else {
+ imageView.setBackgroundColor(UIHelper.getColorForName(item.getDisplayName()));
+ imageView.setImageDrawable(null);
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+ imageView.setImageDrawable(asyncDrawable);
+ try {
+ task.execute(item);
+ } catch (final RejectedExecutionException ignored) {
+ }
+ }
+ }
+ }
+
+ public static boolean cancelPotentialWork(ListItem item, ImageView imageView) {
+ final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+ if (bitmapWorkerTask != null) {
+ final ListItem oldItem = bitmapWorkerTask.item;
+ if (oldItem == null || item != oldItem) {
+ bitmapWorkerTask.cancel(true);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+ if (imageView != null) {
+ final Drawable drawable = imageView.getDrawable();
+ if (drawable instanceof AsyncDrawable) {
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+ return asyncDrawable.getBitmapWorkerTask();
+ }
+ }
+ return null;
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
index 02cbbdfd0..6cf16fdd5 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
@@ -74,941 +74,941 @@ import nl.changer.audiowife.AudioWife;
public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextView.CopyHandler {
- private static final int SENT = 0;
- private static final int RECEIVED = 1;
- private static final int STATUS = 2;
- private static final Pattern XMPP_PATTERN = Pattern
- .compile("xmpp\\:(?:(?:["
- + Patterns.GOOD_IRI_CHAR
- + "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])"
- + "|(?:\\%[a-fA-F0-9]{2}))+");
-
- private ConversationActivity activity;
-
- private DisplayMetrics metrics;
-
- private AudioWife audioWife;
-
- private OnContactPictureClicked mOnContactPictureClickedListener;
- private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
-
- private boolean mIndicateReceived = false;
- private final ListSelectionManager listSelectionManager = new ListSelectionManager();
- private HashMap<Integer, AudioWife> audioPlayer;
- private boolean mUseWhiteBackground = false;
-
- public MessageAdapter(ConversationActivity activity, List<Message> messages) {
- super(activity, 0, messages);
- this.activity = activity;
- metrics = getContext().getResources().getDisplayMetrics();
- updatePreferences();
- }
-
- public void setOnContactPictureClicked(OnContactPictureClicked listener) {
- this.mOnContactPictureClickedListener = listener;
- }
-
- public void setOnContactPictureLongClicked(
- OnContactPictureLongClicked listener) {
- this.mOnContactPictureLongClickedListener = listener;
- }
-
- @Override
- public int getViewTypeCount() {
- return 3;
- }
-
- public int getItemViewType(Message message) {
- if (message.getType() == Message.TYPE_STATUS) {
- return STATUS;
- } else if (message.getStatus() <= Message.STATUS_RECEIVED) {
- return RECEIVED;
- }
-
- return SENT;
- }
-
- @Override
- public int getItemViewType(int position) {
- return getItemViewType(getItem(position));
- }
-
- private int getMessageTextColor(boolean onDark, boolean primary) {
- if (onDark) {
- return activity.getResources().getColor(primary ? R.color.dark : R.color.primary);
- } else {
- return activity.getResources().getColor(primary ? R.color.dark : R.color.primary);
- }
- }
-
- private void displayStatus(ViewHolder viewHolder, Message message, int type, boolean darkBackground) {
- String filesize = null;
- String info = null;
- boolean error = false;
- if (viewHolder.indicatorReceived != null) {
- viewHolder.indicatorReceived.setVisibility(View.GONE);
- viewHolder.indicatorRead.setVisibility(View.GONE);
- }
-
- if (viewHolder.edit_indicator != null) {
- if (message.edited()) {
- viewHolder.edit_indicator.setVisibility(View.VISIBLE);
- } else {
- viewHolder.edit_indicator.setVisibility(View.GONE);
- }
- }
- boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
- && message.getMergedStatus() <= Message.STATUS_RECEIVED;
- if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) {
- FileParams params = message.getFileParams();
- if (params.size > (1 * 1024 * 1024)) {
- filesize = params.size / (1024 * 1024) + " MiB";
- } else if (params.size > (1 * 1024)) {
- filesize = params.size / 1024 + " KiB";
- } else if (params.size > 0) {
- filesize = "1 KiB";
- }
- if (message.getTransferable() != null && message.getTransferable().getStatus() == Transferable.STATUS_FAILED) {
- error = true;
- }
- }
- switch (message.getMergedStatus()) {
- case Message.STATUS_WAITING:
- info = getContext().getString(R.string.waiting);
- break;
- case Message.STATUS_UNSEND:
- Transferable d = message.getTransferable();
- if (d != null) {
- info = getContext().getString(R.string.sending_file, d.getProgress());
- } else {
- info = getContext().getString(R.string.sending);
- }
- break;
- case Message.STATUS_OFFERED:
- info = getContext().getString(R.string.offering);
- break;
- case Message.STATUS_SEND_RECEIVED:
- if (mIndicateReceived) {
- viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
- }
- break;
- case Message.STATUS_SEND_DISPLAYED:
- if (mIndicateReceived) {
- viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
- viewHolder.indicatorRead.setVisibility(View.VISIBLE);
- }
- break;
- case Message.STATUS_SEND_FAILED:
- info = getContext().getString(R.string.send_failed);
- error = true;
- break;
- default:
- if (multiReceived) {
- info = UIHelper.getMessageDisplayName(message);
- }
- break;
- }
- if (error && type == SENT) {
- viewHolder.time.setTextColor(activity.getWarningTextColor());
- } else if (!message.isValidInSession() && type == RECEIVED) {
- viewHolder.time.setTextColor(activity.getUnencryptedTextColor());
- } else {
- viewHolder.time.setTextColor(this.getMessageTextColor(darkBackground,false));
- }
- if (message.getEncryption() == Message.ENCRYPTION_NONE) {
- viewHolder.indicator.setVisibility(View.GONE);
- } else {
- viewHolder.indicator.setVisibility(View.VISIBLE);
- if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
- FingerprintStatus status = message.getConversation()
- .getAccount().getAxolotlService().getFingerprintTrust(
- message.getFingerprint());
-
- if(status == null || (!status.isTrustedAndActive())) {
- viewHolder.indicator.setColorFilter(activity.getWarningTextColor());
- viewHolder.indicator.setAlpha(1.0f);
- } else {
- viewHolder.indicator.clearColorFilter();
- if (darkBackground) {
- viewHolder.indicator.setAlpha(0.7f);
- } else {
- viewHolder.indicator.setAlpha(0.57f);
- }
- }
- } else {
- viewHolder.indicator.clearColorFilter();
- if (darkBackground) {
- viewHolder.indicator.setAlpha(0.7f);
- } else {
- viewHolder.indicator.setAlpha(0.57f);
- }
- }
- }
-
- String formatedTime = UIHelper.readableTimeDifferenceFull(getContext(),
- message.getMergedTimeSent());
- if (message.getStatus() <= Message.STATUS_RECEIVED) {
- if ((filesize != null) && (info != null)) {
- viewHolder.time.setText(formatedTime + " \u00B7 " + filesize +" \u00B7 " + info);
- } else if ((filesize == null) && (info != null)) {
- viewHolder.time.setText(formatedTime + " \u00B7 " + info);
- } else if ((filesize != null) && (info == null)) {
- viewHolder.time.setText(formatedTime + " \u00B7 " + filesize);
- } else {
- viewHolder.time.setText(formatedTime);
- }
- } else {
- if ((filesize != null) && (info != null)) {
- viewHolder.time.setText(filesize + " \u00B7 " + info);
- } else if ((filesize == null) && (info != null)) {
- if (error) {
- viewHolder.time.setText(info + " \u00B7 " + formatedTime);
- } else {
- viewHolder.time.setText(info);
- }
- } else if ((filesize != null) && (info == null)) {
- viewHolder.time.setText(filesize + " \u00B7 " + formatedTime);
- } else {
- viewHolder.time.setText(formatedTime);
- }
- }
- }
-
- private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) {
- viewHolder.aw_player.setVisibility(View.GONE);
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.VISIBLE);
- viewHolder.messageBody.setText(text);
- viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false));
- viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
- viewHolder.messageBody.setTextIsSelectable(false);
- }
-
- private void displayDecryptionFailed(ViewHolder viewHolder, boolean darkBackground) {
- viewHolder.aw_player.setVisibility(View.GONE);
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.VISIBLE);
- viewHolder.messageBody.setText(getContext().getString(
- R.string.decryption_failed));
- viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false));
- viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
- viewHolder.messageBody.setTextIsSelectable(false);
- }
-
- private void displayHeartMessage(final ViewHolder viewHolder, final String body) {
- viewHolder.aw_player.setVisibility(View.GONE);
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.VISIBLE);
- viewHolder.messageBody.setIncludeFontPadding(false);
- Spannable span = new SpannableString(body);
- span.setSpan(new RelativeSizeSpan(4.0f), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- span.setSpan(new ForegroundColorSpan(activity.getWarningTextColor()), 0, body.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- viewHolder.messageBody.setText(span);
- }
-
- private void displayXmppMessage(final ViewHolder viewHolder, final String body) {
- String contact = body.toLowerCase();
- contact = contact.split(":")[1];
- contact = contact.split("\\?")[0];
- String add_contact = activity.getString(R.string.add_to_contact_list) + " (" + contact + ")";
- viewHolder.aw_player.setVisibility(View.GONE);
- viewHolder.download_button.setVisibility(View.VISIBLE);
- viewHolder.download_button.setText(add_contact);
- viewHolder.download_button.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(body));
- activity.startActivity(intent);
- }
- });
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
-
- }
-
- private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground) {
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.VISIBLE);
- viewHolder.messageBody.setIncludeFontPadding(true);
- if (message.getBody() != null) {
- final String nick = UIHelper.getMessageDisplayName(message);
- SpannableStringBuilder body = message.getMergedBody();
- boolean hasMeCommand = message.hasMeCommand();
- if (hasMeCommand) {
- body = body.replace(0, Message.ME_COMMAND.length(), nick + " ");
- }
- if (body.length() > Config.MAX_DISPLAY_MESSAGE_CHARS) {
- body = new SpannableStringBuilder(body, 0, Config.MAX_DISPLAY_MESSAGE_CHARS);
- body.append("\u2026");
- }
- Message.MergeSeparator[] mergeSeparators = body.getSpans(0, body.length(), Message.MergeSeparator.class);
- for (Message.MergeSeparator mergeSeparator : mergeSeparators) {
- int start = body.getSpanStart(mergeSeparator);
- int end = body.getSpanEnd(mergeSeparator);
- body.setSpan(new RelativeSizeSpan(0.3f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- if (message.getType() != Message.TYPE_PRIVATE) {
- if (hasMeCommand) {
- body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- } else {
- String privateMarker;
- if (message.getStatus() <= Message.STATUS_RECEIVED) {
- privateMarker = activity
- .getString(R.string.private_message);
- } else {
- final String to;
- if (message.getCounterpart() != null) {
- to = message.getCounterpart().getResourcepart();
- } else {
- to = "";
- }
- privateMarker = activity.getString(R.string.private_message_to, to);
- }
- body.insert(0, privateMarker);
- int privateMarkerIndex = privateMarker.length();
- body.insert(privateMarkerIndex, " ");
- body.setSpan(new ForegroundColorSpan(getMessageTextColor(darkBackground, false)), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- body.setSpan(new StyleSpan(Typeface.BOLD), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- if (hasMeCommand) {
- body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarkerIndex + 1, privateMarkerIndex + 1 + nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- Linkify.addLinks(body, Linkify.WEB_URLS);
- Linkify.addLinks(body, XMPP_PATTERN, "xmpp");
- Linkify.addLinks(body, GeoHelper.GEO_URI, "geo");
- viewHolder.messageBody.setAutoLinkMask(0);
- viewHolder.messageBody.setText(body);
- viewHolder.messageBody.setTextIsSelectable(true);
- viewHolder.messageBody.setMovementMethod(ClickableMovementMethod.getInstance());
- listSelectionManager.onUpdate(viewHolder.messageBody, message);
-
- } else {
- viewHolder.messageBody.setText("");
- viewHolder.messageBody.setTextIsSelectable(false);
- }
- viewHolder.messageBody.setTextColor(this.getMessageTextColor(darkBackground, true));
- viewHolder.messageBody.setLinkTextColor(this.getMessageTextColor(darkBackground, true));
- viewHolder.messageBody.setHighlightColor(activity.getResources().getColor(darkBackground ? R.color.grey800 : R.color.grey500));
- viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
- }
-
- private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text) {
- viewHolder.aw_player.setVisibility(View.GONE);
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- viewHolder.download_button.setVisibility(View.VISIBLE);
- viewHolder.download_button.setText(text);
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_download_grey600_48dp,0,0,0);
- viewHolder.download_button.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.startDownloadable(message);
- }
- });
- }
-
- private void displayAudioMessage(ViewHolder viewHolder, final Message message, int position) {
- if (audioPlayer == null) audioPlayer = new HashMap<>();
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.aw_player.setVisibility(View.VISIBLE);
- Uri audioFile = Uri.fromFile(activity.xmppConnectionService.getFileBackend().getFile(message));
-
- audioWife = audioPlayer.get(position);
- if (audioWife == null) {
- audioWife = new AudioWife();
- audioWife.init(getContext(), audioFile);
- audioPlayer.put(position, audioWife);
- RelativeLayout vg = new RelativeLayout(activity);
- LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- audioWife.useDefaultUi(vg, layoutInflater);
- viewHolder.aw_player.addView(audioWife.getPlayerUi());
- } else {
- audioWife.cleanPlayerUi();
- viewHolder.aw_player.addView(audioWife.getPlayerUi());
- }
- }
-
- private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
- viewHolder.aw_player.setVisibility(View.GONE);
- viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- viewHolder.download_button.setVisibility(View.VISIBLE);
- String mimeType = message.getMimeType();
- String fullName = "";
- if (mimeType != null) {
- if (message.getMimeType().contains("pdf")) {
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_file_pdf_grey600_48dp,0,0,0);
- } else if (message.getMimeType().contains("vcard")) {
- File file = new File(activity.xmppConnectionService.getFileBackend().getFile(message).toString());
- VCard vcard = null;
- String name = null;
- String version = null;
- try {
- vcard = Ezvcard.parse(file).first();
- } catch (IOException e) {
- e.printStackTrace();
- }
- if (vcard != null) {
- version = vcard.getVersion().toString();
- Log.d(Config.LOGTAG, "VCard version: " + version);
- name = vcard.getFormattedName().getValue();
- fullName = " (" + name + ")";
- }
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_account_card_details_grey600_48dp,0,0,0);
- } else if (message.getMimeType().contains("calendar")) {
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_calendar_grey600_48dp,0,0,0);
- } else {
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_file_grey600_48dp,0,0,0);
- }
- }
- viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message) + fullName));
- viewHolder.download_button.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- openDownloadable(message);
- }
- });
- }
-
- private void displayLocationMessage(ViewHolder viewHolder, final Message message) {
- viewHolder.aw_player.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- String url = GeoHelper.MapPreviewUri(message);
- viewHolder.image.setVisibility(View.VISIBLE);
- viewHolder.image.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- showLocation(message);
- }
- });
- Glide
- .with(activity)
- .load(Uri.parse(url))
- .asBitmap()
- .diskCacheStrategy(DiskCacheStrategy.ALL)
- .fitCenter()
- .placeholder(R.drawable.ic_map_marker_grey600_48dp)
- .error(R.drawable.ic_map_marker_grey600_48dp)
- .into(viewHolder.image);
- viewHolder.image.setMaxWidth(500);
- viewHolder.image.setAdjustViewBounds(true);
- viewHolder.download_button.setVisibility(View.GONE);
- viewHolder.download_button.setText(R.string.show_location);
- viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_map_marker_grey600_48dp, 0, 0, 0);
- viewHolder.download_button.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- showLocation(message);
- }
- });
-
- }
-
- private void displayImageMessage(ViewHolder viewHolder,
- final Message message) {
- viewHolder.aw_player.setVisibility(View.GONE);
- if (viewHolder.download_button != null) {
- viewHolder.download_button.setVisibility(View.GONE);
- }
- viewHolder.messageBody.setVisibility(View.GONE);
- viewHolder.image.setVisibility(View.VISIBLE);
- FileParams params = message.getFileParams();
- double target = metrics.density * 200;
- int scaledW;
- int scaledH;
- if (Math.max(params.height, params.width) * metrics.density <= target) {
- scaledW = (int) (params.width * metrics.density);
- scaledH = (int) (params.height * metrics.density);
- } else if (Math.max(params.height,params.width) <= target) {
- scaledW = params.width;
- scaledH = params.height;
- } else if (params.width <= params.height) {
- scaledW = (int) (params.width / ((double) params.height / target));
- scaledH = (int) target;
- } else {
- scaledW = (int) target;
- scaledH = (int) (params.height / ((double) params.width / target));
- }
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scaledW, scaledH);
- layoutParams.setMargins(0, (int) (metrics.density * 4), 0, (int) (metrics.density * 4));
- viewHolder.image.setLayoutParams(layoutParams);
- activity.loadBitmap(message, viewHolder.image);
- viewHolder.image.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- openDownloadable(message);
- }
- });
- }
-
- private void loadMoreMessages(Conversation conversation) {
- conversation.setLastClearHistory(0);
- activity.xmppConnectionService.updateConversation(conversation);
- conversation.setHasMessagesLeftOnServer(true);
- conversation.setFirstMamReference(null);
- long timestamp = conversation.getLastMessageTransmitted();
- if (timestamp == 0) {
- timestamp = System.currentTimeMillis();
- }
- activity.setMessagesLoaded();
- activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp);
- Toast.makeText(activity, R.string.fetching_history_from_server,Toast.LENGTH_LONG).show();
- }
-
- @Override
- public View getView(int position, View unused, ViewGroup parent) {
- final Message message = getItem(position);
- final boolean isInValidSession = message.isValidInSession();
- final Conversation conversation = message.getConversation();
- final Account account = conversation.getAccount();
- final int type = getItemViewType(position);
- ViewHolder viewHolder;
- View view;
- viewHolder = new ViewHolder();
- switch (type) {
- case SENT:
- view = activity.getLayoutInflater().inflate(
- R.layout.message_sent, parent, false);
- viewHolder.message_box = (LinearLayout) view
- .findViewById(R.id.message_box);
- viewHolder.contact_picture = (ImageView) view
- .findViewById(R.id.message_photo);
- viewHolder.aw_player = (ViewGroup) view.findViewById(R.id.aw_player);
- viewHolder.download_button = (Button) view
- .findViewById(R.id.download_button);
- viewHolder.indicator = (ImageView) view
- .findViewById(R.id.security_indicator);
- viewHolder.edit_indicator = (ImageView) view.findViewById(R.id.edit_indicator);
- viewHolder.image = (ImageView) view
- .findViewById(R.id.message_image);
- viewHolder.messageBody = (CopyTextView) view
- .findViewById(R.id.message_body);
- viewHolder.time = (TextView) view
- .findViewById(R.id.message_time);
- viewHolder.indicatorReceived = (ImageView) view
- .findViewById(R.id.indicator_received);
- viewHolder.indicatorRead = (ImageView) view
- .findViewById(R.id.indicator_read);
- break;
- case RECEIVED:
- view = activity.getLayoutInflater().inflate(
- R.layout.message_received, parent, false);
- viewHolder.message_box = (LinearLayout) view
- .findViewById(R.id.message_box);
- viewHolder.contact_picture = (ImageView) view
- .findViewById(R.id.message_photo);
- viewHolder.aw_player = (ViewGroup) view.findViewById(R.id.aw_player);
- viewHolder.download_button = (Button) view
- .findViewById(R.id.download_button);
- viewHolder.indicator = (ImageView) view
- .findViewById(R.id.security_indicator);
- viewHolder.edit_indicator = (ImageView) view.findViewById(R.id.edit_indicator);
- viewHolder.image = (ImageView) view
- .findViewById(R.id.message_image);
- viewHolder.messageBody = (CopyTextView) view
- .findViewById(R.id.message_body);
- viewHolder.time = (TextView) view
- .findViewById(R.id.message_time);
- viewHolder.indicatorReceived = (ImageView) view
- .findViewById(R.id.indicator_received);
- viewHolder.encryption = (TextView) view.findViewById(R.id.message_encryption);
- break;
- case STATUS:
- view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
- viewHolder.contact_picture = (ImageView) view.findViewById(R.id.message_photo);
- viewHolder.status_message = (TextView) view.findViewById(R.id.status_message);
- viewHolder.load_more_messages = (Button) view.findViewById(R.id.load_more_messages);
- break;
- default:
- view = new View(getContext());
- viewHolder = null;
- break;
- }
- if (viewHolder.messageBody != null) {
- listSelectionManager.onCreate(viewHolder.messageBody);
- viewHolder.messageBody.setCopyHandler(this);
- }
- view.setTag(viewHolder);
- if (viewHolder == null) {
- return view;
- }
-
-
- boolean darkBackground = (type == SENT && (!isInValidSession || !mUseWhiteBackground));
-
- if (type == STATUS) {
- if ("LOAD_MORE".equals(message.getBody())) {
- viewHolder.status_message.setVisibility(View.GONE);
- viewHolder.contact_picture.setVisibility(View.GONE);
- viewHolder.load_more_messages.setVisibility(View.VISIBLE);
- viewHolder.load_more_messages.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- loadMoreMessages(message.getConversation());
- }
- });
- } else {
- viewHolder.status_message.setVisibility(View.VISIBLE);
- viewHolder.contact_picture.setVisibility(View.VISIBLE);
- viewHolder.load_more_messages.setVisibility(View.GONE);
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- viewHolder.contact_picture.setImageBitmap(activity
- .avatarService().get(conversation.getContact(),
- activity.getPixel(32)));
- viewHolder.contact_picture.setAlpha(0.5f);
- }
- viewHolder.status_message.setText(message.getBody());
- }
- return view;
- } else {
- loadAvatar(message, viewHolder.contact_picture);
- }
-
- viewHolder.contact_picture
- .setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
- MessageAdapter.this.mOnContactPictureClickedListener
- .onContactPictureClicked(message);
- }
-
- }
- });
- viewHolder.contact_picture
- .setOnLongClickListener(new OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View v) {
- if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
- MessageAdapter.this.mOnContactPictureLongClickedListener
- .onContactPictureLongClicked(message);
- return true;
- } else {
- return false;
- }
- }
- });
-
- final Transferable transferable = message.getTransferable();
- String mimeType = message.getMimeType();
- if (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING) {
- if (transferable.getStatus() == Transferable.STATUS_OFFER) {
- displayDownloadableMessage(viewHolder,message,activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)));
- } else if (transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) {
- displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
- } else {
- displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first,darkBackground);
- }
- } else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
- displayImageMessage(viewHolder, message);
- } else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
- if (message.getFileParams().width > 0) {
- displayImageMessage(viewHolder,message);
- } else {
- if (mimeType != null) {
- if (message.getMimeType().startsWith("audio/")) {
- displayAudioMessage(viewHolder, message, position);
- } else displayOpenableMessage(viewHolder, message);
- } else displayOpenableMessage(viewHolder, message);
- }
- } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- if (account.isPgpDecryptionServiceConnected()) {
- if (!account.hasPendingPgpIntent(conversation)) {
- displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground);
- } else {
- displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground);
- }
- } else {
- displayInfoMessage(viewHolder,activity.getString(R.string.install_openkeychain),darkBackground);
- if (viewHolder != null) {
- viewHolder.message_box
- .setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.showInstallPgpDialog();
- }
- });
- }
- }
- } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
- displayDecryptionFailed(viewHolder,darkBackground);
- } else {
- if (GeoHelper.isGeoUri(message.getBody())) {
- displayLocationMessage(viewHolder,message);
- } else if (message.bodyIsHeart()) {
- displayHeartMessage(viewHolder, message.getBody().trim());
- } else if (message.bodyIsXmpp()) {
- displayXmppMessage(viewHolder, message.getBody().trim());
- } else if (message.treatAsDownloadable() == Message.Decision.MUST ||
- message.treatAsDownloadable() == Message.Decision.SHOULD) {
- try {
- URL url = new URL(message.getBody());
- displayDownloadableMessage(viewHolder,
- message,
- activity.getString(R.string.check_x_filesize_on_host,
- UIHelper.getFileDescriptionString(activity, message),
- url.getHost()));
- } catch (Exception e) {
- displayDownloadableMessage(viewHolder,
- message,
- activity.getString(R.string.check_x_filesize,
- UIHelper.getFileDescriptionString(activity, message)));
- }
- } else {
- displayTextMessage(viewHolder, message, darkBackground);
- }
- }
-
- if (type == RECEIVED) {
- if(isInValidSession) {
- viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received);
- viewHolder.encryption.setVisibility(View.GONE);
- } else {
- viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_warning);
- viewHolder.encryption.setVisibility(View.VISIBLE);
- viewHolder.encryption.setText(CryptoHelper.encryptionTypeToText(message.getEncryption()));
- }
- }
-
- if (type == SENT) {
- if (mUseWhiteBackground) {
- viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_sent_white);
- } else {
- viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_sent);
- }
- }
-
- displayStatus(viewHolder, message, type, darkBackground);
-
- return view;
- }
-
- @Override
- public void notifyDataSetChanged() {
- listSelectionManager.onBeforeNotifyDataSetChanged();
- super.notifyDataSetChanged();
- listSelectionManager.onAfterNotifyDataSetChanged();
- }
-
- @Override
- public String transformTextForCopy(CharSequence text, int start, int end) {
- return text.toString().substring(start, end);
- }
-
- public void openDownloadable(Message message) {
- DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
- if (!file.exists()) {
- Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
- return;
- }
- String mime = file.getMimeType();
- if (mime.startsWith("image/")) {
- Intent intent = new Intent(getContext(), ShowFullscreenMessageActivity.class);
- intent.putExtra("image", Uri.fromFile(file));
- try {
- activity.startActivity(intent);
- return;
- } catch (ActivityNotFoundException e) {
- //ignored
- }
- } else if (mime.startsWith("video/")) {
- Intent intent = new Intent(getContext(), ShowFullscreenMessageActivity.class);
- intent.putExtra("video", Uri.fromFile(file));
- try {
- activity.startActivity(intent);
- return;
- } catch (ActivityNotFoundException e) {
- //ignored
- }
- }
- Intent openIntent = new Intent(Intent.ACTION_VIEW);
- if (mime == null) {
- mime = "*/*";
- }
- Uri uri;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- try {
- uri = FileProvider.getUriForFile(activity, FileBackend.CONVERSATIONS_FILE_PROVIDER, file);
- } catch (IllegalArgumentException e) {
- Toast.makeText(activity,activity.getString(R.string.no_permission_to_access_x,file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
- return;
- }
- openIntent.setDataAndType(uri, mime);
- openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- } else {
- uri = Uri.fromFile(file);
- }
- openIntent.setDataAndType(uri, mime);
- PackageManager manager = activity.getPackageManager();
- List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
- if (info.size() == 0) {
- openIntent.setDataAndType(Uri.fromFile(file),"*/*");
- }
- try {
- getContext().startActivity(openIntent);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show();
- }
- }
-
- public void showLocation(Message message) {
- for(Intent intent : GeoHelper.createGeoIntentsFromMessage(message)) {
- if (intent.resolveActivity(getContext().getPackageManager()) != null) {
- getContext().startActivity(intent);
- return;
- }
- }
- Toast.makeText(activity,R.string.no_application_found_to_display_location,Toast.LENGTH_SHORT).show();
- }
-
- public void updatePreferences() {
- this.mIndicateReceived = activity.indicateReceived();
- this.mUseWhiteBackground = activity.useWhiteBackground();
- }
-
- public TextView getMessageBody(View view) {
- final Object tag = view.getTag();
- if (tag instanceof ViewHolder) {
- final ViewHolder viewHolder = (ViewHolder) tag;
- return viewHolder.messageBody;
- }
- return null;
- }
-
- public interface OnContactPictureClicked {
- void onContactPictureClicked(Message message);
- }
-
- public interface OnContactPictureLongClicked {
- void onContactPictureLongClicked(Message message);
- }
-
- private static class ViewHolder {
-
- protected LinearLayout message_box;
- protected Button download_button;
- protected ViewGroup aw_player;
- protected ImageView image;
- protected ImageView indicator;
- protected ImageView indicatorReceived;
- protected ImageView indicatorRead;
- protected TextView time;
- protected CopyTextView messageBody;
- protected ImageView contact_picture;
- protected TextView status_message;
- protected TextView encryption;
- public Button load_more_messages;
- public ImageView edit_indicator;
- }
-
- class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
- private final WeakReference<ImageView> imageViewReference;
- private Message message = null;
-
- public BitmapWorkerTask(ImageView imageView) {
- imageViewReference = new WeakReference<>(imageView);
- }
-
- @Override
- protected Bitmap doInBackground(Message... params) {
- return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled());
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null && !isCancelled()) {
- final ImageView imageView = imageViewReference.get();
- if (imageView != null) {
- imageView.setImageBitmap(bitmap);
- imageView.setBackgroundColor(0x00000000);
- }
- }
- }
- }
-
- public void loadAvatar(Message message, ImageView imageView) {
- if (cancelPotentialWork(message, imageView)) {
- final Bitmap bm = activity.avatarService().get(message, activity.getPixel(48), true);
- if (bm != null) {
- cancelPotentialWork(message, imageView);
- imageView.setImageBitmap(bm);
- imageView.setBackgroundColor(0x00000000);
- } else {
- imageView.setBackgroundColor(UIHelper.getColorForName(UIHelper.getMessageDisplayName(message)));
- imageView.setImageDrawable(null);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
- imageView.setImageDrawable(asyncDrawable);
- try {
- task.execute(message);
- } catch (final RejectedExecutionException ignored) {
- }
- }
- }
- }
-
- public static boolean cancelPotentialWork(Message message, ImageView imageView) {
- final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
-
- if (bitmapWorkerTask != null) {
- final Message oldMessage = bitmapWorkerTask.message;
- if (oldMessage == null || message != oldMessage) {
- bitmapWorkerTask.cancel(true);
- } else {
- return false;
- }
- }
- return true;
- }
-
- private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
- if (imageView != null) {
- final Drawable drawable = imageView.getDrawable();
- if (drawable instanceof AsyncDrawable) {
- final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
- return asyncDrawable.getBitmapWorkerTask();
- }
- }
- return null;
- }
-
- static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
-
- public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
- super(res, bitmap);
- bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
- }
-
- public BitmapWorkerTask getBitmapWorkerTask() {
- return bitmapWorkerTaskReference.get();
- }
- }
+ private static final int SENT = 0;
+ private static final int RECEIVED = 1;
+ private static final int STATUS = 2;
+ private static final Pattern XMPP_PATTERN = Pattern
+ .compile("xmpp\\:(?:(?:["
+ + Patterns.GOOD_IRI_CHAR
+ + "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])"
+ + "|(?:\\%[a-fA-F0-9]{2}))+");
+
+ private ConversationActivity activity;
+
+ private DisplayMetrics metrics;
+
+ private AudioWife audioWife;
+
+ private OnContactPictureClicked mOnContactPictureClickedListener;
+ private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
+
+ private boolean mIndicateReceived = false;
+ private final ListSelectionManager listSelectionManager = new ListSelectionManager();
+ private HashMap<Integer, AudioWife> audioPlayer;
+ private boolean mUseWhiteBackground = false;
+
+ public MessageAdapter(ConversationActivity activity, List<Message> messages) {
+ super(activity, 0, messages);
+ this.activity = activity;
+ metrics = getContext().getResources().getDisplayMetrics();
+ updatePreferences();
+ }
+
+ public void setOnContactPictureClicked(OnContactPictureClicked listener) {
+ this.mOnContactPictureClickedListener = listener;
+ }
+
+ public void setOnContactPictureLongClicked(
+ OnContactPictureLongClicked listener) {
+ this.mOnContactPictureLongClickedListener = listener;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 3;
+ }
+
+ public int getItemViewType(Message message) {
+ if (message.getType() == Message.TYPE_STATUS) {
+ return STATUS;
+ } else if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ return RECEIVED;
+ }
+
+ return SENT;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return getItemViewType(getItem(position));
+ }
+
+ private int getMessageTextColor(boolean onDark, boolean primary) {
+ if (onDark) {
+ return activity.getResources().getColor(primary ? R.color.dark : R.color.primary);
+ } else {
+ return activity.getResources().getColor(primary ? R.color.dark : R.color.primary);
+ }
+ }
+
+ private void displayStatus(ViewHolder viewHolder, Message message, int type, boolean darkBackground) {
+ String filesize = null;
+ String info = null;
+ boolean error = false;
+ if (viewHolder.indicatorReceived != null) {
+ viewHolder.indicatorReceived.setVisibility(View.GONE);
+ viewHolder.indicatorRead.setVisibility(View.GONE);
+ }
+
+ if (viewHolder.edit_indicator != null) {
+ if (message.edited()) {
+ viewHolder.edit_indicator.setVisibility(View.VISIBLE);
+ } else {
+ viewHolder.edit_indicator.setVisibility(View.GONE);
+ }
+ }
+ boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
+ && message.getMergedStatus() <= Message.STATUS_RECEIVED;
+ if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) {
+ FileParams params = message.getFileParams();
+ if (params.size > (1 * 1024 * 1024)) {
+ filesize = params.size / (1024 * 1024) + " MiB";
+ } else if (params.size > (1 * 1024)) {
+ filesize = params.size / 1024 + " KiB";
+ } else if (params.size > 0) {
+ filesize = "1 KiB";
+ }
+ if (message.getTransferable() != null && message.getTransferable().getStatus() == Transferable.STATUS_FAILED) {
+ error = true;
+ }
+ }
+ switch (message.getMergedStatus()) {
+ case Message.STATUS_WAITING:
+ info = getContext().getString(R.string.waiting);
+ break;
+ case Message.STATUS_UNSEND:
+ Transferable d = message.getTransferable();
+ if (d != null) {
+ info = getContext().getString(R.string.sending_file, d.getProgress());
+ } else {
+ info = getContext().getString(R.string.sending);
+ }
+ break;
+ case Message.STATUS_OFFERED:
+ info = getContext().getString(R.string.offering);
+ break;
+ case Message.STATUS_SEND_RECEIVED:
+ if (mIndicateReceived) {
+ viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
+ }
+ break;
+ case Message.STATUS_SEND_DISPLAYED:
+ if (mIndicateReceived) {
+ viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
+ viewHolder.indicatorRead.setVisibility(View.VISIBLE);
+ }
+ break;
+ case Message.STATUS_SEND_FAILED:
+ info = getContext().getString(R.string.send_failed);
+ error = true;
+ break;
+ default:
+ if (multiReceived) {
+ info = UIHelper.getMessageDisplayName(message);
+ }
+ break;
+ }
+ if (error && type == SENT) {
+ viewHolder.time.setTextColor(activity.getWarningTextColor());
+ } else if (!message.isValidInSession() && type == RECEIVED) {
+ viewHolder.time.setTextColor(activity.getUnencryptedTextColor());
+ } else {
+ viewHolder.time.setTextColor(this.getMessageTextColor(darkBackground, false));
+ }
+ if (message.getEncryption() == Message.ENCRYPTION_NONE) {
+ viewHolder.indicator.setVisibility(View.GONE);
+ } else {
+ viewHolder.indicator.setVisibility(View.VISIBLE);
+ if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ FingerprintStatus status = message.getConversation()
+ .getAccount().getAxolotlService().getFingerprintTrust(
+ message.getFingerprint());
+
+ if (status == null || (!status.isTrustedAndActive())) {
+ viewHolder.indicator.setColorFilter(activity.getWarningTextColor());
+ viewHolder.indicator.setAlpha(1.0f);
+ } else {
+ viewHolder.indicator.clearColorFilter();
+ if (darkBackground) {
+ viewHolder.indicator.setAlpha(0.7f);
+ } else {
+ viewHolder.indicator.setAlpha(0.57f);
+ }
+ }
+ } else {
+ viewHolder.indicator.clearColorFilter();
+ if (darkBackground) {
+ viewHolder.indicator.setAlpha(0.7f);
+ } else {
+ viewHolder.indicator.setAlpha(0.57f);
+ }
+ }
+ }
+
+ String formatedTime = UIHelper.readableTimeDifferenceFull(getContext(),
+ message.getMergedTimeSent());
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ if ((filesize != null) && (info != null)) {
+ viewHolder.time.setText(formatedTime + " \u00B7 " + filesize + " \u00B7 " + info);
+ } else if ((filesize == null) && (info != null)) {
+ viewHolder.time.setText(formatedTime + " \u00B7 " + info);
+ } else if ((filesize != null) && (info == null)) {
+ viewHolder.time.setText(formatedTime + " \u00B7 " + filesize);
+ } else {
+ viewHolder.time.setText(formatedTime);
+ }
+ } else {
+ if ((filesize != null) && (info != null)) {
+ viewHolder.time.setText(filesize + " \u00B7 " + info);
+ } else if ((filesize == null) && (info != null)) {
+ if (error) {
+ viewHolder.time.setText(info + " \u00B7 " + formatedTime);
+ } else {
+ viewHolder.time.setText(info);
+ }
+ } else if ((filesize != null) && (info == null)) {
+ viewHolder.time.setText(filesize + " \u00B7 " + formatedTime);
+ } else {
+ viewHolder.time.setText(formatedTime);
+ }
+ }
+ }
+
+ private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.VISIBLE);
+ viewHolder.messageBody.setText(text);
+ viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false));
+ viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
+ viewHolder.messageBody.setTextIsSelectable(false);
+ }
+
+ private void displayDecryptionFailed(ViewHolder viewHolder, boolean darkBackground) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.VISIBLE);
+ viewHolder.messageBody.setText(getContext().getString(
+ R.string.decryption_failed));
+ viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false));
+ viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
+ viewHolder.messageBody.setTextIsSelectable(false);
+ }
+
+ private void displayHeartMessage(final ViewHolder viewHolder, final String body) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.VISIBLE);
+ viewHolder.messageBody.setIncludeFontPadding(false);
+ Spannable span = new SpannableString(body);
+ span.setSpan(new RelativeSizeSpan(4.0f), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ span.setSpan(new ForegroundColorSpan(activity.getWarningTextColor()), 0, body.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ viewHolder.messageBody.setText(span);
+ }
+
+ private void displayXmppMessage(final ViewHolder viewHolder, final String body) {
+ String contact = body.toLowerCase();
+ contact = contact.split(":")[1];
+ contact = contact.split("\\?")[0];
+ String add_contact = activity.getString(R.string.add_to_contact_list) + " (" + contact + ")";
+ viewHolder.aw_player.setVisibility(View.GONE);
+ viewHolder.download_button.setVisibility(View.VISIBLE);
+ viewHolder.download_button.setText(add_contact);
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(body));
+ activity.startActivity(intent);
+ }
+ });
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+
+ }
+
+ private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground) {
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.VISIBLE);
+ viewHolder.messageBody.setIncludeFontPadding(true);
+ if (message.getBody() != null) {
+ final String nick = UIHelper.getMessageDisplayName(message);
+ SpannableStringBuilder body = message.getMergedBody();
+ boolean hasMeCommand = message.hasMeCommand();
+ if (hasMeCommand) {
+ body = body.replace(0, Message.ME_COMMAND.length(), nick + " ");
+ }
+ if (body.length() > Config.MAX_DISPLAY_MESSAGE_CHARS) {
+ body = new SpannableStringBuilder(body, 0, Config.MAX_DISPLAY_MESSAGE_CHARS);
+ body.append("\u2026");
+ }
+ Message.MergeSeparator[] mergeSeparators = body.getSpans(0, body.length(), Message.MergeSeparator.class);
+ for (Message.MergeSeparator mergeSeparator : mergeSeparators) {
+ int start = body.getSpanStart(mergeSeparator);
+ int end = body.getSpanEnd(mergeSeparator);
+ body.setSpan(new RelativeSizeSpan(0.3f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (message.getType() != Message.TYPE_PRIVATE) {
+ if (hasMeCommand) {
+ body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ } else {
+ String privateMarker;
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ privateMarker = activity
+ .getString(R.string.private_message);
+ } else {
+ final String to;
+ if (message.getCounterpart() != null) {
+ to = message.getCounterpart().getResourcepart();
+ } else {
+ to = "";
+ }
+ privateMarker = activity.getString(R.string.private_message_to, to);
+ }
+ body.insert(0, privateMarker);
+ int privateMarkerIndex = privateMarker.length();
+ body.insert(privateMarkerIndex, " ");
+ body.setSpan(new ForegroundColorSpan(getMessageTextColor(darkBackground, false)), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ body.setSpan(new StyleSpan(Typeface.BOLD), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (hasMeCommand) {
+ body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarkerIndex + 1, privateMarkerIndex + 1 + nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ Linkify.addLinks(body, Linkify.WEB_URLS);
+ Linkify.addLinks(body, XMPP_PATTERN, "xmpp");
+ Linkify.addLinks(body, GeoHelper.GEO_URI, "geo");
+ viewHolder.messageBody.setAutoLinkMask(0);
+ viewHolder.messageBody.setText(body);
+ viewHolder.messageBody.setTextIsSelectable(true);
+ viewHolder.messageBody.setMovementMethod(ClickableMovementMethod.getInstance());
+ listSelectionManager.onUpdate(viewHolder.messageBody, message);
+
+ } else {
+ viewHolder.messageBody.setText("");
+ viewHolder.messageBody.setTextIsSelectable(false);
+ }
+ viewHolder.messageBody.setTextColor(this.getMessageTextColor(darkBackground, true));
+ viewHolder.messageBody.setLinkTextColor(this.getMessageTextColor(darkBackground, true));
+ viewHolder.messageBody.setHighlightColor(activity.getResources().getColor(darkBackground ? R.color.grey800 : R.color.grey500));
+ viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
+ }
+
+ private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ viewHolder.download_button.setVisibility(View.VISIBLE);
+ viewHolder.download_button.setText(text);
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_download_grey600_48dp, 0, 0, 0);
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.startDownloadable(message);
+ }
+ });
+ }
+
+ private void displayAudioMessage(ViewHolder viewHolder, final Message message, int position) {
+ if (audioPlayer == null) audioPlayer = new HashMap<>();
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.aw_player.setVisibility(View.VISIBLE);
+ Uri audioFile = Uri.fromFile(activity.xmppConnectionService.getFileBackend().getFile(message));
+
+ audioWife = audioPlayer.get(position);
+ if (audioWife == null) {
+ audioWife = new AudioWife();
+ audioWife.init(getContext(), audioFile);
+ audioPlayer.put(position, audioWife);
+ RelativeLayout vg = new RelativeLayout(activity);
+ LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ audioWife.useDefaultUi(vg, layoutInflater);
+ viewHolder.aw_player.addView(audioWife.getPlayerUi());
+ } else {
+ audioWife.cleanPlayerUi();
+ viewHolder.aw_player.addView(audioWife.getPlayerUi());
+ }
+ }
+
+ private void displayOpenableMessage(ViewHolder viewHolder, final Message message) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ viewHolder.download_button.setVisibility(View.VISIBLE);
+ String mimeType = message.getMimeType();
+ String fullName = "";
+ if (mimeType != null) {
+ if (message.getMimeType().contains("pdf")) {
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_file_pdf_grey600_48dp, 0, 0, 0);
+ } else if (message.getMimeType().contains("vcard")) {
+ File file = new File(activity.xmppConnectionService.getFileBackend().getFile(message).toString());
+ VCard vcard = null;
+ String name = null;
+ String version = null;
+ try {
+ vcard = Ezvcard.parse(file).first();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ if (vcard != null) {
+ version = vcard.getVersion().toString();
+ Log.d(Config.LOGTAG, "VCard version: " + version);
+ name = vcard.getFormattedName().getValue();
+ fullName = " (" + name + ")";
+ }
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_account_card_details_grey600_48dp, 0, 0, 0);
+ } else if (message.getMimeType().contains("calendar")) {
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_calendar_grey600_48dp, 0, 0, 0);
+ } else {
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_file_grey600_48dp, 0, 0, 0);
+ }
+ }
+ viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message) + fullName));
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ openDownloadable(message);
+ }
+ });
+ }
+
+ private void displayLocationMessage(ViewHolder viewHolder, final Message message) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ String url = GeoHelper.MapPreviewUri(message);
+ viewHolder.image.setVisibility(View.VISIBLE);
+ viewHolder.image.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ showLocation(message);
+ }
+ });
+ Glide
+ .with(activity)
+ .load(Uri.parse(url))
+ .asBitmap()
+ .diskCacheStrategy(DiskCacheStrategy.ALL)
+ .fitCenter()
+ .placeholder(R.drawable.ic_map_marker_grey600_48dp)
+ .error(R.drawable.ic_map_marker_grey600_48dp)
+ .into(viewHolder.image);
+ viewHolder.image.setMaxWidth(500);
+ viewHolder.image.setAdjustViewBounds(true);
+ viewHolder.download_button.setVisibility(View.GONE);
+ viewHolder.download_button.setText(R.string.show_location);
+ viewHolder.download_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_map_marker_grey600_48dp, 0, 0, 0);
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ showLocation(message);
+ }
+ });
+
+ }
+
+ private void displayImageMessage(ViewHolder viewHolder,
+ final Message message) {
+ viewHolder.aw_player.setVisibility(View.GONE);
+ if (viewHolder.download_button != null) {
+ viewHolder.download_button.setVisibility(View.GONE);
+ }
+ viewHolder.messageBody.setVisibility(View.GONE);
+ viewHolder.image.setVisibility(View.VISIBLE);
+ FileParams params = message.getFileParams();
+ double target = metrics.density * 200;
+ int scaledW;
+ int scaledH;
+ if (Math.max(params.height, params.width) * metrics.density <= target) {
+ scaledW = (int) (params.width * metrics.density);
+ scaledH = (int) (params.height * metrics.density);
+ } else if (Math.max(params.height, params.width) <= target) {
+ scaledW = params.width;
+ scaledH = params.height;
+ } else if (params.width <= params.height) {
+ scaledW = (int) (params.width / ((double) params.height / target));
+ scaledH = (int) target;
+ } else {
+ scaledW = (int) target;
+ scaledH = (int) (params.height / ((double) params.width / target));
+ }
+ LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scaledW, scaledH);
+ layoutParams.setMargins(0, (int) (metrics.density * 4), 0, (int) (metrics.density * 4));
+ viewHolder.image.setLayoutParams(layoutParams);
+ activity.loadBitmap(message, viewHolder.image);
+ viewHolder.image.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ openDownloadable(message);
+ }
+ });
+ }
+
+ private void loadMoreMessages(Conversation conversation) {
+ conversation.setLastClearHistory(0);
+ activity.xmppConnectionService.updateConversation(conversation);
+ conversation.setHasMessagesLeftOnServer(true);
+ conversation.setFirstMamReference(null);
+ long timestamp = conversation.getLastMessageTransmitted();
+ if (timestamp == 0) {
+ timestamp = System.currentTimeMillis();
+ }
+ activity.setMessagesLoaded();
+ activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp);
+ Toast.makeText(activity, R.string.fetching_history_from_server, Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public View getView(int position, View unused, ViewGroup parent) {
+ final Message message = getItem(position);
+ final boolean isInValidSession = message.isValidInSession();
+ final Conversation conversation = message.getConversation();
+ final Account account = conversation.getAccount();
+ final int type = getItemViewType(position);
+ ViewHolder viewHolder;
+ View view;
+ viewHolder = new ViewHolder();
+ switch (type) {
+ case SENT:
+ view = activity.getLayoutInflater().inflate(
+ R.layout.message_sent, parent, false);
+ viewHolder.message_box = (LinearLayout) view
+ .findViewById(R.id.message_box);
+ viewHolder.contact_picture = (ImageView) view
+ .findViewById(R.id.message_photo);
+ viewHolder.aw_player = (ViewGroup) view.findViewById(R.id.aw_player);
+ viewHolder.download_button = (Button) view
+ .findViewById(R.id.download_button);
+ viewHolder.indicator = (ImageView) view
+ .findViewById(R.id.security_indicator);
+ viewHolder.edit_indicator = (ImageView) view.findViewById(R.id.edit_indicator);
+ viewHolder.image = (ImageView) view
+ .findViewById(R.id.message_image);
+ viewHolder.messageBody = (CopyTextView) view
+ .findViewById(R.id.message_body);
+ viewHolder.time = (TextView) view
+ .findViewById(R.id.message_time);
+ viewHolder.indicatorReceived = (ImageView) view
+ .findViewById(R.id.indicator_received);
+ viewHolder.indicatorRead = (ImageView) view
+ .findViewById(R.id.indicator_read);
+ break;
+ case RECEIVED:
+ view = activity.getLayoutInflater().inflate(
+ R.layout.message_received, parent, false);
+ viewHolder.message_box = (LinearLayout) view
+ .findViewById(R.id.message_box);
+ viewHolder.contact_picture = (ImageView) view
+ .findViewById(R.id.message_photo);
+ viewHolder.aw_player = (ViewGroup) view.findViewById(R.id.aw_player);
+ viewHolder.download_button = (Button) view
+ .findViewById(R.id.download_button);
+ viewHolder.indicator = (ImageView) view
+ .findViewById(R.id.security_indicator);
+ viewHolder.edit_indicator = (ImageView) view.findViewById(R.id.edit_indicator);
+ viewHolder.image = (ImageView) view
+ .findViewById(R.id.message_image);
+ viewHolder.messageBody = (CopyTextView) view
+ .findViewById(R.id.message_body);
+ viewHolder.time = (TextView) view
+ .findViewById(R.id.message_time);
+ viewHolder.indicatorReceived = (ImageView) view
+ .findViewById(R.id.indicator_received);
+ viewHolder.encryption = (TextView) view.findViewById(R.id.message_encryption);
+ break;
+ case STATUS:
+ view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
+ viewHolder.contact_picture = (ImageView) view.findViewById(R.id.message_photo);
+ viewHolder.status_message = (TextView) view.findViewById(R.id.status_message);
+ viewHolder.load_more_messages = (Button) view.findViewById(R.id.load_more_messages);
+ break;
+ default:
+ view = new View(getContext());
+ viewHolder = null;
+ break;
+ }
+ if (viewHolder.messageBody != null) {
+ listSelectionManager.onCreate(viewHolder.messageBody);
+ viewHolder.messageBody.setCopyHandler(this);
+ }
+ view.setTag(viewHolder);
+ if (viewHolder == null) {
+ return view;
+ }
+
+
+ boolean darkBackground = (type == SENT && (!isInValidSession || !mUseWhiteBackground));
+
+ if (type == STATUS) {
+ if ("LOAD_MORE".equals(message.getBody())) {
+ viewHolder.status_message.setVisibility(View.GONE);
+ viewHolder.contact_picture.setVisibility(View.GONE);
+ viewHolder.load_more_messages.setVisibility(View.VISIBLE);
+ viewHolder.load_more_messages.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ loadMoreMessages(message.getConversation());
+ }
+ });
+ } else {
+ viewHolder.status_message.setVisibility(View.VISIBLE);
+ viewHolder.contact_picture.setVisibility(View.VISIBLE);
+ viewHolder.load_more_messages.setVisibility(View.GONE);
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ viewHolder.contact_picture.setImageBitmap(activity
+ .avatarService().get(conversation.getContact(),
+ activity.getPixel(32)));
+ viewHolder.contact_picture.setAlpha(0.5f);
+ }
+ viewHolder.status_message.setText(message.getBody());
+ }
+ return view;
+ } else {
+ loadAvatar(message, viewHolder.contact_picture);
+ }
+
+ viewHolder.contact_picture
+ .setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureClickedListener
+ .onContactPictureClicked(message);
+ }
+
+ }
+ });
+ viewHolder.contact_picture
+ .setOnLongClickListener(new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureLongClickedListener
+ .onContactPictureLongClicked(message);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
+
+ final Transferable transferable = message.getTransferable();
+ String mimeType = message.getMimeType();
+ if (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING) {
+ if (transferable.getStatus() == Transferable.STATUS_OFFER) {
+ displayDownloadableMessage(viewHolder, message, activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)));
+ } else if (transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) {
+ displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
+ } else {
+ displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first, darkBackground);
+ }
+ } else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
+ displayImageMessage(viewHolder, message);
+ } else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
+ if (message.getFileParams().width > 0) {
+ displayImageMessage(viewHolder, message);
+ } else {
+ if (mimeType != null) {
+ if (message.getMimeType().startsWith("audio/")) {
+ displayAudioMessage(viewHolder, message, position);
+ } else displayOpenableMessage(viewHolder, message);
+ } else displayOpenableMessage(viewHolder, message);
+ }
+ } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ if (account.isPgpDecryptionServiceConnected()) {
+ if (!account.hasPendingPgpIntent(conversation)) {
+ displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground);
+ } else {
+ displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground);
+ }
+ } else {
+ displayInfoMessage(viewHolder, activity.getString(R.string.install_openkeychain), darkBackground);
+ if (viewHolder != null) {
+ viewHolder.message_box
+ .setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.showInstallPgpDialog();
+ }
+ });
+ }
+ }
+ } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ displayDecryptionFailed(viewHolder, darkBackground);
+ } else {
+ if (GeoHelper.isGeoUri(message.getBody())) {
+ displayLocationMessage(viewHolder, message);
+ } else if (message.bodyIsHeart()) {
+ displayHeartMessage(viewHolder, message.getBody().trim());
+ } else if (message.bodyIsXmpp()) {
+ displayXmppMessage(viewHolder, message.getBody().trim());
+ } else if (message.treatAsDownloadable() == Message.Decision.MUST ||
+ message.treatAsDownloadable() == Message.Decision.SHOULD) {
+ try {
+ URL url = new URL(message.getBody());
+ displayDownloadableMessage(viewHolder,
+ message,
+ activity.getString(R.string.check_x_filesize_on_host,
+ UIHelper.getFileDescriptionString(activity, message),
+ url.getHost()));
+ } catch (Exception e) {
+ displayDownloadableMessage(viewHolder,
+ message,
+ activity.getString(R.string.check_x_filesize,
+ UIHelper.getFileDescriptionString(activity, message)));
+ }
+ } else {
+ displayTextMessage(viewHolder, message, darkBackground);
+ }
+ }
+
+ if (type == RECEIVED) {
+ if (isInValidSession) {
+ viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received);
+ viewHolder.encryption.setVisibility(View.GONE);
+ } else {
+ viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_warning);
+ viewHolder.encryption.setVisibility(View.VISIBLE);
+ viewHolder.encryption.setText(CryptoHelper.encryptionTypeToText(message.getEncryption()));
+ }
+ }
+
+ if (type == SENT) {
+ if (mUseWhiteBackground) {
+ viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_sent_white);
+ } else {
+ viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_sent);
+ }
+ }
+
+ displayStatus(viewHolder, message, type, darkBackground);
+
+ return view;
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ listSelectionManager.onBeforeNotifyDataSetChanged();
+ super.notifyDataSetChanged();
+ listSelectionManager.onAfterNotifyDataSetChanged();
+ }
+
+ @Override
+ public String transformTextForCopy(CharSequence text, int start, int end) {
+ return text.toString().substring(start, end);
+ }
+
+ public void openDownloadable(Message message) {
+ DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
+ if (!file.exists()) {
+ Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ String mime = file.getMimeType();
+ if (mime.startsWith("image/")) {
+ Intent intent = new Intent(getContext(), ShowFullscreenMessageActivity.class);
+ intent.putExtra("image", Uri.fromFile(file));
+ try {
+ activity.startActivity(intent);
+ return;
+ } catch (ActivityNotFoundException e) {
+ //ignored
+ }
+ } else if (mime.startsWith("video/")) {
+ Intent intent = new Intent(getContext(), ShowFullscreenMessageActivity.class);
+ intent.putExtra("video", Uri.fromFile(file));
+ try {
+ activity.startActivity(intent);
+ return;
+ } catch (ActivityNotFoundException e) {
+ //ignored
+ }
+ }
+ Intent openIntent = new Intent(Intent.ACTION_VIEW);
+ if (mime == null) {
+ mime = "*/*";
+ }
+ Uri uri;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ try {
+ uri = FileProvider.getUriForFile(activity, FileBackend.CONVERSATIONS_FILE_PROVIDER, file);
+ } catch (IllegalArgumentException e) {
+ Toast.makeText(activity, activity.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
+ return;
+ }
+ openIntent.setDataAndType(uri, mime);
+ openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } else {
+ uri = Uri.fromFile(file);
+ }
+ openIntent.setDataAndType(uri, mime);
+ PackageManager manager = activity.getPackageManager();
+ List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
+ if (info.size() == 0) {
+ openIntent.setDataAndType(Uri.fromFile(file), "*/*");
+ }
+ try {
+ getContext().startActivity(openIntent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(activity, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public void showLocation(Message message) {
+ for (Intent intent : GeoHelper.createGeoIntentsFromMessage(message)) {
+ if (intent.resolveActivity(getContext().getPackageManager()) != null) {
+ getContext().startActivity(intent);
+ return;
+ }
+ }
+ Toast.makeText(activity, R.string.no_application_found_to_display_location, Toast.LENGTH_SHORT).show();
+ }
+
+ public void updatePreferences() {
+ this.mIndicateReceived = activity.indicateReceived();
+ this.mUseWhiteBackground = activity.useWhiteBackground();
+ }
+
+ public TextView getMessageBody(View view) {
+ final Object tag = view.getTag();
+ if (tag instanceof ViewHolder) {
+ final ViewHolder viewHolder = (ViewHolder) tag;
+ return viewHolder.messageBody;
+ }
+ return null;
+ }
+
+ public interface OnContactPictureClicked {
+ void onContactPictureClicked(Message message);
+ }
+
+ public interface OnContactPictureLongClicked {
+ void onContactPictureLongClicked(Message message);
+ }
+
+ private static class ViewHolder {
+
+ protected LinearLayout message_box;
+ protected Button download_button;
+ protected ViewGroup aw_player;
+ protected ImageView image;
+ protected ImageView indicator;
+ protected ImageView indicatorReceived;
+ protected ImageView indicatorRead;
+ protected TextView time;
+ protected CopyTextView messageBody;
+ protected ImageView contact_picture;
+ protected TextView status_message;
+ protected TextView encryption;
+ public Button load_more_messages;
+ public ImageView edit_indicator;
+ }
+
+ class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
+ private final WeakReference<ImageView> imageViewReference;
+ private Message message = null;
+
+ public BitmapWorkerTask(ImageView imageView) {
+ imageViewReference = new WeakReference<>(imageView);
+ }
+
+ @Override
+ protected Bitmap doInBackground(Message... params) {
+ return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled());
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (bitmap != null && !isCancelled()) {
+ final ImageView imageView = imageViewReference.get();
+ if (imageView != null) {
+ imageView.setImageBitmap(bitmap);
+ imageView.setBackgroundColor(0x00000000);
+ }
+ }
+ }
+ }
+
+ public void loadAvatar(Message message, ImageView imageView) {
+ if (cancelPotentialWork(message, imageView)) {
+ final Bitmap bm = activity.avatarService().get(message, activity.getPixel(48), true);
+ if (bm != null) {
+ cancelPotentialWork(message, imageView);
+ imageView.setImageBitmap(bm);
+ imageView.setBackgroundColor(0x00000000);
+ } else {
+ imageView.setBackgroundColor(UIHelper.getColorForName(UIHelper.getMessageDisplayName(message)));
+ imageView.setImageDrawable(null);
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+ imageView.setImageDrawable(asyncDrawable);
+ try {
+ task.execute(message);
+ } catch (final RejectedExecutionException ignored) {
+ }
+ }
+ }
+ }
+
+ public static boolean cancelPotentialWork(Message message, ImageView imageView) {
+ final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+ if (bitmapWorkerTask != null) {
+ final Message oldMessage = bitmapWorkerTask.message;
+ if (oldMessage == null || message != oldMessage) {
+ bitmapWorkerTask.cancel(true);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+ if (imageView != null) {
+ final Drawable drawable = imageView.getDrawable();
+ if (drawable instanceof AsyncDrawable) {
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+ return asyncDrawable.getBitmapWorkerTask();
+ }
+ }
+ return null;
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormBooleanFieldWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormBooleanFieldWrapper.java
index 2593ac204..51ac79c66 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormBooleanFieldWrapper.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormBooleanFieldWrapper.java
@@ -12,69 +12,69 @@ import de.pixart.messenger.xmpp.forms.Field;
public class FormBooleanFieldWrapper extends FormFieldWrapper {
- protected CheckBox checkBox;
+ protected CheckBox checkBox;
- protected FormBooleanFieldWrapper(Context context, Field field) {
- super(context, field);
- checkBox = (CheckBox) view.findViewById(R.id.field);
- checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- checkBox.setError(null);
- invokeOnFormFieldValuesEdited();
- }
- });
- }
+ protected FormBooleanFieldWrapper(Context context, Field field) {
+ super(context, field);
+ checkBox = (CheckBox) view.findViewById(R.id.field);
+ checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ checkBox.setError(null);
+ invokeOnFormFieldValuesEdited();
+ }
+ });
+ }
- @Override
- protected void setLabel(String label, boolean required) {
- CheckBox checkBox = (CheckBox) view.findViewById(R.id.field);
- checkBox.setText(createSpannableLabelString(label, required));
- }
+ @Override
+ protected void setLabel(String label, boolean required) {
+ CheckBox checkBox = (CheckBox) view.findViewById(R.id.field);
+ checkBox.setText(createSpannableLabelString(label, required));
+ }
- @Override
- public List<String> getValues() {
- List<String> values = new ArrayList<>();
- values.add(Boolean.toString(checkBox.isChecked()));
- return values;
- }
+ @Override
+ public List<String> getValues() {
+ List<String> values = new ArrayList<>();
+ values.add(Boolean.toString(checkBox.isChecked()));
+ return values;
+ }
- @Override
- protected void setValues(List<String> values) {
- if (values.size() == 0) {
- checkBox.setChecked(false);
- } else {
- checkBox.setChecked(Boolean.parseBoolean(values.get(0)));
- }
- }
+ @Override
+ protected void setValues(List<String> values) {
+ if (values.size() == 0) {
+ checkBox.setChecked(false);
+ } else {
+ checkBox.setChecked(Boolean.parseBoolean(values.get(0)));
+ }
+ }
- @Override
- public boolean validates() {
- if (checkBox.isChecked() || !field.isRequired()) {
- return true;
- } else {
- checkBox.setError(context.getString(R.string.this_field_is_required));
- checkBox.requestFocus();
- return false;
- }
- }
+ @Override
+ public boolean validates() {
+ if (checkBox.isChecked() || !field.isRequired()) {
+ return true;
+ } else {
+ checkBox.setError(context.getString(R.string.this_field_is_required));
+ checkBox.requestFocus();
+ return false;
+ }
+ }
- @Override
- public boolean edited() {
- if (field.getValues().size() == 0) {
- return checkBox.isChecked();
- } else {
- return super.edited();
- }
- }
+ @Override
+ public boolean edited() {
+ if (field.getValues().size() == 0) {
+ return checkBox.isChecked();
+ } else {
+ return super.edited();
+ }
+ }
- @Override
- protected int getLayoutResource() {
- return R.layout.form_boolean;
- }
+ @Override
+ protected int getLayoutResource() {
+ return R.layout.form_boolean;
+ }
- @Override
- void setReadOnly(boolean readOnly) {
- checkBox.setEnabled(!readOnly);
- }
+ @Override
+ void setReadOnly(boolean readOnly) {
+ checkBox.setEnabled(!readOnly);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormFieldFactory.java b/src/main/java/de/pixart/messenger/ui/forms/FormFieldFactory.java
index c1318b9f1..5a30e35a5 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormFieldFactory.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormFieldFactory.java
@@ -7,24 +7,23 @@ import java.util.Hashtable;
import de.pixart.messenger.xmpp.forms.Field;
-
public class FormFieldFactory {
- private static final Hashtable<String, Class> typeTable = new Hashtable<>();
-
- static {
- typeTable.put("text-single", FormTextFieldWrapper.class);
- typeTable.put("text-multi", FormTextFieldWrapper.class);
- typeTable.put("text-private", FormTextFieldWrapper.class);
- typeTable.put("jid-single", FormJidSingleFieldWrapper.class);
- typeTable.put("boolean", FormBooleanFieldWrapper.class);
- }
-
- protected static FormFieldWrapper createFromField(Context context, Field field) {
- Class clazz = typeTable.get(field.getType());
- if (clazz == null) {
- clazz = FormTextFieldWrapper.class;
- }
- return FormFieldWrapper.createFromField(clazz, context, field);
- }
+ private static final Hashtable<String, Class> typeTable = new Hashtable<>();
+
+ static {
+ typeTable.put("text-single", FormTextFieldWrapper.class);
+ typeTable.put("text-multi", FormTextFieldWrapper.class);
+ typeTable.put("text-private", FormTextFieldWrapper.class);
+ typeTable.put("jid-single", FormJidSingleFieldWrapper.class);
+ typeTable.put("boolean", FormBooleanFieldWrapper.class);
+ }
+
+ protected static FormFieldWrapper createFromField(Context context, Field field) {
+ Class clazz = typeTable.get(field.getType());
+ if (clazz == null) {
+ clazz = FormTextFieldWrapper.class;
+ }
+ return FormFieldWrapper.createFromField(clazz, context, field);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java
index 5182f3e7a..01b9ea0f9 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java
@@ -14,80 +14,80 @@ import de.pixart.messenger.xmpp.forms.Field;
public abstract class FormFieldWrapper {
- protected final Context context;
- protected final Field field;
- protected final View view;
- protected OnFormFieldValuesEdited onFormFieldValuesEditedListener;
-
- protected FormFieldWrapper(Context context, Field field) {
- this.context = context;
- this.field = field;
- LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- this.view = inflater.inflate(getLayoutResource(), null);
- String label = field.getLabel();
- if (label == null) {
- label = field.getFieldName();
- }
- setLabel(label, field.isRequired());
- }
-
- public final void submit() {
- this.field.setValues(getValues());
- }
-
- public final View getView() {
- return view;
- }
-
- protected abstract void setLabel(String label, boolean required);
-
- abstract List<String> getValues();
-
- protected abstract void setValues(List<String> values);
-
- abstract boolean validates();
-
- abstract protected int getLayoutResource();
-
- abstract void setReadOnly(boolean readOnly);
-
- protected SpannableString createSpannableLabelString(String label, boolean required) {
- SpannableString spannableString = new SpannableString(label + (required ? " *" : ""));
- if (required) {
- int start = label.length();
- int end = label.length() + 2;
- spannableString.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, end, 0);
- spannableString.setSpan(new ForegroundColorSpan(context.getResources().getColor(R.color.accent)), start, end, 0);
- }
- return spannableString;
- }
-
- protected void invokeOnFormFieldValuesEdited() {
- if (this.onFormFieldValuesEditedListener != null) {
- this.onFormFieldValuesEditedListener.onFormFieldValuesEdited();
- }
- }
-
- public boolean edited() {
- return !field.getValues().equals(getValues());
- }
-
- public void setOnFormFieldValuesEditedListener(OnFormFieldValuesEdited listener) {
- this.onFormFieldValuesEditedListener = listener;
- }
-
- protected static <F extends FormFieldWrapper> FormFieldWrapper createFromField(Class<F> c, Context context, Field field) {
- try {
- F fieldWrapper = c.getDeclaredConstructor(Context.class, Field.class).newInstance(context,field);
- fieldWrapper.setValues(field.getValues());
- return fieldWrapper;
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
- public interface OnFormFieldValuesEdited {
- void onFormFieldValuesEdited();
- }
+ protected final Context context;
+ protected final Field field;
+ protected final View view;
+ protected OnFormFieldValuesEdited onFormFieldValuesEditedListener;
+
+ protected FormFieldWrapper(Context context, Field field) {
+ this.context = context;
+ this.field = field;
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ this.view = inflater.inflate(getLayoutResource(), null);
+ String label = field.getLabel();
+ if (label == null) {
+ label = field.getFieldName();
+ }
+ setLabel(label, field.isRequired());
+ }
+
+ public final void submit() {
+ this.field.setValues(getValues());
+ }
+
+ public final View getView() {
+ return view;
+ }
+
+ protected abstract void setLabel(String label, boolean required);
+
+ abstract List<String> getValues();
+
+ protected abstract void setValues(List<String> values);
+
+ abstract boolean validates();
+
+ abstract protected int getLayoutResource();
+
+ abstract void setReadOnly(boolean readOnly);
+
+ protected SpannableString createSpannableLabelString(String label, boolean required) {
+ SpannableString spannableString = new SpannableString(label + (required ? " *" : ""));
+ if (required) {
+ int start = label.length();
+ int end = label.length() + 2;
+ spannableString.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, end, 0);
+ spannableString.setSpan(new ForegroundColorSpan(context.getResources().getColor(R.color.accent)), start, end, 0);
+ }
+ return spannableString;
+ }
+
+ protected void invokeOnFormFieldValuesEdited() {
+ if (this.onFormFieldValuesEditedListener != null) {
+ this.onFormFieldValuesEditedListener.onFormFieldValuesEdited();
+ }
+ }
+
+ public boolean edited() {
+ return !field.getValues().equals(getValues());
+ }
+
+ public void setOnFormFieldValuesEditedListener(OnFormFieldValuesEdited listener) {
+ this.onFormFieldValuesEditedListener = listener;
+ }
+
+ protected static <F extends FormFieldWrapper> FormFieldWrapper createFromField(Class<F> c, Context context, Field field) {
+ try {
+ F fieldWrapper = c.getDeclaredConstructor(Context.class, Field.class).newInstance(context, field);
+ fieldWrapper.setValues(field.getValues());
+ return fieldWrapper;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public interface OnFormFieldValuesEdited {
+ void onFormFieldValuesEdited();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormJidSingleFieldWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormJidSingleFieldWrapper.java
index 8d37f259f..b6aeaf344 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormJidSingleFieldWrapper.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormJidSingleFieldWrapper.java
@@ -12,33 +12,33 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class FormJidSingleFieldWrapper extends FormTextFieldWrapper {
- protected FormJidSingleFieldWrapper(Context context, Field field) {
- super(context, field);
- editText.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
- editText.setHint(R.string.account_settings_example_jabber_id);
- }
+ protected FormJidSingleFieldWrapper(Context context, Field field) {
+ super(context, field);
+ editText.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+ editText.setHint(R.string.account_settings_example_jabber_id);
+ }
- @Override
- public boolean validates() {
- String value = getValue();
- if (!value.isEmpty()) {
- try {
- Jid.fromString(value);
- } catch (InvalidJidException e) {
- editText.setError(context.getString(R.string.invalid_jid));
- editText.requestFocus();
- return false;
- }
- }
- return super.validates();
- }
+ @Override
+ public boolean validates() {
+ String value = getValue();
+ if (!value.isEmpty()) {
+ try {
+ Jid.fromString(value);
+ } catch (InvalidJidException e) {
+ editText.setError(context.getString(R.string.invalid_jid));
+ editText.requestFocus();
+ return false;
+ }
+ }
+ return super.validates();
+ }
- @Override
- protected void setValues(List<String> values) {
- StringBuilder builder = new StringBuilder("");
- for(String value : values) {
- builder.append(value);
- }
- editText.setText(builder.toString());
- }
+ @Override
+ protected void setValues(List<String> values) {
+ StringBuilder builder = new StringBuilder("");
+ for (String value : values) {
+ builder.append(value);
+ }
+ editText.setText(builder.toString());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormTextFieldWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormTextFieldWrapper.java
index cccfd1608..5635bc7b4 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormTextFieldWrapper.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormTextFieldWrapper.java
@@ -15,83 +15,83 @@ import de.pixart.messenger.xmpp.forms.Field;
public class FormTextFieldWrapper extends FormFieldWrapper {
- protected EditText editText;
+ protected EditText editText;
- protected FormTextFieldWrapper(Context context, Field field) {
- super(context, field);
- editText = (EditText) view.findViewById(R.id.field);
- editText.setSingleLine(!"text-multi".equals(field.getType()));
- if ("text-private".equals(field.getType())) {
- editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- }
- editText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
+ protected FormTextFieldWrapper(Context context, Field field) {
+ super(context, field);
+ editText = (EditText) view.findViewById(R.id.field);
+ editText.setSingleLine(!"text-multi".equals(field.getType()));
+ if ("text-private".equals(field.getType())) {
+ editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ }
+ editText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- editText.setError(null);
- invokeOnFormFieldValuesEdited();
- }
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ editText.setError(null);
+ invokeOnFormFieldValuesEdited();
+ }
- @Override
- public void afterTextChanged(Editable s) {
- }
- });
- }
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ });
+ }
- @Override
- protected void setLabel(String label, boolean required) {
- TextView textView = (TextView) view.findViewById(R.id.label);
- textView.setText(createSpannableLabelString(label, required));
- }
+ @Override
+ protected void setLabel(String label, boolean required) {
+ TextView textView = (TextView) view.findViewById(R.id.label);
+ textView.setText(createSpannableLabelString(label, required));
+ }
- protected String getValue() {
- return editText.getText().toString();
- }
+ protected String getValue() {
+ return editText.getText().toString();
+ }
- @Override
- public List<String> getValues() {
- List<String> values = new ArrayList<>();
- for (String line : getValue().split("\\n")) {
- if (line.length() > 0) {
- values.add(line);
- }
- }
- return values;
- }
+ @Override
+ public List<String> getValues() {
+ List<String> values = new ArrayList<>();
+ for (String line : getValue().split("\\n")) {
+ if (line.length() > 0) {
+ values.add(line);
+ }
+ }
+ return values;
+ }
- @Override
- protected void setValues(List<String> values) {
- StringBuilder builder = new StringBuilder("");
- for(int i = 0; i < values.size(); ++i) {
- builder.append(values.get(i));
- if (i < values.size() - 1 && "text-multi".equals(field.getType())) {
- builder.append("\n");
- }
- }
- editText.setText(builder.toString());
- }
+ @Override
+ protected void setValues(List<String> values) {
+ StringBuilder builder = new StringBuilder("");
+ for (int i = 0; i < values.size(); ++i) {
+ builder.append(values.get(i));
+ if (i < values.size() - 1 && "text-multi".equals(field.getType())) {
+ builder.append("\n");
+ }
+ }
+ editText.setText(builder.toString());
+ }
- @Override
- public boolean validates() {
- if (getValue().trim().length() > 0 || !field.isRequired()) {
- return true;
- } else {
- editText.setError(context.getString(R.string.this_field_is_required));
- editText.requestFocus();
- return false;
- }
- }
+ @Override
+ public boolean validates() {
+ if (getValue().trim().length() > 0 || !field.isRequired()) {
+ return true;
+ } else {
+ editText.setError(context.getString(R.string.this_field_is_required));
+ editText.requestFocus();
+ return false;
+ }
+ }
- @Override
- protected int getLayoutResource() {
- return R.layout.form_text;
- }
+ @Override
+ protected int getLayoutResource() {
+ return R.layout.form_text;
+ }
- @Override
- void setReadOnly(boolean readOnly) {
- editText.setEnabled(!readOnly);
- }
+ @Override
+ void setReadOnly(boolean readOnly) {
+ editText.setEnabled(!readOnly);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormWrapper.java
index 05fa922b5..f22bef721 100644
--- a/src/main/java/de/pixart/messenger/ui/forms/FormWrapper.java
+++ b/src/main/java/de/pixart/messenger/ui/forms/FormWrapper.java
@@ -11,62 +11,62 @@ import de.pixart.messenger.xmpp.forms.Field;
public class FormWrapper {
- private final LinearLayout layout;
+ private final LinearLayout layout;
- private final Data form;
+ private final Data form;
- private final List<FormFieldWrapper> fieldWrappers = new ArrayList<>();
+ private final List<FormFieldWrapper> fieldWrappers = new ArrayList<>();
- private FormWrapper(Context context, LinearLayout linearLayout, Data form) {
- this.form = form;
- this.layout = linearLayout;
- this.layout.removeAllViews();
- for(Field field : form.getFields()) {
- FormFieldWrapper fieldWrapper = FormFieldFactory.createFromField(context,field);
- if (fieldWrapper != null) {
- layout.addView(fieldWrapper.getView());
- fieldWrappers.add(fieldWrapper);
- }
- }
- }
+ private FormWrapper(Context context, LinearLayout linearLayout, Data form) {
+ this.form = form;
+ this.layout = linearLayout;
+ this.layout.removeAllViews();
+ for (Field field : form.getFields()) {
+ FormFieldWrapper fieldWrapper = FormFieldFactory.createFromField(context, field);
+ if (fieldWrapper != null) {
+ layout.addView(fieldWrapper.getView());
+ fieldWrappers.add(fieldWrapper);
+ }
+ }
+ }
- public Data submit() {
- for(FormFieldWrapper fieldWrapper : fieldWrappers) {
- fieldWrapper.submit();
- }
- this.form.submit();
- return this.form;
- }
+ public Data submit() {
+ for (FormFieldWrapper fieldWrapper : fieldWrappers) {
+ fieldWrapper.submit();
+ }
+ this.form.submit();
+ return this.form;
+ }
- public boolean validates() {
- boolean validates = true;
- for(FormFieldWrapper fieldWrapper : fieldWrappers) {
- validates &= fieldWrapper.validates();
- }
- return validates;
- }
+ public boolean validates() {
+ boolean validates = true;
+ for (FormFieldWrapper fieldWrapper : fieldWrappers) {
+ validates &= fieldWrapper.validates();
+ }
+ return validates;
+ }
- public void setOnFormFieldValuesEditedListener(FormFieldWrapper.OnFormFieldValuesEdited listener) {
- for(FormFieldWrapper fieldWrapper : fieldWrappers) {
- fieldWrapper.setOnFormFieldValuesEditedListener(listener);
- }
- }
+ public void setOnFormFieldValuesEditedListener(FormFieldWrapper.OnFormFieldValuesEdited listener) {
+ for (FormFieldWrapper fieldWrapper : fieldWrappers) {
+ fieldWrapper.setOnFormFieldValuesEditedListener(listener);
+ }
+ }
- public void setReadOnly(boolean b) {
- for(FormFieldWrapper fieldWrapper : fieldWrappers) {
- fieldWrapper.setReadOnly(b);
- }
- }
+ public void setReadOnly(boolean b) {
+ for (FormFieldWrapper fieldWrapper : fieldWrappers) {
+ fieldWrapper.setReadOnly(b);
+ }
+ }
- public boolean edited() {
- boolean edited = false;
- for(FormFieldWrapper fieldWrapper : fieldWrappers) {
- edited |= fieldWrapper.edited();
- }
- return edited;
- }
+ public boolean edited() {
+ boolean edited = false;
+ for (FormFieldWrapper fieldWrapper : fieldWrappers) {
+ edited |= fieldWrapper.edited();
+ }
+ return edited;
+ }
- public static FormWrapper createInLayout(Context context, LinearLayout layout, Data form) {
- return new FormWrapper(context, layout, form);
- }
+ public static FormWrapper createInLayout(Context context, LinearLayout layout, Data form) {
+ return new FormWrapper(context, layout, form);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/widget/ClickableMovementMethod.java b/src/main/java/de/pixart/messenger/ui/widget/ClickableMovementMethod.java
index dbf88f1d7..c399a7864 100644
--- a/src/main/java/de/pixart/messenger/ui/widget/ClickableMovementMethod.java
+++ b/src/main/java/de/pixart/messenger/ui/widget/ClickableMovementMethod.java
@@ -9,34 +9,34 @@ import android.widget.TextView;
public class ClickableMovementMethod extends ArrowKeyMovementMethod {
- @Override
- public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
- // Just copied from android.text.method.LinkMovementMethod
- if (event.getAction() == MotionEvent.ACTION_UP) {
- int x = (int) event.getX();
- int y = (int) event.getY();
- x -= widget.getTotalPaddingLeft();
- y -= widget.getTotalPaddingTop();
- x += widget.getScrollX();
- y += widget.getScrollY();
- Layout layout = widget.getLayout();
- int line = layout.getLineForVertical(y);
- int off = layout.getOffsetForHorizontal(line, x);
- ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
- if (link.length != 0) {
- link[0].onClick(widget);
- return true;
- }
- }
- return super.onTouchEvent(widget, buffer, event);
- }
+ @Override
+ public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
+ // Just copied from android.text.method.LinkMovementMethod
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ x -= widget.getTotalPaddingLeft();
+ y -= widget.getTotalPaddingTop();
+ x += widget.getScrollX();
+ y += widget.getScrollY();
+ Layout layout = widget.getLayout();
+ int line = layout.getLineForVertical(y);
+ int off = layout.getOffsetForHorizontal(line, x);
+ ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
+ if (link.length != 0) {
+ link[0].onClick(widget);
+ return true;
+ }
+ }
+ return super.onTouchEvent(widget, buffer, event);
+ }
- public static ClickableMovementMethod getInstance() {
- if (sInstance == null) {
- sInstance = new ClickableMovementMethod();
- }
- return sInstance;
- }
+ public static ClickableMovementMethod getInstance() {
+ if (sInstance == null) {
+ sInstance = new ClickableMovementMethod();
+ }
+ return sInstance;
+ }
- private static ClickableMovementMethod sInstance;
+ private static ClickableMovementMethod sInstance;
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/widget/Switch.java b/src/main/java/de/pixart/messenger/ui/widget/Switch.java
index 2bd4eda3d..40d8f4c99 100644
--- a/src/main/java/de/pixart/messenger/ui/widget/Switch.java
+++ b/src/main/java/de/pixart/messenger/ui/widget/Switch.java
@@ -9,60 +9,60 @@ import com.kyleduo.switchbutton.SwitchButton;
public class Switch extends SwitchButton {
- private int mTouchSlop;
- private int mClickTimeout;
- private float mStartX;
- private float mStartY;
- private OnClickListener mOnClickListener;
+ private int mTouchSlop;
+ private int mClickTimeout;
+ private float mStartX;
+ private float mStartY;
+ private OnClickListener mOnClickListener;
- public Switch(Context context) {
- super(context);
- mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout();
- }
+ public Switch(Context context) {
+ super(context);
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout();
+ }
- public Switch(Context context, AttributeSet attrs) {
- super(context, attrs);
- mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout();
- }
+ public Switch(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout();
+ }
- public Switch(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout();
- }
+ public Switch(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout();
+ }
- @Override
- public void setOnClickListener(OnClickListener onClickListener) {
- this.mOnClickListener = onClickListener;
- }
+ @Override
+ public void setOnClickListener(OnClickListener onClickListener) {
+ this.mOnClickListener = onClickListener;
+ }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (!isEnabled()) {
- float deltaX = event.getX() - mStartX;
- float deltaY = event.getY() - mStartY;
- int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mStartX = event.getX();
- mStartY = event.getY();
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- float time = event.getEventTime() - event.getDownTime();
- if (deltaX < mTouchSlop && deltaY < mTouchSlop && time < mClickTimeout) {
- if (mOnClickListener != null) {
- this.mOnClickListener.onClick(this);
- }
- }
- break;
- default:
- break;
- }
- return true;
- }
- return super.onTouchEvent(event);
- }
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!isEnabled()) {
+ float deltaX = event.getX() - mStartX;
+ float deltaY = event.getY() - mStartY;
+ int action = event.getAction();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mStartX = event.getX();
+ mStartY = event.getY();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ float time = event.getEventTime() - event.getDownTime();
+ if (deltaX < mTouchSlop && deltaY < mTouchSlop && time < mClickTimeout) {
+ if (mOnClickListener != null) {
+ this.mOnClickListener.onClick(this);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+ return super.onTouchEvent(event);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java b/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java
index fbd4c9708..dd2d673b4 100644
--- a/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java
+++ b/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java
@@ -35,7 +35,7 @@ public abstract class ConversationsFileObserver {
if (files == null) {
continue;
}
- for(File file : files) {
+ for (File file : files) {
if (file.isDirectory() && !file.getName().equals(".") && !file.getName().equals("..")) {
final String currentPath = file.getAbsolutePath();
if (depth(file) <= 8 && !stack.contains(currentPath) && !observing(currentPath)) {
@@ -44,22 +44,22 @@ public abstract class ConversationsFileObserver {
}
}
}
- for(FileObserver observer : mObservers) {
+ for (FileObserver observer : mObservers) {
observer.startWatching();
}
}
private static int depth(File file) {
int depth = 0;
- while((file = file.getParentFile()) != null) {
+ while ((file = file.getParentFile()) != null) {
depth++;
}
return depth;
}
private boolean observing(String path) {
- for(SingleFileObserver observer : mObservers) {
- if(path.equals(observer.path)) {
+ for (SingleFileObserver observer : mObservers) {
+ if (path.equals(observer.path)) {
return true;
}
}
@@ -67,7 +67,7 @@ public abstract class ConversationsFileObserver {
}
public synchronized void stopWatching() {
- for(FileObserver observer : mObservers) {
+ for (FileObserver observer : mObservers) {
observer.stopWatching();
}
mObservers.clear();
@@ -85,7 +85,7 @@ public abstract class ConversationsFileObserver {
@Override
public void onEvent(int event, String filename) {
- ConversationsFileObserver.this.onEvent(event, path+'/'+filename);
+ ConversationsFileObserver.this.onEvent(event, path + '/' + filename);
}
}
diff --git a/src/main/java/de/pixart/messenger/utils/CryptoHelper.java b/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
index 510f60352..ec4f750fd 100644
--- a/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/CryptoHelper.java
@@ -31,179 +31,179 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public final class CryptoHelper {
- public static final String FILETRANSFER = "?FILETRANSFERv1:";
- private final static char[] hexArray = "0123456789abcdef".toCharArray();
-
- public static final Pattern UUID_PATTERN = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}");
- final public static byte[] ONE = new byte[] { 0, 0, 0, 1 };
-
- public static String bytesToHex(byte[] bytes) {
- char[] hexChars = new char[bytes.length * 2];
- for (int j = 0; j < bytes.length; j++) {
- int v = bytes[j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- public static byte[] hexToBytes(String hexString) {
- int len = hexString.length();
- byte[] array = new byte[len / 2];
- for (int i = 0; i < len; i += 2) {
- array[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
- .digit(hexString.charAt(i + 1), 16));
- }
- return array;
- }
-
- public static String hexToString(final String hexString) {
- return new String(hexToBytes(hexString));
- }
-
- public static byte[] concatenateByteArrays(byte[] a, byte[] b) {
- byte[] result = new byte[a.length + b.length];
- System.arraycopy(a, 0, result, 0, a.length);
- System.arraycopy(b, 0, result, a.length, b.length);
- return result;
- }
-
- /**
- * Escapes usernames or passwords for SASL.
- */
- public static String saslEscape(final String s) {
- final StringBuilder sb = new StringBuilder((int) (s.length() * 1.1));
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- switch (c) {
- case ',':
- sb.append("=2C");
- break;
- case '=':
- sb.append("=3D");
- break;
- default:
- sb.append(c);
- break;
- }
- }
- return sb.toString();
- }
-
- public static String saslPrep(final String s) {
- return Normalizer.normalize(s, Normalizer.Form.NFKC);
- }
-
- public static String prettifyFingerprint(String fingerprint) {
- if (fingerprint==null) {
- return "";
- } else if (fingerprint.length() < 40) {
- return fingerprint;
- }
- StringBuilder builder = new StringBuilder(fingerprint.toLowerCase(Locale.US).replaceAll("\\s", ""));
- for(int i=8;i<builder.length();i+=9) {
- builder.insert(i, ' ');
- }
- return builder.toString();
- }
-
- public static String prettifyFingerprintCert(String fingerprint) {
- StringBuilder builder = new StringBuilder(fingerprint);
- for(int i=2;i < builder.length(); i+=3) {
- builder.insert(i,':');
- }
- return builder.toString();
- }
-
- public static String[] getOrderedCipherSuites(final String[] platformSupportedCipherSuites) {
- final Collection<String> cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS));
- final List<String> platformCiphers = Arrays.asList(platformSupportedCipherSuites);
- cipherSuites.retainAll(platformCiphers);
- cipherSuites.addAll(platformCiphers);
- filterWeakCipherSuites(cipherSuites);
- return cipherSuites.toArray(new String[cipherSuites.size()]);
- }
-
- private static void filterWeakCipherSuites(final Collection<String> cipherSuites) {
- final Iterator<String> it = cipherSuites.iterator();
- while (it.hasNext()) {
- String cipherName = it.next();
- // remove all ciphers with no or very weak encryption or no authentication
- for (String weakCipherPattern : Config.WEAK_CIPHER_PATTERNS) {
- if (cipherName.contains(weakCipherPattern)) {
- it.remove();
- break;
- }
- }
- }
- }
-
- public static Pair<Jid,String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException, CertificateParsingException {
- Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
- List<String> emails = new ArrayList<>();
- if (alternativeNames != null) {
- for(List<?> san : alternativeNames) {
- Integer type = (Integer) san.get(0);
- if (type == 1) {
- emails.add((String) san.get(1));
- }
- }
- }
- X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
- if (emails.size() == 0) {
- emails.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()));
- }
- String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue());
- if (emails.size() >= 1) {
- return new Pair<>(Jid.fromString(emails.get(0)), name);
- } else {
- return null;
- }
- }
-
- public static Bundle extractCertificateInformation(X509Certificate certificate) {
- Bundle information = new Bundle();
- try {
- JcaX509CertificateHolder holder = new JcaX509CertificateHolder(certificate);
- X500Name subject = holder.getSubject();
- try {
- information.putString("subject_cn", subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
- } catch (Exception e) {
- //ignored
- }
- try {
- information.putString("subject_o",subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
- } catch (Exception e) {
- //ignored
- }
-
- X500Name issuer = holder.getIssuer();
- try {
- information.putString("issuer_cn", issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
- } catch (Exception e) {
- //ignored
- }
- try {
- information.putString("issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
- } catch (Exception e) {
- //ignored
- }
- try {
- information.putString("sha1", getFingerprintCert(certificate.getEncoded()));
- } catch (Exception e) {
-
- }
- return information;
- } catch (CertificateEncodingException e) {
- return information;
- }
- }
-
- public static String getFingerprintCert(byte[] input) throws NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("SHA-1");
- byte[] fingerprint = md.digest(input);
- return prettifyFingerprintCert(bytesToHex(fingerprint));
- }
+ public static final String FILETRANSFER = "?FILETRANSFERv1:";
+ private final static char[] hexArray = "0123456789abcdef".toCharArray();
+
+ public static final Pattern UUID_PATTERN = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}");
+ final public static byte[] ONE = new byte[]{0, 0, 0, 1};
+
+ public static String bytesToHex(byte[] bytes) {
+ char[] hexChars = new char[bytes.length * 2];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = hexArray[v >>> 4];
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+ public static byte[] hexToBytes(String hexString) {
+ int len = hexString.length();
+ byte[] array = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ array[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
+ .digit(hexString.charAt(i + 1), 16));
+ }
+ return array;
+ }
+
+ public static String hexToString(final String hexString) {
+ return new String(hexToBytes(hexString));
+ }
+
+ public static byte[] concatenateByteArrays(byte[] a, byte[] b) {
+ byte[] result = new byte[a.length + b.length];
+ System.arraycopy(a, 0, result, 0, a.length);
+ System.arraycopy(b, 0, result, a.length, b.length);
+ return result;
+ }
+
+ /**
+ * Escapes usernames or passwords for SASL.
+ */
+ public static String saslEscape(final String s) {
+ final StringBuilder sb = new StringBuilder((int) (s.length() * 1.1));
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case ',':
+ sb.append("=2C");
+ break;
+ case '=':
+ sb.append("=3D");
+ break;
+ default:
+ sb.append(c);
+ break;
+ }
+ }
+ return sb.toString();
+ }
+
+ public static String saslPrep(final String s) {
+ return Normalizer.normalize(s, Normalizer.Form.NFKC);
+ }
+
+ public static String prettifyFingerprint(String fingerprint) {
+ if (fingerprint == null) {
+ return "";
+ } else if (fingerprint.length() < 40) {
+ return fingerprint;
+ }
+ StringBuilder builder = new StringBuilder(fingerprint.toLowerCase(Locale.US).replaceAll("\\s", ""));
+ for (int i = 8; i < builder.length(); i += 9) {
+ builder.insert(i, ' ');
+ }
+ return builder.toString();
+ }
+
+ public static String prettifyFingerprintCert(String fingerprint) {
+ StringBuilder builder = new StringBuilder(fingerprint);
+ for (int i = 2; i < builder.length(); i += 3) {
+ builder.insert(i, ':');
+ }
+ return builder.toString();
+ }
+
+ public static String[] getOrderedCipherSuites(final String[] platformSupportedCipherSuites) {
+ final Collection<String> cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS));
+ final List<String> platformCiphers = Arrays.asList(platformSupportedCipherSuites);
+ cipherSuites.retainAll(platformCiphers);
+ cipherSuites.addAll(platformCiphers);
+ filterWeakCipherSuites(cipherSuites);
+ return cipherSuites.toArray(new String[cipherSuites.size()]);
+ }
+
+ private static void filterWeakCipherSuites(final Collection<String> cipherSuites) {
+ final Iterator<String> it = cipherSuites.iterator();
+ while (it.hasNext()) {
+ String cipherName = it.next();
+ // remove all ciphers with no or very weak encryption or no authentication
+ for (String weakCipherPattern : Config.WEAK_CIPHER_PATTERNS) {
+ if (cipherName.contains(weakCipherPattern)) {
+ it.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ public static Pair<Jid, String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException, CertificateParsingException {
+ Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
+ List<String> emails = new ArrayList<>();
+ if (alternativeNames != null) {
+ for (List<?> san : alternativeNames) {
+ Integer type = (Integer) san.get(0);
+ if (type == 1) {
+ emails.add((String) san.get(1));
+ }
+ }
+ }
+ X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
+ if (emails.size() == 0) {
+ emails.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()));
+ }
+ String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue());
+ if (emails.size() >= 1) {
+ return new Pair<>(Jid.fromString(emails.get(0)), name);
+ } else {
+ return null;
+ }
+ }
+
+ public static Bundle extractCertificateInformation(X509Certificate certificate) {
+ Bundle information = new Bundle();
+ try {
+ JcaX509CertificateHolder holder = new JcaX509CertificateHolder(certificate);
+ X500Name subject = holder.getSubject();
+ try {
+ information.putString("subject_cn", subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+ try {
+ information.putString("subject_o", subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+
+ X500Name issuer = holder.getIssuer();
+ try {
+ information.putString("issuer_cn", issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+ try {
+ information.putString("issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+ try {
+ information.putString("sha1", getFingerprintCert(certificate.getEncoded()));
+ } catch (Exception e) {
+
+ }
+ return information;
+ } catch (CertificateEncodingException e) {
+ return information;
+ }
+ }
+
+ public static String getFingerprintCert(byte[] input) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ byte[] fingerprint = md.digest(input);
+ return prettifyFingerprintCert(bytesToHex(fingerprint));
+ }
public static String getAccountFingerprint(Account account) {
try {
@@ -214,16 +214,16 @@ public final class CryptoHelper {
}
}
- public static int encryptionTypeToText(int encryption) {
- switch (encryption) {
- case Message.ENCRYPTION_OTR:
- return R.string.encryption_choice_otr;
- case Message.ENCRYPTION_AXOLOTL:
- return R.string.encryption_choice_omemo;
- case Message.ENCRYPTION_NONE:
- return R.string.encryption_choice_unencrypted;
- default:
- return R.string.encryption_choice_pgp;
- }
- }
+ public static int encryptionTypeToText(int encryption) {
+ switch (encryption) {
+ case Message.ENCRYPTION_OTR:
+ return R.string.encryption_choice_otr;
+ case Message.ENCRYPTION_AXOLOTL:
+ return R.string.encryption_choice_omemo;
+ case Message.ENCRYPTION_NONE:
+ return R.string.encryption_choice_unencrypted;
+ default:
+ return R.string.encryption_choice_pgp;
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/DNSHelper.java b/src/main/java/de/pixart/messenger/utils/DNSHelper.java
index 4a692c910..70f600375 100644
--- a/src/main/java/de/pixart/messenger/utils/DNSHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/DNSHelper.java
@@ -40,260 +40,260 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class DNSHelper {
- public static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
- public static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
- public static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
- public static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z");
- public static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
+ public static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ public static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ public static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ public static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z");
+ public static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
- protected static Client client = new Client();
+ protected static Client client = new Client();
- protected static Context context;
+ protected static Context context;
- public static Bundle getSRVRecord(final Jid jid, Context context) throws IOException {
- DNSHelper.context = context;
+ public static Bundle getSRVRecord(final Jid jid, Context context) throws IOException {
+ DNSHelper.context = context;
final String host = jid.getDomainpart();
- final List<InetAddress> servers = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? getDnsServers(context) : getDnsServersPreLollipop();
- Bundle b = new Bundle();
- boolean interrupted = false;
- for(InetAddress server : servers) {
- if (Thread.currentThread().isInterrupted()) {
- interrupted = true;
- break;
- }
- b = queryDNS(host, server);
- if (b.containsKey("values")) {
- return b;
- }
- }
- if (!b.containsKey("values")) {
- Log.d(Config.LOGTAG,(interrupted ? "Thread interrupted during DNS query" :"all dns queries failed") + ". provide fallback A record");
- ArrayList<Parcelable> values = new ArrayList<>();
- values.add(createNamePortBundle(host, 5222, false));
- b.putParcelableArrayList("values",values);
- }
- return b;
- }
+ final List<InetAddress> servers = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? getDnsServers(context) : getDnsServersPreLollipop();
+ Bundle b = new Bundle();
+ boolean interrupted = false;
+ for (InetAddress server : servers) {
+ if (Thread.currentThread().isInterrupted()) {
+ interrupted = true;
+ break;
+ }
+ b = queryDNS(host, server);
+ if (b.containsKey("values")) {
+ return b;
+ }
+ }
+ if (!b.containsKey("values")) {
+ Log.d(Config.LOGTAG, (interrupted ? "Thread interrupted during DNS query" : "all dns queries failed") + ". provide fallback A record");
+ ArrayList<Parcelable> values = new ArrayList<>();
+ values.add(createNamePortBundle(host, 5222, false));
+ b.putParcelableArrayList("values", values);
+ }
+ return b;
+ }
- @TargetApi(21)
- private static List<InetAddress> getDnsServers(Context context) {
- List<InetAddress> servers = new ArrayList<>();
- ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks();
- if (networks == null) {
- return getDnsServersPreLollipop();
- }
- for(int i = 0; i < networks.length; ++i) {
- LinkProperties linkProperties = connectivityManager.getLinkProperties(networks[i]);
- if (linkProperties != null) {
- if (hasDefaultRoute(linkProperties)) {
- servers.addAll(0, getIPv4First(linkProperties.getDnsServers()));
- } else {
- servers.addAll(getIPv4First(linkProperties.getDnsServers()));
- }
- }
- }
- if (servers.size() > 0) {
- Log.d(Config.LOGTAG, "used lollipop variant to discover dns servers in " + networks.length + " networks");
- }
- return servers.size() > 0 ? servers : getDnsServersPreLollipop();
- }
+ @TargetApi(21)
+ private static List<InetAddress> getDnsServers(Context context) {
+ List<InetAddress> servers = new ArrayList<>();
+ ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks();
+ if (networks == null) {
+ return getDnsServersPreLollipop();
+ }
+ for (int i = 0; i < networks.length; ++i) {
+ LinkProperties linkProperties = connectivityManager.getLinkProperties(networks[i]);
+ if (linkProperties != null) {
+ if (hasDefaultRoute(linkProperties)) {
+ servers.addAll(0, getIPv4First(linkProperties.getDnsServers()));
+ } else {
+ servers.addAll(getIPv4First(linkProperties.getDnsServers()));
+ }
+ }
+ }
+ if (servers.size() > 0) {
+ Log.d(Config.LOGTAG, "used lollipop variant to discover dns servers in " + networks.length + " networks");
+ }
+ return servers.size() > 0 ? servers : getDnsServersPreLollipop();
+ }
- private static List<InetAddress> getIPv4First(List<InetAddress> in) {
- List<InetAddress> out = new ArrayList<>();
- for(InetAddress addr : in) {
- if (addr instanceof Inet4Address) {
- out.add(0, addr);
- } else {
- out.add(addr);
- }
- }
- return out;
- }
+ private static List<InetAddress> getIPv4First(List<InetAddress> in) {
+ List<InetAddress> out = new ArrayList<>();
+ for (InetAddress addr : in) {
+ if (addr instanceof Inet4Address) {
+ out.add(0, addr);
+ } else {
+ out.add(addr);
+ }
+ }
+ return out;
+ }
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- private static boolean hasDefaultRoute(LinkProperties linkProperties) {
- for(RouteInfo route: linkProperties.getRoutes()) {
- if (route.isDefaultRoute()) {
- return true;
- }
- }
- return false;
- }
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private static boolean hasDefaultRoute(LinkProperties linkProperties) {
+ for (RouteInfo route : linkProperties.getRoutes()) {
+ if (route.isDefaultRoute()) {
+ return true;
+ }
+ }
+ return false;
+ }
- private static List<InetAddress> getDnsServersPreLollipop() {
- List<InetAddress> servers = new ArrayList<>();
- String[] dns = client.findDNS();
- for(int i = 0; i < dns.length; ++i) {
- try {
- servers.add(InetAddress.getByName(dns[i]));
- } catch (UnknownHostException e) {
- //ignore
- }
- }
- return servers;
- }
+ private static List<InetAddress> getDnsServersPreLollipop() {
+ List<InetAddress> servers = new ArrayList<>();
+ String[] dns = client.findDNS();
+ for (int i = 0; i < dns.length; ++i) {
+ try {
+ servers.add(InetAddress.getByName(dns[i]));
+ } catch (UnknownHostException e) {
+ //ignore
+ }
+ }
+ return servers;
+ }
- private static class TlsSrv {
- private final SRV srv;
- private final boolean tls;
+ private static class TlsSrv {
+ private final SRV srv;
+ private final boolean tls;
- public TlsSrv(SRV srv, boolean tls) {
- this.srv = srv;
- this.tls = tls;
- }
- }
+ public TlsSrv(SRV srv, boolean tls) {
+ this.srv = srv;
+ this.tls = tls;
+ }
+ }
- private static void fillSrvMaps(final String qname, final InetAddress dnsServer, final Map<Integer, List<TlsSrv>> priorities, final Map<String, List<String>> ips4, final Map<String, List<String>> ips6, final boolean tls) throws IOException {
- final DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress());
- for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) {
- for (Record rr : rrset) {
- Data d = rr.getPayload();
+ private static void fillSrvMaps(final String qname, final InetAddress dnsServer, final Map<Integer, List<TlsSrv>> priorities, final Map<String, List<String>> ips4, final Map<String, List<String>> ips6, final boolean tls) throws IOException {
+ final DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress());
+ for (Record[] rrset : new Record[][]{message.getAnswers(), message.getAdditionalResourceRecords()}) {
+ for (Record rr : rrset) {
+ Data d = rr.getPayload();
final String name = rr.getName() != null ? rr.getName().toLowerCase(Locale.US) : null;
if (d instanceof SRV && NameUtil.idnEquals(qname, name)) {
- SRV srv = (SRV) d;
- if (!priorities.containsKey(srv.getPriority())) {
- priorities.put(srv.getPriority(),new ArrayList<TlsSrv>());
- }
- priorities.get(srv.getPriority()).add(new TlsSrv(srv, tls));
+ SRV srv = (SRV) d;
+ if (!priorities.containsKey(srv.getPriority())) {
+ priorities.put(srv.getPriority(), new ArrayList<TlsSrv>());
+ }
+ priorities.get(srv.getPriority()).add(new TlsSrv(srv, tls));
} else if (d instanceof SRV) {
- Log.d(Config.LOGTAG,"found unrecognized SRV record with name: "+name);
- }
- if (d instanceof A) {
- A a = (A) d;
- if (!ips4.containsKey(name)) {
- ips4.put(name, new ArrayList<String>());
- }
- ips4.get(name).add(a.toString());
- }
- if (d instanceof AAAA) {
- AAAA aaaa = (AAAA) d;
- if (!ips6.containsKey(name)) {
- ips6.put(name, new ArrayList<String>());
- }
- ips6.get(name).add("[" + aaaa.toString() + "]");
- }
- }
- }
- }
+ Log.d(Config.LOGTAG, "found unrecognized SRV record with name: " + name);
+ }
+ if (d instanceof A) {
+ A a = (A) d;
+ if (!ips4.containsKey(name)) {
+ ips4.put(name, new ArrayList<String>());
+ }
+ ips4.get(name).add(a.toString());
+ }
+ if (d instanceof AAAA) {
+ AAAA aaaa = (AAAA) d;
+ if (!ips6.containsKey(name)) {
+ ips6.put(name, new ArrayList<String>());
+ }
+ ips6.get(name).add("[" + aaaa.toString() + "]");
+ }
+ }
+ }
+ }
- public static Bundle queryDNS(String host, InetAddress dnsServer) {
- Bundle bundle = new Bundle();
- try {
- client.setTimeout(Config.SOCKET_TIMEOUT * 1000);
- final String qname = "_xmpp-client._tcp." + host.toLowerCase(Locale.US);
- final String tlsQname = "_xmpps-client._tcp." + host.toLowerCase(Locale.US);
- Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host);
+ public static Bundle queryDNS(String host, InetAddress dnsServer) {
+ Bundle bundle = new Bundle();
+ try {
+ client.setTimeout(Config.SOCKET_TIMEOUT * 1000);
+ final String qname = "_xmpp-client._tcp." + host.toLowerCase(Locale.US);
+ final String tlsQname = "_xmpps-client._tcp." + host.toLowerCase(Locale.US);
+ Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host);
- final Map<Integer, List<TlsSrv>> priorities = new TreeMap<>();
- final Map<String, List<String>> ips4 = new TreeMap<>();
- final Map<String, List<String>> ips6 = new TreeMap<>();
+ final Map<Integer, List<TlsSrv>> priorities = new TreeMap<>();
+ final Map<String, List<String>> ips4 = new TreeMap<>();
+ final Map<String, List<String>> ips6 = new TreeMap<>();
- fillSrvMaps(qname, dnsServer, priorities, ips4, ips6, false);
- fillSrvMaps(tlsQname, dnsServer, priorities, ips4, ips6, true);
+ fillSrvMaps(qname, dnsServer, priorities, ips4, ips6, false);
+ fillSrvMaps(tlsQname, dnsServer, priorities, ips4, ips6, true);
- final List<TlsSrv> result = new ArrayList<>();
- for (final List<TlsSrv> s : priorities.values()) {
- result.addAll(s);
- }
+ final List<TlsSrv> result = new ArrayList<>();
+ for (final List<TlsSrv> s : priorities.values()) {
+ result.addAll(s);
+ }
- final ArrayList<Bundle> values = new ArrayList<>();
- if (result.size() == 0) {
- DNSMessage response;
- try {
- response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
- for (int i = 0; i < response.getAnswers().length; ++i) {
- values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
- }
- } catch (SocketTimeoutException e) {
- Log.d(Config.LOGTAG,"ignoring timeout exception when querying A record on "+dnsServer.getHostAddress());
- }
- try {
- response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
- for (int i = 0; i < response.getAnswers().length; ++i) {
- values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
- }
- } catch (SocketTimeoutException e) {
- Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress());
- }
- values.add(createNamePortBundle(host, 5222, false));
- bundle.putParcelableArrayList("values", values);
- return bundle;
- }
- for (final TlsSrv tlsSrv : result) {
- final SRV srv = tlsSrv.srv;
+ final ArrayList<Bundle> values = new ArrayList<>();
+ if (result.size() == 0) {
+ DNSMessage response;
+ try {
+ response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
+ for (int i = 0; i < response.getAnswers().length; ++i) {
+ values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
+ }
+ } catch (SocketTimeoutException e) {
+ Log.d(Config.LOGTAG, "ignoring timeout exception when querying A record on " + dnsServer.getHostAddress());
+ }
+ try {
+ response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
+ for (int i = 0; i < response.getAnswers().length; ++i) {
+ values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
+ }
+ } catch (SocketTimeoutException e) {
+ Log.d(Config.LOGTAG, "ignoring timeout exception when querying AAAA record on " + dnsServer.getHostAddress());
+ }
+ values.add(createNamePortBundle(host, 5222, false));
+ bundle.putParcelableArrayList("values", values);
+ return bundle;
+ }
+ for (final TlsSrv tlsSrv : result) {
+ final SRV srv = tlsSrv.srv;
final String name = srv.getName() != null ? srv.getName().toLowerCase(Locale.US) : null;
- if (ips6.containsKey(name)) {
- values.add(createNamePortBundle(name,srv.getPort(),ips6, tlsSrv.tls));
- } else {
- try {
- DNSMessage response = client.query(name, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
- for (int i = 0; i < response.getAnswers().length; ++i) {
- values.add(createNamePortBundle(name, srv.getPort(), response.getAnswers()[i].getPayload(), tlsSrv.tls));
- }
- } catch (SocketTimeoutException e) {
- Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress());
- }
- }
- if (ips4.containsKey(name)) {
- values.add(createNamePortBundle(name,srv.getPort(),ips4, tlsSrv.tls));
- } else {
- DNSMessage response = client.query(name, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
- for(int i = 0; i < response.getAnswers().length; ++i) {
- values.add(createNamePortBundle(name,srv.getPort(),response.getAnswers()[i].getPayload(), tlsSrv.tls));
- }
- }
- values.add(createNamePortBundle(name, srv.getPort(), tlsSrv.tls));
- }
- bundle.putParcelableArrayList("values", values);
- } catch (SocketTimeoutException e) {
- bundle.putString("error", "timeout");
- } catch (Exception e) {
- bundle.putString("error", "unhandled");
- }
- return bundle;
- }
+ if (ips6.containsKey(name)) {
+ values.add(createNamePortBundle(name, srv.getPort(), ips6, tlsSrv.tls));
+ } else {
+ try {
+ DNSMessage response = client.query(name, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
+ for (int i = 0; i < response.getAnswers().length; ++i) {
+ values.add(createNamePortBundle(name, srv.getPort(), response.getAnswers()[i].getPayload(), tlsSrv.tls));
+ }
+ } catch (SocketTimeoutException e) {
+ Log.d(Config.LOGTAG, "ignoring timeout exception when querying AAAA record on " + dnsServer.getHostAddress());
+ }
+ }
+ if (ips4.containsKey(name)) {
+ values.add(createNamePortBundle(name, srv.getPort(), ips4, tlsSrv.tls));
+ } else {
+ DNSMessage response = client.query(name, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
+ for (int i = 0; i < response.getAnswers().length; ++i) {
+ values.add(createNamePortBundle(name, srv.getPort(), response.getAnswers()[i].getPayload(), tlsSrv.tls));
+ }
+ }
+ values.add(createNamePortBundle(name, srv.getPort(), tlsSrv.tls));
+ }
+ bundle.putParcelableArrayList("values", values);
+ } catch (SocketTimeoutException e) {
+ bundle.putString("error", "timeout");
+ } catch (Exception e) {
+ bundle.putString("error", "unhandled");
+ }
+ return bundle;
+ }
- private static Bundle createNamePortBundle(String name, int port, final boolean tls) {
- Bundle namePort = new Bundle();
- namePort.putString("name", name);
- namePort.putBoolean("tls", tls);
- namePort.putInt("port", port);
- return namePort;
- }
+ private static Bundle createNamePortBundle(String name, int port, final boolean tls) {
+ Bundle namePort = new Bundle();
+ namePort.putString("name", name);
+ namePort.putBoolean("tls", tls);
+ namePort.putInt("port", port);
+ return namePort;
+ }
- private static Bundle createNamePortBundle(String name, int port, Map<String, List<String>> ips, final boolean tls) {
- Bundle namePort = new Bundle();
- namePort.putString("name", name);
- namePort.putBoolean("tls", tls);
- namePort.putInt("port", port);
- if (ips!=null) {
- List<String> ip = ips.get(name);
- Collections.shuffle(ip, new Random());
- namePort.putString("ip", ip.get(0));
- }
- return namePort;
- }
+ private static Bundle createNamePortBundle(String name, int port, Map<String, List<String>> ips, final boolean tls) {
+ Bundle namePort = new Bundle();
+ namePort.putString("name", name);
+ namePort.putBoolean("tls", tls);
+ namePort.putInt("port", port);
+ if (ips != null) {
+ List<String> ip = ips.get(name);
+ Collections.shuffle(ip, new Random());
+ namePort.putString("ip", ip.get(0));
+ }
+ return namePort;
+ }
- private static Bundle createNamePortBundle(String name, int port, Data data, final boolean tls) {
- Bundle namePort = new Bundle();
- namePort.putString("name", name);
- namePort.putBoolean("tls", tls);
- namePort.putInt("port", port);
- if (data instanceof A) {
- namePort.putString("ip", data.toString());
- } else if (data instanceof AAAA) {
- namePort.putString("ip","["+data.toString()+"]");
- }
- return namePort;
- }
+ private static Bundle createNamePortBundle(String name, int port, Data data, final boolean tls) {
+ Bundle namePort = new Bundle();
+ namePort.putString("name", name);
+ namePort.putBoolean("tls", tls);
+ namePort.putInt("port", port);
+ if (data instanceof A) {
+ namePort.putString("ip", data.toString());
+ } else if (data instanceof AAAA) {
+ namePort.putString("ip", "[" + data.toString() + "]");
+ }
+ return namePort;
+ }
- public static boolean isIp(final String server) {
- return server != null && (
- PATTERN_IPV4.matcher(server).matches()
- || PATTERN_IPV6.matcher(server).matches()
- || PATTERN_IPV6_6HEX4DEC.matcher(server).matches()
- || PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches()
- || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches());
- }
+ public static boolean isIp(final String server) {
+ return server != null && (
+ PATTERN_IPV4.matcher(server).matches()
+ || PATTERN_IPV6.matcher(server).matches()
+ || PATTERN_IPV6_6HEX4DEC.matcher(server).matches()
+ || PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches()
+ || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/ExceptionHandler.java b/src/main/java/de/pixart/messenger/utils/ExceptionHandler.java
index 40648dc04..ec38ea754 100644
--- a/src/main/java/de/pixart/messenger/utils/ExceptionHandler.java
+++ b/src/main/java/de/pixart/messenger/utils/ExceptionHandler.java
@@ -9,23 +9,23 @@ import java.lang.Thread.UncaughtExceptionHandler;
public class ExceptionHandler implements UncaughtExceptionHandler {
- private UncaughtExceptionHandler defaultHandler;
- private Context context;
+ private UncaughtExceptionHandler defaultHandler;
+ private Context context;
- public ExceptionHandler(Context context) {
- this.context = context;
- this.defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
- }
+ public ExceptionHandler(Context context) {
+ this.context = context;
+ this.defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+ }
- @Override
- public void uncaughtException(Thread thread, Throwable ex) {
- Writer result = new StringWriter();
- PrintWriter printWriter = new PrintWriter(result);
- ex.printStackTrace(printWriter);
- String stacktrace = result.toString();
- printWriter.close();
- ExceptionHelper.writeToStacktraceFile(context, stacktrace);
- this.defaultHandler.uncaughtException(thread, ex);
- }
+ @Override
+ public void uncaughtException(Thread thread, Throwable ex) {
+ Writer result = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(result);
+ ex.printStackTrace(printWriter);
+ String stacktrace = result.toString();
+ printWriter.close();
+ ExceptionHelper.writeToStacktraceFile(context, stacktrace);
+ this.defaultHandler.uncaughtException(thread, ex);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java b/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java
index b405f24d4..5cf46c151 100644
--- a/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/ExceptionHelper.java
@@ -31,107 +31,108 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class ExceptionHelper {
- private static SimpleDateFormat DATE_FORMATs = new SimpleDateFormat("yyyy-MM-dd");
- public static void init(Context context) {
- if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler)) {
- Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(
- context));
- }
- }
+ private static SimpleDateFormat DATE_FORMATs = new SimpleDateFormat("yyyy-MM-dd");
- public static boolean checkForCrash(ConversationActivity activity, final XmppConnectionService service) {
- try {
- final SharedPreferences preferences = PreferenceManager
- .getDefaultSharedPreferences(activity);
- boolean crashreport = preferences.getBoolean("crashreport", true);
- if (!crashreport || Config.BUG_REPORTS == null) {
- return false;
- }
- List<Account> accounts = service.getAccounts();
- Account account = null;
- for (int i = 0; i < accounts.size(); ++i) {
- if (!accounts.get(i).isOptionSet(Account.OPTION_DISABLED)) {
- account = accounts.get(i);
- break;
- }
- }
- if (account == null) {
- return false;
- }
- final Account finalAccount = account;
- FileInputStream file = activity.openFileInput("stacktrace.txt");
- InputStreamReader inputStreamReader = new InputStreamReader(file);
- BufferedReader stacktrace = new BufferedReader(inputStreamReader);
- final StringBuilder report = new StringBuilder();
- PackageManager pm = activity.getPackageManager();
- PackageInfo packageInfo;
- try {
- packageInfo = pm.getPackageInfo(activity.getPackageName(), PackageManager.GET_SIGNATURES);
- report.append("Version: " + packageInfo.versionName + '\n');
- report.append("Last Update: " + DATE_FORMATs.format(new Date(packageInfo.lastUpdateTime)) + '\n');
- Signature[] signatures = packageInfo.signatures;
- if (signatures != null && signatures.length >= 1) {
- report.append("SHA-1: " + CryptoHelper.getFingerprintCert(packageInfo.signatures[0].toByteArray()) + "\n");
- }
- report.append('\n');
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- String line;
- while ((line = stacktrace.readLine()) != null) {
- report.append(line);
- report.append('\n');
- }
- file.close();
- activity.deleteFile("stacktrace.txt");
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setTitle(activity.getString(R.string.crash_report_title));
- builder.setMessage(activity.getText(R.string.crash_report_message));
- builder.setPositiveButton(activity.getText(R.string.send_now),
- new OnClickListener() {
+ public static void init(Context context) {
+ if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler)) {
+ Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(
+ context));
+ }
+ }
- @Override
- public void onClick(DialogInterface dialog, int which) {
+ public static boolean checkForCrash(ConversationActivity activity, final XmppConnectionService service) {
+ try {
+ final SharedPreferences preferences = PreferenceManager
+ .getDefaultSharedPreferences(activity);
+ boolean crashreport = preferences.getBoolean("crashreport", true);
+ if (!crashreport || Config.BUG_REPORTS == null) {
+ return false;
+ }
+ List<Account> accounts = service.getAccounts();
+ Account account = null;
+ for (int i = 0; i < accounts.size(); ++i) {
+ if (!accounts.get(i).isOptionSet(Account.OPTION_DISABLED)) {
+ account = accounts.get(i);
+ break;
+ }
+ }
+ if (account == null) {
+ return false;
+ }
+ final Account finalAccount = account;
+ FileInputStream file = activity.openFileInput("stacktrace.txt");
+ InputStreamReader inputStreamReader = new InputStreamReader(file);
+ BufferedReader stacktrace = new BufferedReader(inputStreamReader);
+ final StringBuilder report = new StringBuilder();
+ PackageManager pm = activity.getPackageManager();
+ PackageInfo packageInfo;
+ try {
+ packageInfo = pm.getPackageInfo(activity.getPackageName(), PackageManager.GET_SIGNATURES);
+ report.append("Version: " + packageInfo.versionName + '\n');
+ report.append("Last Update: " + DATE_FORMATs.format(new Date(packageInfo.lastUpdateTime)) + '\n');
+ Signature[] signatures = packageInfo.signatures;
+ if (signatures != null && signatures.length >= 1) {
+ report.append("SHA-1: " + CryptoHelper.getFingerprintCert(packageInfo.signatures[0].toByteArray()) + "\n");
+ }
+ report.append('\n');
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ String line;
+ while ((line = stacktrace.readLine()) != null) {
+ report.append(line);
+ report.append('\n');
+ }
+ file.close();
+ activity.deleteFile("stacktrace.txt");
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle(activity.getString(R.string.crash_report_title));
+ builder.setMessage(activity.getText(R.string.crash_report_message));
+ builder.setPositiveButton(activity.getText(R.string.send_now),
+ new OnClickListener() {
- Log.d(Config.LOGTAG, "using account="
- + finalAccount.getJid().toBareJid()
- + " to send in stack trace");
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
- Conversation conversation = null;
- try {
- conversation = service.findOrCreateConversation(finalAccount,
- Jid.fromString(Config.BUG_REPORTS), false);
- } catch (final InvalidJidException ignored) {
- }
- Message message = new Message(conversation, report
- .toString(), Message.ENCRYPTION_NONE);
- service.sendMessage(message);
- }
- });
- builder.setNegativeButton(activity.getText(R.string.send_never),
- new OnClickListener() {
+ Log.d(Config.LOGTAG, "using account="
+ + finalAccount.getJid().toBareJid()
+ + " to send in stack trace");
- @Override
- public void onClick(DialogInterface dialog, int which) {
- preferences.edit().putBoolean("crash_report", false)
- .apply();
- }
- });
- builder.create().show();
- return true;
- } catch (final IOException ignored) {
- return false;
- }
- }
+ Conversation conversation = null;
+ try {
+ conversation = service.findOrCreateConversation(finalAccount,
+ Jid.fromString(Config.BUG_REPORTS), false);
+ } catch (final InvalidJidException ignored) {
+ }
+ Message message = new Message(conversation, report
+ .toString(), Message.ENCRYPTION_NONE);
+ service.sendMessage(message);
+ }
+ });
+ builder.setNegativeButton(activity.getText(R.string.send_never),
+ new OnClickListener() {
- public static void writeToStacktraceFile(Context context, String msg) {
- try {
- OutputStream os = context.openFileOutput("stacktrace.txt", Context.MODE_PRIVATE);
- os.write(msg.getBytes());
- os.flush();
- os.close();
- } catch (IOException ignored) {
- }
- }
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ preferences.edit().putBoolean("crash_report", false)
+ .apply();
+ }
+ });
+ builder.create().show();
+ return true;
+ } catch (final IOException ignored) {
+ return false;
+ }
+ }
+
+ public static void writeToStacktraceFile(Context context, String msg) {
+ try {
+ OutputStream os = context.openFileOutput("stacktrace.txt", Context.MODE_PRIVATE);
+ os.write(msg.getBytes());
+ os.flush();
+ os.close();
+ } catch (IOException ignored) {
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/ExifHelper.java b/src/main/java/de/pixart/messenger/utils/ExifHelper.java
index 0ac4d6e4d..230af54d1 100644
--- a/src/main/java/de/pixart/messenger/utils/ExifHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/ExifHelper.java
@@ -66,7 +66,7 @@ public class ExifHelper {
if (!read(is, buf, 6)) return 0;
length -= 6;
if (pack(buf, 0, 4, false) == 0x45786966 &&
- pack(buf, 4, 2, false) == 0) {
+ pack(buf, 4, 2, false) == 0) {
break;
}
}
@@ -136,7 +136,7 @@ public class ExifHelper {
}
private static int pack(byte[] bytes, int offset, int length,
- boolean littleEndian) {
+ boolean littleEndian) {
int step = 1;
if (littleEndian) {
offset += length - 1;
diff --git a/src/main/java/de/pixart/messenger/utils/FileUtils.java b/src/main/java/de/pixart/messenger/utils/FileUtils.java
index fa69de49b..1500b6246 100644
--- a/src/main/java/de/pixart/messenger/utils/FileUtils.java
+++ b/src/main/java/de/pixart/messenger/utils/FileUtils.java
@@ -14,145 +14,145 @@ import java.io.File;
public class FileUtils {
- /**
- * Get a file path from a Uri. This will get the the path for Storage Access
- * Framework Documents, as well as the _data field for the MediaStore and
- * other file-based ContentProviders.
- *
- * @param context The context.
- * @param uri The Uri to query.
- * @author paulburke
- */
- @SuppressLint("NewApi")
- public static String getPath(final Context context, final Uri uri) {
- if (uri == null) {
- return null;
- }
-
- final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
-
- // DocumentProvider
- if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
- // ExternalStorageProvider
- if (isExternalStorageDocument(uri)) {
- final String docId = DocumentsContract.getDocumentId(uri);
- final String[] split = docId.split(":");
- final String type = split[0];
-
- if ("primary".equalsIgnoreCase(type)) {
- return Environment.getExternalStorageDirectory() + "/" + split[1];
- }
-
- // TODO handle non-primary volumes
- }
- // DownloadsProvider
- else if (isDownloadsDocument(uri)) {
-
- final String id = DocumentsContract.getDocumentId(uri);
- final Uri contentUri = ContentUris.withAppendedId(
- Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
-
- return getDataColumn(context, contentUri, null, null);
- }
- // MediaProvider
- else if (isMediaDocument(uri)) {
- final String docId = DocumentsContract.getDocumentId(uri);
- final String[] split = docId.split(":");
- final String type = split[0];
-
- Uri contentUri = null;
- if ("image".equals(type)) {
- contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
- } else if ("video".equals(type)) {
- contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
- } else if ("audio".equals(type)) {
- contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
- }
-
- final String selection = "_id=?";
- final String[] selectionArgs = new String[]{
- split[1]
- };
-
- return getDataColumn(context, contentUri, selection, selectionArgs);
- }
- }
- // MediaStore (and general)
- else if ("content".equalsIgnoreCase(uri.getScheme())) {
- String path = getDataColumn(context, uri, null, null);
- if (path != null) {
- File file = new File(path);
- if (!file.canRead()) {
- return null;
- }
- }
- return path;
- }
- // File
- else if ("file".equalsIgnoreCase(uri.getScheme())) {
- return uri.getPath();
- }
-
- return null;
- }
-
- /**
- * Get the value of the data column for this Uri. This is useful for
- * MediaStore Uris, and other file-based ContentProviders.
- *
- * @param context The context.
- * @param uri The Uri to query.
- * @param selection (Optional) Filter used in the query.
- * @param selectionArgs (Optional) Selection arguments used in the query.
- * @return The value of the _data column, which is typically a file path.
- */
- public static String getDataColumn(Context context, Uri uri, String selection,
- String[] selectionArgs) {
-
- Cursor cursor = null;
- final String column = "_data";
- final String[] projection = {
- column
- };
-
- try {
- cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,null);
- if (cursor != null && cursor.moveToFirst()) {
- final int column_index = cursor.getColumnIndexOrThrow(column);
- return cursor.getString(column_index);
- }
- } catch(Exception e) {
- return null;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- return null;
- }
-
-
- /**
- * @param uri The Uri to check.
- * @return Whether the Uri authority is ExternalStorageProvider.
- */
- public static boolean isExternalStorageDocument(Uri uri) {
- return "com.android.externalstorage.documents".equals(uri.getAuthority());
- }
-
- /**
- * @param uri The Uri to check.
- * @return Whether the Uri authority is DownloadsProvider.
- */
- public static boolean isDownloadsDocument(Uri uri) {
- return "com.android.providers.downloads.documents".equals(uri.getAuthority());
- }
-
- /**
- * @param uri The Uri to check.
- * @return Whether the Uri authority is MediaProvider.
- */
- public static boolean isMediaDocument(Uri uri) {
- return "com.android.providers.media.documents".equals(uri.getAuthority());
- }
+ /**
+ * Get a file path from a Uri. This will get the the path for Storage Access
+ * Framework Documents, as well as the _data field for the MediaStore and
+ * other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @author paulburke
+ */
+ @SuppressLint("NewApi")
+ public static String getPath(final Context context, final Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+
+ final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+
+ // DocumentProvider
+ if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
+ // ExternalStorageProvider
+ if (isExternalStorageDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ if ("primary".equalsIgnoreCase(type)) {
+ return Environment.getExternalStorageDirectory() + "/" + split[1];
+ }
+
+ // TODO handle non-primary volumes
+ }
+ // DownloadsProvider
+ else if (isDownloadsDocument(uri)) {
+
+ final String id = DocumentsContract.getDocumentId(uri);
+ final Uri contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+
+ return getDataColumn(context, contentUri, null, null);
+ }
+ // MediaProvider
+ else if (isMediaDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ Uri contentUri = null;
+ if ("image".equals(type)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if ("video".equals(type)) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else if ("audio".equals(type)) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ }
+
+ final String selection = "_id=?";
+ final String[] selectionArgs = new String[]{
+ split[1]
+ };
+
+ return getDataColumn(context, contentUri, selection, selectionArgs);
+ }
+ }
+ // MediaStore (and general)
+ else if ("content".equalsIgnoreCase(uri.getScheme())) {
+ String path = getDataColumn(context, uri, null, null);
+ if (path != null) {
+ File file = new File(path);
+ if (!file.canRead()) {
+ return null;
+ }
+ }
+ return path;
+ }
+ // File
+ else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the value of the data column for this Uri. This is useful for
+ * MediaStore Uris, and other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @param selection (Optional) Filter used in the query.
+ * @param selectionArgs (Optional) Selection arguments used in the query.
+ * @return The value of the _data column, which is typically a file path.
+ */
+ public static String getDataColumn(Context context, Uri uri, String selection,
+ String[] selectionArgs) {
+
+ Cursor cursor = null;
+ final String column = "_data";
+ final String[] projection = {
+ column
+ };
+
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ final int column_index = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(column_index);
+ }
+ } catch (Exception e) {
+ return null;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ */
+ public static boolean isExternalStorageDocument(Uri uri) {
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ */
+ public static boolean isDownloadsDocument(Uri uri) {
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ */
+ public static boolean isMediaDocument(Uri uri) {
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/GeoHelper.java b/src/main/java/de/pixart/messenger/utils/GeoHelper.java
index 0fe63e095..8f263c593 100644
--- a/src/main/java/de/pixart/messenger/utils/GeoHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/GeoHelper.java
@@ -14,13 +14,13 @@ import de.pixart.messenger.entities.Conversation;
import de.pixart.messenger.entities.Message;
public class GeoHelper {
- public static Pattern GEO_URI = Pattern.compile("geo:([\\-0-9.]+),([\\-0-9.]+)(?:,([\\-0-9.]+))?(?:\\?(.*))?", Pattern.CASE_INSENSITIVE);
+ public static Pattern GEO_URI = Pattern.compile("geo:([\\-0-9.]+),([\\-0-9.]+)(?:,([\\-0-9.]+))?(?:\\?(.*))?", Pattern.CASE_INSENSITIVE);
- public static boolean isGeoUri(String body) {
- return body != null && GEO_URI.matcher(body).matches();
- }
+ public static boolean isGeoUri(String body) {
+ return body != null && GEO_URI.matcher(body).matches();
+ }
- public static String MapPreviewUri (Message message) {
+ public static String MapPreviewUri(Message message) {
Matcher matcher = GEO_URI.matcher(message.getBody());
if (!matcher.matches()) {
return null;
@@ -42,62 +42,62 @@ public class GeoHelper {
return "https://maps.google.com/maps/api/staticmap?center=" + latitude + "," + longitude + "&size=500x500&scale=2&format=jpg&markers=" + latitude + "," + longitude + "&sensor=false";
}
- public static ArrayList<Intent> createGeoIntentsFromMessage(Message message) {
- final ArrayList<Intent> intents = new ArrayList<>();
- Matcher matcher = GEO_URI.matcher(message.getBody());
- if (!matcher.matches()) {
- return intents;
- }
- double latitude;
- double longitude;
- try {
- latitude = Double.parseDouble(matcher.group(1));
- if (latitude > 90.0 || latitude < -90.0) {
- return intents;
- }
- longitude = Double.parseDouble(matcher.group(2));
- if (longitude > 180.0 || longitude < -180.0) {
- return intents;
- }
- } catch (NumberFormatException nfe) {
- return intents;
- }
- final Conversation conversation = message.getConversation();
- String label;
- if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) {
- try {
- label = "(" + URLEncoder.encode(message.getConversation().getName(), "UTF-8") + ")";
- } catch (UnsupportedEncodingException e) {
- label = "";
- }
- } else {
- label = "";
- }
+ public static ArrayList<Intent> createGeoIntentsFromMessage(Message message) {
+ final ArrayList<Intent> intents = new ArrayList<>();
+ Matcher matcher = GEO_URI.matcher(message.getBody());
+ if (!matcher.matches()) {
+ return intents;
+ }
+ double latitude;
+ double longitude;
+ try {
+ latitude = Double.parseDouble(matcher.group(1));
+ if (latitude > 90.0 || latitude < -90.0) {
+ return intents;
+ }
+ longitude = Double.parseDouble(matcher.group(2));
+ if (longitude > 180.0 || longitude < -180.0) {
+ return intents;
+ }
+ } catch (NumberFormatException nfe) {
+ return intents;
+ }
+ final Conversation conversation = message.getConversation();
+ String label;
+ if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) {
+ try {
+ label = "(" + URLEncoder.encode(message.getConversation().getName(), "UTF-8") + ")";
+ } catch (UnsupportedEncodingException e) {
+ label = "";
+ }
+ } else {
+ label = "";
+ }
- Intent locationPluginIntent = new Intent("de.pixart.messenger.location.show");
- locationPluginIntent.putExtra("latitude",latitude);
- locationPluginIntent.putExtra("longitude",longitude);
- if (message.getStatus() != Message.STATUS_RECEIVED) {
- locationPluginIntent.putExtra("jid",conversation.getAccount().getJid().toString());
- locationPluginIntent.putExtra("name",conversation.getAccount().getJid().getLocalpart());
- } else {
- Contact contact = message.getContact();
- if (contact != null) {
- locationPluginIntent.putExtra("name", contact.getDisplayName());
- locationPluginIntent.putExtra("jid", contact.getJid().toString());
- } else {
- locationPluginIntent.putExtra("name", UIHelper.getDisplayedMucCounterpart(message.getCounterpart()));
- }
- }
- intents.add(locationPluginIntent);
+ Intent locationPluginIntent = new Intent("de.pixart.messenger.location.show");
+ locationPluginIntent.putExtra("latitude", latitude);
+ locationPluginIntent.putExtra("longitude", longitude);
+ if (message.getStatus() != Message.STATUS_RECEIVED) {
+ locationPluginIntent.putExtra("jid", conversation.getAccount().getJid().toString());
+ locationPluginIntent.putExtra("name", conversation.getAccount().getJid().getLocalpart());
+ } else {
+ Contact contact = message.getContact();
+ if (contact != null) {
+ locationPluginIntent.putExtra("name", contact.getDisplayName());
+ locationPluginIntent.putExtra("jid", contact.getJid().toString());
+ } else {
+ locationPluginIntent.putExtra("name", UIHelper.getDisplayedMucCounterpart(message.getCounterpart()));
+ }
+ }
+ intents.add(locationPluginIntent);
- Intent geoIntent = new Intent(Intent.ACTION_VIEW);
- geoIntent.setData(Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude) + "?q=" + String.valueOf(latitude) + "," + String.valueOf(longitude) + label));
- intents.add(geoIntent);
+ Intent geoIntent = new Intent(Intent.ACTION_VIEW);
+ geoIntent.setData(Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude) + "?q=" + String.valueOf(latitude) + "," + String.valueOf(longitude) + label));
+ intents.add(geoIntent);
- Intent httpIntent = new Intent(Intent.ACTION_VIEW);
- httpIntent.setData(Uri.parse("https://maps.google.com/maps?q=loc:"+String.valueOf(latitude) + "," + String.valueOf(longitude) +label));
- intents.add(httpIntent);
- return intents;
- }
+ Intent httpIntent = new Intent(Intent.ACTION_VIEW);
+ httpIntent.setData(Uri.parse("https://maps.google.com/maps?q=loc:" + String.valueOf(latitude) + "," + String.valueOf(longitude) + label));
+ intents.add(httpIntent);
+ return intents;
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/MimeUtils.java b/src/main/java/de/pixart/messenger/utils/MimeUtils.java
index f90822c2e..366ed308b 100644
--- a/src/main/java/de/pixart/messenger/utils/MimeUtils.java
+++ b/src/main/java/de/pixart/messenger/utils/MimeUtils.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
package de.pixart.messenger.utils;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -21,6 +22,7 @@ import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
+
/**
* Utilities for dealing with MIME types.
* Used to implement java.net.URLConnection and android.webkit.MimeTypeMap.
@@ -28,6 +30,7 @@ import java.util.Properties;
public final class MimeUtils {
private static final Map<String, String> mimeTypeToExtensionMap = new HashMap<String, String>();
private static final Map<String, String> extensionToMimeTypeMap = new HashMap<String, String>();
+
static {
// The following table is based on /etc/mime.types data minus
// chemical/* MIME types and MIME types that don't map to any
@@ -373,6 +376,7 @@ public final class MimeUtils {
add("x-epoc/x-sisx-app", "sisx");
applyOverrides();
}
+
private static void add(String mimeType, String extension) {
// If we have an existing x -> y mapping, we do not want to
// override it with another mapping x -> y2.
@@ -386,6 +390,7 @@ public final class MimeUtils {
extensionToMimeTypeMap.put(extension, mimeType);
}
}
+
private static InputStream getContentTypesPropertiesStream() {
// User override?
String userTable = System.getProperty("content.types.user.table");
@@ -408,6 +413,7 @@ public final class MimeUtils {
}
return null;
}
+
/**
* This isn't what the RI does. The RI doesn't have hard-coded defaults, so supplying your
* own "content.types.user.table" means you don't get any of the built-ins, and the built-ins
@@ -436,10 +442,13 @@ public final class MimeUtils {
} catch (IOException ignored) {
}
}
+
private MimeUtils() {
}
+
/**
* Returns true if the given MIME type has an entry in the map.
+ *
* @param mimeType A MIME type (i.e. text/plain)
* @return True iff there is a mimeType entry in the map.
*/
@@ -449,8 +458,10 @@ public final class MimeUtils {
}
return mimeTypeToExtensionMap.containsKey(mimeType);
}
+
/**
* Returns the MIME type for the given extension.
+ *
* @param extension A file extension without the leading '.'
* @return The MIME type for the given extension or null iff there is none.
*/
@@ -460,8 +471,10 @@ public final class MimeUtils {
}
return extensionToMimeTypeMap.get(extension.toLowerCase());
}
+
/**
* Returns true if the given extension has a registered MIME type.
+ *
* @param extension A file extension without the leading '.'
* @return True iff there is an extension entry in the map.
*/
@@ -471,10 +484,12 @@ public final class MimeUtils {
}
return extensionToMimeTypeMap.containsKey(extension);
}
+
/**
* Returns the registered extension for the given MIME type. Note that some
* MIME types map to multiple extensions. This call will return the most
* common extension for the given MIME type.
+ *
* @param mimeType A MIME type (i.e. text/plain)
* @return The extension for the given MIME type or null iff there is none.
*/
diff --git a/src/main/java/de/pixart/messenger/utils/OnPhoneContactsLoadedListener.java b/src/main/java/de/pixart/messenger/utils/OnPhoneContactsLoadedListener.java
index 049154745..f19258dda 100644
--- a/src/main/java/de/pixart/messenger/utils/OnPhoneContactsLoadedListener.java
+++ b/src/main/java/de/pixart/messenger/utils/OnPhoneContactsLoadedListener.java
@@ -5,5 +5,5 @@ import android.os.Bundle;
import java.util.List;
public interface OnPhoneContactsLoadedListener {
- public void onPhoneContactsLoaded(List<Bundle> phoneContacts);
+ public void onPhoneContactsLoaded(List<Bundle> phoneContacts);
}
diff --git a/src/main/java/de/pixart/messenger/utils/PRNGFixes.java b/src/main/java/de/pixart/messenger/utils/PRNGFixes.java
index be456a30c..211ebca8e 100644
--- a/src/main/java/de/pixart/messenger/utils/PRNGFixes.java
+++ b/src/main/java/de/pixart/messenger/utils/PRNGFixes.java
@@ -21,145 +21,144 @@ import java.security.Security;
/**
* Fixes for the output of the default PRNG having low entropy.
- *
+ * <p>
* The fixes need to be applied via {@link #apply()} before any use of Java
* Cryptography Architecture primitives. A good place to invoke them is in the
* application's {@code onCreate}.
*/
public final class PRNGFixes {
- private static final int VERSION_CODE_JELLY_BEAN = 16;
- private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
- private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = getBuildFingerprintAndDeviceSerial();
+ private static final int VERSION_CODE_JELLY_BEAN = 16;
+ private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
+ private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = getBuildFingerprintAndDeviceSerial();
- /** Hidden constructor to prevent instantiation. */
- private PRNGFixes() {
- }
+ /**
+ * Hidden constructor to prevent instantiation.
+ */
+ private PRNGFixes() {
+ }
- /**
- * Applies all fixes.
- *
- * @throws SecurityException
- * if a fix is needed but could not be applied.
- */
- public static void apply() {
- applyOpenSSLFix();
- installLinuxPRNGSecureRandom();
- }
+ /**
+ * Applies all fixes.
+ *
+ * @throws SecurityException if a fix is needed but could not be applied.
+ */
+ public static void apply() {
+ applyOpenSSLFix();
+ installLinuxPRNGSecureRandom();
+ }
- /**
- * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
- * fix is not needed.
- *
- * @throws SecurityException
- * if the fix is needed but could not be applied.
- */
- private static void applyOpenSSLFix() throws SecurityException {
- if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
- || (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
- // No need to apply the fix
- return;
- }
+ /**
+ * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
+ * fix is not needed.
+ *
+ * @throws SecurityException if the fix is needed but could not be applied.
+ */
+ private static void applyOpenSSLFix() throws SecurityException {
+ if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
+ || (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
+ // No need to apply the fix
+ return;
+ }
- try {
- // Mix in the device- and invocation-specific seed.
- Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
- .getMethod("RAND_seed", byte[].class)
- .invoke(null, generateSeed());
+ try {
+ // Mix in the device- and invocation-specific seed.
+ Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
+ .getMethod("RAND_seed", byte[].class)
+ .invoke(null, generateSeed());
- // Mix output of Linux PRNG into OpenSSL's PRNG
- int bytesRead = (Integer) Class
- .forName(
- "org.apache.harmony.xnet.provider.jsse.NativeCrypto")
- .getMethod("RAND_load_file", String.class, long.class)
- .invoke(null, "/dev/urandom", 1024);
- if (bytesRead != 1024) {
- throw new IOException(
- "Unexpected number of bytes read from Linux PRNG: "
- + bytesRead);
- }
- } catch (Exception e) {
- throw new SecurityException("Failed to seed OpenSSL PRNG", e);
- }
- }
+ // Mix output of Linux PRNG into OpenSSL's PRNG
+ int bytesRead = (Integer) Class
+ .forName(
+ "org.apache.harmony.xnet.provider.jsse.NativeCrypto")
+ .getMethod("RAND_load_file", String.class, long.class)
+ .invoke(null, "/dev/urandom", 1024);
+ if (bytesRead != 1024) {
+ throw new IOException(
+ "Unexpected number of bytes read from Linux PRNG: "
+ + bytesRead);
+ }
+ } catch (Exception e) {
+ throw new SecurityException("Failed to seed OpenSSL PRNG", e);
+ }
+ }
- /**
- * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
- * default. Does nothing if the implementation is already the default or if
- * there is not need to install the implementation.
- *
- * @throws SecurityException
- * if the fix is needed but could not be applied.
- */
- private static void installLinuxPRNGSecureRandom() throws SecurityException {
- if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
- // No need to apply the fix
- return;
- }
+ /**
+ * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
+ * default. Does nothing if the implementation is already the default or if
+ * there is not need to install the implementation.
+ *
+ * @throws SecurityException if the fix is needed but could not be applied.
+ */
+ private static void installLinuxPRNGSecureRandom() throws SecurityException {
+ if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
+ // No need to apply the fix
+ return;
+ }
- // Install a Linux PRNG-based SecureRandom implementation as the
- // default, if not yet installed.
- Provider[] secureRandomProviders = Security
- .getProviders("SecureRandom.SHA1PRNG");
- if ((secureRandomProviders == null)
- || (secureRandomProviders.length < 1)
- || (!LinuxPRNGSecureRandomProvider.class
- .equals(secureRandomProviders[0].getClass()))) {
- Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
- }
+ // Install a Linux PRNG-based SecureRandom implementation as the
+ // default, if not yet installed.
+ Provider[] secureRandomProviders = Security
+ .getProviders("SecureRandom.SHA1PRNG");
+ if ((secureRandomProviders == null)
+ || (secureRandomProviders.length < 1)
+ || (!LinuxPRNGSecureRandomProvider.class
+ .equals(secureRandomProviders[0].getClass()))) {
+ Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
+ }
- // Assert that new SecureRandom() and
- // SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
- // by the Linux PRNG-based SecureRandom implementation.
- SecureRandom rng1 = new SecureRandom();
- if (!LinuxPRNGSecureRandomProvider.class.equals(rng1.getProvider()
- .getClass())) {
- throw new SecurityException(
- "new SecureRandom() backed by wrong Provider: "
- + rng1.getProvider().getClass());
- }
+ // Assert that new SecureRandom() and
+ // SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
+ // by the Linux PRNG-based SecureRandom implementation.
+ SecureRandom rng1 = new SecureRandom();
+ if (!LinuxPRNGSecureRandomProvider.class.equals(rng1.getProvider()
+ .getClass())) {
+ throw new SecurityException(
+ "new SecureRandom() backed by wrong Provider: "
+ + rng1.getProvider().getClass());
+ }
- SecureRandom rng2;
- try {
- rng2 = SecureRandom.getInstance("SHA1PRNG");
- } catch (NoSuchAlgorithmException e) {
- throw new SecurityException("SHA1PRNG not available", e);
- }
- if (!LinuxPRNGSecureRandomProvider.class.equals(rng2.getProvider()
- .getClass())) {
- throw new SecurityException(
- "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
- + " Provider: " + rng2.getProvider().getClass());
- }
- }
+ SecureRandom rng2;
+ try {
+ rng2 = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ throw new SecurityException("SHA1PRNG not available", e);
+ }
+ if (!LinuxPRNGSecureRandomProvider.class.equals(rng2.getProvider()
+ .getClass())) {
+ throw new SecurityException(
+ "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
+ + " Provider: " + rng2.getProvider().getClass());
+ }
+ }
- /**
- * {@code Provider} of {@code SecureRandom} engines which pass through all
- * requests to the Linux PRNG.
- */
- private static class LinuxPRNGSecureRandomProvider extends Provider {
+ /**
+ * {@code Provider} of {@code SecureRandom} engines which pass through all
+ * requests to the Linux PRNG.
+ */
+ private static class LinuxPRNGSecureRandomProvider extends Provider {
- public LinuxPRNGSecureRandomProvider() {
- super("LinuxPRNG", 1.0,
- "A Linux-specific random number provider that uses"
- + " /dev/urandom");
- // Although /dev/urandom is not a SHA-1 PRNG, some apps
- // explicitly request a SHA1PRNG SecureRandom and we thus need to
- // prevent them from getting the default implementation whose output
- // may have low entropy.
- put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
- put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
- }
- }
+ public LinuxPRNGSecureRandomProvider() {
+ super("LinuxPRNG", 1.0,
+ "A Linux-specific random number provider that uses"
+ + " /dev/urandom");
+ // Although /dev/urandom is not a SHA-1 PRNG, some apps
+ // explicitly request a SHA1PRNG SecureRandom and we thus need to
+ // prevent them from getting the default implementation whose output
+ // may have low entropy.
+ put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
+ put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
+ }
+ }
- /**
- * {@link SecureRandomSpi} which passes all requests to the Linux PRNG (
- * {@code /dev/urandom}).
- */
- public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
+ /**
+ * {@link SecureRandomSpi} which passes all requests to the Linux PRNG (
+ * {@code /dev/urandom}).
+ */
+ public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
/*
- * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
+ * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
* are passed through to the Linux PRNG (/dev/urandom). Instances of
* this class seed themselves by mixing in the current time, PID, UID,
* build fingerprint, and hardware serial number (where available) into
@@ -170,158 +169,158 @@ public final class PRNGFixes {
* duplicated PRNG output.
*/
- private static final File URANDOM_FILE = new File("/dev/urandom");
+ private static final File URANDOM_FILE = new File("/dev/urandom");
- private static final Object sLock = new Object();
+ private static final Object sLock = new Object();
- /**
- * Input stream for reading from Linux PRNG or {@code null} if not yet
- * opened.
- *
- * @GuardedBy("sLock")
- */
- private static DataInputStream sUrandomIn;
+ /**
+ * Input stream for reading from Linux PRNG or {@code null} if not yet
+ * opened.
+ *
+ * @GuardedBy("sLock")
+ */
+ private static DataInputStream sUrandomIn;
- /**
- * Output stream for writing to Linux PRNG or {@code null} if not yet
- * opened.
- *
- * @GuardedBy("sLock")
- */
- private static OutputStream sUrandomOut;
+ /**
+ * Output stream for writing to Linux PRNG or {@code null} if not yet
+ * opened.
+ *
+ * @GuardedBy("sLock")
+ */
+ private static OutputStream sUrandomOut;
- /**
- * Whether this engine instance has been seeded. This is needed because
- * each instance needs to seed itself if the client does not explicitly
- * seed it.
- */
- private boolean mSeeded;
+ /**
+ * Whether this engine instance has been seeded. This is needed because
+ * each instance needs to seed itself if the client does not explicitly
+ * seed it.
+ */
+ private boolean mSeeded;
- @Override
- protected void engineSetSeed(byte[] bytes) {
- try {
- OutputStream out;
- synchronized (sLock) {
- out = getUrandomOutputStream();
- }
- out.write(bytes);
- out.flush();
- } catch (IOException e) {
- // On a small fraction of devices /dev/urandom is not writable.
- // Log and ignore.
- Log.w(PRNGFixes.class.getSimpleName(),
- "Failed to mix seed into " + URANDOM_FILE);
- } finally {
- mSeeded = true;
- }
- }
+ @Override
+ protected void engineSetSeed(byte[] bytes) {
+ try {
+ OutputStream out;
+ synchronized (sLock) {
+ out = getUrandomOutputStream();
+ }
+ out.write(bytes);
+ out.flush();
+ } catch (IOException e) {
+ // On a small fraction of devices /dev/urandom is not writable.
+ // Log and ignore.
+ Log.w(PRNGFixes.class.getSimpleName(),
+ "Failed to mix seed into " + URANDOM_FILE);
+ } finally {
+ mSeeded = true;
+ }
+ }
- @Override
- protected void engineNextBytes(byte[] bytes) {
- if (!mSeeded) {
- // Mix in the device- and invocation-specific seed.
- engineSetSeed(generateSeed());
- }
+ @Override
+ protected void engineNextBytes(byte[] bytes) {
+ if (!mSeeded) {
+ // Mix in the device- and invocation-specific seed.
+ engineSetSeed(generateSeed());
+ }
- try {
- DataInputStream in;
- synchronized (sLock) {
- in = getUrandomInputStream();
- }
- synchronized (in) {
- in.readFully(bytes);
- }
- } catch (IOException e) {
- throw new SecurityException("Failed to read from "
- + URANDOM_FILE, e);
- }
- }
+ try {
+ DataInputStream in;
+ synchronized (sLock) {
+ in = getUrandomInputStream();
+ }
+ synchronized (in) {
+ in.readFully(bytes);
+ }
+ } catch (IOException e) {
+ throw new SecurityException("Failed to read from "
+ + URANDOM_FILE, e);
+ }
+ }
- @Override
- protected byte[] engineGenerateSeed(int size) {
- byte[] seed = new byte[size];
- engineNextBytes(seed);
- return seed;
- }
+ @Override
+ protected byte[] engineGenerateSeed(int size) {
+ byte[] seed = new byte[size];
+ engineNextBytes(seed);
+ return seed;
+ }
- private DataInputStream getUrandomInputStream() {
- synchronized (sLock) {
- if (sUrandomIn == null) {
- // NOTE: Consider inserting a BufferedInputStream between
- // DataInputStream and FileInputStream if you need higher
- // PRNG output performance and can live with future PRNG
- // output being pulled into this process prematurely.
- try {
- sUrandomIn = new DataInputStream(new FileInputStream(
- URANDOM_FILE));
- } catch (IOException e) {
- throw new SecurityException("Failed to open "
- + URANDOM_FILE + " for reading", e);
- }
- }
- return sUrandomIn;
- }
- }
+ private DataInputStream getUrandomInputStream() {
+ synchronized (sLock) {
+ if (sUrandomIn == null) {
+ // NOTE: Consider inserting a BufferedInputStream between
+ // DataInputStream and FileInputStream if you need higher
+ // PRNG output performance and can live with future PRNG
+ // output being pulled into this process prematurely.
+ try {
+ sUrandomIn = new DataInputStream(new FileInputStream(
+ URANDOM_FILE));
+ } catch (IOException e) {
+ throw new SecurityException("Failed to open "
+ + URANDOM_FILE + " for reading", e);
+ }
+ }
+ return sUrandomIn;
+ }
+ }
- private OutputStream getUrandomOutputStream() throws IOException {
- synchronized (sLock) {
- if (sUrandomOut == null) {
- sUrandomOut = new FileOutputStream(URANDOM_FILE);
- }
- return sUrandomOut;
- }
- }
- }
+ private OutputStream getUrandomOutputStream() throws IOException {
+ synchronized (sLock) {
+ if (sUrandomOut == null) {
+ sUrandomOut = new FileOutputStream(URANDOM_FILE);
+ }
+ return sUrandomOut;
+ }
+ }
+ }
- /**
- * Generates a device- and invocation-specific seed to be mixed into the
- * Linux PRNG.
- */
- private static byte[] generateSeed() {
- try {
- ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
- DataOutputStream seedBufferOut = new DataOutputStream(seedBuffer);
- seedBufferOut.writeLong(System.currentTimeMillis());
- seedBufferOut.writeLong(System.nanoTime());
- seedBufferOut.writeInt(Process.myPid());
- seedBufferOut.writeInt(Process.myUid());
- seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
- seedBufferOut.close();
- return seedBuffer.toByteArray();
- } catch (IOException e) {
- throw new SecurityException("Failed to generate seed", e);
- }
- }
+ /**
+ * Generates a device- and invocation-specific seed to be mixed into the
+ * Linux PRNG.
+ */
+ private static byte[] generateSeed() {
+ try {
+ ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
+ DataOutputStream seedBufferOut = new DataOutputStream(seedBuffer);
+ seedBufferOut.writeLong(System.currentTimeMillis());
+ seedBufferOut.writeLong(System.nanoTime());
+ seedBufferOut.writeInt(Process.myPid());
+ seedBufferOut.writeInt(Process.myUid());
+ seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
+ seedBufferOut.close();
+ return seedBuffer.toByteArray();
+ } catch (IOException e) {
+ throw new SecurityException("Failed to generate seed", e);
+ }
+ }
- /**
- * Gets the hardware serial number of this device.
- *
- * @return serial number or {@code null} if not available.
- */
- private static String getDeviceSerialNumber() {
- // We're using the Reflection API because Build.SERIAL is only available
- // since API Level 9 (Gingerbread, Android 2.3).
- try {
- return (String) Build.class.getField("SERIAL").get(null);
- } catch (Exception ignored) {
- return null;
- }
- }
+ /**
+ * Gets the hardware serial number of this device.
+ *
+ * @return serial number or {@code null} if not available.
+ */
+ private static String getDeviceSerialNumber() {
+ // We're using the Reflection API because Build.SERIAL is only available
+ // since API Level 9 (Gingerbread, Android 2.3).
+ try {
+ return (String) Build.class.getField("SERIAL").get(null);
+ } catch (Exception ignored) {
+ return null;
+ }
+ }
- private static byte[] getBuildFingerprintAndDeviceSerial() {
- StringBuilder result = new StringBuilder();
- String fingerprint = Build.FINGERPRINT;
- if (fingerprint != null) {
- result.append(fingerprint);
- }
- String serial = getDeviceSerialNumber();
- if (serial != null) {
- result.append(serial);
- }
- try {
- return result.toString().getBytes("UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("UTF-8 encoding not supported");
- }
- }
+ private static byte[] getBuildFingerprintAndDeviceSerial() {
+ StringBuilder result = new StringBuilder();
+ String fingerprint = Build.FINGERPRINT;
+ if (fingerprint != null) {
+ result.append(fingerprint);
+ }
+ String serial = getDeviceSerialNumber();
+ if (serial != null) {
+ result.append(serial);
+ }
+ try {
+ return result.toString().getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("UTF-8 encoding not supported");
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/PhoneHelper.java b/src/main/java/de/pixart/messenger/utils/PhoneHelper.java
index 805b76673..cdc51f729 100644
--- a/src/main/java/de/pixart/messenger/utils/PhoneHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/PhoneHelper.java
@@ -19,124 +19,124 @@ import java.util.concurrent.RejectedExecutionException;
public class PhoneHelper {
- public static void loadPhoneContacts(Context context, final OnPhoneContactsLoadedListener listener) {
- final List<Bundle> phoneContacts = new ArrayList<>();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
- && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
- listener.onPhoneContactsLoaded(phoneContacts);
- return;
- }
- final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
- ContactsContract.Data.DISPLAY_NAME,
- ContactsContract.Data.PHOTO_URI,
- ContactsContract.Data.LOOKUP_KEY,
- ContactsContract.CommonDataKinds.Im.DATA};
-
- final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\""
- + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
- + "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL
- + "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
- + "\")";
-
- CursorLoader mCursorLoader = new NotThrowCursorLoader(context,
- ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null,
- null);
- mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
-
- @Override
- public void onLoadComplete(Loader<Cursor> arg0, Cursor cursor) {
- if (cursor != null) {
- while (cursor.moveToNext()) {
- Bundle contact = new Bundle();
- contact.putInt("phoneid", cursor.getInt(cursor
- .getColumnIndex(ContactsContract.Data._ID)));
- contact.putString(
- "displayname",
- cursor.getString(cursor
- .getColumnIndex(ContactsContract.Data.DISPLAY_NAME)));
- contact.putString("photouri", cursor.getString(cursor
- .getColumnIndex(ContactsContract.Data.PHOTO_URI)));
- contact.putString("lookup", cursor.getString(cursor
- .getColumnIndex(ContactsContract.Data.LOOKUP_KEY)));
-
- contact.putString(
- "jid",
- cursor.getString(cursor
- .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
- phoneContacts.add(contact);
- }
- cursor.close();
- }
-
- if (listener != null) {
- listener.onPhoneContactsLoaded(phoneContacts);
- }
- }
- });
- try {
- mCursorLoader.startLoading();
- } catch (RejectedExecutionException e) {
- if (listener != null) {
- listener.onPhoneContactsLoaded(phoneContacts);
- }
- }
- }
-
- private static class NotThrowCursorLoader extends CursorLoader {
-
- public NotThrowCursorLoader(Context c, Uri u, String[] p, String s, String[] sa, String so) {
- super(c, u, p, s, sa, so);
- }
-
- @Override
- public Cursor loadInBackground() {
-
- try {
- return (super.loadInBackground());
- } catch (Throwable e) {
- return(null);
- }
- }
-
- }
-
- public static Uri getSelfiUri(Context context) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
- && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
- return null;
- }
- String[] mProjection = new String[]{Profile._ID, Profile.PHOTO_URI};
- Cursor mProfileCursor = context.getContentResolver().query(
- Profile.CONTENT_URI, mProjection, null, null, null);
-
- if (mProfileCursor == null || mProfileCursor.getCount() == 0) {
- return null;
- } else {
- mProfileCursor.moveToFirst();
- String uri = mProfileCursor.getString(1);
- mProfileCursor.close();
- if (uri == null) {
- return null;
- } else {
- return Uri.parse(uri);
- }
- }
- }
-
- public static String getVersionName(Context context) {
- final String packageName = context == null ? null : context.getPackageName();
- if (packageName != null) {
- try {
- return context.getPackageManager().getPackageInfo(packageName, 0).versionName;
- } catch (final PackageManager.NameNotFoundException | RuntimeException e) {
- return "unknown";
- }
- } else {
- return "unknown";
- }
- }
-
- public static String getOSVersion(Context context) {
- return "Android/" + android.os.Build.MODEL + "/" + android.os.Build.VERSION.RELEASE;
+ public static void loadPhoneContacts(Context context, final OnPhoneContactsLoadedListener listener) {
+ final List<Bundle> phoneContacts = new ArrayList<>();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
+ listener.onPhoneContactsLoaded(phoneContacts);
+ return;
+ }
+ final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
+ ContactsContract.Data.DISPLAY_NAME,
+ ContactsContract.Data.PHOTO_URI,
+ ContactsContract.Data.LOOKUP_KEY,
+ ContactsContract.CommonDataKinds.Im.DATA};
+
+ final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\""
+ + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
+ + "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL
+ + "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
+ + "\")";
+
+ CursorLoader mCursorLoader = new NotThrowCursorLoader(context,
+ ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null,
+ null);
+ mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
+
+ @Override
+ public void onLoadComplete(Loader<Cursor> arg0, Cursor cursor) {
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ Bundle contact = new Bundle();
+ contact.putInt("phoneid", cursor.getInt(cursor
+ .getColumnIndex(ContactsContract.Data._ID)));
+ contact.putString(
+ "displayname",
+ cursor.getString(cursor
+ .getColumnIndex(ContactsContract.Data.DISPLAY_NAME)));
+ contact.putString("photouri", cursor.getString(cursor
+ .getColumnIndex(ContactsContract.Data.PHOTO_URI)));
+ contact.putString("lookup", cursor.getString(cursor
+ .getColumnIndex(ContactsContract.Data.LOOKUP_KEY)));
+
+ contact.putString(
+ "jid",
+ cursor.getString(cursor
+ .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
+ phoneContacts.add(contact);
+ }
+ cursor.close();
+ }
+
+ if (listener != null) {
+ listener.onPhoneContactsLoaded(phoneContacts);
+ }
+ }
+ });
+ try {
+ mCursorLoader.startLoading();
+ } catch (RejectedExecutionException e) {
+ if (listener != null) {
+ listener.onPhoneContactsLoaded(phoneContacts);
+ }
+ }
+ }
+
+ private static class NotThrowCursorLoader extends CursorLoader {
+
+ public NotThrowCursorLoader(Context c, Uri u, String[] p, String s, String[] sa, String so) {
+ super(c, u, p, s, sa, so);
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+
+ try {
+ return (super.loadInBackground());
+ } catch (Throwable e) {
+ return (null);
+ }
+ }
+
+ }
+
+ public static Uri getSelfiUri(Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+ String[] mProjection = new String[]{Profile._ID, Profile.PHOTO_URI};
+ Cursor mProfileCursor = context.getContentResolver().query(
+ Profile.CONTENT_URI, mProjection, null, null, null);
+
+ if (mProfileCursor == null || mProfileCursor.getCount() == 0) {
+ return null;
+ } else {
+ mProfileCursor.moveToFirst();
+ String uri = mProfileCursor.getString(1);
+ mProfileCursor.close();
+ if (uri == null) {
+ return null;
+ } else {
+ return Uri.parse(uri);
+ }
+ }
+ }
+
+ public static String getVersionName(Context context) {
+ final String packageName = context == null ? null : context.getPackageName();
+ if (packageName != null) {
+ try {
+ return context.getPackageManager().getPackageInfo(packageName, 0).versionName;
+ } catch (final PackageManager.NameNotFoundException | RuntimeException e) {
+ return "unknown";
+ }
+ } else {
+ return "unknown";
+ }
+ }
+
+ public static String getOSVersion(Context context) {
+ return "Android/" + android.os.Build.MODEL + "/" + android.os.Build.VERSION.RELEASE;
}
}
diff --git a/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java b/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java
index 661fda807..ad3629354 100644
--- a/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/SSLSocketHelper.java
@@ -14,60 +14,60 @@ import javax.net.ssl.SSLSocketFactory;
public class SSLSocketHelper {
- public static void setSecurity(final SSLSocket sslSocket) throws NoSuchAlgorithmException {
- final String[] supportProtocols;
- final Collection<String> supportedProtocols = new LinkedList<>(
- Arrays.asList(sslSocket.getSupportedProtocols()));
- supportedProtocols.remove("SSLv3");
- supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]);
+ public static void setSecurity(final SSLSocket sslSocket) throws NoSuchAlgorithmException {
+ final String[] supportProtocols;
+ final Collection<String> supportedProtocols = new LinkedList<>(
+ Arrays.asList(sslSocket.getSupportedProtocols()));
+ supportedProtocols.remove("SSLv3");
+ supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]);
- sslSocket.setEnabledProtocols(supportProtocols);
+ sslSocket.setEnabledProtocols(supportProtocols);
- final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
- sslSocket.getSupportedCipherSuites());
- if (cipherSuites.length > 0) {
- sslSocket.setEnabledCipherSuites(cipherSuites);
- }
- }
+ final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
+ sslSocket.getSupportedCipherSuites());
+ if (cipherSuites.length > 0) {
+ sslSocket.setEnabledCipherSuites(cipherSuites);
+ }
+ }
- public static void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) {
- if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
- ((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname);
- } else {
- try {
- socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
- } catch (Throwable e) {
- // ignore any error, we just can't set the hostname...
- }
- }
- }
+ public static void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) {
+ if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ ((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname);
+ } else {
+ try {
+ socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
+ } catch (Throwable e) {
+ // ignore any error, we just can't set the hostname...
+ }
+ }
+ }
- public static void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) {
- try {
- if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
- // can't call directly because of @hide?
- //((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")});
- android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}});
- } else {
- final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class);
- // the concatenation of 8-bit, length prefixed protocol names, just one in our case...
- // http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
- final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8");
- final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1];
- lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow
- System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length);
- method.invoke(socket, new Object[]{lengthPrefixedProtocols});
- }
- } catch (Throwable e) {
- // ignore any error, we just can't set the alpn protocol...
- }
- }
+ public static void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) {
+ try {
+ if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+ // can't call directly because of @hide?
+ //((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")});
+ android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}});
+ } else {
+ final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class);
+ // the concatenation of 8-bit, length prefixed protocol names, just one in our case...
+ // http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
+ final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8");
+ final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1];
+ lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow
+ System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length);
+ method.invoke(socket, new Object[]{lengthPrefixedProtocols});
+ }
+ } catch (Throwable e) {
+ // ignore any error, we just can't set the alpn protocol...
+ }
+ }
- public static SSLContext getSSLContext() throws NoSuchAlgorithmException {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- return SSLContext.getInstance("TLSv1.2");
- } else {
- return SSLContext.getInstance("TLS");
- }
- }
+ public static SSLContext getSSLContext() throws NoSuchAlgorithmException {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ return SSLContext.getInstance("TLSv1.2");
+ } else {
+ return SSLContext.getInstance("TLS");
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java b/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java
index 116149ec1..6b60a424f 100644
--- a/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java
+++ b/src/main/java/de/pixart/messenger/utils/SerialSingleThreadExecutor.java
@@ -9,43 +9,43 @@ import java.util.concurrent.Executors;
public class SerialSingleThreadExecutor implements Executor {
- final Executor executor = Executors.newSingleThreadExecutor();
- protected final Queue<Runnable> tasks = new ArrayDeque();
- Runnable active;
-
- public SerialSingleThreadExecutor() {
- this(false);
- }
-
- public SerialSingleThreadExecutor(boolean prepareLooper) {
- if (prepareLooper) {
- execute(new Runnable() {
- @Override
- public void run() {
- Looper.prepare();
- }
- });
- }
- }
-
- public synchronized void execute(final Runnable r) {
- tasks.offer(new Runnable() {
- public void run() {
- try {
- r.run();
- } finally {
- scheduleNext();
- }
- }
- });
- if (active == null) {
- scheduleNext();
- }
- }
-
- protected synchronized void scheduleNext() {
- if ((active = tasks.poll()) != null) {
- executor.execute(active);
- }
- }
+ final Executor executor = Executors.newSingleThreadExecutor();
+ protected final Queue<Runnable> tasks = new ArrayDeque();
+ Runnable active;
+
+ public SerialSingleThreadExecutor() {
+ this(false);
+ }
+
+ public SerialSingleThreadExecutor(boolean prepareLooper) {
+ if (prepareLooper) {
+ execute(new Runnable() {
+ @Override
+ public void run() {
+ Looper.prepare();
+ }
+ });
+ }
+ }
+
+ public synchronized void execute(final Runnable r) {
+ tasks.offer(new Runnable() {
+ public void run() {
+ try {
+ r.run();
+ } finally {
+ scheduleNext();
+ }
+ }
+ });
+ if (active == null) {
+ scheduleNext();
+ }
+ }
+
+ protected synchronized void scheduleNext() {
+ if ((active = tasks.poll()) != null) {
+ executor.execute(active);
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java b/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java
index ee7be8e9c..e3465fb9b 100644
--- a/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java
+++ b/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java
@@ -12,46 +12,46 @@ import de.pixart.messenger.Config;
public class SocksSocketFactory {
- public static void createSocksConnection(Socket socket, String destination, int port) throws IOException {
- InputStream proxyIs = socket.getInputStream();
- OutputStream proxyOs = socket.getOutputStream();
- proxyOs.write(new byte[]{0x05, 0x01, 0x00});
- byte[] response = new byte[2];
- proxyIs.read(response);
- byte[] dest = destination.getBytes();
- ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
- request.put(new byte[]{0x05, 0x01, 0x00, 0x03});
- request.put((byte) dest.length);
- request.put(dest);
- request.putShort((short) port);
- proxyOs.write(request.array());
- response = new byte[7 + dest.length];
- proxyIs.read(response);
- if (response[1] != 0x00) {
- throw new SocksConnectionException();
- }
- }
-
- public static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException {
- Socket socket = new Socket();
- try {
- socket.connect(address, Config.CONNECT_TIMEOUT * 1000);
- } catch (IOException e) {
- throw new SocksProxyNotFoundException();
- }
- createSocksConnection(socket, destination, port);
- return socket;
- }
-
- public static Socket createSocketOverTor(String destination, int port) throws IOException {
- return createSocket(new InetSocketAddress(InetAddress.getLocalHost(), 9050), destination, port);
- }
-
- static class SocksConnectionException extends IOException {
-
- }
-
- public static class SocksProxyNotFoundException extends IOException {
-
- }
+ public static void createSocksConnection(Socket socket, String destination, int port) throws IOException {
+ InputStream proxyIs = socket.getInputStream();
+ OutputStream proxyOs = socket.getOutputStream();
+ proxyOs.write(new byte[]{0x05, 0x01, 0x00});
+ byte[] response = new byte[2];
+ proxyIs.read(response);
+ byte[] dest = destination.getBytes();
+ ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
+ request.put(new byte[]{0x05, 0x01, 0x00, 0x03});
+ request.put((byte) dest.length);
+ request.put(dest);
+ request.putShort((short) port);
+ proxyOs.write(request.array());
+ response = new byte[7 + dest.length];
+ proxyIs.read(response);
+ if (response[1] != 0x00) {
+ throw new SocksConnectionException();
+ }
+ }
+
+ public static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException {
+ Socket socket = new Socket();
+ try {
+ socket.connect(address, Config.CONNECT_TIMEOUT * 1000);
+ } catch (IOException e) {
+ throw new SocksProxyNotFoundException();
+ }
+ createSocksConnection(socket, destination, port);
+ return socket;
+ }
+
+ public static Socket createSocketOverTor(String destination, int port) throws IOException {
+ return createSocket(new InetSocketAddress(InetAddress.getLocalHost(), 9050), destination, port);
+ }
+
+ static class SocksConnectionException extends IOException {
+
+ }
+
+ public static class SocksProxyNotFoundException extends IOException {
+
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/UIHelper.java b/src/main/java/de/pixart/messenger/utils/UIHelper.java
index 100eff4ea..0bb87e6c7 100644
--- a/src/main/java/de/pixart/messenger/utils/UIHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/UIHelper.java
@@ -24,310 +24,310 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class UIHelper {
- private static String BLACK_HEART_SUIT = "\u2665";
- private static String HEAVY_BLACK_HEART_SUIT = "\u2764";
- private static String WHITE_HEART_SUIT = "\u2661";
+ private static String BLACK_HEART_SUIT = "\u2665";
+ private static String HEAVY_BLACK_HEART_SUIT = "\u2764";
+ private static String WHITE_HEART_SUIT = "\u2661";
- public static final ArrayList<String> HEARTS = new ArrayList<>(Arrays.asList(BLACK_HEART_SUIT,HEAVY_BLACK_HEART_SUIT,WHITE_HEART_SUIT));
+ public static final ArrayList<String> HEARTS = new ArrayList<>(Arrays.asList(BLACK_HEART_SUIT, HEAVY_BLACK_HEART_SUIT, WHITE_HEART_SUIT));
- private static final ArrayList<String> LOCATION_QUESTIONS = new ArrayList<>(Arrays.asList(
- "where are you", //en
- "where are you now", //en
- "where are you right now", //en
- "whats your 20", //en
- "what is your 20", //en
- "what's your 20", //en
- "whats your twenty", //en
- "what is your twenty", //en
- "what's your twenty", //en
- "wo bist du", //de
- "wo bist du jetzt", //de
- "wo bist du gerade", //de
- "wo seid ihr", //de
- "wo seid ihr jetzt", //de
- "wo seid ihr gerade", //de
- "dónde estás", //es
- "donde estas" //es
- ));
+ private static final ArrayList<String> LOCATION_QUESTIONS = new ArrayList<>(Arrays.asList(
+ "where are you", //en
+ "where are you now", //en
+ "where are you right now", //en
+ "whats your 20", //en
+ "what is your 20", //en
+ "what's your 20", //en
+ "whats your twenty", //en
+ "what is your twenty", //en
+ "what's your twenty", //en
+ "wo bist du", //de
+ "wo bist du jetzt", //de
+ "wo bist du gerade", //de
+ "wo seid ihr", //de
+ "wo seid ihr jetzt", //de
+ "wo seid ihr gerade", //de
+ "dónde estás", //es
+ "donde estas" //es
+ ));
- private static final int SHORT_DATE_FLAGS = DateUtils.FORMAT_SHOW_DATE
- | DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL;
- private static final int FULL_DATE_FLAGS = DateUtils.FORMAT_SHOW_TIME
- | DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE;
+ private static final int SHORT_DATE_FLAGS = DateUtils.FORMAT_SHOW_DATE
+ | DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL;
+ private static final int FULL_DATE_FLAGS = DateUtils.FORMAT_SHOW_TIME
+ | DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE;
- public static String readableTimeDifference(Context context, long time) {
- return readableTimeDifference(context, time, false);
- }
+ public static String readableTimeDifference(Context context, long time) {
+ return readableTimeDifference(context, time, false);
+ }
- public static String readableTimeDifferenceFull(Context context, long time) {
- return readableTimeDifference(context, time, true);
- }
+ public static String readableTimeDifferenceFull(Context context, long time) {
+ return readableTimeDifference(context, time, true);
+ }
- private static String readableTimeDifference(Context context, long time,
- boolean fullDate) {
- if (time == 0) {
- return context.getString(R.string.just_now);
- }
- Date date = new Date(time);
- long difference = (System.currentTimeMillis() - time) / 1000;
- if (difference < 60) {
- return context.getString(R.string.just_now);
- } else if (difference < 60 * 2) {
- return context.getString(R.string.minute_ago);
- } else if (difference < 60 * 15) {
- return context.getString(R.string.minutes_ago,Math.round(difference / 60.0));
- } else if (today(date)) {
- java.text.DateFormat df = DateFormat.getTimeFormat(context);
- return df.format(date);
- } else {
- if (fullDate) {
- return DateUtils.formatDateTime(context, date.getTime(),
- FULL_DATE_FLAGS);
- } else {
- return DateUtils.formatDateTime(context, date.getTime(),
- SHORT_DATE_FLAGS);
- }
- }
- }
+ private static String readableTimeDifference(Context context, long time,
+ boolean fullDate) {
+ if (time == 0) {
+ return context.getString(R.string.just_now);
+ }
+ Date date = new Date(time);
+ long difference = (System.currentTimeMillis() - time) / 1000;
+ if (difference < 60) {
+ return context.getString(R.string.just_now);
+ } else if (difference < 60 * 2) {
+ return context.getString(R.string.minute_ago);
+ } else if (difference < 60 * 15) {
+ return context.getString(R.string.minutes_ago, Math.round(difference / 60.0));
+ } else if (today(date)) {
+ java.text.DateFormat df = DateFormat.getTimeFormat(context);
+ return df.format(date);
+ } else {
+ if (fullDate) {
+ return DateUtils.formatDateTime(context, date.getTime(),
+ FULL_DATE_FLAGS);
+ } else {
+ return DateUtils.formatDateTime(context, date.getTime(),
+ SHORT_DATE_FLAGS);
+ }
+ }
+ }
- private static boolean today(Date date) {
- return sameDay(date,new Date(System.currentTimeMillis()));
- }
+ private static boolean today(Date date) {
+ return sameDay(date, new Date(System.currentTimeMillis()));
+ }
- private static boolean sameDay(Date a, Date b) {
- Calendar cal1 = Calendar.getInstance();
- Calendar cal2 = Calendar.getInstance();
- cal1.setTime(a);
- cal2.setTime(b);
- return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
- && cal1.get(Calendar.DAY_OF_YEAR) == cal2
- .get(Calendar.DAY_OF_YEAR);
- }
+ private static boolean sameDay(Date a, Date b) {
+ Calendar cal1 = Calendar.getInstance();
+ Calendar cal2 = Calendar.getInstance();
+ cal1.setTime(a);
+ cal2.setTime(b);
+ return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
+ && cal1.get(Calendar.DAY_OF_YEAR) == cal2
+ .get(Calendar.DAY_OF_YEAR);
+ }
- public static String lastseen(Context context, boolean active, long time) {
- long difference = (System.currentTimeMillis() - time) / 1000;
- active = active && difference <= 300;
- if (active || difference < 60) {
- return context.getString(R.string.last_seen_now);
- } else if (difference < 60 * 2) {
- return context.getString(R.string.last_seen_min);
- } else if (difference < 60 * 60) {
- return context.getString(R.string.last_seen_mins,
- Math.round(difference / 60.0));
- } else if (difference < 60 * 60 * 2) {
- return context.getString(R.string.last_seen_hour);
- } else if (difference < 60 * 60 * 24) {
- return context.getString(R.string.last_seen_hours,
- Math.round(difference / (60.0 * 60.0)));
- } else if (difference < 60 * 60 * 48) {
- return context.getString(R.string.last_seen_day);
- } else {
- return context.getString(R.string.last_seen_days,
- Math.round(difference / (60.0 * 60.0 * 24.0)));
- }
- }
+ public static String lastseen(Context context, boolean active, long time) {
+ long difference = (System.currentTimeMillis() - time) / 1000;
+ active = active && difference <= 300;
+ if (active || difference < 60) {
+ return context.getString(R.string.last_seen_now);
+ } else if (difference < 60 * 2) {
+ return context.getString(R.string.last_seen_min);
+ } else if (difference < 60 * 60) {
+ return context.getString(R.string.last_seen_mins,
+ Math.round(difference / 60.0));
+ } else if (difference < 60 * 60 * 2) {
+ return context.getString(R.string.last_seen_hour);
+ } else if (difference < 60 * 60 * 24) {
+ return context.getString(R.string.last_seen_hours,
+ Math.round(difference / (60.0 * 60.0)));
+ } else if (difference < 60 * 60 * 48) {
+ return context.getString(R.string.last_seen_day);
+ } else {
+ return context.getString(R.string.last_seen_days,
+ Math.round(difference / (60.0 * 60.0 * 24.0)));
+ }
+ }
- public static int getColorForName(String name) {
- if (name == null || name.isEmpty()) {
- return 0xFF202020;
- }
- int colors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5,
- 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722,
- 0xFF795548, 0xFF607d8b};
- return colors[(int) ((name.hashCode() & 0xffffffffl) % colors.length)];
- }
+ public static int getColorForName(String name) {
+ if (name == null || name.isEmpty()) {
+ return 0xFF202020;
+ }
+ int colors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5,
+ 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722,
+ 0xFF795548, 0xFF607d8b};
+ return colors[(int) ((name.hashCode() & 0xffffffffl) % colors.length)];
+ }
- public static Pair<String,Boolean> getMessagePreview(final Context context, final Message message) {
- final Transferable d = message.getTransferable();
- if (d != null ) {
- switch (d.getStatus()) {
- case Transferable.STATUS_CHECKING:
- return new Pair<>(context.getString(R.string.checking_x,
- getFileDescriptionString(context,message)),true);
- case Transferable.STATUS_DOWNLOADING:
- return new Pair<>(context.getString(R.string.receiving_x_file,
- getFileDescriptionString(context,message),
- d.getProgress()),true);
- case Transferable.STATUS_OFFER:
- case Transferable.STATUS_OFFER_CHECK_FILESIZE:
- return new Pair<>(context.getString(R.string.x_file_offered_for_download,
- getFileDescriptionString(context,message)),true);
- case Transferable.STATUS_DELETED:
- return new Pair<>(context.getString(R.string.file_deleted),true);
- case Transferable.STATUS_FAILED:
- return new Pair<>(context.getString(R.string.file_transmission_failed),true);
- case Transferable.STATUS_UPLOADING:
- if (message.getStatus() == Message.STATUS_OFFERED) {
- return new Pair<>(context.getString(R.string.offering_x_file,
- getFileDescriptionString(context, message)), true);
- } else {
- return new Pair<>(context.getString(R.string.sending_x_file,
- getFileDescriptionString(context, message)), true);
- }
- default:
- return new Pair<>("",false);
- }
- } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- return new Pair<>(context.getString(R.string.pgp_message),true);
- } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
- return new Pair<>(context.getString(R.string.decryption_failed), true);
- } else if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
- if (message.getStatus() == Message.STATUS_RECEIVED) {
- return new Pair<>(context.getString(R.string.received_x_file,
- getFileDescriptionString(context, message)), true);
- } else {
- return new Pair<>(getFileDescriptionString(context,message),true);
- }
- } else {
- String body = message.getBody();
+ public static Pair<String, Boolean> getMessagePreview(final Context context, final Message message) {
+ final Transferable d = message.getTransferable();
+ if (d != null) {
+ switch (d.getStatus()) {
+ case Transferable.STATUS_CHECKING:
+ return new Pair<>(context.getString(R.string.checking_x,
+ getFileDescriptionString(context, message)), true);
+ case Transferable.STATUS_DOWNLOADING:
+ return new Pair<>(context.getString(R.string.receiving_x_file,
+ getFileDescriptionString(context, message),
+ d.getProgress()), true);
+ case Transferable.STATUS_OFFER:
+ case Transferable.STATUS_OFFER_CHECK_FILESIZE:
+ return new Pair<>(context.getString(R.string.x_file_offered_for_download,
+ getFileDescriptionString(context, message)), true);
+ case Transferable.STATUS_DELETED:
+ return new Pair<>(context.getString(R.string.file_deleted), true);
+ case Transferable.STATUS_FAILED:
+ return new Pair<>(context.getString(R.string.file_transmission_failed), true);
+ case Transferable.STATUS_UPLOADING:
+ if (message.getStatus() == Message.STATUS_OFFERED) {
+ return new Pair<>(context.getString(R.string.offering_x_file,
+ getFileDescriptionString(context, message)), true);
+ } else {
+ return new Pair<>(context.getString(R.string.sending_x_file,
+ getFileDescriptionString(context, message)), true);
+ }
+ default:
+ return new Pair<>("", false);
+ }
+ } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ return new Pair<>(context.getString(R.string.pgp_message), true);
+ } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ return new Pair<>(context.getString(R.string.decryption_failed), true);
+ } else if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
+ if (message.getStatus() == Message.STATUS_RECEIVED) {
+ return new Pair<>(context.getString(R.string.received_x_file,
+ getFileDescriptionString(context, message)), true);
+ } else {
+ return new Pair<>(getFileDescriptionString(context, message), true);
+ }
+ } else {
+ String body = message.getBody();
if (body.length() > 256) {
body = body.substring(0, 256);
}
if (body.startsWith(Message.ME_COMMAND)) {
- return new Pair<>(body.replaceAll("^" + Message.ME_COMMAND,
- UIHelper.getMessageDisplayName(message) + " "), false);
- } else if (GeoHelper.isGeoUri(message.getBody())) {
- if (message.getStatus() == Message.STATUS_RECEIVED) {
- return new Pair<>(context.getString(R.string.received_location), true);
- } else {
- return new Pair<>(context.getString(R.string.location), true);
- }
+ return new Pair<>(body.replaceAll("^" + Message.ME_COMMAND,
+ UIHelper.getMessageDisplayName(message) + " "), false);
+ } else if (GeoHelper.isGeoUri(message.getBody())) {
+ if (message.getStatus() == Message.STATUS_RECEIVED) {
+ return new Pair<>(context.getString(R.string.received_location), true);
+ } else {
+ return new Pair<>(context.getString(R.string.location), true);
+ }
} else if (message.bodyIsXmpp()) {
if (message.getStatus() == Message.STATUS_RECEIVED) {
return new Pair<>(context.getString(R.string.received_contact), true);
} else {
return new Pair<>(context.getString(R.string.contact), true);
}
- } else if (message.treatAsDownloadable() == Message.Decision.MUST) {
- return new Pair<>(context.getString(R.string.x_file_offered_for_download,
- getFileDescriptionString(context,message)),true);
- } else{
- return new Pair<>(body.trim(), false);
- }
- }
- }
+ } else if (message.treatAsDownloadable() == Message.Decision.MUST) {
+ return new Pair<>(context.getString(R.string.x_file_offered_for_download,
+ getFileDescriptionString(context, message)), true);
+ } else {
+ return new Pair<>(body.trim(), false);
+ }
+ }
+ }
- public static String getFileDescriptionString(final Context context, final Message message) {
- if (message.getType() == Message.TYPE_IMAGE) {
- return context.getString(R.string.image);
- }
- final String mime = message.getMimeType();
- if (mime == null) {
- return context.getString(R.string.file);
- } else if (mime.startsWith("audio/")) {
- return context.getString(R.string.audio);
- } else if(mime.startsWith("video/")) {
- return context.getString(R.string.video);
- } else if (mime.startsWith("image/")) {
- return context.getString(R.string.image);
- } else if (mime.contains("pdf")) {
- return context.getString(R.string.pdf_document) ;
- } else if (mime.contains("application/vnd.android.package-archive")) {
- return context.getString(R.string.apk) ;
- } else if (mime.contains("vcard")) {
- return context.getString(R.string.vcard) ;
- } else {
- return mime;
- }
- }
+ public static String getFileDescriptionString(final Context context, final Message message) {
+ if (message.getType() == Message.TYPE_IMAGE) {
+ return context.getString(R.string.image);
+ }
+ final String mime = message.getMimeType();
+ if (mime == null) {
+ return context.getString(R.string.file);
+ } else if (mime.startsWith("audio/")) {
+ return context.getString(R.string.audio);
+ } else if (mime.startsWith("video/")) {
+ return context.getString(R.string.video);
+ } else if (mime.startsWith("image/")) {
+ return context.getString(R.string.image);
+ } else if (mime.contains("pdf")) {
+ return context.getString(R.string.pdf_document);
+ } else if (mime.contains("application/vnd.android.package-archive")) {
+ return context.getString(R.string.apk);
+ } else if (mime.contains("vcard")) {
+ return context.getString(R.string.vcard);
+ } else {
+ return mime;
+ }
+ }
- public static String getMessageDisplayName(final Message message) {
- final Conversation conversation = message.getConversation();
- if (message.getStatus() == Message.STATUS_RECEIVED) {
- final Contact contact = message.getContact();
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- if (contact != null) {
- return contact.getDisplayName();
- } else {
- return getDisplayedMucCounterpart(message.getCounterpart());
- }
- } else {
- return contact != null ? contact.getDisplayName() : "";
- }
- } else {
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- return conversation.getMucOptions().getSelf().getName();
- } else {
- final Jid jid = conversation.getAccount().getJid();
- return jid.hasLocalpart() ? jid.getLocalpart() : jid.toDomainJid().toString();
- }
- }
- }
+ public static String getMessageDisplayName(final Message message) {
+ final Conversation conversation = message.getConversation();
+ if (message.getStatus() == Message.STATUS_RECEIVED) {
+ final Contact contact = message.getContact();
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ if (contact != null) {
+ return contact.getDisplayName();
+ } else {
+ return getDisplayedMucCounterpart(message.getCounterpart());
+ }
+ } else {
+ return contact != null ? contact.getDisplayName() : "";
+ }
+ } else {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ return conversation.getMucOptions().getSelf().getName();
+ } else {
+ final Jid jid = conversation.getAccount().getJid();
+ return jid.hasLocalpart() ? jid.getLocalpart() : jid.toDomainJid().toString();
+ }
+ }
+ }
- public static String getMessageHint(Context context, Conversation conversation) {
- switch (conversation.getNextEncryption()) {
- case Message.ENCRYPTION_NONE:
- if (Config.multipleEncryptionChoices()) {
- return context.getString(R.string.send_unencrypted_message);
- } else {
- return context.getString(R.string.send_message_to_x,conversation.getName());
- }
- case Message.ENCRYPTION_OTR:
- return context.getString(R.string.send_otr_message);
- case Message.ENCRYPTION_AXOLOTL:
- AxolotlService axolotlService = conversation.getAccount().getAxolotlService();
- if (axolotlService != null && axolotlService.trustedSessionVerified(conversation)) {
- return context.getString(R.string.send_omemo_x509_message);
- } else {
- return context.getString(R.string.send_omemo_message);
- }
- case Message.ENCRYPTION_PGP:
- return context.getString(R.string.send_pgp_message);
- default:
- return "";
- }
- }
+ public static String getMessageHint(Context context, Conversation conversation) {
+ switch (conversation.getNextEncryption()) {
+ case Message.ENCRYPTION_NONE:
+ if (Config.multipleEncryptionChoices()) {
+ return context.getString(R.string.send_unencrypted_message);
+ } else {
+ return context.getString(R.string.send_message_to_x, conversation.getName());
+ }
+ case Message.ENCRYPTION_OTR:
+ return context.getString(R.string.send_otr_message);
+ case Message.ENCRYPTION_AXOLOTL:
+ AxolotlService axolotlService = conversation.getAccount().getAxolotlService();
+ if (axolotlService != null && axolotlService.trustedSessionVerified(conversation)) {
+ return context.getString(R.string.send_omemo_x509_message);
+ } else {
+ return context.getString(R.string.send_omemo_message);
+ }
+ case Message.ENCRYPTION_PGP:
+ return context.getString(R.string.send_pgp_message);
+ default:
+ return "";
+ }
+ }
- public static String getDisplayedMucCounterpart(final Jid counterpart) {
- if (counterpart==null) {
- return "";
- } else if (!counterpart.isBareJid()) {
- return counterpart.getResourcepart().trim();
- } else {
- return counterpart.toString().trim();
- }
- }
+ public static String getDisplayedMucCounterpart(final Jid counterpart) {
+ if (counterpart == null) {
+ return "";
+ } else if (!counterpart.isBareJid()) {
+ return counterpart.getResourcepart().trim();
+ } else {
+ return counterpart.toString().trim();
+ }
+ }
- public static boolean receivedLocationQuestion(Message message) {
- if (message == null
- || message.getStatus() != Message.STATUS_RECEIVED
- || message.getType() != Message.TYPE_TEXT) {
- return false;
- }
- String body = message.getBody() == null ? null : message.getBody().trim().toLowerCase(Locale.getDefault());
- body = body.replace("?","").replace("¿","");
- return LOCATION_QUESTIONS.contains(body);
- }
+ public static boolean receivedLocationQuestion(Message message) {
+ if (message == null
+ || message.getStatus() != Message.STATUS_RECEIVED
+ || message.getType() != Message.TYPE_TEXT) {
+ return false;
+ }
+ String body = message.getBody() == null ? null : message.getBody().trim().toLowerCase(Locale.getDefault());
+ body = body.replace("?", "").replace("¿", "");
+ return LOCATION_QUESTIONS.contains(body);
+ }
- public static ListItem.Tag getTagForStatus(Context context, Presence.Status status) {
- switch (status) {
- case CHAT:
- return new ListItem.Tag(context.getString(R.string.presence_chat), 0xff259b24);
- case AWAY:
- return new ListItem.Tag(context.getString(R.string.presence_away), 0xffff9800);
- case XA:
- return new ListItem.Tag(context.getString(R.string.presence_xa), 0xfff44336);
- case DND:
- return new ListItem.Tag(context.getString(R.string.presence_dnd), 0xfff44336);
- default:
- return new ListItem.Tag(context.getString(R.string.presence_online), 0xff259b24);
- }
- }
+ public static ListItem.Tag getTagForStatus(Context context, Presence.Status status) {
+ switch (status) {
+ case CHAT:
+ return new ListItem.Tag(context.getString(R.string.presence_chat), 0xff259b24);
+ case AWAY:
+ return new ListItem.Tag(context.getString(R.string.presence_away), 0xffff9800);
+ case XA:
+ return new ListItem.Tag(context.getString(R.string.presence_xa), 0xfff44336);
+ case DND:
+ return new ListItem.Tag(context.getString(R.string.presence_dnd), 0xfff44336);
+ default:
+ return new ListItem.Tag(context.getString(R.string.presence_online), 0xff259b24);
+ }
+ }
- public static String tranlasteType(Context context, String type) {
- switch (type.toLowerCase()) {
- case "pc":
- return context.getString(R.string.type_pc);
- case "phone":
- return context.getString(R.string.type_phone);
- case "tablet":
- return context.getString(R.string.type_tablet);
- case "web":
- return context.getString(R.string.type_web);
- case "console":
- return context.getString(R.string.type_console);
- default:
- return type;
- }
- }
+ public static String tranlasteType(Context context, String type) {
+ switch (type.toLowerCase()) {
+ case "pc":
+ return context.getString(R.string.type_pc);
+ case "phone":
+ return context.getString(R.string.type_phone);
+ case "tablet":
+ return context.getString(R.string.type_tablet);
+ case "web":
+ return context.getString(R.string.type_web);
+ case "console":
+ return context.getString(R.string.type_console);
+ default:
+ return type;
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/XmlHelper.java b/src/main/java/de/pixart/messenger/utils/XmlHelper.java
index 29729f9d1..299deed28 100644
--- a/src/main/java/de/pixart/messenger/utils/XmlHelper.java
+++ b/src/main/java/de/pixart/messenger/utils/XmlHelper.java
@@ -1,13 +1,13 @@
package de.pixart.messenger.utils;
public class XmlHelper {
- public static String encodeEntities(String content) {
- content = content.replace("&", "&amp;");
- content = content.replace("<", "&lt;");
- content = content.replace(">", "&gt;");
- content = content.replace("\"", "&quot;");
- content = content.replace("'", "&apos;");
- content = content.replaceAll("[\\p{Cntrl}&&[^\n\t\r]]", "");
- return content;
- }
+ public static String encodeEntities(String content) {
+ content = content.replace("&", "&amp;");
+ content = content.replace("<", "&lt;");
+ content = content.replace(">", "&gt;");
+ content = content.replace("\"", "&quot;");
+ content = content.replace("'", "&apos;");
+ content = content.replaceAll("[\\p{Cntrl}&&[^\n\t\r]]", "");
+ return content;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/Xmlns.java b/src/main/java/de/pixart/messenger/utils/Xmlns.java
index a4022e03e..675d2db80 100644
--- a/src/main/java/de/pixart/messenger/utils/Xmlns.java
+++ b/src/main/java/de/pixart/messenger/utils/Xmlns.java
@@ -1,10 +1,10 @@
package de.pixart.messenger.utils;
public final class Xmlns {
- public static final String BLOCKING = "urn:xmpp:blocking";
- public static final String ROSTER = "jabber:iq:roster";
- public static final String REGISTER = "jabber:iq:register";
- public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams";
- public static final String HTTP_UPLOAD = "urn:xmpp:http:upload";
+ public static final String BLOCKING = "urn:xmpp:blocking";
+ public static final String ROSTER = "jabber:iq:roster";
+ public static final String REGISTER = "jabber:iq:register";
+ public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams";
+ public static final String HTTP_UPLOAD = "urn:xmpp:http:upload";
public static final String STANZA_IDS = "urn:xmpp:sid:0";
}
diff --git a/src/main/java/de/pixart/messenger/utils/XmppUri.java b/src/main/java/de/pixart/messenger/utils/XmppUri.java
index dc52f6637..51f2b808a 100644
--- a/src/main/java/de/pixart/messenger/utils/XmppUri.java
+++ b/src/main/java/de/pixart/messenger/utils/XmppUri.java
@@ -13,139 +13,140 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class XmppUri {
- protected String jid;
- protected boolean muc;
- protected String fingerprint;
- protected List<Fingerprint> fingerprints = new ArrayList<>();
- public static final String OMEMO_URI_PARAM = "omemo-sid-";
- public static final String OTR_URI_PARAM = "otr-fingerprint";
-
- public XmppUri(String uri) {
- try {
- parse(Uri.parse(uri));
- } catch (IllegalArgumentException e) {
- try {
- jid = Jid.fromString(uri).toBareJid().toString();
- } catch (InvalidJidException e2) {
- jid = null;
- }
- }
- }
-
- public XmppUri(Uri uri) {
- parse(uri);
- }
-
- public static boolean isXmppUri(String uri) {
- String scheme = Uri.parse(uri).getScheme();
- return "xmpp".equalsIgnoreCase(scheme);
- }
-
- protected void parse(Uri uri) {
- String scheme = uri.getScheme();
- String host = uri.getHost();
- List<String> segments = uri.getPathSegments();
- if ("https".equalsIgnoreCase(scheme) && "jabber.pix-art.de".equalsIgnoreCase(host)) {
- if (segments.size() >= 2 && segments.get(1).contains("@")) {
- // sample : https://conversations.im/i/foo@bar.com
- try {
- jid = Jid.fromString(segments.get(1)).toString();
- } catch (Exception e) {
- jid = null;
- }
- } else if (segments.size() >= 3) {
- // sample : https://conversations.im/i/foo/bar.com
- jid = segments.get(1) + "@" + segments.get(2);
- }
- muc = segments.size() > 1 && "j".equalsIgnoreCase(segments.get(0));
- } else if ("xmpp".equalsIgnoreCase(scheme)) {
- // sample: xmpp:foo@bar.com
- muc = "join".equalsIgnoreCase(uri.getQuery());
- if (uri.getAuthority() != null) {
- jid = uri.getAuthority();
- } else {
- jid = uri.getSchemeSpecificPart().split("\\?")[0];
- }
- this.fingerprints = parseFingerprints(uri.getQuery());
- } else if ("imto".equalsIgnoreCase(scheme)) {
- // sample: imto://xmpp/foo@bar.com
- try {
- jid = URLDecoder.decode(uri.getEncodedPath(), "UTF-8").split("/")[1];
- } catch (final UnsupportedEncodingException ignored) {
- jid = null;
- }
- } else {
- try {
- jid = Jid.fromString(uri.toString()).toBareJid().toString();
- } catch (final InvalidJidException ignored) {
- jid = null;
- }
- }
- }
-
- protected List<Fingerprint> parseFingerprints(String query) {
- List<Fingerprint> fingerprints = new ArrayList<>();
- String[] pairs = query == null ? new String[0] : query.split(";");
- for(String pair : pairs) {
- String[] parts = pair.split("=",2);
- if (parts.length == 2) {
- String key = parts[0].toLowerCase(Locale.US);
- String value = parts[1];
- if (OTR_URI_PARAM.equals(key)) {
- fingerprints.add(new Fingerprint(FingerprintType.OTR,value));
- }
- if (key.startsWith(OMEMO_URI_PARAM)) {
- try {
- int id = Integer.parseInt(key.substring(OMEMO_URI_PARAM.length()));
- fingerprints.add(new Fingerprint(FingerprintType.OMEMO,value,id));
- } catch (Exception e) {
- //ignoring invalid device id
- }
- }
- return null;
- }
- }
- return fingerprints;
- }
-
- public Jid getJid() {
- try {
- return this.jid == null ? null :Jid.fromString(this.jid.toLowerCase());
- } catch (InvalidJidException e) {
- return null;
- }
- }
-
- public List<Fingerprint> getFingerprints() {
- return this.fingerprints;
- }
-
- public boolean hasFingerprints() {
- return fingerprints.size() > 0;
- }
- public enum FingerprintType {
- OMEMO,
- OTR
- }
-
- public static class Fingerprint {
- public final FingerprintType type;
- public final String fingerprint;
- public final int deviceId;
-
- public Fingerprint(FingerprintType type, String fingerprint) {
- this(type, fingerprint, 0);
- }
-
- public Fingerprint(FingerprintType type, String fingerprint, int deviceId) {
- this.type = type;
- this.fingerprint = fingerprint;
- this.deviceId = deviceId;
- }
-
- @Override
- public String toString() {
- return type.toString()+": "+fingerprint+(deviceId != 0 ? " "+String.valueOf(deviceId) : "");
- }
- }
+ protected String jid;
+ protected boolean muc;
+ protected String fingerprint;
+ protected List<Fingerprint> fingerprints = new ArrayList<>();
+ public static final String OMEMO_URI_PARAM = "omemo-sid-";
+ public static final String OTR_URI_PARAM = "otr-fingerprint";
+
+ public XmppUri(String uri) {
+ try {
+ parse(Uri.parse(uri));
+ } catch (IllegalArgumentException e) {
+ try {
+ jid = Jid.fromString(uri).toBareJid().toString();
+ } catch (InvalidJidException e2) {
+ jid = null;
+ }
+ }
+ }
+
+ public XmppUri(Uri uri) {
+ parse(uri);
+ }
+
+ public static boolean isXmppUri(String uri) {
+ String scheme = Uri.parse(uri).getScheme();
+ return "xmpp".equalsIgnoreCase(scheme);
+ }
+
+ protected void parse(Uri uri) {
+ String scheme = uri.getScheme();
+ String host = uri.getHost();
+ List<String> segments = uri.getPathSegments();
+ if ("https".equalsIgnoreCase(scheme) && "jabber.pix-art.de".equalsIgnoreCase(host)) {
+ if (segments.size() >= 2 && segments.get(1).contains("@")) {
+ // sample : https://conversations.im/i/foo@bar.com
+ try {
+ jid = Jid.fromString(segments.get(1)).toString();
+ } catch (Exception e) {
+ jid = null;
+ }
+ } else if (segments.size() >= 3) {
+ // sample : https://conversations.im/i/foo/bar.com
+ jid = segments.get(1) + "@" + segments.get(2);
+ }
+ muc = segments.size() > 1 && "j".equalsIgnoreCase(segments.get(0));
+ } else if ("xmpp".equalsIgnoreCase(scheme)) {
+ // sample: xmpp:foo@bar.com
+ muc = "join".equalsIgnoreCase(uri.getQuery());
+ if (uri.getAuthority() != null) {
+ jid = uri.getAuthority();
+ } else {
+ jid = uri.getSchemeSpecificPart().split("\\?")[0];
+ }
+ this.fingerprints = parseFingerprints(uri.getQuery());
+ } else if ("imto".equalsIgnoreCase(scheme)) {
+ // sample: imto://xmpp/foo@bar.com
+ try {
+ jid = URLDecoder.decode(uri.getEncodedPath(), "UTF-8").split("/")[1];
+ } catch (final UnsupportedEncodingException ignored) {
+ jid = null;
+ }
+ } else {
+ try {
+ jid = Jid.fromString(uri.toString()).toBareJid().toString();
+ } catch (final InvalidJidException ignored) {
+ jid = null;
+ }
+ }
+ }
+
+ protected List<Fingerprint> parseFingerprints(String query) {
+ List<Fingerprint> fingerprints = new ArrayList<>();
+ String[] pairs = query == null ? new String[0] : query.split(";");
+ for (String pair : pairs) {
+ String[] parts = pair.split("=", 2);
+ if (parts.length == 2) {
+ String key = parts[0].toLowerCase(Locale.US);
+ String value = parts[1];
+ if (OTR_URI_PARAM.equals(key)) {
+ fingerprints.add(new Fingerprint(FingerprintType.OTR, value));
+ }
+ if (key.startsWith(OMEMO_URI_PARAM)) {
+ try {
+ int id = Integer.parseInt(key.substring(OMEMO_URI_PARAM.length()));
+ fingerprints.add(new Fingerprint(FingerprintType.OMEMO, value, id));
+ } catch (Exception e) {
+ //ignoring invalid device id
+ }
+ }
+ return null;
+ }
+ }
+ return fingerprints;
+ }
+
+ public Jid getJid() {
+ try {
+ return this.jid == null ? null : Jid.fromString(this.jid.toLowerCase());
+ } catch (InvalidJidException e) {
+ return null;
+ }
+ }
+
+ public List<Fingerprint> getFingerprints() {
+ return this.fingerprints;
+ }
+
+ public boolean hasFingerprints() {
+ return fingerprints.size() > 0;
+ }
+
+ public enum FingerprintType {
+ OMEMO,
+ OTR
+ }
+
+ public static class Fingerprint {
+ public final FingerprintType type;
+ public final String fingerprint;
+ public final int deviceId;
+
+ public Fingerprint(FingerprintType type, String fingerprint) {
+ this(type, fingerprint, 0);
+ }
+
+ public Fingerprint(FingerprintType type, String fingerprint, int deviceId) {
+ this.type = type;
+ this.fingerprint = fingerprint;
+ this.deviceId = deviceId;
+ }
+
+ @Override
+ public String toString() {
+ return type.toString() + ": " + fingerprint + (deviceId != 0 ? " " + String.valueOf(deviceId) : "");
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/utils/video/MediaController.java b/src/main/java/de/pixart/messenger/utils/video/MediaController.java
index 7af113931..2b47afade 100644
--- a/src/main/java/de/pixart/messenger/utils/video/MediaController.java
+++ b/src/main/java/de/pixart/messenger/utils/video/MediaController.java
@@ -199,7 +199,7 @@ public class MediaController {
int rotationValue = Integer.valueOf(rotation);
int originalWidth = Integer.valueOf(width);
int originalHeight = Integer.valueOf(height);
- float ratio = (float)video_width / (float)video_height; // 16:9 = 1,7778, 4:3 = 1,3333
+ float ratio = (float) video_width / (float) video_height; // 16:9 = 1,7778, 4:3 = 1,3333
if (video_height > video_width) {
resultHeight = Config.VIDEO_SIZE;
diff --git a/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java b/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java
index aa4542fa3..3c53941ea 100644
--- a/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java
+++ b/src/main/java/de/pixart/messenger/utils/video/TextureRenderer.java
@@ -43,23 +43,23 @@ public class TextureRenderer {
private static final String VERTEX_SHADER =
"uniform mat4 uMVPMatrix;\n" +
- "uniform mat4 uSTMatrix;\n" +
- "attribute vec4 aPosition;\n" +
- "attribute vec4 aTextureCoord;\n" +
- "varying vec2 vTextureCoord;\n" +
- "void main() {\n" +
- " gl_Position = uMVPMatrix * aPosition;\n" +
- " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
- "}\n";
+ "uniform mat4 uSTMatrix;\n" +
+ "attribute vec4 aPosition;\n" +
+ "attribute vec4 aTextureCoord;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "void main() {\n" +
+ " gl_Position = uMVPMatrix * aPosition;\n" +
+ " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
+ "}\n";
private static final String FRAGMENT_SHADER =
"#extension GL_OES_EGL_image_external : require\n" +
- "precision mediump float;\n" +
- "varying vec2 vTextureCoord;\n" +
- "uniform samplerExternalOES sTexture;\n" +
- "void main() {\n" +
- " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
- "}\n";
+ "precision mediump float;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "uniform samplerExternalOES sTexture;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+ "}\n";
private float[] mMVPMatrix = new float[16];
private float[] mSTMatrix = new float[16];
diff --git a/src/main/java/de/pixart/messenger/utils/video/Track.java b/src/main/java/de/pixart/messenger/utils/video/Track.java
index 347c65490..18111a675 100644
--- a/src/main/java/de/pixart/messenger/utils/video/Track.java
+++ b/src/main/java/de/pixart/messenger/utils/video/Track.java
@@ -70,7 +70,7 @@ public class Track {
public Track(int id, MediaFormat format, boolean isAudio) throws Exception {
trackId = id;
if (!isAudio) {
- sampleDurations.add((long)3015);
+ sampleDurations.add((long) 3015);
duration = 3015;
width = format.getInteger(MediaFormat.KEY_WIDTH);
height = format.getInteger(MediaFormat.KEY_HEIGHT);
@@ -136,7 +136,7 @@ public class Track {
sampleDescriptionBox.addBox(visualSampleEntry);
}
} else {
- sampleDurations.add((long)1024);
+ sampleDurations.add((long) 1024);
duration = 1024;
isAudio = true;
volume = 1;
@@ -167,7 +167,7 @@ public class Track {
AudioSpecificConfig audioSpecificConfig = new AudioSpecificConfig();
audioSpecificConfig.setAudioObjectType(2);
- audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get((int)audioSampleEntry.getSampleRate()));
+ audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get((int) audioSampleEntry.getSampleRate()));
audioSpecificConfig.setChannelConfiguration(audioSampleEntry.getChannelCount());
decoderConfigDescriptor.setAudioSpecificInfo(audioSpecificConfig);
diff --git a/src/main/java/de/pixart/messenger/xml/Element.java b/src/main/java/de/pixart/messenger/xml/Element.java
index ca8cdf0a7..7ed08a483 100644
--- a/src/main/java/de/pixart/messenger/xml/Element.java
+++ b/src/main/java/de/pixart/messenger/xml/Element.java
@@ -12,178 +12,178 @@ import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
public class Element {
- private final String name;
- private Hashtable<String, String> attributes = new Hashtable<>();
- private String content;
- protected List<Element> children = new ArrayList<>();
-
- public Element(String name) {
- this.name = name;
- }
-
- public Element(String name, String xmlns) {
- this.name = name;
- this.setAttribute("xmlns", xmlns);
- }
-
- public Element addChild(Element child) {
- this.content = null;
- children.add(child);
- return child;
- }
-
- public Element addChild(String name) {
- this.content = null;
- Element child = new Element(name);
- children.add(child);
- return child;
- }
-
- public Element addChild(String name, String xmlns) {
- this.content = null;
- Element child = new Element(name);
- child.setAttribute("xmlns", xmlns);
- children.add(child);
- return child;
- }
-
- public Element setContent(String content) {
- this.content = content;
- this.children.clear();
- return this;
- }
-
- public Element findChild(String name) {
- for (Element child : this.children) {
- if (child.getName().equals(name)) {
- return child;
- }
- }
- return null;
- }
-
- public String findChildContent(String name) {
- Element element = findChild(name);
- return element == null ? null : element.getContent();
- }
-
- public Element findChild(String name, String xmlns) {
- for (Element child : this.children) {
- if (name.equals(child.getName()) && xmlns.equals(child.getAttribute("xmlns"))) {
- return child;
- }
- }
- return null;
- }
-
- public String findChildContent(String name, String xmlns) {
- Element element = findChild(name,xmlns);
- return element == null ? null : element.getContent();
- }
-
- public boolean hasChild(final String name) {
- return findChild(name) != null;
- }
-
- public boolean hasChild(final String name, final String xmlns) {
- return findChild(name, xmlns) != null;
- }
-
- public List<Element> getChildren() {
- return this.children;
- }
-
- public Element setChildren(List<Element> children) {
- this.children = children;
- return this;
- }
-
- public final String getContent() {
- return content;
- }
-
- public Element setAttribute(String name, String value) {
- if (name != null && value != null) {
- this.attributes.put(name, value);
- }
- return this;
- }
-
- public Element setAttributes(Hashtable<String, String> attributes) {
- this.attributes = attributes;
- return this;
- }
-
- public String getAttribute(String name) {
- if (this.attributes.containsKey(name)) {
- return this.attributes.get(name);
- } else {
- return null;
- }
- }
-
- public Jid getAttributeAsJid(String name) {
- final String jid = this.getAttribute(name);
- if (jid != null && !jid.isEmpty()) {
- try {
- return Jid.fromString(jid);
- } catch (final InvalidJidException e) {
- Log.e(Config.LOGTAG, "could not parse jid " + jid);
- return null;
- }
- }
- return null;
- }
-
- public Hashtable<String, String> getAttributes() {
- return this.attributes;
- }
-
- public String toString() {
- StringBuilder elementOutput = new StringBuilder();
- if ((content == null) && (children.size() == 0)) {
- Tag emptyTag = Tag.empty(name);
- emptyTag.setAtttributes(this.attributes);
- elementOutput.append(emptyTag.toString());
- } else {
- Tag startTag = Tag.start(name);
- startTag.setAtttributes(this.attributes);
- elementOutput.append(startTag);
- if (content != null) {
- elementOutput.append(XmlHelper.encodeEntities(content));
- } else {
- for (Element child : children) {
- elementOutput.append(child.toString());
- }
- }
- Tag endTag = Tag.end(name);
- elementOutput.append(endTag);
- }
- return elementOutput.toString();
- }
-
- public final String getName() {
- return name;
- }
-
- public void clearChildren() {
- this.children.clear();
- }
-
- public void setAttribute(String name, long value) {
- this.setAttribute(name, Long.toString(value));
- }
-
- public void setAttribute(String name, int value) {
- this.setAttribute(name, Integer.toString(value));
- }
-
- public boolean getAttributeAsBoolean(String name) {
- String attr = getAttribute(name);
- return (attr != null && (attr.equalsIgnoreCase("true") || attr.equalsIgnoreCase("1")));
- }
-
- public String getNamespace() {
- return getAttribute("xmlns");
- }
+ private final String name;
+ private Hashtable<String, String> attributes = new Hashtable<>();
+ private String content;
+ protected List<Element> children = new ArrayList<>();
+
+ public Element(String name) {
+ this.name = name;
+ }
+
+ public Element(String name, String xmlns) {
+ this.name = name;
+ this.setAttribute("xmlns", xmlns);
+ }
+
+ public Element addChild(Element child) {
+ this.content = null;
+ children.add(child);
+ return child;
+ }
+
+ public Element addChild(String name) {
+ this.content = null;
+ Element child = new Element(name);
+ children.add(child);
+ return child;
+ }
+
+ public Element addChild(String name, String xmlns) {
+ this.content = null;
+ Element child = new Element(name);
+ child.setAttribute("xmlns", xmlns);
+ children.add(child);
+ return child;
+ }
+
+ public Element setContent(String content) {
+ this.content = content;
+ this.children.clear();
+ return this;
+ }
+
+ public Element findChild(String name) {
+ for (Element child : this.children) {
+ if (child.getName().equals(name)) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ public String findChildContent(String name) {
+ Element element = findChild(name);
+ return element == null ? null : element.getContent();
+ }
+
+ public Element findChild(String name, String xmlns) {
+ for (Element child : this.children) {
+ if (name.equals(child.getName()) && xmlns.equals(child.getAttribute("xmlns"))) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ public String findChildContent(String name, String xmlns) {
+ Element element = findChild(name, xmlns);
+ return element == null ? null : element.getContent();
+ }
+
+ public boolean hasChild(final String name) {
+ return findChild(name) != null;
+ }
+
+ public boolean hasChild(final String name, final String xmlns) {
+ return findChild(name, xmlns) != null;
+ }
+
+ public List<Element> getChildren() {
+ return this.children;
+ }
+
+ public Element setChildren(List<Element> children) {
+ this.children = children;
+ return this;
+ }
+
+ public final String getContent() {
+ return content;
+ }
+
+ public Element setAttribute(String name, String value) {
+ if (name != null && value != null) {
+ this.attributes.put(name, value);
+ }
+ return this;
+ }
+
+ public Element setAttributes(Hashtable<String, String> attributes) {
+ this.attributes = attributes;
+ return this;
+ }
+
+ public String getAttribute(String name) {
+ if (this.attributes.containsKey(name)) {
+ return this.attributes.get(name);
+ } else {
+ return null;
+ }
+ }
+
+ public Jid getAttributeAsJid(String name) {
+ final String jid = this.getAttribute(name);
+ if (jid != null && !jid.isEmpty()) {
+ try {
+ return Jid.fromString(jid);
+ } catch (final InvalidJidException e) {
+ Log.e(Config.LOGTAG, "could not parse jid " + jid);
+ return null;
+ }
+ }
+ return null;
+ }
+
+ public Hashtable<String, String> getAttributes() {
+ return this.attributes;
+ }
+
+ public String toString() {
+ StringBuilder elementOutput = new StringBuilder();
+ if ((content == null) && (children.size() == 0)) {
+ Tag emptyTag = Tag.empty(name);
+ emptyTag.setAtttributes(this.attributes);
+ elementOutput.append(emptyTag.toString());
+ } else {
+ Tag startTag = Tag.start(name);
+ startTag.setAtttributes(this.attributes);
+ elementOutput.append(startTag);
+ if (content != null) {
+ elementOutput.append(XmlHelper.encodeEntities(content));
+ } else {
+ for (Element child : children) {
+ elementOutput.append(child.toString());
+ }
+ }
+ Tag endTag = Tag.end(name);
+ elementOutput.append(endTag);
+ }
+ return elementOutput.toString();
+ }
+
+ public final String getName() {
+ return name;
+ }
+
+ public void clearChildren() {
+ this.children.clear();
+ }
+
+ public void setAttribute(String name, long value) {
+ this.setAttribute(name, Long.toString(value));
+ }
+
+ public void setAttribute(String name, int value) {
+ this.setAttribute(name, Integer.toString(value));
+ }
+
+ public boolean getAttributeAsBoolean(String name) {
+ String attr = getAttribute(name);
+ return (attr != null && (attr.equalsIgnoreCase("true") || attr.equalsIgnoreCase("1")));
+ }
+
+ public String getNamespace() {
+ return getAttribute("xmlns");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xml/Tag.java b/src/main/java/de/pixart/messenger/xml/Tag.java
index b604140f0..8e6944a40 100644
--- a/src/main/java/de/pixart/messenger/xml/Tag.java
+++ b/src/main/java/de/pixart/messenger/xml/Tag.java
@@ -8,97 +8,97 @@ import java.util.Set;
import de.pixart.messenger.utils.XmlHelper;
public class Tag {
- public static final int NO = -1;
- public static final int START = 0;
- public static final int END = 1;
- public static final int EMPTY = 2;
-
- protected int type;
- protected String name;
- protected Hashtable<String, String> attributes = new Hashtable<String, String>();
-
- protected Tag(int type, String name) {
- this.type = type;
- this.name = name;
- }
-
- public static Tag no(String text) {
- return new Tag(NO, text);
- }
-
- public static Tag start(String name) {
- return new Tag(START, name);
- }
-
- public static Tag end(String name) {
- return new Tag(END, name);
- }
-
- public static Tag empty(String name) {
- return new Tag(EMPTY, name);
- }
-
- public String getName() {
- return name;
- }
-
- public String getAttribute(String attrName) {
- return this.attributes.get(attrName);
- }
-
- public Tag setAttribute(String attrName, String attrValue) {
- this.attributes.put(attrName, attrValue);
- return this;
- }
-
- public Tag setAtttributes(Hashtable<String, String> attributes) {
- this.attributes = attributes;
- return this;
- }
-
- public boolean isStart(String needle) {
- if (needle == null)
- return false;
- return (this.type == START) && (needle.equals(this.name));
- }
-
- public boolean isEnd(String needle) {
- if (needle == null)
- return false;
- return (this.type == END) && (needle.equals(this.name));
- }
-
- public boolean isNo() {
- return (this.type == NO);
- }
-
- public String toString() {
- StringBuilder tagOutput = new StringBuilder();
- tagOutput.append('<');
- if (type == END) {
- tagOutput.append('/');
- }
- tagOutput.append(name);
- if (type != END) {
- Set<Entry<String, String>> attributeSet = attributes.entrySet();
- Iterator<Entry<String, String>> it = attributeSet.iterator();
- while (it.hasNext()) {
- Entry<String, String> entry = it.next();
- tagOutput.append(' ');
- tagOutput.append(entry.getKey());
- tagOutput.append("=\"");
- tagOutput.append(XmlHelper.encodeEntities(entry.getValue()));
- tagOutput.append('"');
- }
- }
- if (type == EMPTY) {
- tagOutput.append('/');
- }
- tagOutput.append('>');
- return tagOutput.toString();
- }
-
- public Hashtable<String, String> getAttributes() {
- return this.attributes;
- }
+ public static final int NO = -1;
+ public static final int START = 0;
+ public static final int END = 1;
+ public static final int EMPTY = 2;
+
+ protected int type;
+ protected String name;
+ protected Hashtable<String, String> attributes = new Hashtable<String, String>();
+
+ protected Tag(int type, String name) {
+ this.type = type;
+ this.name = name;
+ }
+
+ public static Tag no(String text) {
+ return new Tag(NO, text);
+ }
+
+ public static Tag start(String name) {
+ return new Tag(START, name);
+ }
+
+ public static Tag end(String name) {
+ return new Tag(END, name);
+ }
+
+ public static Tag empty(String name) {
+ return new Tag(EMPTY, name);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getAttribute(String attrName) {
+ return this.attributes.get(attrName);
+ }
+
+ public Tag setAttribute(String attrName, String attrValue) {
+ this.attributes.put(attrName, attrValue);
+ return this;
+ }
+
+ public Tag setAtttributes(Hashtable<String, String> attributes) {
+ this.attributes = attributes;
+ return this;
+ }
+
+ public boolean isStart(String needle) {
+ if (needle == null)
+ return false;
+ return (this.type == START) && (needle.equals(this.name));
+ }
+
+ public boolean isEnd(String needle) {
+ if (needle == null)
+ return false;
+ return (this.type == END) && (needle.equals(this.name));
+ }
+
+ public boolean isNo() {
+ return (this.type == NO);
+ }
+
+ public String toString() {
+ StringBuilder tagOutput = new StringBuilder();
+ tagOutput.append('<');
+ if (type == END) {
+ tagOutput.append('/');
+ }
+ tagOutput.append(name);
+ if (type != END) {
+ Set<Entry<String, String>> attributeSet = attributes.entrySet();
+ Iterator<Entry<String, String>> it = attributeSet.iterator();
+ while (it.hasNext()) {
+ Entry<String, String> entry = it.next();
+ tagOutput.append(' ');
+ tagOutput.append(entry.getKey());
+ tagOutput.append("=\"");
+ tagOutput.append(XmlHelper.encodeEntities(entry.getValue()));
+ tagOutput.append('"');
+ }
+ }
+ if (type == EMPTY) {
+ tagOutput.append('/');
+ }
+ tagOutput.append('>');
+ return tagOutput.toString();
+ }
+
+ public Hashtable<String, String> getAttributes() {
+ return this.attributes;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xml/TagWriter.java b/src/main/java/de/pixart/messenger/xml/TagWriter.java
index 61220773a..0a663ce1b 100644
--- a/src/main/java/de/pixart/messenger/xml/TagWriter.java
+++ b/src/main/java/de/pixart/messenger/xml/TagWriter.java
@@ -9,96 +9,96 @@ import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class TagWriter {
- private OutputStreamWriter outputStream;
- private boolean finshed = false;
- private LinkedBlockingQueue<AbstractStanza> writeQueue = new LinkedBlockingQueue<AbstractStanza>();
- private Thread asyncStanzaWriter = new Thread() {
- private boolean shouldStop = false;
+ private OutputStreamWriter outputStream;
+ private boolean finshed = false;
+ private LinkedBlockingQueue<AbstractStanza> writeQueue = new LinkedBlockingQueue<AbstractStanza>();
+ private Thread asyncStanzaWriter = new Thread() {
+ private boolean shouldStop = false;
- @Override
- public void run() {
- while (!shouldStop) {
- if ((finshed) && (writeQueue.size() == 0)) {
- return;
- }
- try {
- AbstractStanza output = writeQueue.take();
- outputStream.write(output.toString());
- outputStream.flush();
- } catch (Exception e) {
- shouldStop = true;
- }
- }
- }
- };
+ @Override
+ public void run() {
+ while (!shouldStop) {
+ if ((finshed) && (writeQueue.size() == 0)) {
+ return;
+ }
+ try {
+ AbstractStanza output = writeQueue.take();
+ outputStream.write(output.toString());
+ outputStream.flush();
+ } catch (Exception e) {
+ shouldStop = true;
+ }
+ }
+ }
+ };
- public TagWriter() {
- }
+ public TagWriter() {
+ }
- public void setOutputStream(OutputStream out) throws IOException {
- if (out == null) {
- throw new IOException();
- }
- this.outputStream = new OutputStreamWriter(out);
- }
+ public void setOutputStream(OutputStream out) throws IOException {
+ if (out == null) {
+ throw new IOException();
+ }
+ this.outputStream = new OutputStreamWriter(out);
+ }
- public TagWriter beginDocument() throws IOException {
- if (outputStream == null) {
- throw new IOException("output stream was null");
- }
- outputStream.write("<?xml version='1.0'?>");
- outputStream.flush();
- return this;
- }
+ public TagWriter beginDocument() throws IOException {
+ if (outputStream == null) {
+ throw new IOException("output stream was null");
+ }
+ outputStream.write("<?xml version='1.0'?>");
+ outputStream.flush();
+ return this;
+ }
- public TagWriter writeTag(Tag tag) throws IOException {
- if (outputStream == null) {
- throw new IOException("output stream was null");
- }
- outputStream.write(tag.toString());
- outputStream.flush();
- return this;
- }
+ public TagWriter writeTag(Tag tag) throws IOException {
+ if (outputStream == null) {
+ throw new IOException("output stream was null");
+ }
+ outputStream.write(tag.toString());
+ outputStream.flush();
+ return this;
+ }
- public TagWriter writeElement(Element element) throws IOException {
- if (outputStream == null) {
- throw new IOException("output stream was null");
- }
- outputStream.write(element.toString());
- outputStream.flush();
- return this;
- }
+ public TagWriter writeElement(Element element) throws IOException {
+ if (outputStream == null) {
+ throw new IOException("output stream was null");
+ }
+ outputStream.write(element.toString());
+ outputStream.flush();
+ return this;
+ }
- public TagWriter writeStanzaAsync(AbstractStanza stanza) {
- if (finshed) {
- return this;
- } else {
- if (!asyncStanzaWriter.isAlive()) {
- try {
- asyncStanzaWriter.start();
- } catch (IllegalThreadStateException e) {
- // already started
- }
- }
- writeQueue.add(stanza);
- return this;
- }
- }
+ public TagWriter writeStanzaAsync(AbstractStanza stanza) {
+ if (finshed) {
+ return this;
+ } else {
+ if (!asyncStanzaWriter.isAlive()) {
+ try {
+ asyncStanzaWriter.start();
+ } catch (IllegalThreadStateException e) {
+ // already started
+ }
+ }
+ writeQueue.add(stanza);
+ return this;
+ }
+ }
- public void finish() {
- this.finshed = true;
- }
+ public void finish() {
+ this.finshed = true;
+ }
- public boolean finished() {
- return (this.writeQueue.size() == 0);
- }
+ public boolean finished() {
+ return (this.writeQueue.size() == 0);
+ }
- public boolean isActive() {
- return outputStream != null;
- }
+ public boolean isActive() {
+ return outputStream != null;
+ }
- public void forceClose() {
- finish();
- outputStream = null;
- }
+ public void forceClose() {
+ finish();
+ outputStream = null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xml/XmlReader.java b/src/main/java/de/pixart/messenger/xml/XmlReader.java
index 0b7796c6c..3c8cfa1f2 100644
--- a/src/main/java/de/pixart/messenger/xml/XmlReader.java
+++ b/src/main/java/de/pixart/messenger/xml/XmlReader.java
@@ -15,111 +15,111 @@ import java.io.InputStreamReader;
import de.pixart.messenger.Config;
public class XmlReader {
- private XmlPullParser parser;
- private PowerManager.WakeLock wakeLock;
- private InputStream is;
+ private XmlPullParser parser;
+ private PowerManager.WakeLock wakeLock;
+ private InputStream is;
- public XmlReader(WakeLock wakeLock) {
- this.parser = Xml.newPullParser();
- try {
- this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- } catch (XmlPullParserException e) {
- Log.d(Config.LOGTAG, "error setting namespace feature on parser");
- }
- this.wakeLock = wakeLock;
- }
+ public XmlReader(WakeLock wakeLock) {
+ this.parser = Xml.newPullParser();
+ try {
+ this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ } catch (XmlPullParserException e) {
+ Log.d(Config.LOGTAG, "error setting namespace feature on parser");
+ }
+ this.wakeLock = wakeLock;
+ }
- public void setInputStream(InputStream inputStream) throws IOException {
- if (inputStream == null) {
- throw new IOException();
- }
- this.is = inputStream;
- try {
- parser.setInput(new InputStreamReader(this.is));
- } catch (XmlPullParserException e) {
- throw new IOException("error resetting parser");
- }
- }
+ public void setInputStream(InputStream inputStream) throws IOException {
+ if (inputStream == null) {
+ throw new IOException();
+ }
+ this.is = inputStream;
+ try {
+ parser.setInput(new InputStreamReader(this.is));
+ } catch (XmlPullParserException e) {
+ throw new IOException("error resetting parser");
+ }
+ }
- public void reset() throws IOException {
- if (this.is == null) {
- throw new IOException();
- }
- try {
- parser.setInput(new InputStreamReader(this.is));
- } catch (XmlPullParserException e) {
- throw new IOException("error resetting parser");
- }
- }
+ public void reset() throws IOException {
+ if (this.is == null) {
+ throw new IOException();
+ }
+ try {
+ parser.setInput(new InputStreamReader(this.is));
+ } catch (XmlPullParserException e) {
+ throw new IOException("error resetting parser");
+ }
+ }
- public Tag readTag() throws XmlPullParserException, IOException {
- if (wakeLock.isHeld()) {
- try {
- wakeLock.release();
- } catch (RuntimeException re) {
- Log.d(Config.LOGTAG,"runtime exception releasing wakelock before reading tag "+re.getMessage());
- }
- }
- try {
- while (this.is != null && parser.next() != XmlPullParser.END_DOCUMENT) {
- wakeLock.acquire();
- if (parser.getEventType() == XmlPullParser.START_TAG) {
- Tag tag = Tag.start(parser.getName());
- for (int i = 0; i < parser.getAttributeCount(); ++i) {
- tag.setAttribute(parser.getAttributeName(i),
- parser.getAttributeValue(i));
- }
- String xmlns = parser.getNamespace();
- if (xmlns != null) {
- tag.setAttribute("xmlns", xmlns);
- }
- return tag;
- } else if (parser.getEventType() == XmlPullParser.END_TAG) {
- return Tag.end(parser.getName());
- } else if (parser.getEventType() == XmlPullParser.TEXT) {
- return Tag.no(parser.getText());
- }
- }
+ public Tag readTag() throws XmlPullParserException, IOException {
+ if (wakeLock.isHeld()) {
+ try {
+ wakeLock.release();
+ } catch (RuntimeException re) {
+ Log.d(Config.LOGTAG, "runtime exception releasing wakelock before reading tag " + re.getMessage());
+ }
+ }
+ try {
+ while (this.is != null && parser.next() != XmlPullParser.END_DOCUMENT) {
+ wakeLock.acquire();
+ if (parser.getEventType() == XmlPullParser.START_TAG) {
+ Tag tag = Tag.start(parser.getName());
+ for (int i = 0; i < parser.getAttributeCount(); ++i) {
+ tag.setAttribute(parser.getAttributeName(i),
+ parser.getAttributeValue(i));
+ }
+ String xmlns = parser.getNamespace();
+ if (xmlns != null) {
+ tag.setAttribute("xmlns", xmlns);
+ }
+ return tag;
+ } else if (parser.getEventType() == XmlPullParser.END_TAG) {
+ return Tag.end(parser.getName());
+ } else if (parser.getEventType() == XmlPullParser.TEXT) {
+ return Tag.no(parser.getText());
+ }
+ }
- } catch (Throwable throwable) {
- throw new IOException("xml parser mishandled "+throwable.getClass().getName(), throwable);
- } finally {
- if (wakeLock.isHeld()) {
- try {
- wakeLock.release();
- } catch (RuntimeException re) {
- Log.d(Config.LOGTAG,"runtime exception releasing wakelock after exception "+re.getMessage());
- }
- }
- }
- return null;
- }
+ } catch (Throwable throwable) {
+ throw new IOException("xml parser mishandled " + throwable.getClass().getName(), throwable);
+ } finally {
+ if (wakeLock.isHeld()) {
+ try {
+ wakeLock.release();
+ } catch (RuntimeException re) {
+ Log.d(Config.LOGTAG, "runtime exception releasing wakelock after exception " + re.getMessage());
+ }
+ }
+ }
+ return null;
+ }
- public Element readElement(Tag currentTag) throws XmlPullParserException,
- IOException {
- Element element = new Element(currentTag.getName());
- element.setAttributes(currentTag.getAttributes());
- Tag nextTag = this.readTag();
- if (nextTag == null) {
- throw new IOException("interrupted mid tag");
- }
- if (nextTag.isNo()) {
- element.setContent(nextTag.getName());
- nextTag = this.readTag();
- if (nextTag == null) {
- throw new IOException("interrupted mid tag");
- }
- }
- while (!nextTag.isEnd(element.getName())) {
- if (!nextTag.isNo()) {
- Element child = this.readElement(nextTag);
- element.addChild(child);
- }
- nextTag = this.readTag();
- if (nextTag == null) {
- throw new IOException("interrupted mid tag");
- }
- }
- return element;
- }
+ public Element readElement(Tag currentTag) throws XmlPullParserException,
+ IOException {
+ Element element = new Element(currentTag.getName());
+ element.setAttributes(currentTag.getAttributes());
+ Tag nextTag = this.readTag();
+ if (nextTag == null) {
+ throw new IOException("interrupted mid tag");
+ }
+ if (nextTag.isNo()) {
+ element.setContent(nextTag.getName());
+ nextTag = this.readTag();
+ if (nextTag == null) {
+ throw new IOException("interrupted mid tag");
+ }
+ }
+ while (!nextTag.isEnd(element.getName())) {
+ if (!nextTag.isNo()) {
+ Element child = this.readElement(nextTag);
+ element.addChild(child);
+ }
+ nextTag = this.readTag();
+ if (nextTag == null) {
+ throw new IOException("interrupted mid tag");
+ }
+ }
+ return element;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnAdvancedStreamFeaturesLoaded.java b/src/main/java/de/pixart/messenger/xmpp/OnAdvancedStreamFeaturesLoaded.java
index 572b61bc1..3ca11c068 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnAdvancedStreamFeaturesLoaded.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnAdvancedStreamFeaturesLoaded.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Account;
public interface OnAdvancedStreamFeaturesLoaded {
- public void onAdvancedStreamFeaturesAvailable(final Account account);
+ public void onAdvancedStreamFeaturesAvailable(final Account account);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnBindListener.java b/src/main/java/de/pixart/messenger/xmpp/OnBindListener.java
index cafc3fadb..710a807dd 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnBindListener.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnBindListener.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Account;
public interface OnBindListener {
- public void onBind(Account account);
+ public void onBind(Account account);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnContactStatusChanged.java b/src/main/java/de/pixart/messenger/xmpp/OnContactStatusChanged.java
index be1c51b72..66cd24858 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnContactStatusChanged.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnContactStatusChanged.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Contact;
public interface OnContactStatusChanged {
- public void onContactStatusChanged(final Contact contact, final boolean online);
+ public void onContactStatusChanged(final Contact contact, final boolean online);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnIqPacketReceived.java b/src/main/java/de/pixart/messenger/xmpp/OnIqPacketReceived.java
index b07b6258e..797eba155 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnIqPacketReceived.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnIqPacketReceived.java
@@ -4,5 +4,5 @@ import de.pixart.messenger.entities.Account;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public interface OnIqPacketReceived extends PacketReceived {
- public void onIqPacketReceived(Account account, IqPacket packet);
+ public void onIqPacketReceived(Account account, IqPacket packet);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnKeyStatusUpdated.java b/src/main/java/de/pixart/messenger/xmpp/OnKeyStatusUpdated.java
index 3727d37d7..925a2544d 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnKeyStatusUpdated.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnKeyStatusUpdated.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.crypto.axolotl.AxolotlService;
public interface OnKeyStatusUpdated {
- public void onKeyStatusUpdated(AxolotlService.FetchStatus report);
+ public void onKeyStatusUpdated(AxolotlService.FetchStatus report);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java b/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java
index 3ab3bd75a..7ff5658a8 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnMessageAcknowledged.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Account;
public interface OnMessageAcknowledged {
- public void onMessageAcknowledged(Account account, String id);
+ public void onMessageAcknowledged(Account account, String id);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnMessagePacketReceived.java b/src/main/java/de/pixart/messenger/xmpp/OnMessagePacketReceived.java
index 9a0f3bb2b..d4d54f8df 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnMessagePacketReceived.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnMessagePacketReceived.java
@@ -4,5 +4,5 @@ import de.pixart.messenger.entities.Account;
import de.pixart.messenger.xmpp.stanzas.MessagePacket;
public interface OnMessagePacketReceived extends PacketReceived {
- public void onMessagePacketReceived(Account account, MessagePacket packet);
+ public void onMessagePacketReceived(Account account, MessagePacket packet);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnPresencePacketReceived.java b/src/main/java/de/pixart/messenger/xmpp/OnPresencePacketReceived.java
index 5419443a2..2df725ff7 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnPresencePacketReceived.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnPresencePacketReceived.java
@@ -4,5 +4,5 @@ import de.pixart.messenger.entities.Account;
import de.pixart.messenger.xmpp.stanzas.PresencePacket;
public interface OnPresencePacketReceived extends PacketReceived {
- public void onPresencePacketReceived(Account account, PresencePacket packet);
+ public void onPresencePacketReceived(Account account, PresencePacket packet);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnStatusChanged.java b/src/main/java/de/pixart/messenger/xmpp/OnStatusChanged.java
index 7f1e07b9c..40b7b313a 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnStatusChanged.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnStatusChanged.java
@@ -3,5 +3,5 @@ package de.pixart.messenger.xmpp;
import de.pixart.messenger.entities.Account;
public interface OnStatusChanged {
- public void onStatusChanged(Account account);
+ public void onStatusChanged(Account account);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/OnUpdateBlocklist.java b/src/main/java/de/pixart/messenger/xmpp/OnUpdateBlocklist.java
index 4b8d90ec7..bf02e2f29 100644
--- a/src/main/java/de/pixart/messenger/xmpp/OnUpdateBlocklist.java
+++ b/src/main/java/de/pixart/messenger/xmpp/OnUpdateBlocklist.java
@@ -1,13 +1,13 @@
package de.pixart.messenger.xmpp;
public interface OnUpdateBlocklist {
- // Use an enum instead of a boolean to make sure we don't run into the boolean trap
- // (`onUpdateBlocklist(true)' doesn't read well, and could be confusing).
- public static enum Status {
- BLOCKED,
- UNBLOCKED
- }
+ // Use an enum instead of a boolean to make sure we don't run into the boolean trap
+ // (`onUpdateBlocklist(true)' doesn't read well, and could be confusing).
+ public static enum Status {
+ BLOCKED,
+ UNBLOCKED
+ }
- @SuppressWarnings("MethodNameSameAsClassName")
- public void OnUpdateBlocklist(final Status status);
+ @SuppressWarnings("MethodNameSameAsClassName")
+ public void OnUpdateBlocklist(final Status status);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
index 2642ee84a..bcb5904a7 100644
--- a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
+++ b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java
@@ -94,784 +94,783 @@ import de.pixart.messenger.xmpp.stanzas.streammgmt.ResumePacket;
public class XmppConnection implements Runnable {
- private static final int PACKET_IQ = 0;
- private static final int PACKET_MESSAGE = 1;
- private static final int PACKET_PRESENCE = 2;
- protected Account account;
- private final WakeLock wakeLock;
- private Socket socket;
- private XmlReader tagReader;
- private TagWriter tagWriter;
- private final Features features = new Features(this);
- private boolean needsBinding = true;
- private boolean shouldAuthenticate = true;
- private Element streamFeatures;
- private final HashMap<Jid, ServiceDiscoveryResult> disco = new HashMap<>();
-
- private String streamId = null;
- private int smVersion = 3;
- private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>();
-
- private int stanzasReceived = 0;
- private int stanzasSent = 0;
- private long lastPacketReceived = 0;
- private long lastPingSent = 0;
- private long lastConnect = 0;
- private long lastSessionStarted = 0;
- private long lastDiscoStarted = 0;
- private AtomicInteger mPendingServiceDiscoveries = new AtomicInteger(0);
- private AtomicBoolean mWaitForDisco = new AtomicBoolean(true);
- private boolean mInteractive = false;
- private int attempt = 0;
+ private static final int PACKET_IQ = 0;
+ private static final int PACKET_MESSAGE = 1;
+ private static final int PACKET_PRESENCE = 2;
+ protected Account account;
+ private final WakeLock wakeLock;
+ private Socket socket;
+ private XmlReader tagReader;
+ private TagWriter tagWriter;
+ private final Features features = new Features(this);
+ private boolean needsBinding = true;
+ private boolean shouldAuthenticate = true;
+ private Element streamFeatures;
+ private final HashMap<Jid, ServiceDiscoveryResult> disco = new HashMap<>();
+
+ private String streamId = null;
+ private int smVersion = 3;
+ private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>();
+
+ private int stanzasReceived = 0;
+ private int stanzasSent = 0;
+ private long lastPacketReceived = 0;
+ private long lastPingSent = 0;
+ private long lastConnect = 0;
+ private long lastSessionStarted = 0;
+ private long lastDiscoStarted = 0;
+ private AtomicInteger mPendingServiceDiscoveries = new AtomicInteger(0);
+ private AtomicBoolean mWaitForDisco = new AtomicBoolean(true);
+ private boolean mInteractive = false;
+ private int attempt = 0;
public static String errorMessage = null;
- private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>();
- private OnPresencePacketReceived presenceListener = null;
- private OnJinglePacketReceived jingleListener = null;
- private OnIqPacketReceived unregisteredIqListener = null;
- private OnMessagePacketReceived messageListener = null;
- private OnStatusChanged statusListener = null;
- private OnBindListener bindListener = null;
- private final ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>();
- private OnMessageAcknowledged acknowledgedListener = null;
- private XmppConnectionService mXmppConnectionService = null;
+ private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>();
+ private OnPresencePacketReceived presenceListener = null;
+ private OnJinglePacketReceived jingleListener = null;
+ private OnIqPacketReceived unregisteredIqListener = null;
+ private OnMessagePacketReceived messageListener = null;
+ private OnStatusChanged statusListener = null;
+ private OnBindListener bindListener = null;
+ private final ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>();
+ private OnMessageAcknowledged acknowledgedListener = null;
+ private XmppConnectionService mXmppConnectionService = null;
private EditAccountActivity mEditAccountActivity = null;
- private SaslMechanism saslMechanism;
+ private SaslMechanism saslMechanism;
private class MyKeyManager implements X509KeyManager {
- @Override
- public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
- return account.getPrivateKeyAlias();
- }
-
- @Override
- public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
- return null;
- }
-
- @Override
- public X509Certificate[] getCertificateChain(String alias) {
- Log.d(Config.LOGTAG,"getting certificate chain");
- try {
- return KeyChain.getCertificateChain(mXmppConnectionService, alias);
- } catch (Exception e) {
- Log.d(Config.LOGTAG,e.getMessage());
- return new X509Certificate[0];
- }
- }
-
- @Override
- public String[] getClientAliases(String s, Principal[] principals) {
- return new String[0];
- }
-
- @Override
- public String[] getServerAliases(String s, Principal[] principals) {
- return new String[0];
- }
-
- @Override
- public PrivateKey getPrivateKey(String alias) {
- try {
- return KeyChain.getPrivateKey(mXmppConnectionService, alias);
- } catch (Exception e) {
- return null;
- }
- }
- }
-
- private Identity mServerIdentity = Identity.UNKNOWN;
-
- public final OnIqPacketReceived registrationResponseListener = new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- account.setOption(Account.OPTION_REGISTER, false);
- forceCloseSocket();
- changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
- } else {
- final List<String> PASSWORD_TOO_WEAK_MSGS = Arrays.asList(
- "The password is too weak",
- "Please use a longer password.");
- final Element error = packet.findChild("error");
- Account.State state = Account.State.REGISTRATION_FAILED;
+ @Override
+ public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
+ return account.getPrivateKeyAlias();
+ }
+
+ @Override
+ public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
+ return null;
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain(String alias) {
+ Log.d(Config.LOGTAG, "getting certificate chain");
+ try {
+ return KeyChain.getCertificateChain(mXmppConnectionService, alias);
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, e.getMessage());
+ return new X509Certificate[0];
+ }
+ }
+
+ @Override
+ public String[] getClientAliases(String s, Principal[] principals) {
+ return new String[0];
+ }
+
+ @Override
+ public String[] getServerAliases(String s, Principal[] principals) {
+ return new String[0];
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String alias) {
+ try {
+ return KeyChain.getPrivateKey(mXmppConnectionService, alias);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ }
+
+ private Identity mServerIdentity = Identity.UNKNOWN;
+
+ public final OnIqPacketReceived registrationResponseListener = new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ account.setOption(Account.OPTION_REGISTER, false);
+ forceCloseSocket();
+ changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
+ } else {
+ final List<String> PASSWORD_TOO_WEAK_MSGS = Arrays.asList(
+ "The password is too weak",
+ "Please use a longer password.");
+ final Element error = packet.findChild("error");
+ Account.State state = Account.State.REGISTRATION_FAILED;
mXmppConnectionService.deleteAccount(account);
- if (error != null) {
+ if (error != null) {
if (error.hasChild("text")) {
errorMessage = error.findChildContent("text");
Log.d(Config.LOGTAG, "Error creating account : " + error.findChildContent("text"));
}
- if (error.hasChild("conflict")) {
- state = Account.State.REGISTRATION_CONFLICT;
- } else if (error.hasChild("resource-constraint")
- && "wait".equals(error.getAttribute("type"))) {
- state = Account.State.REGISTRATION_PLEASE_WAIT;
- } else if (error.hasChild("not-acceptable")
- && PASSWORD_TOO_WEAK_MSGS.contains(error.findChildContent("text"))) {
- state = Account.State.REGISTRATION_PASSWORD_TOO_WEAK;
- }
- }
+ if (error.hasChild("conflict")) {
+ state = Account.State.REGISTRATION_CONFLICT;
+ } else if (error.hasChild("resource-constraint")
+ && "wait".equals(error.getAttribute("type"))) {
+ state = Account.State.REGISTRATION_PLEASE_WAIT;
+ } else if (error.hasChild("not-acceptable")
+ && PASSWORD_TOO_WEAK_MSGS.contains(error.findChildContent("text"))) {
+ state = Account.State.REGISTRATION_PASSWORD_TOO_WEAK;
+ }
+ }
Log.d(Config.LOGTAG, "Delete account because of error " + error);
- changeStatus(state);
- forceCloseSocket();
- }
- }
- };
-
- public XmppConnection(final Account account, final XmppConnectionService service) {
- this.account = account;
- this.wakeLock = service.getPowerManager().newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toBareJid().toString());
- tagWriter = new TagWriter();
- mXmppConnectionService = service;
- }
-
- protected void changeStatus(final Account.State nextStatus) {
- if (account.getStatus() != nextStatus) {
- if ((nextStatus == Account.State.OFFLINE)
- && (account.getStatus() != Account.State.CONNECTING)
- && (account.getStatus() != Account.State.ONLINE)
- && (account.getStatus() != Account.State.DISABLED)) {
- return;
- }
- if (nextStatus == Account.State.ONLINE) {
- this.attempt = 0;
- }
- account.setStatus(nextStatus);
- if (statusListener != null) {
- statusListener.onStatusChanged(account);
- }
- }
- }
-
- public void prepareNewConnection() {
- this.lastConnect = SystemClock.elapsedRealtime();
- this.lastPingSent = SystemClock.elapsedRealtime();
- this.lastDiscoStarted = Long.MAX_VALUE;
- this.changeStatus(Account.State.CONNECTING);
- }
-
- protected void connect() {
+ changeStatus(state);
+ forceCloseSocket();
+ }
+ }
+ };
+
+ public XmppConnection(final Account account, final XmppConnectionService service) {
+ this.account = account;
+ this.wakeLock = service.getPowerManager().newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toBareJid().toString());
+ tagWriter = new TagWriter();
+ mXmppConnectionService = service;
+ }
+
+ protected void changeStatus(final Account.State nextStatus) {
+ if (account.getStatus() != nextStatus) {
+ if ((nextStatus == Account.State.OFFLINE)
+ && (account.getStatus() != Account.State.CONNECTING)
+ && (account.getStatus() != Account.State.ONLINE)
+ && (account.getStatus() != Account.State.DISABLED)) {
+ return;
+ }
+ if (nextStatus == Account.State.ONLINE) {
+ this.attempt = 0;
+ }
+ account.setStatus(nextStatus);
+ if (statusListener != null) {
+ statusListener.onStatusChanged(account);
+ }
+ }
+ }
+
+ public void prepareNewConnection() {
+ this.lastConnect = SystemClock.elapsedRealtime();
+ this.lastPingSent = SystemClock.elapsedRealtime();
+ this.lastDiscoStarted = Long.MAX_VALUE;
+ this.changeStatus(Account.State.CONNECTING);
+ }
+
+ protected void connect() {
if (mXmppConnectionService.areMessagesInitialized()) {
mXmppConnectionService.resetSendingToWaiting(account);
}
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting");
- features.encryptionEnabled = false;
- this.attempt++;
- switch (account.getJid().getDomainpart()) {
- case "chat.facebook.com":
- mServerIdentity = Identity.FACEBOOK;
- break;
- case "nimbuzz.com":
- mServerIdentity = Identity.NIMBUZZ;
- break;
- default:
- mServerIdentity = Identity.UNKNOWN;
- break;
- }
- try {
- shouldAuthenticate = needsBinding = !account.isOptionSet(Account.OPTION_REGISTER);
- tagReader = new XmlReader(wakeLock);
- tagWriter = new TagWriter();
- this.changeStatus(Account.State.CONNECTING);
- final boolean useTor = mXmppConnectionService.useTorToConnect() || account.isOnion();
- final boolean extended = mXmppConnectionService.showExtendedConnectionOptions();
- if (Config.XMPP_IP != null && Config.XMPP_Ports != null) {
- Integer[] XMPP_Port = Config.XMPP_Ports;
- Integer Port = XMPP_Port[new Random().nextInt(XMPP_Port.length)];
- socket = new Socket();
- try {
- socket.connect(new InetSocketAddress(Config.XMPP_IP, Port), Config.SOCKET_TIMEOUT * 1000);
- } catch (IOException e) {
- throw new IOException();
- }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting");
+ features.encryptionEnabled = false;
+ this.attempt++;
+ switch (account.getJid().getDomainpart()) {
+ case "chat.facebook.com":
+ mServerIdentity = Identity.FACEBOOK;
+ break;
+ case "nimbuzz.com":
+ mServerIdentity = Identity.NIMBUZZ;
+ break;
+ default:
+ mServerIdentity = Identity.UNKNOWN;
+ break;
+ }
+ try {
+ shouldAuthenticate = needsBinding = !account.isOptionSet(Account.OPTION_REGISTER);
+ tagReader = new XmlReader(wakeLock);
+ tagWriter = new TagWriter();
+ this.changeStatus(Account.State.CONNECTING);
+ final boolean useTor = mXmppConnectionService.useTorToConnect() || account.isOnion();
+ final boolean extended = mXmppConnectionService.showExtendedConnectionOptions();
+ if (Config.XMPP_IP != null && Config.XMPP_Ports != null) {
+ Integer[] XMPP_Port = Config.XMPP_Ports;
+ Integer Port = XMPP_Port[new Random().nextInt(XMPP_Port.length)];
+ socket = new Socket();
+ try {
+ socket.connect(new InetSocketAddress(Config.XMPP_IP, Port), Config.SOCKET_TIMEOUT * 1000);
+ } catch (IOException e) {
+ throw new IOException();
+ }
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": connect to " + Config.XMPP_IP + ":" + Port);
- startXmpp();
- }
- else if (useTor) {
- String destination;
- if (account.getHostname() == null || account.getHostname().isEmpty()) {
- destination = account.getServer().toString();
- } else {
- destination = account.getHostname();
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": connect to " + destination + " via Tor");
- socket = SocksSocketFactory.createSocketOverTor(destination, account.getPort());
- startXmpp();
- } else if (extended && account.getHostname() != null && !account.getHostname().isEmpty()) {
-
- InetSocketAddress address = new InetSocketAddress(account.getHostname(), account.getPort());
-
- features.encryptionEnabled = account.getPort() == 5223;
-
- try {
- if (features.encryptionEnabled) {
- try {
- final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
- socket = tlsFactoryVerifier.factory.createSocket();
- socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
- final SSLSession session = ((SSLSocket) socket).getSession();
- if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), session)) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
- throw new SecurityException();
- }
- } catch (KeyManagementException e) {
- features.encryptionEnabled = false;
- socket = new Socket();
- }
- } else {
- socket = new Socket();
- socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
- }
- } catch (IOException e) {
- throw new UnknownHostException();
- }
- startXmpp();
- } else if (DNSHelper.isIp(account.getServer().toString())) {
- socket = new Socket();
- try {
- socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000);
- } catch (IOException e) {
- throw new UnknownHostException();
- }
- startXmpp();
- } else {
- final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService);
- final ArrayList<Parcelable> values = result.getParcelableArrayList("values");
- for (Iterator<Parcelable> iterator = values.iterator(); iterator.hasNext(); ) {
- if (Thread.currentThread().isInterrupted()) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Thread was interrupted");
- return;
- }
- final Bundle namePort = (Bundle) iterator.next();
- try {
- String srvRecordServer;
- try {
- srvRecordServer = IDN.toASCII(namePort.getString("name"));
- } catch (final IllegalArgumentException e) {
- // TODO: Handle me?`
- srvRecordServer = "";
- }
- final int srvRecordPort = namePort.getInt("port");
- final String srvIpServer = namePort.getString("ip");
- // if tls is true, encryption is implied and must not be started
- features.encryptionEnabled = namePort.getBoolean("tls");
- final InetSocketAddress addr;
- if (srvIpServer != null) {
- addr = new InetSocketAddress(srvIpServer, srvRecordPort);
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": using values from dns " + srvRecordServer
- + "[" + srvIpServer + "]:" + srvRecordPort + " tls: " + features.encryptionEnabled);
- } else {
- addr = new InetSocketAddress(srvRecordServer, srvRecordPort);
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": using values from dns "
- + srvRecordServer + ":" + srvRecordPort + " tls: " + features.encryptionEnabled);
- }
-
- if (!features.encryptionEnabled) {
- socket = new Socket();
- socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
- } else {
- final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
- socket = tlsFactoryVerifier.factory.createSocket();
-
- if (socket == null) {
- throw new IOException("could not initialize ssl socket");
- }
-
- SSLSocketHelper.setSecurity((SSLSocket) socket);
- SSLSocketHelper.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) socket, account.getServer().getDomainpart());
- SSLSocketHelper.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) socket, "xmpp-client");
-
- socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
-
- if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) socket).getSession())) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
- throw new SecurityException();
- }
- }
-
- if (startXmpp())
- break; // successfully connected to server that speaks xmpp
- } catch (final SecurityException e) {
- throw e;
- } catch (final Throwable e) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() + "(" + e.getClass().getName() + ")");
- if (!iterator.hasNext()) {
- throw new UnknownHostException();
- }
- }
- }
- }
- processStream();
- } catch (final java.lang.SecurityException e) {
- this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION);
- } catch (final IncompatibleServerException e) {
- this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
- } catch (final SecurityException e) {
- this.changeStatus(Account.State.SECURITY_ERROR);
- } catch (final UnauthorizedException e) {
- this.changeStatus(Account.State.UNAUTHORIZED);
- } catch (final PaymentRequiredException e) {
- this.changeStatus(Account.State.PAYMENT_REQUIRED);
- } catch (final UnknownHostException | ConnectException e) {
- this.changeStatus(Account.State.SERVER_NOT_FOUND);
- } catch (final SocksSocketFactory.SocksProxyNotFoundException e) {
- this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
- } catch(final StreamErrorHostUnknown e) {
- this.changeStatus(Account.State.HOST_UNKNOWN);
- } catch(final StreamErrorPolicyViolation e) {
- this.changeStatus(Account.State.POLICY_VIOLATION);
- } catch(final StreamError e) {
- this.changeStatus(Account.State.STREAM_ERROR);
- } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
- this.changeStatus(Account.State.OFFLINE);
- this.attempt = Math.max(0, this.attempt - 1);
- } finally {
- forceCloseSocket();
- if (wakeLock.isHeld()) {
- try {
- wakeLock.release();
- } catch (final RuntimeException ignored) {
- }
- }
- }
- }
-
- /**
- * Starts xmpp protocol, call after connecting to socket
- * @return true if server returns with valid xmpp, false otherwise
- * @throws IOException Unknown tag on connect
- * @throws XmlPullParserException Bad Xml
- * @throws NoSuchAlgorithmException Other error
+ startXmpp();
+ } else if (useTor) {
+ String destination;
+ if (account.getHostname() == null || account.getHostname().isEmpty()) {
+ destination = account.getServer().toString();
+ } else {
+ destination = account.getHostname();
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": connect to " + destination + " via Tor");
+ socket = SocksSocketFactory.createSocketOverTor(destination, account.getPort());
+ startXmpp();
+ } else if (extended && account.getHostname() != null && !account.getHostname().isEmpty()) {
+
+ InetSocketAddress address = new InetSocketAddress(account.getHostname(), account.getPort());
+
+ features.encryptionEnabled = account.getPort() == 5223;
+
+ try {
+ if (features.encryptionEnabled) {
+ try {
+ final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
+ socket = tlsFactoryVerifier.factory.createSocket();
+ socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
+ final SSLSession session = ((SSLSocket) socket).getSession();
+ if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), session)) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
+ throw new SecurityException();
+ }
+ } catch (KeyManagementException e) {
+ features.encryptionEnabled = false;
+ socket = new Socket();
+ }
+ } else {
+ socket = new Socket();
+ socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
+ }
+ } catch (IOException e) {
+ throw new UnknownHostException();
+ }
+ startXmpp();
+ } else if (DNSHelper.isIp(account.getServer().toString())) {
+ socket = new Socket();
+ try {
+ socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000);
+ } catch (IOException e) {
+ throw new UnknownHostException();
+ }
+ startXmpp();
+ } else {
+ final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService);
+ final ArrayList<Parcelable> values = result.getParcelableArrayList("values");
+ for (Iterator<Parcelable> iterator = values.iterator(); iterator.hasNext(); ) {
+ if (Thread.currentThread().isInterrupted()) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Thread was interrupted");
+ return;
+ }
+ final Bundle namePort = (Bundle) iterator.next();
+ try {
+ String srvRecordServer;
+ try {
+ srvRecordServer = IDN.toASCII(namePort.getString("name"));
+ } catch (final IllegalArgumentException e) {
+ // TODO: Handle me?`
+ srvRecordServer = "";
+ }
+ final int srvRecordPort = namePort.getInt("port");
+ final String srvIpServer = namePort.getString("ip");
+ // if tls is true, encryption is implied and must not be started
+ features.encryptionEnabled = namePort.getBoolean("tls");
+ final InetSocketAddress addr;
+ if (srvIpServer != null) {
+ addr = new InetSocketAddress(srvIpServer, srvRecordPort);
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ + ": using values from dns " + srvRecordServer
+ + "[" + srvIpServer + "]:" + srvRecordPort + " tls: " + features.encryptionEnabled);
+ } else {
+ addr = new InetSocketAddress(srvRecordServer, srvRecordPort);
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ + ": using values from dns "
+ + srvRecordServer + ":" + srvRecordPort + " tls: " + features.encryptionEnabled);
+ }
+
+ if (!features.encryptionEnabled) {
+ socket = new Socket();
+ socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
+ } else {
+ final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
+ socket = tlsFactoryVerifier.factory.createSocket();
+
+ if (socket == null) {
+ throw new IOException("could not initialize ssl socket");
+ }
+
+ SSLSocketHelper.setSecurity((SSLSocket) socket);
+ SSLSocketHelper.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) socket, account.getServer().getDomainpart());
+ SSLSocketHelper.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) socket, "xmpp-client");
+
+ socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
+
+ if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) socket).getSession())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
+ throw new SecurityException();
+ }
+ }
+
+ if (startXmpp())
+ break; // successfully connected to server that speaks xmpp
+ } catch (final SecurityException e) {
+ throw e;
+ } catch (final Throwable e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() + "(" + e.getClass().getName() + ")");
+ if (!iterator.hasNext()) {
+ throw new UnknownHostException();
+ }
+ }
+ }
+ }
+ processStream();
+ } catch (final java.lang.SecurityException e) {
+ this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION);
+ } catch (final IncompatibleServerException e) {
+ this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
+ } catch (final SecurityException e) {
+ this.changeStatus(Account.State.SECURITY_ERROR);
+ } catch (final UnauthorizedException e) {
+ this.changeStatus(Account.State.UNAUTHORIZED);
+ } catch (final PaymentRequiredException e) {
+ this.changeStatus(Account.State.PAYMENT_REQUIRED);
+ } catch (final UnknownHostException | ConnectException e) {
+ this.changeStatus(Account.State.SERVER_NOT_FOUND);
+ } catch (final SocksSocketFactory.SocksProxyNotFoundException e) {
+ this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
+ } catch (final StreamErrorHostUnknown e) {
+ this.changeStatus(Account.State.HOST_UNKNOWN);
+ } catch (final StreamErrorPolicyViolation e) {
+ this.changeStatus(Account.State.POLICY_VIOLATION);
+ } catch (final StreamError e) {
+ this.changeStatus(Account.State.STREAM_ERROR);
+ } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
+ this.changeStatus(Account.State.OFFLINE);
+ this.attempt = Math.max(0, this.attempt - 1);
+ } finally {
+ forceCloseSocket();
+ if (wakeLock.isHeld()) {
+ try {
+ wakeLock.release();
+ } catch (final RuntimeException ignored) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Starts xmpp protocol, call after connecting to socket
+ *
+ * @return true if server returns with valid xmpp, false otherwise
+ * @throws IOException Unknown tag on connect
+ * @throws XmlPullParserException Bad Xml
+ * @throws NoSuchAlgorithmException Other error
*/
- private boolean startXmpp() throws IOException, XmlPullParserException, NoSuchAlgorithmException {
- tagWriter.setOutputStream(socket.getOutputStream());
- tagReader.setInputStream(socket.getInputStream());
- tagWriter.beginDocument();
- sendStartStream();
- Tag nextTag;
- while ((nextTag = tagReader.readTag()) != null) {
- if (nextTag.isStart("stream")) {
- return true;
- } else {
- throw new IOException("unknown tag on connect");
- }
- }
- if (socket.isConnected()) {
- socket.close();
- }
- return false;
- }
-
- private static class TlsFactoryVerifier {
- private final SSLSocketFactory factory;
- private final HostnameVerifier verifier;
-
- public TlsFactoryVerifier(final SSLSocketFactory factory, final HostnameVerifier verifier) throws IOException {
- this.factory = factory;
- this.verifier = verifier;
- if (factory == null || verifier == null) {
- throw new IOException("could not setup ssl");
- }
- }
- }
-
- private TlsFactoryVerifier getTlsFactoryVerifier() throws NoSuchAlgorithmException, KeyManagementException, IOException {
- final SSLContext sc = SSLSocketHelper.getSSLContext();
- MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
- KeyManager[] keyManager;
- if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
- keyManager = new KeyManager[]{new MyKeyManager()};
- } else {
- keyManager = null;
- }
- sc.init(keyManager, new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()}, mXmppConnectionService.getRNG());
- final SSLSocketFactory factory = sc.getSocketFactory();
- final HostnameVerifier verifier;
- if (mInteractive) {
- verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier());
- } else {
- verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier());
- }
-
- return new TlsFactoryVerifier(factory, verifier);
- }
-
- @Override
- public void run() {
- forceCloseSocket();
- connect();
- }
-
- private void processStream() throws XmlPullParserException, IOException, NoSuchAlgorithmException {
- Tag nextTag = tagReader.readTag();
- while (nextTag != null && !nextTag.isEnd("stream")) {
- if (nextTag.isStart("error")) {
- processStreamError(nextTag);
- } else if (nextTag.isStart("features")) {
- processStreamFeatures(nextTag);
- } else if (nextTag.isStart("proceed")) {
- switchOverToTls(nextTag);
- } else if (nextTag.isStart("success")) {
- final String challenge = tagReader.readElement(nextTag).getContent();
- try {
- saslMechanism.getResponse(challenge);
- } catch (final SaslMechanism.AuthenticationException e) {
- disconnect(true);
- Log.e(Config.LOGTAG, String.valueOf(e));
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": logged in");
- account.setKey(Account.PINNED_MECHANISM_KEY,
- String.valueOf(saslMechanism.getPriority()));
- tagReader.reset();
- sendStartStream();
- final Tag tag = tagReader.readTag();
- if (tag != null && tag.isStart("stream")) {
- processStream();
- } else {
- throw new IOException("server didn't restart stream after successful auth");
- }
- break;
- } else if (nextTag.isStart("failure")) {
- final Element failure = tagReader.readElement(nextTag);
- final String text = failure.findChildContent("text");
- if (failure.hasChild("account-disabled")
- && text != null
- && text.contains("renew")
- && Config.MAGIC_CREATE_DOMAIN != null
- && text.contains(Config.MAGIC_CREATE_DOMAIN)) {
- throw new PaymentRequiredException();
- } else {
- throw new UnauthorizedException();
- }
- } else if (nextTag.isStart("challenge")) {
- final String challenge = tagReader.readElement(nextTag).getContent();
- final Element response = new Element("response");
- response.setAttribute("xmlns",
- "urn:ietf:params:xml:ns:xmpp-sasl");
- try {
- response.setContent(saslMechanism.getResponse(challenge));
- } catch (final SaslMechanism.AuthenticationException e) {
- // TODO: Send auth abort tag.
- Log.e(Config.LOGTAG, e.toString());
- }
- tagWriter.writeElement(response);
- } else if (nextTag.isStart("enabled")) {
- final Element enabled = tagReader.readElement(nextTag);
- if ("true".equals(enabled.getAttribute("resume"))) {
- this.streamId = enabled.getAttribute("id");
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": stream management(" + smVersion
- + ") enabled (resumable)");
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": stream management(" + smVersion + ") enabled");
- }
- this.stanzasReceived = 0;
- final RequestPacket r = new RequestPacket(smVersion);
- tagWriter.writeStanzaAsync(r);
- } else if (nextTag.isStart("resumed")) {
- lastPacketReceived = SystemClock.elapsedRealtime();
- final Element resumed = tagReader.readElement(nextTag);
- final String h = resumed.getAttribute("h");
- try {
- ArrayList<AbstractAcknowledgeableStanza> failedStanzas = new ArrayList<>();
- synchronized (this.mStanzaQueue) {
- final int serverCount = Integer.parseInt(h);
- if (serverCount != stanzasSent) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
- + ": session resumed with lost packages");
- stanzasSent = serverCount;
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": session resumed");
- }
- acknowledgeStanzaUpTo(serverCount);
- for (int i = 0; i < this.mStanzaQueue.size(); ++i) {
- failedStanzas.add(mStanzaQueue.valueAt(i));
- }
- mStanzaQueue.clear();
- }
- Log.d(Config.LOGTAG, "resending " + failedStanzas.size() + " stanzas");
- for (AbstractAcknowledgeableStanza packet : failedStanzas) {
- if (packet instanceof MessagePacket) {
- MessagePacket message = (MessagePacket) packet;
- mXmppConnectionService.markMessage(account,
- message.getTo().toBareJid(),
- message.getId(),
- Message.STATUS_UNSEND);
- }
- sendPacket(packet);
- }
- } catch (final NumberFormatException ignored) {
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid()+ ": online with resource " + account.getResource());
- changeStatus(Account.State.ONLINE);
- } else if (nextTag.isStart("r")) {
- tagReader.readElement(nextTag);
- if (Config.EXTENDED_SM_LOGGING) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": acknowledging stanza #" + this.stanzasReceived);
- }
- final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
- tagWriter.writeStanzaAsync(ack);
- } else if (nextTag.isStart("a")) {
- final Element ack = tagReader.readElement(nextTag);
- lastPacketReceived = SystemClock.elapsedRealtime();
- try {
- synchronized (this.mStanzaQueue) {
- final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
- acknowledgeStanzaUpTo(serverSequence);
- }
- } catch (NumberFormatException | NullPointerException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": server send ack without sequence number");
- }
- } else if (nextTag.isStart("failed")) {
- Element failed = tagReader.readElement(nextTag);
- try {
- final int serverCount = Integer.parseInt(failed.getAttribute("h"));
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed but server acknowledged stanza #"+serverCount);
- synchronized (this.mStanzaQueue) {
- acknowledgeStanzaUpTo(serverCount);
- }
- } catch (NumberFormatException | NullPointerException e) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed");
- }
- resetStreamId();
- if (account.getStatus() != Account.State.ONLINE) {
- sendBindRequest();
- }
- } else if (nextTag.isStart("iq")) {
- processIq(nextTag);
- } else if (nextTag.isStart("message")) {
- processMessage(nextTag);
- } else if (nextTag.isStart("presence")) {
- processPresence(nextTag);
- }
- nextTag = tagReader.readTag();
- }
- }
-
- private void acknowledgeStanzaUpTo(int serverCount) {
- for (int i = 0; i < mStanzaQueue.size(); ++i) {
- if (serverCount >= mStanzaQueue.keyAt(i)) {
- if (Config.EXTENDED_SM_LOGGING) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server acknowledged stanza #" + mStanzaQueue.keyAt(i));
- }
- AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i);
- if (stanza instanceof MessagePacket && acknowledgedListener != null) {
- MessagePacket packet = (MessagePacket) stanza;
- acknowledgedListener.onMessageAcknowledged(account, packet.getId());
- }
- mStanzaQueue.removeAt(i);
- i--;
- }
- }
- }
-
- private Element processPacket(final Tag currentTag, final int packetType)
- throws XmlPullParserException, IOException {
- Element element;
- switch (packetType) {
- case PACKET_IQ:
- element = new IqPacket();
- break;
- case PACKET_MESSAGE:
- element = new MessagePacket();
- break;
- case PACKET_PRESENCE:
- element = new PresencePacket();
- break;
- default:
- return null;
- }
- element.setAttributes(currentTag.getAttributes());
- Tag nextTag = tagReader.readTag();
- if (nextTag == null) {
- throw new IOException("interrupted mid tag");
- }
- while (!nextTag.isEnd(element.getName())) {
- if (!nextTag.isNo()) {
- final Element child = tagReader.readElement(nextTag);
- final String type = currentTag.getAttribute("type");
- if (packetType == PACKET_IQ
- && "jingle".equals(child.getName())
- && ("set".equalsIgnoreCase(type) || "get"
- .equalsIgnoreCase(type))) {
- element = new JinglePacket();
- element.setAttributes(currentTag.getAttributes());
- }
- element.addChild(child);
- }
- nextTag = tagReader.readTag();
- if (nextTag == null) {
- throw new IOException("interrupted mid tag");
- }
- }
- if (stanzasReceived == Integer.MAX_VALUE) {
- resetStreamId();
- throw new IOException("time to restart the session. cant handle >2 billion pcks");
- }
- ++stanzasReceived;
- lastPacketReceived = SystemClock.elapsedRealtime();
- if (Config.BACKGROUND_STANZA_LOGGING && mXmppConnectionService.checkListeners()) {
- Log.d(Config.LOGTAG,"[background stanza] "+element);
- }
- return element;
- }
-
- private void processIq(final Tag currentTag) throws XmlPullParserException, IOException {
- final IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ);
-
- if (packet.getId() == null) {
- return; // an iq packet without id is definitely invalid
- }
-
- if (packet instanceof JinglePacket) {
- if (this.jingleListener != null) {
- this.jingleListener.onJinglePacketReceived(account,(JinglePacket) packet);
- }
- } else {
- OnIqPacketReceived callback = null;
- synchronized (this.packetCallbacks) {
- if (packetCallbacks.containsKey(packet.getId())) {
- final Pair<IqPacket, OnIqPacketReceived> packetCallbackDuple = packetCallbacks.get(packet.getId());
- // Packets to the server should have responses from the server
- if (packetCallbackDuple.first.toServer(account)) {
- if (packet.fromServer(account) || mServerIdentity == Identity.FACEBOOK) {
- callback = packetCallbackDuple.second;
- packetCallbacks.remove(packet.getId());
- } else {
- Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet");
- }
- } else {
- if (packet.getFrom().equals(packetCallbackDuple.first.getTo())) {
- callback = packetCallbackDuple.second;
- packetCallbacks.remove(packet.getId());
- } else {
- Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet");
- }
- }
- } else if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
- callback = this.unregisteredIqListener;
- }
- }
- if (callback != null) {
- callback.onIqPacketReceived(account,packet);
- }
- }
- }
-
- private void processMessage(final Tag currentTag) throws XmlPullParserException, IOException {
- final MessagePacket packet = (MessagePacket) processPacket(currentTag,PACKET_MESSAGE);
- this.messageListener.onMessagePacketReceived(account, packet);
- }
-
- private void processPresence(final Tag currentTag) throws XmlPullParserException, IOException {
- PresencePacket packet = (PresencePacket) processPacket(currentTag, PACKET_PRESENCE);
- this.presenceListener.onPresencePacketReceived(account, packet);
- }
-
- private void sendStartTLS() throws IOException {
- final Tag startTLS = Tag.empty("starttls");
- startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls");
- tagWriter.writeTag(startTLS);
- }
-
-
-
- private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
- tagReader.readTag();
- try {
- final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
- final InetAddress address = socket == null ? null : socket.getInetAddress();
-
- if (address == null) {
- throw new IOException("could not setup ssl");
- }
-
- final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true);
-
- if (sslSocket == null) {
- throw new IOException("could not initialize ssl socket");
- }
-
- SSLSocketHelper.setSecurity(sslSocket);
-
- if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
- throw new SecurityException();
- }
- tagReader.setInputStream(sslSocket.getInputStream());
- tagWriter.setOutputStream(sslSocket.getOutputStream());
- sendStartStream();
- Log.d(Config.LOGTAG, account.getJid().toBareJid()+ ": TLS connection established");
- features.encryptionEnabled = true;
- final Tag tag = tagReader.readTag();
- if (tag != null && tag.isStart("stream")) {
- processStream();
- } else {
- throw new IOException("server didn't restart stream after STARTTLS");
- }
- sslSocket.close();
- } catch (final NoSuchAlgorithmException | KeyManagementException e1) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
- throw new SecurityException();
- }
- }
-
- private void processStreamFeatures(final Tag currentTag)
- throws XmlPullParserException, IOException {
- this.streamFeatures = tagReader.readElement(currentTag);
- if (this.streamFeatures.hasChild("starttls") && !features.encryptionEnabled) {
- sendStartTLS();
- } else if (this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) {
- if (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS) {
- sendRegistryRequest();
- } else {
- throw new IncompatibleServerException();
- }
- } else if (!this.streamFeatures.hasChild("register")
- && account.isOptionSet(Account.OPTION_REGISTER)) {
- forceCloseSocket();
- changeStatus(Account.State.REGISTRATION_NOT_SUPPORTED);
- } else if (this.streamFeatures.hasChild("mechanisms")
- && shouldAuthenticate
- && (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
- authenticate();
- } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion) && streamId != null) {
- if (Config.EXTENDED_SM_LOGGING) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": resuming after stanza #"+stanzasReceived);
- }
- final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived, smVersion);
- this.tagWriter.writeStanzaAsync(resume);
- } else if (needsBinding) {
- if (this.streamFeatures.hasChild("bind")) {
- sendBindRequest();
- } else {
- throw new IncompatibleServerException();
- }
- }
- }
-
- private void authenticate() throws IOException {
- final List<String> mechanisms = extractMechanisms(streamFeatures
- .findChild("mechanisms"));
- final Element auth = new Element("auth");
- auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
- if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) {
- saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG());
- } else if (mechanisms.contains("SCRAM-SHA-1")) {
- saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG());
- } else if (mechanisms.contains("PLAIN")) {
- saslMechanism = new Plain(tagWriter, account);
- } else if (mechanisms.contains("DIGEST-MD5")) {
- saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
- } else if (mechanisms.contains("ANONYMOUS")) {
- saslMechanism = new Anonymous(tagWriter, account, mXmppConnectionService.getRNG());
- }
- if (saslMechanism != null) {
+ private boolean startXmpp() throws IOException, XmlPullParserException, NoSuchAlgorithmException {
+ tagWriter.setOutputStream(socket.getOutputStream());
+ tagReader.setInputStream(socket.getInputStream());
+ tagWriter.beginDocument();
+ sendStartStream();
+ Tag nextTag;
+ while ((nextTag = tagReader.readTag()) != null) {
+ if (nextTag.isStart("stream")) {
+ return true;
+ } else {
+ throw new IOException("unknown tag on connect");
+ }
+ }
+ if (socket.isConnected()) {
+ socket.close();
+ }
+ return false;
+ }
+
+ private static class TlsFactoryVerifier {
+ private final SSLSocketFactory factory;
+ private final HostnameVerifier verifier;
+
+ public TlsFactoryVerifier(final SSLSocketFactory factory, final HostnameVerifier verifier) throws IOException {
+ this.factory = factory;
+ this.verifier = verifier;
+ if (factory == null || verifier == null) {
+ throw new IOException("could not setup ssl");
+ }
+ }
+ }
+
+ private TlsFactoryVerifier getTlsFactoryVerifier() throws NoSuchAlgorithmException, KeyManagementException, IOException {
+ final SSLContext sc = SSLSocketHelper.getSSLContext();
+ MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
+ KeyManager[] keyManager;
+ if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
+ keyManager = new KeyManager[]{new MyKeyManager()};
+ } else {
+ keyManager = null;
+ }
+ sc.init(keyManager, new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()}, mXmppConnectionService.getRNG());
+ final SSLSocketFactory factory = sc.getSocketFactory();
+ final HostnameVerifier verifier;
+ if (mInteractive) {
+ verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier());
+ } else {
+ verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier());
+ }
+
+ return new TlsFactoryVerifier(factory, verifier);
+ }
+
+ @Override
+ public void run() {
+ forceCloseSocket();
+ connect();
+ }
+
+ private void processStream() throws XmlPullParserException, IOException, NoSuchAlgorithmException {
+ Tag nextTag = tagReader.readTag();
+ while (nextTag != null && !nextTag.isEnd("stream")) {
+ if (nextTag.isStart("error")) {
+ processStreamError(nextTag);
+ } else if (nextTag.isStart("features")) {
+ processStreamFeatures(nextTag);
+ } else if (nextTag.isStart("proceed")) {
+ switchOverToTls(nextTag);
+ } else if (nextTag.isStart("success")) {
+ final String challenge = tagReader.readElement(nextTag).getContent();
+ try {
+ saslMechanism.getResponse(challenge);
+ } catch (final SaslMechanism.AuthenticationException e) {
+ disconnect(true);
+ Log.e(Config.LOGTAG, String.valueOf(e));
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": logged in");
+ account.setKey(Account.PINNED_MECHANISM_KEY,
+ String.valueOf(saslMechanism.getPriority()));
+ tagReader.reset();
+ sendStartStream();
+ final Tag tag = tagReader.readTag();
+ if (tag != null && tag.isStart("stream")) {
+ processStream();
+ } else {
+ throw new IOException("server didn't restart stream after successful auth");
+ }
+ break;
+ } else if (nextTag.isStart("failure")) {
+ final Element failure = tagReader.readElement(nextTag);
+ final String text = failure.findChildContent("text");
+ if (failure.hasChild("account-disabled")
+ && text != null
+ && text.contains("renew")
+ && Config.MAGIC_CREATE_DOMAIN != null
+ && text.contains(Config.MAGIC_CREATE_DOMAIN)) {
+ throw new PaymentRequiredException();
+ } else {
+ throw new UnauthorizedException();
+ }
+ } else if (nextTag.isStart("challenge")) {
+ final String challenge = tagReader.readElement(nextTag).getContent();
+ final Element response = new Element("response");
+ response.setAttribute("xmlns",
+ "urn:ietf:params:xml:ns:xmpp-sasl");
+ try {
+ response.setContent(saslMechanism.getResponse(challenge));
+ } catch (final SaslMechanism.AuthenticationException e) {
+ // TODO: Send auth abort tag.
+ Log.e(Config.LOGTAG, e.toString());
+ }
+ tagWriter.writeElement(response);
+ } else if (nextTag.isStart("enabled")) {
+ final Element enabled = tagReader.readElement(nextTag);
+ if ("true".equals(enabled.getAttribute("resume"))) {
+ this.streamId = enabled.getAttribute("id");
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ + ": stream management(" + smVersion
+ + ") enabled (resumable)");
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ + ": stream management(" + smVersion + ") enabled");
+ }
+ this.stanzasReceived = 0;
+ final RequestPacket r = new RequestPacket(smVersion);
+ tagWriter.writeStanzaAsync(r);
+ } else if (nextTag.isStart("resumed")) {
+ lastPacketReceived = SystemClock.elapsedRealtime();
+ final Element resumed = tagReader.readElement(nextTag);
+ final String h = resumed.getAttribute("h");
+ try {
+ ArrayList<AbstractAcknowledgeableStanza> failedStanzas = new ArrayList<>();
+ synchronized (this.mStanzaQueue) {
+ final int serverCount = Integer.parseInt(h);
+ if (serverCount != stanzasSent) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ + ": session resumed with lost packages");
+ stanzasSent = serverCount;
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": session resumed");
+ }
+ acknowledgeStanzaUpTo(serverCount);
+ for (int i = 0; i < this.mStanzaQueue.size(); ++i) {
+ failedStanzas.add(mStanzaQueue.valueAt(i));
+ }
+ mStanzaQueue.clear();
+ }
+ Log.d(Config.LOGTAG, "resending " + failedStanzas.size() + " stanzas");
+ for (AbstractAcknowledgeableStanza packet : failedStanzas) {
+ if (packet instanceof MessagePacket) {
+ MessagePacket message = (MessagePacket) packet;
+ mXmppConnectionService.markMessage(account,
+ message.getTo().toBareJid(),
+ message.getId(),
+ Message.STATUS_UNSEND);
+ }
+ sendPacket(packet);
+ }
+ } catch (final NumberFormatException ignored) {
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
+ changeStatus(Account.State.ONLINE);
+ } else if (nextTag.isStart("r")) {
+ tagReader.readElement(nextTag);
+ if (Config.EXTENDED_SM_LOGGING) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": acknowledging stanza #" + this.stanzasReceived);
+ }
+ final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
+ tagWriter.writeStanzaAsync(ack);
+ } else if (nextTag.isStart("a")) {
+ final Element ack = tagReader.readElement(nextTag);
+ lastPacketReceived = SystemClock.elapsedRealtime();
+ try {
+ synchronized (this.mStanzaQueue) {
+ final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
+ acknowledgeStanzaUpTo(serverSequence);
+ }
+ } catch (NumberFormatException | NullPointerException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server send ack without sequence number");
+ }
+ } else if (nextTag.isStart("failed")) {
+ Element failed = tagReader.readElement(nextTag);
+ try {
+ final int serverCount = Integer.parseInt(failed.getAttribute("h"));
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed but server acknowledged stanza #" + serverCount);
+ synchronized (this.mStanzaQueue) {
+ acknowledgeStanzaUpTo(serverCount);
+ }
+ } catch (NumberFormatException | NullPointerException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resumption failed");
+ }
+ resetStreamId();
+ if (account.getStatus() != Account.State.ONLINE) {
+ sendBindRequest();
+ }
+ } else if (nextTag.isStart("iq")) {
+ processIq(nextTag);
+ } else if (nextTag.isStart("message")) {
+ processMessage(nextTag);
+ } else if (nextTag.isStart("presence")) {
+ processPresence(nextTag);
+ }
+ nextTag = tagReader.readTag();
+ }
+ }
+
+ private void acknowledgeStanzaUpTo(int serverCount) {
+ for (int i = 0; i < mStanzaQueue.size(); ++i) {
+ if (serverCount >= mStanzaQueue.keyAt(i)) {
+ if (Config.EXTENDED_SM_LOGGING) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server acknowledged stanza #" + mStanzaQueue.keyAt(i));
+ }
+ AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i);
+ if (stanza instanceof MessagePacket && acknowledgedListener != null) {
+ MessagePacket packet = (MessagePacket) stanza;
+ acknowledgedListener.onMessageAcknowledged(account, packet.getId());
+ }
+ mStanzaQueue.removeAt(i);
+ i--;
+ }
+ }
+ }
+
+ private Element processPacket(final Tag currentTag, final int packetType)
+ throws XmlPullParserException, IOException {
+ Element element;
+ switch (packetType) {
+ case PACKET_IQ:
+ element = new IqPacket();
+ break;
+ case PACKET_MESSAGE:
+ element = new MessagePacket();
+ break;
+ case PACKET_PRESENCE:
+ element = new PresencePacket();
+ break;
+ default:
+ return null;
+ }
+ element.setAttributes(currentTag.getAttributes());
+ Tag nextTag = tagReader.readTag();
+ if (nextTag == null) {
+ throw new IOException("interrupted mid tag");
+ }
+ while (!nextTag.isEnd(element.getName())) {
+ if (!nextTag.isNo()) {
+ final Element child = tagReader.readElement(nextTag);
+ final String type = currentTag.getAttribute("type");
+ if (packetType == PACKET_IQ
+ && "jingle".equals(child.getName())
+ && ("set".equalsIgnoreCase(type) || "get"
+ .equalsIgnoreCase(type))) {
+ element = new JinglePacket();
+ element.setAttributes(currentTag.getAttributes());
+ }
+ element.addChild(child);
+ }
+ nextTag = tagReader.readTag();
+ if (nextTag == null) {
+ throw new IOException("interrupted mid tag");
+ }
+ }
+ if (stanzasReceived == Integer.MAX_VALUE) {
+ resetStreamId();
+ throw new IOException("time to restart the session. cant handle >2 billion pcks");
+ }
+ ++stanzasReceived;
+ lastPacketReceived = SystemClock.elapsedRealtime();
+ if (Config.BACKGROUND_STANZA_LOGGING && mXmppConnectionService.checkListeners()) {
+ Log.d(Config.LOGTAG, "[background stanza] " + element);
+ }
+ return element;
+ }
+
+ private void processIq(final Tag currentTag) throws XmlPullParserException, IOException {
+ final IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ);
+
+ if (packet.getId() == null) {
+ return; // an iq packet without id is definitely invalid
+ }
+
+ if (packet instanceof JinglePacket) {
+ if (this.jingleListener != null) {
+ this.jingleListener.onJinglePacketReceived(account, (JinglePacket) packet);
+ }
+ } else {
+ OnIqPacketReceived callback = null;
+ synchronized (this.packetCallbacks) {
+ if (packetCallbacks.containsKey(packet.getId())) {
+ final Pair<IqPacket, OnIqPacketReceived> packetCallbackDuple = packetCallbacks.get(packet.getId());
+ // Packets to the server should have responses from the server
+ if (packetCallbackDuple.first.toServer(account)) {
+ if (packet.fromServer(account) || mServerIdentity == Identity.FACEBOOK) {
+ callback = packetCallbackDuple.second;
+ packetCallbacks.remove(packet.getId());
+ } else {
+ Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet");
+ }
+ } else {
+ if (packet.getFrom().equals(packetCallbackDuple.first.getTo())) {
+ callback = packetCallbackDuple.second;
+ packetCallbacks.remove(packet.getId());
+ } else {
+ Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet");
+ }
+ }
+ } else if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
+ callback = this.unregisteredIqListener;
+ }
+ }
+ if (callback != null) {
+ callback.onIqPacketReceived(account, packet);
+ }
+ }
+ }
+
+ private void processMessage(final Tag currentTag) throws XmlPullParserException, IOException {
+ final MessagePacket packet = (MessagePacket) processPacket(currentTag, PACKET_MESSAGE);
+ this.messageListener.onMessagePacketReceived(account, packet);
+ }
+
+ private void processPresence(final Tag currentTag) throws XmlPullParserException, IOException {
+ PresencePacket packet = (PresencePacket) processPacket(currentTag, PACKET_PRESENCE);
+ this.presenceListener.onPresencePacketReceived(account, packet);
+ }
+
+ private void sendStartTLS() throws IOException {
+ final Tag startTLS = Tag.empty("starttls");
+ startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls");
+ tagWriter.writeTag(startTLS);
+ }
+
+
+ private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
+ tagReader.readTag();
+ try {
+ final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
+ final InetAddress address = socket == null ? null : socket.getInetAddress();
+
+ if (address == null) {
+ throw new IOException("could not setup ssl");
+ }
+
+ final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true);
+
+ if (sslSocket == null) {
+ throw new IOException("could not initialize ssl socket");
+ }
+
+ SSLSocketHelper.setSecurity(sslSocket);
+
+ if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
+ throw new SecurityException();
+ }
+ tagReader.setInputStream(sslSocket.getInputStream());
+ tagWriter.setOutputStream(sslSocket.getOutputStream());
+ sendStartStream();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS connection established");
+ features.encryptionEnabled = true;
+ final Tag tag = tagReader.readTag();
+ if (tag != null && tag.isStart("stream")) {
+ processStream();
+ } else {
+ throw new IOException("server didn't restart stream after STARTTLS");
+ }
+ sslSocket.close();
+ } catch (final NoSuchAlgorithmException | KeyManagementException e1) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
+ throw new SecurityException();
+ }
+ }
+
+ private void processStreamFeatures(final Tag currentTag)
+ throws XmlPullParserException, IOException {
+ this.streamFeatures = tagReader.readElement(currentTag);
+ if (this.streamFeatures.hasChild("starttls") && !features.encryptionEnabled) {
+ sendStartTLS();
+ } else if (this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) {
+ if (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS) {
+ sendRegistryRequest();
+ } else {
+ throw new IncompatibleServerException();
+ }
+ } else if (!this.streamFeatures.hasChild("register")
+ && account.isOptionSet(Account.OPTION_REGISTER)) {
+ forceCloseSocket();
+ changeStatus(Account.State.REGISTRATION_NOT_SUPPORTED);
+ } else if (this.streamFeatures.hasChild("mechanisms")
+ && shouldAuthenticate
+ && (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
+ authenticate();
+ } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion) && streamId != null) {
+ if (Config.EXTENDED_SM_LOGGING) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": resuming after stanza #" + stanzasReceived);
+ }
+ final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived, smVersion);
+ this.tagWriter.writeStanzaAsync(resume);
+ } else if (needsBinding) {
+ if (this.streamFeatures.hasChild("bind")) {
+ sendBindRequest();
+ } else {
+ throw new IncompatibleServerException();
+ }
+ }
+ }
+
+ private void authenticate() throws IOException {
+ final List<String> mechanisms = extractMechanisms(streamFeatures
+ .findChild("mechanisms"));
+ final Element auth = new Element("auth");
+ auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
+ if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) {
+ saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG());
+ } else if (mechanisms.contains("SCRAM-SHA-1")) {
+ saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG());
+ } else if (mechanisms.contains("PLAIN")) {
+ saslMechanism = new Plain(tagWriter, account);
+ } else if (mechanisms.contains("DIGEST-MD5")) {
+ saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
+ } else if (mechanisms.contains("ANONYMOUS")) {
+ saslMechanism = new Anonymous(tagWriter, account, mXmppConnectionService.getRNG());
+ }
+ if (saslMechanism != null) {
final int pinnedMechanism = account.getKeyAsInt(Account.PINNED_MECHANISM_KEY, -1);
if (pinnedMechanism > saslMechanism.getPriority()) {
Log.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() +
@@ -879,839 +878,840 @@ public class XmppConnection implements Runnable {
") than pinned priority (" + pinnedMechanism +
"). Possible downgrade attack?");
throw new SecurityException();
- }
- Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
- auth.setAttribute("mechanism", saslMechanism.getMechanism());
- if (!saslMechanism.getClientFirstMessage().isEmpty()) {
- auth.setContent(saslMechanism.getClientFirstMessage());
- }
- tagWriter.writeElement(auth);
- } else {
- throw new IncompatibleServerException();
- }
- }
-
- private List<String> extractMechanisms(final Element stream) {
- final ArrayList<String> mechanisms = new ArrayList<>(stream
- .getChildren().size());
- for (final Element child : stream.getChildren()) {
- mechanisms.add(child.getContent());
- }
- return mechanisms;
- }
-
- private void sendRegistryRequest() {
- final IqPacket register = new IqPacket(IqPacket.TYPE.GET);
- register.query("jabber:iq:register");
- register.setTo(account.getServer());
- sendUnmodifiedIqPacket(register, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- boolean failed = false;
- if (packet.getType() == IqPacket.TYPE.RESULT
- && packet.query().hasChild("username")
- && (packet.query().hasChild("password"))) {
- final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
- final Element username = new Element("username").setContent(account.getUsername());
- final Element password = new Element("password").setContent(account.getPassword());
- register.query("jabber:iq:register").addChild(username);
- register.query().addChild(password);
- register.setFrom(account.getJid().toBareJid());
- sendUnmodifiedIqPacket(register, registrationResponseListener);
- } else if (packet.getType() == IqPacket.TYPE.RESULT
- && (packet.query().hasChild("x", "jabber:x:data"))) {
- final Data data = Data.parse(packet.query().findChild("x", "jabber:x:data"));
- final Element blob = packet.query().findChild("data", "urn:xmpp:bob");
- final String id = packet.getId();
-
- Bitmap captcha = null;
- if (blob != null) {
- try {
- final String base64Blob = blob.getContent();
- final byte[] strBlob = Base64.decode(base64Blob, Base64.DEFAULT);
- InputStream stream = new ByteArrayInputStream(strBlob);
- captcha = BitmapFactory.decodeStream(stream);
- } catch (Exception e) {
- //ignored
- }
- } else {
- try {
- Field url = data.getFieldByName("url");
- String urlString = url.findChildContent("value");
- URL uri = new URL(urlString);
- captcha = BitmapFactory.decodeStream(uri.openConnection().getInputStream());
- } catch (IOException e) {
- Log.e(Config.LOGTAG, e.toString());
- }
- }
-
- if (captcha != null) {
- failed = !mXmppConnectionService.displayCaptchaRequest(account, id, data, captcha);
- }
- } else {
- failed = true;
- }
-
- if (failed) {
- final Element instructions = packet.query().findChild("instructions");
- setAccountCreationFailed((instructions != null) ? instructions.getContent() : "");
- }
- }
- });
- }
-
- private void setAccountCreationFailed(String instructions) {
- changeStatus(Account.State.REGISTRATION_FAILED);
- disconnect(true);
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": could not register. instructions are"
- + instructions);
- }
-
- public void resetEverything() {
- resetAttemptCount();
- resetStreamId();
- clearIqCallbacks();
- mStanzaQueue.clear();
- synchronized (this.disco) {
- disco.clear();
- }
- }
-
- private void sendBindRequest() {
- while(!mXmppConnectionService.areMessagesInitialized() && socket != null && !socket.isClosed()) {
- try {
- Thread.sleep(500);
- } catch (final InterruptedException ignored) {
- }
- }
- needsBinding = false;
- clearIqCallbacks();
- final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind")
- .addChild("resource").setContent(account.getResource());
- this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
- return;
- }
- final Element bind = packet.findChild("bind");
- if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) {
- final Element jid = bind.findChild("jid");
- if (jid != null && jid.getContent() != null) {
- try {
+ }
+ Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
+ auth.setAttribute("mechanism", saslMechanism.getMechanism());
+ if (!saslMechanism.getClientFirstMessage().isEmpty()) {
+ auth.setContent(saslMechanism.getClientFirstMessage());
+ }
+ tagWriter.writeElement(auth);
+ } else {
+ throw new IncompatibleServerException();
+ }
+ }
+
+ private List<String> extractMechanisms(final Element stream) {
+ final ArrayList<String> mechanisms = new ArrayList<>(stream
+ .getChildren().size());
+ for (final Element child : stream.getChildren()) {
+ mechanisms.add(child.getContent());
+ }
+ return mechanisms;
+ }
+
+ private void sendRegistryRequest() {
+ final IqPacket register = new IqPacket(IqPacket.TYPE.GET);
+ register.query("jabber:iq:register");
+ register.setTo(account.getServer());
+ sendUnmodifiedIqPacket(register, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ boolean failed = false;
+ if (packet.getType() == IqPacket.TYPE.RESULT
+ && packet.query().hasChild("username")
+ && (packet.query().hasChild("password"))) {
+ final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
+ final Element username = new Element("username").setContent(account.getUsername());
+ final Element password = new Element("password").setContent(account.getPassword());
+ register.query("jabber:iq:register").addChild(username);
+ register.query().addChild(password);
+ register.setFrom(account.getJid().toBareJid());
+ sendUnmodifiedIqPacket(register, registrationResponseListener);
+ } else if (packet.getType() == IqPacket.TYPE.RESULT
+ && (packet.query().hasChild("x", "jabber:x:data"))) {
+ final Data data = Data.parse(packet.query().findChild("x", "jabber:x:data"));
+ final Element blob = packet.query().findChild("data", "urn:xmpp:bob");
+ final String id = packet.getId();
+
+ Bitmap captcha = null;
+ if (blob != null) {
+ try {
+ final String base64Blob = blob.getContent();
+ final byte[] strBlob = Base64.decode(base64Blob, Base64.DEFAULT);
+ InputStream stream = new ByteArrayInputStream(strBlob);
+ captcha = BitmapFactory.decodeStream(stream);
+ } catch (Exception e) {
+ //ignored
+ }
+ } else {
+ try {
+ Field url = data.getFieldByName("url");
+ String urlString = url.findChildContent("value");
+ URL uri = new URL(urlString);
+ captcha = BitmapFactory.decodeStream(uri.openConnection().getInputStream());
+ } catch (IOException e) {
+ Log.e(Config.LOGTAG, e.toString());
+ }
+ }
+
+ if (captcha != null) {
+ failed = !mXmppConnectionService.displayCaptchaRequest(account, id, data, captcha);
+ }
+ } else {
+ failed = true;
+ }
+
+ if (failed) {
+ final Element instructions = packet.query().findChild("instructions");
+ setAccountCreationFailed((instructions != null) ? instructions.getContent() : "");
+ }
+ }
+ });
+ }
+
+ private void setAccountCreationFailed(String instructions) {
+ changeStatus(Account.State.REGISTRATION_FAILED);
+ disconnect(true);
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": could not register. instructions are"
+ + instructions);
+ }
+
+ public void resetEverything() {
+ resetAttemptCount();
+ resetStreamId();
+ clearIqCallbacks();
+ mStanzaQueue.clear();
+ synchronized (this.disco) {
+ disco.clear();
+ }
+ }
+
+ private void sendBindRequest() {
+ while (!mXmppConnectionService.areMessagesInitialized() && socket != null && !socket.isClosed()) {
+ try {
+ Thread.sleep(500);
+ } catch (final InterruptedException ignored) {
+ }
+ }
+ needsBinding = false;
+ clearIqCallbacks();
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind")
+ .addChild("resource").setContent(account.getResource());
+ this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
+ return;
+ }
+ final Element bind = packet.findChild("bind");
+ if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) {
+ final Element jid = bind.findChild("jid");
+ if (jid != null && jid.getContent() != null) {
+ try {
if (account.setJid(Jid.fromString(jid.getContent()))) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": bare jid changed during bind. updating database");
mXmppConnectionService.databaseBackend.updateAccount(account);
}
if (streamFeatures.hasChild("session")
- && !streamFeatures.findChild("session").hasChild("optional")) {
- sendStartSession();
- } else {
- sendPostBindInitialization();
- }
- return;
- } catch (final InvalidJidException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": server reported invalid jid ("+jid.getContent()+") on bind");
- }
- } else {
- Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure. (no jid)");
- }
- } else {
- Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString());
- }
- forceCloseSocket();
- changeStatus(Account.State.BIND_FAILURE);
- }
- });
- }
-
- private void clearIqCallbacks() {
- final IqPacket failurePacket = new IqPacket(IqPacket.TYPE.TIMEOUT);
- final ArrayList<OnIqPacketReceived> callbacks = new ArrayList<>();
- synchronized (this.packetCallbacks) {
- if (this.packetCallbacks.size() == 0) {
- return;
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": clearing "+this.packetCallbacks.size()+" iq callbacks");
- final Iterator<Pair<IqPacket, OnIqPacketReceived>> iterator = this.packetCallbacks.values().iterator();
- while (iterator.hasNext()) {
- Pair<IqPacket, OnIqPacketReceived> entry = iterator.next();
- callbacks.add(entry.second);
- iterator.remove();
- }
- }
- for(OnIqPacketReceived callback : callbacks) {
- callback.onIqPacketReceived(account,failurePacket);
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": done clearing iq callbacks. " + this.packetCallbacks.size() + " left");
- }
-
- public void sendDiscoTimeout() {
- if (mWaitForDisco.compareAndSet(true, false)) {
- finalizeBind();
- }
- }
-
- private void sendStartSession() {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": sending legacy session to outdated server");
- final IqPacket startSession = new IqPacket(IqPacket.TYPE.SET);
- startSession.addChild("session", "urn:ietf:params:xml:ns:xmpp-session");
- this.sendUnmodifiedIqPacket(startSession, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- sendPostBindInitialization();
- } else if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions");
- disconnect(true);
- }
- }
- });
- }
-
- private void sendPostBindInitialization() {
- smVersion = 0;
- if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
- smVersion = 3;
- } else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) {
- smVersion = 2;
- }
- if (smVersion != 0) {
- synchronized (this.mStanzaQueue) {
- final EnablePacket enable = new EnablePacket(smVersion);
- tagWriter.writeStanzaAsync(enable);
- stanzasSent = 0;
- mStanzaQueue.clear();
- }
- }
+ && !streamFeatures.findChild("session").hasChild("optional")) {
+ sendStartSession();
+ } else {
+ sendPostBindInitialization();
+ }
+ return;
+ } catch (final InvalidJidException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server reported invalid jid (" + jid.getContent() + ") on bind");
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure. (no jid)");
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString());
+ }
+ forceCloseSocket();
+ changeStatus(Account.State.BIND_FAILURE);
+ }
+ });
+ }
+
+ private void clearIqCallbacks() {
+ final IqPacket failurePacket = new IqPacket(IqPacket.TYPE.TIMEOUT);
+ final ArrayList<OnIqPacketReceived> callbacks = new ArrayList<>();
+ synchronized (this.packetCallbacks) {
+ if (this.packetCallbacks.size() == 0) {
+ return;
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": clearing " + this.packetCallbacks.size() + " iq callbacks");
+ final Iterator<Pair<IqPacket, OnIqPacketReceived>> iterator = this.packetCallbacks.values().iterator();
+ while (iterator.hasNext()) {
+ Pair<IqPacket, OnIqPacketReceived> entry = iterator.next();
+ callbacks.add(entry.second);
+ iterator.remove();
+ }
+ }
+ for (OnIqPacketReceived callback : callbacks) {
+ callback.onIqPacketReceived(account, failurePacket);
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": done clearing iq callbacks. " + this.packetCallbacks.size() + " left");
+ }
+
+ public void sendDiscoTimeout() {
+ if (mWaitForDisco.compareAndSet(true, false)) {
+ finalizeBind();
+ }
+ }
+
+ private void sendStartSession() {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": sending legacy session to outdated server");
+ final IqPacket startSession = new IqPacket(IqPacket.TYPE.SET);
+ startSession.addChild("session", "urn:ietf:params:xml:ns:xmpp-session");
+ this.sendUnmodifiedIqPacket(startSession, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ sendPostBindInitialization();
+ } else if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions");
+ disconnect(true);
+ }
+ }
+ });
+ }
+
+ private void sendPostBindInitialization() {
+ smVersion = 0;
+ if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
+ smVersion = 3;
+ } else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) {
+ smVersion = 2;
+ }
+ if (smVersion != 0) {
+ synchronized (this.mStanzaQueue) {
+ final EnablePacket enable = new EnablePacket(smVersion);
+ tagWriter.writeStanzaAsync(enable);
+ stanzasSent = 0;
+ mStanzaQueue.clear();
+ }
+ }
features.carbonsEnabled = false;
- features.blockListRequested = false;
- synchronized (this.disco) {
- this.disco.clear();
- }
- mPendingServiceDiscoveries.set(0);
+ features.blockListRequested = false;
+ synchronized (this.disco) {
+ this.disco.clear();
+ }
+ mPendingServiceDiscoveries.set(0);
mWaitForDisco.set(mServerIdentity != Identity.NIMBUZZ && smVersion != 0);
- lastDiscoStarted = SystemClock.elapsedRealtime();
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": starting service discovery");
- mXmppConnectionService.scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
- Element caps = streamFeatures.findChild("c");
- final String hash = caps == null ? null : caps.getAttribute("hash");
- final String ver = caps == null ? null : caps.getAttribute("ver");
- ServiceDiscoveryResult discoveryResult = null;
- if (hash != null && ver != null) {
- discoveryResult = mXmppConnectionService.getCachedServiceDiscoveryResult(new Pair<>(hash, ver));
- }
- if (discoveryResult == null) {
- sendServiceDiscoveryInfo(account.getServer());
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": server caps came from cache");
- disco.put(account.getServer(), discoveryResult);
- }
- sendServiceDiscoveryInfo(account.getJid().toBareJid());
- sendServiceDiscoveryItems(account.getServer());
-
- if (!mWaitForDisco.get()) {
- finalizeBind();
- }
- this.lastSessionStarted = SystemClock.elapsedRealtime();
- }
-
- private void sendServiceDiscoveryInfo(final Jid jid) {
- mPendingServiceDiscoveries.incrementAndGet();
- final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
- iq.setTo(jid);
- iq.query("http://jabber.org/protocol/disco#info");
- this.sendIqPacket(iq, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- boolean advancedStreamFeaturesLoaded;
- synchronized (XmppConnection.this.disco) {
- ServiceDiscoveryResult result = new ServiceDiscoveryResult(packet);
- for (final ServiceDiscoveryResult.Identity id : result.getIdentities()) {
- if (mServerIdentity == Identity.UNKNOWN && id.getType().equals("im") &&
- id.getCategory().equals("server") && id.getName() != null &&
- jid.equals(account.getServer())) {
- switch (id.getName()) {
- case "Prosody":
- mServerIdentity = Identity.PROSODY;
- break;
- case "ejabberd":
- mServerIdentity = Identity.EJABBERD;
- break;
- case "Slack-XMPP":
- mServerIdentity = Identity.SLACK;
- break;
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server name: " + id.getName());
- }
- }
- if (jid.equals(account.getServer())) {
- mXmppConnectionService.databaseBackend.insertDiscoveryResult(result);
- }
- disco.put(jid, result);
- advancedStreamFeaturesLoaded = disco.containsKey(account.getServer())
- && disco.containsKey(account.getJid().toBareJid());
- }
- if (advancedStreamFeaturesLoaded && (jid.equals(account.getServer()) || jid.equals(account.getJid().toBareJid()))) {
- enableAdvancedStreamFeatures();
- }
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString());
- }
- if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
- if (mPendingServiceDiscoveries.decrementAndGet() == 0
- && mWaitForDisco.compareAndSet(true, false)) {
- finalizeBind();
- }
- }
- }
- });
- }
-
- private void finalizeBind() {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
- if (bindListener != null) {
- bindListener.onBind(account);
- }
- changeStatus(Account.State.ONLINE);
- }
-
- private void enableAdvancedStreamFeatures() {
- if (getFeatures().carbons() && !features.carbonsEnabled) {
- sendEnableCarbons();
- }
- if (getFeatures().blocking() && !features.blockListRequested) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Requesting block list");
- this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser());
- }
- for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
- listener.onAdvancedStreamFeaturesAvailable(account);
- }
- }
-
- private void sendServiceDiscoveryItems(final Jid server) {
- mPendingServiceDiscoveries.incrementAndGet();
- final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
- iq.setTo(server.toDomainJid());
- iq.query("http://jabber.org/protocol/disco#items");
- this.sendIqPacket(iq, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- final List<Element> elements = packet.query().getChildren();
- for (final Element element : elements) {
- if (element.getName().equals("item")) {
- final Jid jid = element.getAttributeAsJid("jid");
- if (jid != null && !jid.equals(account.getServer())) {
- sendServiceDiscoveryInfo(jid);
- }
- }
- }
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco items of " + server);
- }
- if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
- if (mPendingServiceDiscoveries.decrementAndGet() == 0
- && mWaitForDisco.compareAndSet(true, false)) {
- finalizeBind();
- }
- }
- }
- });
- }
-
- private void sendEnableCarbons() {
- final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.addChild("enable", "urn:xmpp:carbons:2");
- this.sendIqPacket(iq, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(final Account account, final IqPacket packet) {
- if (!packet.hasChild("error")) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": successfully enabled carbons");
- features.carbonsEnabled = true;
- } else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()
- + ": error enableing carbons " + packet.toString());
- }
- }
- });
- }
-
- private void processStreamError(final Tag currentTag)
- throws XmlPullParserException, IOException {
- final Element streamError = tagReader.readElement(currentTag);
- if (streamError == null) {
- return;
- }
- if (streamError.hasChild("conflict")) {
- final String resource = account.getResource().split("\\.")[0];
- account.setResource(resource + "." + nextRandomId());
- Log.d(Config.LOGTAG,
- account.getJid().toBareJid() + ": switching resource due to conflict ("
- + account.getResource() + ")");
- throw new IOException();
- } else if (streamError.hasChild("host-unknown")) {
- throw new StreamErrorHostUnknown();
- } else if (streamError.hasChild("policy-violation")) {
- throw new StreamErrorPolicyViolation();
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString());
- throw new StreamError();
- }
- }
-
- private void sendStartStream() throws IOException {
- final Tag stream = Tag.start("stream:stream");
- stream.setAttribute("to", account.getServer().toString());
- stream.setAttribute("version", "1.0");
- stream.setAttribute("xml:lang", "en");
- stream.setAttribute("xmlns", "jabber:client");
- stream.setAttribute("xmlns:stream", "http://etherx.jabber.org/streams");
- tagWriter.writeTag(stream);
- }
-
- private String nextRandomId() {
- return new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
- }
-
- public String sendIqPacket(final IqPacket packet, final OnIqPacketReceived callback) {
- packet.setFrom(account.getJid());
- return this.sendUnmodifiedIqPacket(packet, callback);
- }
-
- public synchronized String sendUnmodifiedIqPacket(final IqPacket packet, final OnIqPacketReceived callback) {
- if (packet.getId() == null) {
- final String id = nextRandomId();
- packet.setAttribute("id", id);
- }
- if (callback != null) {
- synchronized (this.packetCallbacks) {
- packetCallbacks.put(packet.getId(), new Pair<>(packet, callback));
- }
- }
- this.sendPacket(packet);
- return packet.getId();
- }
-
- public void sendMessagePacket(final MessagePacket packet) {
- this.sendPacket(packet);
- }
-
- public void sendPresencePacket(final PresencePacket packet) {
- this.sendPacket(packet);
- }
-
- private synchronized void sendPacket(final AbstractStanza packet) {
- if (stanzasSent == Integer.MAX_VALUE) {
- resetStreamId();
- disconnect(true);
- return;
- }
- synchronized (this.mStanzaQueue) {
- tagWriter.writeStanzaAsync(packet);
- if (packet instanceof AbstractAcknowledgeableStanza) {
- AbstractAcknowledgeableStanza stanza = (AbstractAcknowledgeableStanza) packet;
- ++stanzasSent;
- this.mStanzaQueue.append(stanzasSent, stanza);
- if (stanza instanceof MessagePacket && stanza.getId() != null && getFeatures().sm()) {
- if (Config.EXTENDED_SM_LOGGING) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": requesting ack for message stanza #" + stanzasSent);
- }
- tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion));
- }
- }
- }
- }
-
- public void sendPing() {
- if (!r()) {
- final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
- iq.setFrom(account.getJid());
- iq.addChild("ping", "urn:xmpp:ping");
- this.sendIqPacket(iq, null);
- }
- this.lastPingSent = SystemClock.elapsedRealtime();
- }
-
- public void setOnMessagePacketReceivedListener(
- final OnMessagePacketReceived listener) {
- this.messageListener = listener;
- }
-
- public void setOnUnregisteredIqPacketReceivedListener(
- final OnIqPacketReceived listener) {
- this.unregisteredIqListener = listener;
- }
-
- public void setOnPresencePacketReceivedListener(
- final OnPresencePacketReceived listener) {
- this.presenceListener = listener;
- }
-
- public void setOnJinglePacketReceivedListener(
- final OnJinglePacketReceived listener) {
- this.jingleListener = listener;
- }
-
- public void setOnStatusChangedListener(final OnStatusChanged listener) {
- this.statusListener = listener;
- }
-
- public void setOnBindListener(final OnBindListener listener) {
- this.bindListener = listener;
- }
-
- public void setOnMessageAcknowledgeListener(final OnMessageAcknowledged listener) {
- this.acknowledgedListener = listener;
- }
-
- public void addOnAdvancedStreamFeaturesAvailableListener(final OnAdvancedStreamFeaturesLoaded listener) {
- if (!this.advancedStreamFeaturesLoadedListeners.contains(listener)) {
- this.advancedStreamFeaturesLoadedListeners.add(listener);
- }
- }
-
- public void waitForPush() {
- if (tagWriter.isActive()) {
- tagWriter.finish();
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- while(!tagWriter.finished()) {
- Thread.sleep(10);
- }
- socket.close();
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": closed tcp without closing stream");
- changeStatus(Account.State.OFFLINE);
- } catch (IOException | InterruptedException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": error while closing socket for waitForPush()");
- }
- }
- }).start();
- } else {
- forceCloseSocket();
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": closed tcp without closing stream (no waiting)");
- }
- }
-
- private void forceCloseSocket() {
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": io exception "+e.getMessage()+" during force close");
- }
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": socket was null during force close");
- }
- }
-
- public void interrupt() {
- Thread.currentThread().interrupt();
- }
-
- public void disconnect(final boolean force) {
- interrupt();
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
- if (force) {
- tagWriter.forceClose();
- forceCloseSocket();
- } else {
- if (tagWriter.isActive()) {
- tagWriter.finish();
- try {
- int i = 0;
- boolean warned = false;
- while (!tagWriter.finished() && socket.isConnected() && i <= 10) {
- if (!warned) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid()+": waiting for tag writer to finish");
- warned = true;
- }
- try {
- Thread.sleep(200);
- } catch(InterruptedException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": sleep interrupted");
- }
- i++;
- }
- if (warned) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": tag writer has finished");
- }
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": closing stream");
- tagWriter.writeTag(Tag.end("stream:stream"));
- } catch (final IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": io exception during disconnect ("+e.getMessage()+")");
- } finally {
- forceCloseSocket();
- }
- }
- }
- }
-
- public void resetStreamId() {
- this.streamId = null;
- }
-
- private List<Entry<Jid, ServiceDiscoveryResult>> findDiscoItemsByFeature(final String feature) {
- synchronized (this.disco) {
- final List<Entry<Jid, ServiceDiscoveryResult>> items = new ArrayList<>();
- for (final Entry<Jid, ServiceDiscoveryResult> cursor : this.disco.entrySet()) {
- if (cursor.getValue().getFeatures().contains(feature)) {
- items.add(cursor);
- }
- }
- return items;
- }
- }
-
- public Jid findDiscoItemByFeature(final String feature) {
- final List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(feature);
- if (items.size() >= 1) {
- return items.get(0).getKey();
- }
- return null;
- }
-
- public boolean r() {
- if (getFeatures().sm()) {
- this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion));
- return true;
- } else {
- return false;
- }
- }
-
- public String getMucServer() {
- synchronized (this.disco) {
- for (final Entry<Jid, ServiceDiscoveryResult> cursor : disco.entrySet()) {
- final ServiceDiscoveryResult value = cursor.getValue();
- if (value.getFeatures().contains("http://jabber.org/protocol/muc")
- && !value.getFeatures().contains("jabber:iq:gateway")
- && !value.hasIdentity("conference", "irc")) {
- return cursor.getKey().toString();
- }
- }
- }
- return null;
- }
-
- public int getTimeToNextAttempt() {
- final int interval = Math.min((int) (25 * Math.pow(1.3, attempt)), 300);
- final int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000);
- return interval - secondsSinceLast;
- }
-
- public int getAttempt() {
- return this.attempt;
- }
-
- public Features getFeatures() {
- return this.features;
- }
-
- public long getLastSessionEstablished() {
- final long diff = SystemClock.elapsedRealtime() - this.lastSessionStarted;
- return System.currentTimeMillis() - diff;
- }
-
- public long getLastConnect() {
- return this.lastConnect;
- }
-
- public long getLastPingSent() {
- return this.lastPingSent;
- }
-
- public long getLastDiscoStarted() {
- return this.lastDiscoStarted;
- }
- public long getLastPacketReceived() {
- return this.lastPacketReceived;
- }
-
- public void sendActive() {
- this.sendPacket(new ActivePacket());
- }
-
- public void sendInactive() {
- this.sendPacket(new InactivePacket());
- }
-
- public void resetAttemptCount() {
- this.attempt = 0;
- this.lastConnect = 0;
- }
-
- public void setInteractive(boolean interactive) {
- this.mInteractive = interactive;
- }
-
- public Identity getServerIdentity() {
- return mServerIdentity;
- }
-
- private class UnauthorizedException extends IOException {
-
- }
-
- private class SecurityException extends IOException {
-
- }
-
- private class IncompatibleServerException extends IOException {
-
- }
-
- private class StreamErrorHostUnknown extends StreamError {
-
- }
-
- private class StreamErrorPolicyViolation extends StreamError {
-
- }
+ lastDiscoStarted = SystemClock.elapsedRealtime();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": starting service discovery");
+ mXmppConnectionService.scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
+ Element caps = streamFeatures.findChild("c");
+ final String hash = caps == null ? null : caps.getAttribute("hash");
+ final String ver = caps == null ? null : caps.getAttribute("ver");
+ ServiceDiscoveryResult discoveryResult = null;
+ if (hash != null && ver != null) {
+ discoveryResult = mXmppConnectionService.getCachedServiceDiscoveryResult(new Pair<>(hash, ver));
+ }
+ if (discoveryResult == null) {
+ sendServiceDiscoveryInfo(account.getServer());
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server caps came from cache");
+ disco.put(account.getServer(), discoveryResult);
+ }
+ sendServiceDiscoveryInfo(account.getJid().toBareJid());
+ sendServiceDiscoveryItems(account.getServer());
- private class StreamError extends IOException {
+ if (!mWaitForDisco.get()) {
+ finalizeBind();
+ }
+ this.lastSessionStarted = SystemClock.elapsedRealtime();
+ }
+
+ private void sendServiceDiscoveryInfo(final Jid jid) {
+ mPendingServiceDiscoveries.incrementAndGet();
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
+ iq.setTo(jid);
+ iq.query("http://jabber.org/protocol/disco#info");
+ this.sendIqPacket(iq, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ boolean advancedStreamFeaturesLoaded;
+ synchronized (XmppConnection.this.disco) {
+ ServiceDiscoveryResult result = new ServiceDiscoveryResult(packet);
+ for (final ServiceDiscoveryResult.Identity id : result.getIdentities()) {
+ if (mServerIdentity == Identity.UNKNOWN && id.getType().equals("im") &&
+ id.getCategory().equals("server") && id.getName() != null &&
+ jid.equals(account.getServer())) {
+ switch (id.getName()) {
+ case "Prosody":
+ mServerIdentity = Identity.PROSODY;
+ break;
+ case "ejabberd":
+ mServerIdentity = Identity.EJABBERD;
+ break;
+ case "Slack-XMPP":
+ mServerIdentity = Identity.SLACK;
+ break;
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server name: " + id.getName());
+ }
+ }
+ if (jid.equals(account.getServer())) {
+ mXmppConnectionService.databaseBackend.insertDiscoveryResult(result);
+ }
+ disco.put(jid, result);
+ advancedStreamFeaturesLoaded = disco.containsKey(account.getServer())
+ && disco.containsKey(account.getJid().toBareJid());
+ }
+ if (advancedStreamFeaturesLoaded && (jid.equals(account.getServer()) || jid.equals(account.getJid().toBareJid()))) {
+ enableAdvancedStreamFeatures();
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString());
+ }
+ if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
+ if (mPendingServiceDiscoveries.decrementAndGet() == 0
+ && mWaitForDisco.compareAndSet(true, false)) {
+ finalizeBind();
+ }
+ }
+ }
+ });
+ }
+
+ private void finalizeBind() {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
+ if (bindListener != null) {
+ bindListener.onBind(account);
+ }
+ changeStatus(Account.State.ONLINE);
+ }
+
+ private void enableAdvancedStreamFeatures() {
+ if (getFeatures().carbons() && !features.carbonsEnabled) {
+ sendEnableCarbons();
+ }
+ if (getFeatures().blocking() && !features.blockListRequested) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Requesting block list");
+ this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser());
+ }
+ for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
+ listener.onAdvancedStreamFeaturesAvailable(account);
+ }
+ }
+
+ private void sendServiceDiscoveryItems(final Jid server) {
+ mPendingServiceDiscoveries.incrementAndGet();
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
+ iq.setTo(server.toDomainJid());
+ iq.query("http://jabber.org/protocol/disco#items");
+ this.sendIqPacket(iq, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ final List<Element> elements = packet.query().getChildren();
+ for (final Element element : elements) {
+ if (element.getName().equals("item")) {
+ final Jid jid = element.getAttributeAsJid("jid");
+ if (jid != null && !jid.equals(account.getServer())) {
+ sendServiceDiscoveryInfo(jid);
+ }
+ }
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco items of " + server);
+ }
+ if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
+ if (mPendingServiceDiscoveries.decrementAndGet() == 0
+ && mWaitForDisco.compareAndSet(true, false)) {
+ finalizeBind();
+ }
+ }
+ }
+ });
+ }
+
+ private void sendEnableCarbons() {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.addChild("enable", "urn:xmpp:carbons:2");
+ this.sendIqPacket(iq, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(final Account account, final IqPacket packet) {
+ if (!packet.hasChild("error")) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": successfully enabled carbons");
+ features.carbonsEnabled = true;
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ + ": error enableing carbons " + packet.toString());
+ }
+ }
+ });
+ }
+
+ private void processStreamError(final Tag currentTag)
+ throws XmlPullParserException, IOException {
+ final Element streamError = tagReader.readElement(currentTag);
+ if (streamError == null) {
+ return;
+ }
+ if (streamError.hasChild("conflict")) {
+ final String resource = account.getResource().split("\\.")[0];
+ account.setResource(resource + "." + nextRandomId());
+ Log.d(Config.LOGTAG,
+ account.getJid().toBareJid() + ": switching resource due to conflict ("
+ + account.getResource() + ")");
+ throw new IOException();
+ } else if (streamError.hasChild("host-unknown")) {
+ throw new StreamErrorHostUnknown();
+ } else if (streamError.hasChild("policy-violation")) {
+ throw new StreamErrorPolicyViolation();
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": stream error " + streamError.toString());
+ throw new StreamError();
+ }
+ }
+
+ private void sendStartStream() throws IOException {
+ final Tag stream = Tag.start("stream:stream");
+ stream.setAttribute("to", account.getServer().toString());
+ stream.setAttribute("version", "1.0");
+ stream.setAttribute("xml:lang", "en");
+ stream.setAttribute("xmlns", "jabber:client");
+ stream.setAttribute("xmlns:stream", "http://etherx.jabber.org/streams");
+ tagWriter.writeTag(stream);
+ }
+
+ private String nextRandomId() {
+ return new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
+ }
+
+ public String sendIqPacket(final IqPacket packet, final OnIqPacketReceived callback) {
+ packet.setFrom(account.getJid());
+ return this.sendUnmodifiedIqPacket(packet, callback);
+ }
+
+ public synchronized String sendUnmodifiedIqPacket(final IqPacket packet, final OnIqPacketReceived callback) {
+ if (packet.getId() == null) {
+ final String id = nextRandomId();
+ packet.setAttribute("id", id);
+ }
+ if (callback != null) {
+ synchronized (this.packetCallbacks) {
+ packetCallbacks.put(packet.getId(), new Pair<>(packet, callback));
+ }
+ }
+ this.sendPacket(packet);
+ return packet.getId();
+ }
+
+ public void sendMessagePacket(final MessagePacket packet) {
+ this.sendPacket(packet);
+ }
+
+ public void sendPresencePacket(final PresencePacket packet) {
+ this.sendPacket(packet);
+ }
+
+ private synchronized void sendPacket(final AbstractStanza packet) {
+ if (stanzasSent == Integer.MAX_VALUE) {
+ resetStreamId();
+ disconnect(true);
+ return;
+ }
+ synchronized (this.mStanzaQueue) {
+ tagWriter.writeStanzaAsync(packet);
+ if (packet instanceof AbstractAcknowledgeableStanza) {
+ AbstractAcknowledgeableStanza stanza = (AbstractAcknowledgeableStanza) packet;
+ ++stanzasSent;
+ this.mStanzaQueue.append(stanzasSent, stanza);
+ if (stanza instanceof MessagePacket && stanza.getId() != null && getFeatures().sm()) {
+ if (Config.EXTENDED_SM_LOGGING) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": requesting ack for message stanza #" + stanzasSent);
+ }
+ tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion));
+ }
+ }
+ }
+ }
+
+ public void sendPing() {
+ if (!r()) {
+ final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
+ iq.setFrom(account.getJid());
+ iq.addChild("ping", "urn:xmpp:ping");
+ this.sendIqPacket(iq, null);
+ }
+ this.lastPingSent = SystemClock.elapsedRealtime();
+ }
+
+ public void setOnMessagePacketReceivedListener(
+ final OnMessagePacketReceived listener) {
+ this.messageListener = listener;
+ }
+
+ public void setOnUnregisteredIqPacketReceivedListener(
+ final OnIqPacketReceived listener) {
+ this.unregisteredIqListener = listener;
+ }
+
+ public void setOnPresencePacketReceivedListener(
+ final OnPresencePacketReceived listener) {
+ this.presenceListener = listener;
+ }
+
+ public void setOnJinglePacketReceivedListener(
+ final OnJinglePacketReceived listener) {
+ this.jingleListener = listener;
+ }
+
+ public void setOnStatusChangedListener(final OnStatusChanged listener) {
+ this.statusListener = listener;
+ }
+
+ public void setOnBindListener(final OnBindListener listener) {
+ this.bindListener = listener;
+ }
+
+ public void setOnMessageAcknowledgeListener(final OnMessageAcknowledged listener) {
+ this.acknowledgedListener = listener;
+ }
+
+ public void addOnAdvancedStreamFeaturesAvailableListener(final OnAdvancedStreamFeaturesLoaded listener) {
+ if (!this.advancedStreamFeaturesLoadedListeners.contains(listener)) {
+ this.advancedStreamFeaturesLoadedListeners.add(listener);
+ }
+ }
+
+ public void waitForPush() {
+ if (tagWriter.isActive()) {
+ tagWriter.finish();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ while (!tagWriter.finished()) {
+ Thread.sleep(10);
+ }
+ socket.close();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": closed tcp without closing stream");
+ changeStatus(Account.State.OFFLINE);
+ } catch (IOException | InterruptedException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": error while closing socket for waitForPush()");
+ }
+ }
+ }).start();
+ } else {
+ forceCloseSocket();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": closed tcp without closing stream (no waiting)");
+ }
+ }
+
+ private void forceCloseSocket() {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": io exception " + e.getMessage() + " during force close");
+ }
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": socket was null during force close");
+ }
+ }
+
+ public void interrupt() {
+ Thread.currentThread().interrupt();
+ }
+
+ public void disconnect(final boolean force) {
+ interrupt();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force=" + Boolean.valueOf(force));
+ if (force) {
+ tagWriter.forceClose();
+ forceCloseSocket();
+ } else {
+ if (tagWriter.isActive()) {
+ tagWriter.finish();
+ try {
+ int i = 0;
+ boolean warned = false;
+ while (!tagWriter.finished() && socket.isConnected() && i <= 10) {
+ if (!warned) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": waiting for tag writer to finish");
+ warned = true;
+ }
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": sleep interrupted");
+ }
+ i++;
+ }
+ if (warned) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": tag writer has finished");
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": closing stream");
+ tagWriter.writeTag(Tag.end("stream:stream"));
+ } catch (final IOException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": io exception during disconnect (" + e.getMessage() + ")");
+ } finally {
+ forceCloseSocket();
+ }
+ }
+ }
+ }
+
+ public void resetStreamId() {
+ this.streamId = null;
+ }
+
+ private List<Entry<Jid, ServiceDiscoveryResult>> findDiscoItemsByFeature(final String feature) {
+ synchronized (this.disco) {
+ final List<Entry<Jid, ServiceDiscoveryResult>> items = new ArrayList<>();
+ for (final Entry<Jid, ServiceDiscoveryResult> cursor : this.disco.entrySet()) {
+ if (cursor.getValue().getFeatures().contains(feature)) {
+ items.add(cursor);
+ }
+ }
+ return items;
+ }
+ }
+
+ public Jid findDiscoItemByFeature(final String feature) {
+ final List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(feature);
+ if (items.size() >= 1) {
+ return items.get(0).getKey();
+ }
+ return null;
+ }
+
+ public boolean r() {
+ if (getFeatures().sm()) {
+ this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public String getMucServer() {
+ synchronized (this.disco) {
+ for (final Entry<Jid, ServiceDiscoveryResult> cursor : disco.entrySet()) {
+ final ServiceDiscoveryResult value = cursor.getValue();
+ if (value.getFeatures().contains("http://jabber.org/protocol/muc")
+ && !value.getFeatures().contains("jabber:iq:gateway")
+ && !value.hasIdentity("conference", "irc")) {
+ return cursor.getKey().toString();
+ }
+ }
+ }
+ return null;
+ }
+
+ public int getTimeToNextAttempt() {
+ final int interval = Math.min((int) (25 * Math.pow(1.3, attempt)), 300);
+ final int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000);
+ return interval - secondsSinceLast;
+ }
+
+ public int getAttempt() {
+ return this.attempt;
+ }
+
+ public Features getFeatures() {
+ return this.features;
+ }
+
+ public long getLastSessionEstablished() {
+ final long diff = SystemClock.elapsedRealtime() - this.lastSessionStarted;
+ return System.currentTimeMillis() - diff;
+ }
+
+ public long getLastConnect() {
+ return this.lastConnect;
+ }
+
+ public long getLastPingSent() {
+ return this.lastPingSent;
+ }
+
+ public long getLastDiscoStarted() {
+ return this.lastDiscoStarted;
+ }
+
+ public long getLastPacketReceived() {
+ return this.lastPacketReceived;
+ }
+
+ public void sendActive() {
+ this.sendPacket(new ActivePacket());
+ }
- }
+ public void sendInactive() {
+ this.sendPacket(new InactivePacket());
+ }
- private class PaymentRequiredException extends IOException {
+ public void resetAttemptCount() {
+ this.attempt = 0;
+ this.lastConnect = 0;
+ }
- }
+ public void setInteractive(boolean interactive) {
+ this.mInteractive = interactive;
+ }
- public enum Identity {
- FACEBOOK,
- SLACK,
- EJABBERD,
- PROSODY,
- NIMBUZZ,
- UNKNOWN
- }
+ public Identity getServerIdentity() {
+ return mServerIdentity;
+ }
- public class Features {
- XmppConnection connection;
- private boolean carbonsEnabled = false;
- private boolean encryptionEnabled = false;
- private boolean blockListRequested = false;
+ private class UnauthorizedException extends IOException {
- public Features(final XmppConnection connection) {
- this.connection = connection;
- }
+ }
- private boolean hasDiscoFeature(final Jid server, final String feature) {
- synchronized (XmppConnection.this.disco) {
- return connection.disco.containsKey(server) &&
- connection.disco.get(server).getFeatures().contains(feature);
- }
- }
+ private class SecurityException extends IOException {
- public boolean carbons() {
- return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
- }
+ }
- public boolean blocking() {
- return hasDiscoFeature(account.getServer(), Xmlns.BLOCKING);
- }
+ private class IncompatibleServerException extends IOException {
+
+ }
+
+ private class StreamErrorHostUnknown extends StreamError {
+
+ }
+
+ private class StreamErrorPolicyViolation extends StreamError {
+
+ }
+
+ private class StreamError extends IOException {
+
+ }
+
+ private class PaymentRequiredException extends IOException {
+
+ }
+
+ public enum Identity {
+ FACEBOOK,
+ SLACK,
+ EJABBERD,
+ PROSODY,
+ NIMBUZZ,
+ UNKNOWN
+ }
+
+ public class Features {
+ XmppConnection connection;
+ private boolean carbonsEnabled = false;
+ private boolean encryptionEnabled = false;
+ private boolean blockListRequested = false;
+
+ public Features(final XmppConnection connection) {
+ this.connection = connection;
+ }
+
+ private boolean hasDiscoFeature(final Jid server, final String feature) {
+ synchronized (XmppConnection.this.disco) {
+ return connection.disco.containsKey(server) &&
+ connection.disco.get(server).getFeatures().contains(feature);
+ }
+ }
+
+ public boolean carbons() {
+ return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
+ }
+
+ public boolean blocking() {
+ return hasDiscoFeature(account.getServer(), Xmlns.BLOCKING);
+ }
public boolean spamReporting() {
return hasDiscoFeature(account.getServer(), "urn:xmpp:reporting:reason:spam:0");
}
- public boolean register() {
- return hasDiscoFeature(account.getServer(), Xmlns.REGISTER);
- }
-
- public boolean sm() {
- return streamId != null
- || (connection.streamFeatures != null && connection.streamFeatures.hasChild("sm"));
- }
-
- public boolean csi() {
- return connection.streamFeatures != null && connection.streamFeatures.hasChild("csi", "urn:xmpp:csi:0");
- }
-
- public boolean pep() {
- synchronized (XmppConnection.this.disco) {
- ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
- return info != null && info.hasIdentity("pubsub", "pep");
- }
- }
-
- public boolean pepPersistent() {
- synchronized (XmppConnection.this.disco) {
- ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
- return info != null && info.getFeatures().contains("http://jabber.org/protocol/pubsub#persistent-items");
- }
- }
-
- public boolean mam() {
- return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:mam:0")
- || hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
- }
-
- public boolean push() {
- return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:push:0")
- || hasDiscoFeature(account.getServer(), "urn:xmpp:push:0");
- }
-
- public boolean rosterVersioning() {
- return connection.streamFeatures != null && connection.streamFeatures.hasChild("ver");
- }
-
- public void setBlockListRequested(boolean value) {
- this.blockListRequested = value;
- }
-
- public boolean httpUpload(long filesize) {
- if (Config.DISABLE_HTTP_UPLOAD) {
- return false;
- } else {
- List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(Xmlns.HTTP_UPLOAD);
- if (items.size() > 0) {
- try {
- long maxsize = Long.parseLong(items.get(0).getValue().getExtendedDiscoInformation(Xmlns.HTTP_UPLOAD, "max-file-size"));
- if(filesize <= maxsize) {
- return true;
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": http upload is not available for files with size "+filesize+" (max is "+maxsize+")");
- return false;
- }
- } catch (Exception e) {
- return true;
- }
- } else {
- return false;
- }
- }
- }
-
- public long getMaxHttpUploadSize() {
- List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(Xmlns.HTTP_UPLOAD);
- if (items.size() > 0) {
- try {
- return Long.parseLong(items.get(0).getValue().getExtendedDiscoInformation(Xmlns.HTTP_UPLOAD, "max-file-size"));
- } catch (Exception e) {
- return -1;
- }
- } else {
- return -1;
- }
- }
+ public boolean register() {
+ return hasDiscoFeature(account.getServer(), Xmlns.REGISTER);
+ }
+
+ public boolean sm() {
+ return streamId != null
+ || (connection.streamFeatures != null && connection.streamFeatures.hasChild("sm"));
+ }
+
+ public boolean csi() {
+ return connection.streamFeatures != null && connection.streamFeatures.hasChild("csi", "urn:xmpp:csi:0");
+ }
+
+ public boolean pep() {
+ synchronized (XmppConnection.this.disco) {
+ ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
+ return info != null && info.hasIdentity("pubsub", "pep");
+ }
+ }
+
+ public boolean pepPersistent() {
+ synchronized (XmppConnection.this.disco) {
+ ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
+ return info != null && info.getFeatures().contains("http://jabber.org/protocol/pubsub#persistent-items");
+ }
+ }
+
+ public boolean mam() {
+ return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:mam:0")
+ || hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
+ }
+
+ public boolean push() {
+ return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:push:0")
+ || hasDiscoFeature(account.getServer(), "urn:xmpp:push:0");
+ }
+
+ public boolean rosterVersioning() {
+ return connection.streamFeatures != null && connection.streamFeatures.hasChild("ver");
+ }
+
+ public void setBlockListRequested(boolean value) {
+ this.blockListRequested = value;
+ }
+
+ public boolean httpUpload(long filesize) {
+ if (Config.DISABLE_HTTP_UPLOAD) {
+ return false;
+ } else {
+ List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(Xmlns.HTTP_UPLOAD);
+ if (items.size() > 0) {
+ try {
+ long maxsize = Long.parseLong(items.get(0).getValue().getExtendedDiscoInformation(Xmlns.HTTP_UPLOAD, "max-file-size"));
+ if (filesize <= maxsize) {
+ return true;
+ } else {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": http upload is not available for files with size " + filesize + " (max is " + maxsize + ")");
+ return false;
+ }
+ } catch (Exception e) {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ public long getMaxHttpUploadSize() {
+ List<Entry<Jid, ServiceDiscoveryResult>> items = findDiscoItemsByFeature(Xmlns.HTTP_UPLOAD);
+ if (items.size() > 0) {
+ try {
+ return Long.parseLong(items.get(0).getValue().getExtendedDiscoInformation(Xmlns.HTTP_UPLOAD, "max-file-size"));
+ } catch (Exception e) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
public boolean stanzaIds() {
- return hasDiscoFeature(account.getJid().toBareJid(),Xmlns.STANZA_IDS);
+ return hasDiscoFeature(account.getJid().toBareJid(), Xmlns.STANZA_IDS);
}
- }
+ }
- private IqGenerator getIqGenerator() {
- return mXmppConnectionService.getIqGenerator();
- }
+ private IqGenerator getIqGenerator() {
+ return mXmppConnectionService.getIqGenerator();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/chatstate/ChatState.java b/src/main/java/de/pixart/messenger/xmpp/chatstate/ChatState.java
index c3117455c..169d0bb66 100644
--- a/src/main/java/de/pixart/messenger/xmpp/chatstate/ChatState.java
+++ b/src/main/java/de/pixart/messenger/xmpp/chatstate/ChatState.java
@@ -4,29 +4,29 @@ import de.pixart.messenger.xml.Element;
public enum ChatState {
- ACTIVE, INACTIVE, GONE, COMPOSING, PAUSED;
+ ACTIVE, INACTIVE, GONE, COMPOSING, PAUSED;
- public static ChatState parse(Element element) {
- final String NAMESPACE = "http://jabber.org/protocol/chatstates";
- if (element.hasChild("active",NAMESPACE)) {
- return ACTIVE;
- } else if (element.hasChild("inactive",NAMESPACE)) {
- return INACTIVE;
- } else if (element.hasChild("composing",NAMESPACE)) {
- return COMPOSING;
- } else if (element.hasChild("gone",NAMESPACE)) {
- return GONE;
- } else if (element.hasChild("paused",NAMESPACE)) {
- return PAUSED;
- } else {
- return null;
- }
- }
+ public static ChatState parse(Element element) {
+ final String NAMESPACE = "http://jabber.org/protocol/chatstates";
+ if (element.hasChild("active", NAMESPACE)) {
+ return ACTIVE;
+ } else if (element.hasChild("inactive", NAMESPACE)) {
+ return INACTIVE;
+ } else if (element.hasChild("composing", NAMESPACE)) {
+ return COMPOSING;
+ } else if (element.hasChild("gone", NAMESPACE)) {
+ return GONE;
+ } else if (element.hasChild("paused", NAMESPACE)) {
+ return PAUSED;
+ } else {
+ return null;
+ }
+ }
- public static Element toElement(ChatState state) {
- final String NAMESPACE = "http://jabber.org/protocol/chatstates";
- final Element element = new Element(state.toString().toLowerCase());
- element.setAttribute("xmlns",NAMESPACE);
- return element;
- }
+ public static Element toElement(ChatState state) {
+ final String NAMESPACE = "http://jabber.org/protocol/chatstates";
+ final Element element = new Element(state.toString().toLowerCase());
+ element.setAttribute("xmlns", NAMESPACE);
+ return element;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/forms/Data.java b/src/main/java/de/pixart/messenger/xmpp/forms/Data.java
index 8ae70f9c7..f92e468e5 100644
--- a/src/main/java/de/pixart/messenger/xmpp/forms/Data.java
+++ b/src/main/java/de/pixart/messenger/xmpp/forms/Data.java
@@ -9,91 +9,91 @@ import de.pixart.messenger.xml.Element;
public class Data extends Element {
- public static final String FORM_TYPE = "FORM_TYPE";
-
- public Data() {
- super("x");
- this.setAttribute("xmlns","jabber:x:data");
- }
-
- public List<Field> getFields() {
- ArrayList<Field> fields = new ArrayList<Field>();
- for(Element child : getChildren()) {
- if (child.getName().equals("field")
- && !FORM_TYPE.equals(child.getAttribute("var"))) {
- fields.add(Field.parse(child));
- }
- }
- return fields;
- }
-
- public Field getFieldByName(String needle) {
- for(Element child : getChildren()) {
- if (child.getName().equals("field")
- && needle.equals(child.getAttribute("var"))) {
- return Field.parse(child);
- }
- }
- return null;
- }
-
- public void put(String name, String value) {
- Field field = getFieldByName(name);
- if (field == null) {
- field = new Field(name);
- this.addChild(field);
- }
- field.setValue(value);
- }
-
- public void put(String name, Collection<String> values) {
- Field field = getFieldByName(name);
- if (field == null) {
- field = new Field(name);
- this.addChild(field);
- }
- field.setValues(values);
- }
-
- public void submit() {
- this.setAttribute("type","submit");
- removeUnnecessaryChildren();
- for(Field field : getFields()) {
- field.removeNonValueChildren();
- }
- }
-
- private void removeUnnecessaryChildren() {
- for(Iterator<Element> iterator = this.children.iterator(); iterator.hasNext();) {
- Element element = iterator.next();
- if (!element.getName().equals("field") && !element.getName().equals("title")) {
- iterator.remove();
- }
- }
- }
-
- public static Data parse(Element element) {
- Data data = new Data();
- data.setAttributes(element.getAttributes());
- data.setChildren(element.getChildren());
- return data;
- }
-
- public void setFormType(String formType) {
- this.put(FORM_TYPE, formType);
- }
-
- public String getFormType() {
- String type = getValue(FORM_TYPE);
- return type == null ? "" : type;
- }
-
- public String getValue(String name) {
- Field field = this.getFieldByName(name);
- return field == null ? null : field.getValue();
- }
-
- public String getTitle() {
- return findChildContent("title");
- }
+ public static final String FORM_TYPE = "FORM_TYPE";
+
+ public Data() {
+ super("x");
+ this.setAttribute("xmlns", "jabber:x:data");
+ }
+
+ public List<Field> getFields() {
+ ArrayList<Field> fields = new ArrayList<Field>();
+ for (Element child : getChildren()) {
+ if (child.getName().equals("field")
+ && !FORM_TYPE.equals(child.getAttribute("var"))) {
+ fields.add(Field.parse(child));
+ }
+ }
+ return fields;
+ }
+
+ public Field getFieldByName(String needle) {
+ for (Element child : getChildren()) {
+ if (child.getName().equals("field")
+ && needle.equals(child.getAttribute("var"))) {
+ return Field.parse(child);
+ }
+ }
+ return null;
+ }
+
+ public void put(String name, String value) {
+ Field field = getFieldByName(name);
+ if (field == null) {
+ field = new Field(name);
+ this.addChild(field);
+ }
+ field.setValue(value);
+ }
+
+ public void put(String name, Collection<String> values) {
+ Field field = getFieldByName(name);
+ if (field == null) {
+ field = new Field(name);
+ this.addChild(field);
+ }
+ field.setValues(values);
+ }
+
+ public void submit() {
+ this.setAttribute("type", "submit");
+ removeUnnecessaryChildren();
+ for (Field field : getFields()) {
+ field.removeNonValueChildren();
+ }
+ }
+
+ private void removeUnnecessaryChildren() {
+ for (Iterator<Element> iterator = this.children.iterator(); iterator.hasNext(); ) {
+ Element element = iterator.next();
+ if (!element.getName().equals("field") && !element.getName().equals("title")) {
+ iterator.remove();
+ }
+ }
+ }
+
+ public static Data parse(Element element) {
+ Data data = new Data();
+ data.setAttributes(element.getAttributes());
+ data.setChildren(element.getChildren());
+ return data;
+ }
+
+ public void setFormType(String formType) {
+ this.put(FORM_TYPE, formType);
+ }
+
+ public String getFormType() {
+ String type = getValue(FORM_TYPE);
+ return type == null ? "" : type;
+ }
+
+ public String getValue(String name) {
+ Field field = this.getFieldByName(name);
+ return field == null ? null : field.getValue();
+ }
+
+ public String getTitle() {
+ return findChildContent("title");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/forms/Field.java b/src/main/java/de/pixart/messenger/xmpp/forms/Field.java
index 4cf5fc6b7..a05f9538e 100644
--- a/src/main/java/de/pixart/messenger/xmpp/forms/Field.java
+++ b/src/main/java/de/pixart/messenger/xmpp/forms/Field.java
@@ -9,73 +9,73 @@ import de.pixart.messenger.xml.Element;
public class Field extends Element {
- public Field(String name) {
- super("field");
- this.setAttribute("var",name);
- }
+ public Field(String name) {
+ super("field");
+ this.setAttribute("var", name);
+ }
- private Field() {
- super("field");
- }
+ private Field() {
+ super("field");
+ }
- public String getFieldName() {
- return this.getAttribute("var");
- }
+ public String getFieldName() {
+ return this.getAttribute("var");
+ }
- public void setValue(String value) {
- this.children.clear();
- this.addChild("value").setContent(value);
- }
+ public void setValue(String value) {
+ this.children.clear();
+ this.addChild("value").setContent(value);
+ }
- public void setValues(Collection<String> values) {
- this.children.clear();
- for(String value : values) {
- this.addChild("value").setContent(value);
- }
- }
+ public void setValues(Collection<String> values) {
+ this.children.clear();
+ for (String value : values) {
+ this.addChild("value").setContent(value);
+ }
+ }
- public void removeNonValueChildren() {
- for(Iterator<Element> iterator = this.children.iterator(); iterator.hasNext();) {
- Element element = iterator.next();
- if (!element.getName().equals("value")) {
- iterator.remove();
- }
- }
- }
+ public void removeNonValueChildren() {
+ for (Iterator<Element> iterator = this.children.iterator(); iterator.hasNext(); ) {
+ Element element = iterator.next();
+ if (!element.getName().equals("value")) {
+ iterator.remove();
+ }
+ }
+ }
- public static Field parse(Element element) {
- Field field = new Field();
- field.setAttributes(element.getAttributes());
- field.setChildren(element.getChildren());
- return field;
- }
+ public static Field parse(Element element) {
+ Field field = new Field();
+ field.setAttributes(element.getAttributes());
+ field.setChildren(element.getChildren());
+ return field;
+ }
- public String getValue() {
- return findChildContent("value");
- }
+ public String getValue() {
+ return findChildContent("value");
+ }
- public List<String> getValues() {
- List<String> values = new ArrayList<>();
- for(Element child : getChildren()) {
- if ("value".equals(child.getName())) {
- String content = child.getContent();
- if (content != null) {
- values.add(content);
- }
- }
- }
- return values;
- }
+ public List<String> getValues() {
+ List<String> values = new ArrayList<>();
+ for (Element child : getChildren()) {
+ if ("value".equals(child.getName())) {
+ String content = child.getContent();
+ if (content != null) {
+ values.add(content);
+ }
+ }
+ }
+ return values;
+ }
- public String getLabel() {
- return getAttribute("label");
- }
+ public String getLabel() {
+ return getAttribute("label");
+ }
- public String getType() {
- return getAttribute("type");
- }
+ public String getType() {
+ return getAttribute("type");
+ }
- public boolean isRequired() {
- return hasChild("required");
- }
+ public boolean isRequired() {
+ return hasChild("required");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jid/InvalidJidException.java b/src/main/java/de/pixart/messenger/xmpp/jid/InvalidJidException.java
index 9f091063a..fa1b396d4 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jid/InvalidJidException.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jid/InvalidJidException.java
@@ -8,7 +8,7 @@ public class InvalidJidException extends Exception {
public final static String INVALID_PART_LENGTH = "JID part must be between 0 and 1023 characters";
public final static String INVALID_CHARACTER = "JID contains an invalid character";
public final static String STRINGPREP_FAIL = "The STRINGPREP operation has failed for the given JID";
- public final static String IS_NULL = "JID can not be NULL";
+ public final static String IS_NULL = "JID can not be NULL";
/**
* Constructs a new {@code Exception} that includes the current stack trace.
@@ -31,7 +31,7 @@ public class InvalidJidException extends Exception {
* specified detail message and the specified cause.
*
* @param detailMessage the detail message for this exception.
- * @param throwable the cause of this exception.
+ * @param throwable the cause of this exception.
*/
public InvalidJidException(final String detailMessage, final Throwable throwable) {
super(detailMessage, throwable);
diff --git a/src/main/java/de/pixart/messenger/xmpp/jid/Jid.java b/src/main/java/de/pixart/messenger/xmpp/jid/Jid.java
index c596367ca..123e6ee8f 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jid/Jid.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jid/Jid.java
@@ -15,225 +15,225 @@ import gnu.inet.encoding.StringprepException;
*/
public final class Jid {
- private static LruCache<String,Jid> cache = new LruCache<>(1024);
+ private static LruCache<String, Jid> cache = new LruCache<>(1024);
- private final String localpart;
- private final String domainpart;
- private final String resourcepart;
+ private final String localpart;
+ private final String domainpart;
+ private final String resourcepart;
// It's much more efficient to store the full JID as well as the parts instead of figuring them
// all out every time (since some characters are displayed but aren't used for comparisons).
private final String displayjid;
- public String getLocalpart() {
- return localpart;
- }
-
- public String getDomainpart() {
- return IDN.toUnicode(domainpart);
- }
-
- public String getResourcepart() {
- return resourcepart;
- }
-
- public static Jid fromSessionID(final SessionID id) throws InvalidJidException{
- if (id.getUserID().isEmpty()) {
- return Jid.fromString(id.getAccountID());
- } else {
- return Jid.fromString(id.getAccountID()+"/"+id.getUserID());
- }
- }
-
- public static Jid fromString(final String jid) throws InvalidJidException {
- return Jid.fromString(jid, false);
- }
-
- public static Jid fromString(final String jid, final boolean safe) throws InvalidJidException {
- return new Jid(jid, safe);
- }
-
- public static Jid fromParts(final String localpart,
- final String domainpart,
- final String resourcepart) throws InvalidJidException {
- String out;
- if (localpart == null || localpart.isEmpty()) {
- out = domainpart;
- } else {
- out = localpart + "@" + domainpart;
- }
- if (resourcepart != null && !resourcepart.isEmpty()) {
- out = out + "/" + resourcepart;
- }
- return new Jid(out, false);
- }
-
- private Jid(final String jid, final boolean safe) throws InvalidJidException {
- if (jid == null) throw new InvalidJidException(InvalidJidException.IS_NULL);
-
- Jid fromCache = Jid.cache.get(jid);
- if (fromCache != null) {
+ public String getLocalpart() {
+ return localpart;
+ }
+
+ public String getDomainpart() {
+ return IDN.toUnicode(domainpart);
+ }
+
+ public String getResourcepart() {
+ return resourcepart;
+ }
+
+ public static Jid fromSessionID(final SessionID id) throws InvalidJidException {
+ if (id.getUserID().isEmpty()) {
+ return Jid.fromString(id.getAccountID());
+ } else {
+ return Jid.fromString(id.getAccountID() + "/" + id.getUserID());
+ }
+ }
+
+ public static Jid fromString(final String jid) throws InvalidJidException {
+ return Jid.fromString(jid, false);
+ }
+
+ public static Jid fromString(final String jid, final boolean safe) throws InvalidJidException {
+ return new Jid(jid, safe);
+ }
+
+ public static Jid fromParts(final String localpart,
+ final String domainpart,
+ final String resourcepart) throws InvalidJidException {
+ String out;
+ if (localpart == null || localpart.isEmpty()) {
+ out = domainpart;
+ } else {
+ out = localpart + "@" + domainpart;
+ }
+ if (resourcepart != null && !resourcepart.isEmpty()) {
+ out = out + "/" + resourcepart;
+ }
+ return new Jid(out, false);
+ }
+
+ private Jid(final String jid, final boolean safe) throws InvalidJidException {
+ if (jid == null) throw new InvalidJidException(InvalidJidException.IS_NULL);
+
+ Jid fromCache = Jid.cache.get(jid);
+ if (fromCache != null) {
displayjid = fromCache.displayjid;
- localpart = fromCache.localpart;
- domainpart = fromCache.domainpart;
- resourcepart = fromCache.resourcepart;
- return;
- }
-
- // Hackish Android way to count the number of chars in a string... should work everywhere.
- final int atCount = jid.length() - jid.replace("@", "").length();
- final int slashCount = jid.length() - jid.replace("/", "").length();
-
- // Throw an error if there's anything obvious wrong with the JID...
- if (jid.isEmpty() || jid.length() > 3071) {
- throw new InvalidJidException(InvalidJidException.INVALID_LENGTH);
- }
-
- // Go ahead and check if the localpart or resourcepart is empty.
- if (jid.startsWith("@") || (jid.endsWith("@") && slashCount == 0) || jid.startsWith("/") || (jid.endsWith("/") && slashCount < 2)) {
- throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER);
- }
+ localpart = fromCache.localpart;
+ domainpart = fromCache.domainpart;
+ resourcepart = fromCache.resourcepart;
+ return;
+ }
+
+ // Hackish Android way to count the number of chars in a string... should work everywhere.
+ final int atCount = jid.length() - jid.replace("@", "").length();
+ final int slashCount = jid.length() - jid.replace("/", "").length();
+
+ // Throw an error if there's anything obvious wrong with the JID...
+ if (jid.isEmpty() || jid.length() > 3071) {
+ throw new InvalidJidException(InvalidJidException.INVALID_LENGTH);
+ }
+
+ // Go ahead and check if the localpart or resourcepart is empty.
+ if (jid.startsWith("@") || (jid.endsWith("@") && slashCount == 0) || jid.startsWith("/") || (jid.endsWith("/") && slashCount < 2)) {
+ throw new InvalidJidException(InvalidJidException.INVALID_CHARACTER);
+ }
String finaljid;
- final int domainpartStart;
- final int atLoc = jid.indexOf("@");
- final int slashLoc = jid.indexOf("/");
- // If there is no "@" in the JID (eg. "example.net" or "example.net/resource")
- // or there are one or more "@" signs but they're all in the resourcepart (eg. "example.net/@/rp@"):
- if (atCount == 0 || (atCount > 0 && slashLoc != -1 && atLoc > slashLoc)) {
- localpart = "";
+ final int domainpartStart;
+ final int atLoc = jid.indexOf("@");
+ final int slashLoc = jid.indexOf("/");
+ // If there is no "@" in the JID (eg. "example.net" or "example.net/resource")
+ // or there are one or more "@" signs but they're all in the resourcepart (eg. "example.net/@/rp@"):
+ if (atCount == 0 || (atCount > 0 && slashLoc != -1 && atLoc > slashLoc)) {
+ localpart = "";
finaljid = "";
- domainpartStart = 0;
- } else {
- final String lp = jid.substring(0, atLoc);
- try {
- localpart = Config.DISABLE_STRING_PREP || safe ? lp : Stringprep.nodeprep(lp);
- } catch (final StringprepException e) {
- throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
- }
- if (localpart.isEmpty() || localpart.length() > 1023) {
- throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
- }
- domainpartStart = atLoc + 1;
+ domainpartStart = 0;
+ } else {
+ final String lp = jid.substring(0, atLoc);
+ try {
+ localpart = Config.DISABLE_STRING_PREP || safe ? lp : Stringprep.nodeprep(lp);
+ } catch (final StringprepException e) {
+ throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
+ }
+ if (localpart.isEmpty() || localpart.length() > 1023) {
+ throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
+ }
+ domainpartStart = atLoc + 1;
finaljid = lp + "@";
- }
-
- final String dp;
- if (slashCount > 0) {
- final String rp = jid.substring(slashLoc + 1, jid.length());
- try {
- resourcepart = Config.DISABLE_STRING_PREP || safe ? rp : Stringprep.resourceprep(rp);
- } catch (final StringprepException e) {
- throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
- }
- if (resourcepart.isEmpty() || resourcepart.length() > 1023) {
- throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
- }
- try {
- dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, slashLoc)), IDN.USE_STD3_ASCII_RULES);
- } catch (final StringprepException e) {
- throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
- }
+ }
+
+ final String dp;
+ if (slashCount > 0) {
+ final String rp = jid.substring(slashLoc + 1, jid.length());
+ try {
+ resourcepart = Config.DISABLE_STRING_PREP || safe ? rp : Stringprep.resourceprep(rp);
+ } catch (final StringprepException e) {
+ throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
+ }
+ if (resourcepart.isEmpty() || resourcepart.length() > 1023) {
+ throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
+ }
+ try {
+ dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, slashLoc)), IDN.USE_STD3_ASCII_RULES);
+ } catch (final StringprepException e) {
+ throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
+ }
finaljid = finaljid + dp + "/" + rp;
- } else {
- resourcepart = "";
- try{
- dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, jid.length())), IDN.USE_STD3_ASCII_RULES);
- } catch (final StringprepException e) {
- throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
- }
+ } else {
+ resourcepart = "";
+ try {
+ dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, jid.length())), IDN.USE_STD3_ASCII_RULES);
+ } catch (final StringprepException e) {
+ throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
+ }
finaljid = finaljid + dp;
}
- // Remove trailing "." before storing the domain part.
- if (dp.endsWith(".")) {
- try {
- domainpart = IDN.toASCII(dp.substring(0, dp.length() - 1), IDN.USE_STD3_ASCII_RULES);
- } catch (final IllegalArgumentException e) {
- throw new InvalidJidException(e);
- }
- } else {
- try {
- domainpart = IDN.toASCII(dp, IDN.USE_STD3_ASCII_RULES);
- } catch (final IllegalArgumentException e) {
- throw new InvalidJidException(e);
- }
- }
-
- // TODO: Find a proper domain validation library; validate individual parts, separators, etc.
- if (domainpart.isEmpty() || domainpart.length() > 1023) {
- throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
- }
-
- Jid.cache.put(jid, this);
+ // Remove trailing "." before storing the domain part.
+ if (dp.endsWith(".")) {
+ try {
+ domainpart = IDN.toASCII(dp.substring(0, dp.length() - 1), IDN.USE_STD3_ASCII_RULES);
+ } catch (final IllegalArgumentException e) {
+ throw new InvalidJidException(e);
+ }
+ } else {
+ try {
+ domainpart = IDN.toASCII(dp, IDN.USE_STD3_ASCII_RULES);
+ } catch (final IllegalArgumentException e) {
+ throw new InvalidJidException(e);
+ }
+ }
+
+ // TODO: Find a proper domain validation library; validate individual parts, separators, etc.
+ if (domainpart.isEmpty() || domainpart.length() > 1023) {
+ throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
+ }
+
+ Jid.cache.put(jid, this);
this.displayjid = finaljid;
- }
-
- public Jid toBareJid() {
- try {
- return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, "");
- } catch (final InvalidJidException e) {
- // This should never happen.
- throw new AssertionError("Jid " + this.toString() + " invalid");
- }
- }
-
- public Jid toDomainJid() {
- try {
- return resourcepart.isEmpty() && localpart.isEmpty() ? this : fromString(getDomainpart());
- } catch (final InvalidJidException e) {
- // This should never happen.
- throw new AssertionError("Jid " + this.toString() + " invalid");
- }
- }
-
- @Override
- public String toString() {
+ }
+
+ public Jid toBareJid() {
+ try {
+ return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, "");
+ } catch (final InvalidJidException e) {
+ // This should never happen.
+ throw new AssertionError("Jid " + this.toString() + " invalid");
+ }
+ }
+
+ public Jid toDomainJid() {
+ try {
+ return resourcepart.isEmpty() && localpart.isEmpty() ? this : fromString(getDomainpart());
+ } catch (final InvalidJidException e) {
+ // This should never happen.
+ throw new AssertionError("Jid " + this.toString() + " invalid");
+ }
+ }
+
+ @Override
+ public String toString() {
return displayjid;
}
public String toPreppedString() {
- String out;
- if (hasLocalpart()) {
- out = localpart + '@' + domainpart;
- } else {
- out = domainpart;
- }
- if (!resourcepart.isEmpty()) {
- out += '/'+resourcepart;
- }
- return out;
- }
-
- @Override
- public boolean equals(final Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- final Jid jid = (Jid) o;
-
- return jid.hashCode() == this.hashCode();
- }
-
- @Override
- public int hashCode() {
- int result = localpart.hashCode();
- result = 31 * result + domainpart.hashCode();
- result = 31 * result + resourcepart.hashCode();
- return result;
- }
-
- public boolean hasLocalpart() {
- return !localpart.isEmpty();
- }
-
- public boolean isBareJid() {
- return this.resourcepart.isEmpty();
- }
-
- public boolean isDomainJid() {
- return !this.hasLocalpart();
- }
+ String out;
+ if (hasLocalpart()) {
+ out = localpart + '@' + domainpart;
+ } else {
+ out = domainpart;
+ }
+ if (!resourcepart.isEmpty()) {
+ out += '/' + resourcepart;
+ }
+ return out;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final Jid jid = (Jid) o;
+
+ return jid.hashCode() == this.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = localpart.hashCode();
+ result = 31 * result + domainpart.hashCode();
+ result = 31 * result + resourcepart.hashCode();
+ return result;
+ }
+
+ public boolean hasLocalpart() {
+ return !localpart.isEmpty();
+ }
+
+ public boolean isBareJid() {
+ return this.resourcepart.isEmpty();
+ }
+
+ public boolean isDomainJid() {
+ return !this.hasLocalpart();
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java
index 166fa6c69..6f87db473 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java
@@ -8,57 +8,57 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class JingleCandidate {
- public static int TYPE_UNKNOWN;
- public static int TYPE_DIRECT = 0;
- public static int TYPE_PROXY = 1;
-
- private boolean ours;
- private boolean usedByCounterpart = false;
- private String cid;
- private String host;
- private int port;
- private int type;
- private Jid jid;
- private int priority;
-
- public JingleCandidate(String cid, boolean ours) {
- this.ours = ours;
- this.cid = cid;
- }
-
- public String getCid() {
- return cid;
- }
-
- public void setHost(String host) {
- this.host = host;
- }
-
- public String getHost() {
- return this.host;
- }
-
- public void setJid(final Jid jid) {
- this.jid = jid;
- }
-
- public Jid getJid() {
- return this.jid;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public int getPort() {
- return this.port;
- }
-
- public void setType(int type) {
- this.type = type;
- }
-
- public void setType(String type) {
+ public static int TYPE_UNKNOWN;
+ public static int TYPE_DIRECT = 0;
+ public static int TYPE_PROXY = 1;
+
+ private boolean ours;
+ private boolean usedByCounterpart = false;
+ private String cid;
+ private String host;
+ private int port;
+ private int type;
+ private Jid jid;
+ private int priority;
+
+ public JingleCandidate(String cid, boolean ours) {
+ this.ours = ours;
+ this.cid = cid;
+ }
+
+ public String getCid() {
+ return cid;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public String getHost() {
+ return this.host;
+ }
+
+ public void setJid(final Jid jid) {
+ this.jid = jid;
+ }
+
+ public Jid getJid() {
+ return this.jid;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public int getPort() {
+ return this.port;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public void setType(String type) {
switch (type) {
case "proxy":
this.type = TYPE_PROXY;
@@ -70,78 +70,78 @@ public class JingleCandidate {
this.type = TYPE_UNKNOWN;
break;
}
- }
-
- public void setPriority(int i) {
- this.priority = i;
- }
-
- public int getPriority() {
- return this.priority;
- }
-
- public boolean equals(JingleCandidate other) {
- return this.getCid().equals(other.getCid());
- }
-
- public boolean equalValues(JingleCandidate other) {
- return other != null && other.getHost().equals(this.getHost()) && (other.getPort() == this.getPort());
- }
-
- public boolean isOurs() {
- return ours;
- }
-
- public int getType() {
- return this.type;
- }
-
- public static List<JingleCandidate> parse(List<Element> canditates) {
- List<JingleCandidate> parsedCandidates = new ArrayList<>();
- for (Element c : canditates) {
- parsedCandidates.add(JingleCandidate.parse(c));
- }
- return parsedCandidates;
- }
-
- public static JingleCandidate parse(Element candidate) {
- JingleCandidate parsedCandidate = new JingleCandidate(
- candidate.getAttribute("cid"), false);
- parsedCandidate.setHost(candidate.getAttribute("host"));
- parsedCandidate.setJid(candidate.getAttributeAsJid("jid"));
- parsedCandidate.setType(candidate.getAttribute("type"));
- parsedCandidate.setPriority(Integer.parseInt(candidate
- .getAttribute("priority")));
- parsedCandidate
- .setPort(Integer.parseInt(candidate.getAttribute("port")));
- return parsedCandidate;
- }
-
- public Element toElement() {
- Element element = new Element("candidate");
- element.setAttribute("cid", this.getCid());
- element.setAttribute("host", this.getHost());
- element.setAttribute("port", Integer.toString(this.getPort()));
- element.setAttribute("jid", this.getJid().toString());
- element.setAttribute("priority", Integer.toString(this.getPriority()));
- if (this.getType() == TYPE_DIRECT) {
- element.setAttribute("type", "direct");
- } else if (this.getType() == TYPE_PROXY) {
- element.setAttribute("type", "proxy");
- }
- return element;
- }
-
- public void flagAsUsedByCounterpart() {
- this.usedByCounterpart = true;
- }
-
- public boolean isUsedByCounterpart() {
- return this.usedByCounterpart;
- }
-
- public String toString() {
- return this.getHost() + ":" + this.getPort() + " (prio="
- + this.getPriority() + ")";
- }
+ }
+
+ public void setPriority(int i) {
+ this.priority = i;
+ }
+
+ public int getPriority() {
+ return this.priority;
+ }
+
+ public boolean equals(JingleCandidate other) {
+ return this.getCid().equals(other.getCid());
+ }
+
+ public boolean equalValues(JingleCandidate other) {
+ return other != null && other.getHost().equals(this.getHost()) && (other.getPort() == this.getPort());
+ }
+
+ public boolean isOurs() {
+ return ours;
+ }
+
+ public int getType() {
+ return this.type;
+ }
+
+ public static List<JingleCandidate> parse(List<Element> canditates) {
+ List<JingleCandidate> parsedCandidates = new ArrayList<>();
+ for (Element c : canditates) {
+ parsedCandidates.add(JingleCandidate.parse(c));
+ }
+ return parsedCandidates;
+ }
+
+ public static JingleCandidate parse(Element candidate) {
+ JingleCandidate parsedCandidate = new JingleCandidate(
+ candidate.getAttribute("cid"), false);
+ parsedCandidate.setHost(candidate.getAttribute("host"));
+ parsedCandidate.setJid(candidate.getAttributeAsJid("jid"));
+ parsedCandidate.setType(candidate.getAttribute("type"));
+ parsedCandidate.setPriority(Integer.parseInt(candidate
+ .getAttribute("priority")));
+ parsedCandidate
+ .setPort(Integer.parseInt(candidate.getAttribute("port")));
+ return parsedCandidate;
+ }
+
+ public Element toElement() {
+ Element element = new Element("candidate");
+ element.setAttribute("cid", this.getCid());
+ element.setAttribute("host", this.getHost());
+ element.setAttribute("port", Integer.toString(this.getPort()));
+ element.setAttribute("jid", this.getJid().toString());
+ element.setAttribute("priority", Integer.toString(this.getPriority()));
+ if (this.getType() == TYPE_DIRECT) {
+ element.setAttribute("type", "direct");
+ } else if (this.getType() == TYPE_PROXY) {
+ element.setAttribute("type", "proxy");
+ }
+ return element;
+ }
+
+ public void flagAsUsedByCounterpart() {
+ this.usedByCounterpart = true;
+ }
+
+ public boolean isUsedByCounterpart() {
+ return this.usedByCounterpart;
+ }
+
+ public String toString() {
+ return this.getHost() + ":" + this.getPort() + " (prio="
+ + this.getPriority() + ")";
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java
index 5417b8deb..29b9db382 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java
@@ -40,1048 +40,1048 @@ import de.pixart.messenger.xmpp.jingle.stanzas.Reason;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class JingleConnection implements Transferable {
- private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+ private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
- private JingleConnectionManager mJingleConnectionManager;
- private XmppConnectionService mXmppConnectionService;
+ private JingleConnectionManager mJingleConnectionManager;
+ private XmppConnectionService mXmppConnectionService;
- protected static final int JINGLE_STATUS_INITIATED = 0;
- protected static final int JINGLE_STATUS_ACCEPTED = 1;
- protected static final int JINGLE_STATUS_FINISHED = 4;
- protected static final int JINGLE_STATUS_TRANSMITTING = 5;
- protected static final int JINGLE_STATUS_FAILED = 99;
+ protected static final int JINGLE_STATUS_INITIATED = 0;
+ protected static final int JINGLE_STATUS_ACCEPTED = 1;
+ protected static final int JINGLE_STATUS_FINISHED = 4;
+ protected static final int JINGLE_STATUS_TRANSMITTING = 5;
+ protected static final int JINGLE_STATUS_FAILED = 99;
- private Content.Version ftVersion = Content.Version.FT_3;
+ private Content.Version ftVersion = Content.Version.FT_3;
- private int ibbBlockSize = 8192;
+ private int ibbBlockSize = 8192;
- private int mJingleStatus = -1;
- private int mStatus = Transferable.STATUS_UNKNOWN;
- private Message message;
- private String sessionId;
- private Account account;
- private Jid initiator;
- private Jid responder;
- private List<JingleCandidate> candidates = new ArrayList<>();
- private ConcurrentHashMap<String, JingleSocks5Transport> connections = new ConcurrentHashMap<>();
+ private int mJingleStatus = -1;
+ private int mStatus = Transferable.STATUS_UNKNOWN;
+ private Message message;
+ private String sessionId;
+ private Account account;
+ private Jid initiator;
+ private Jid responder;
+ private List<JingleCandidate> candidates = new ArrayList<>();
+ private ConcurrentHashMap<String, JingleSocks5Transport> connections = new ConcurrentHashMap<>();
- private String transportId;
- private Element fileOffer;
- private DownloadableFile file = null;
+ private String transportId;
+ private Element fileOffer;
+ private DownloadableFile file = null;
- private String contentName;
- private String contentCreator;
+ private String contentName;
+ private String contentCreator;
- private int mProgress = 0;
+ private int mProgress = 0;
- private boolean receivedCandidate = false;
- private boolean sentCandidate = false;
+ private boolean receivedCandidate = false;
+ private boolean sentCandidate = false;
- private boolean acceptedAutomatically = false;
+ private boolean acceptedAutomatically = false;
- private XmppAxolotlMessage mXmppAxolotlMessage;
+ private XmppAxolotlMessage mXmppAxolotlMessage;
- private JingleTransport transport = null;
+ private JingleTransport transport = null;
- private OutputStream mFileOutputStream;
- private InputStream mFileInputStream;
+ private OutputStream mFileOutputStream;
+ private InputStream mFileInputStream;
- private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
+ private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() != IqPacket.TYPE.RESULT) {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() != IqPacket.TYPE.RESULT) {
fail(IqParser.extractErrorMessage(packet));
- }
- }
- };
-
- final OnFileTransmissionStatusChanged onFileTransmissionSatusChanged = new OnFileTransmissionStatusChanged() {
-
- @Override
- public void onFileTransmitted(DownloadableFile file) {
- if (responder.equals(account.getJid())) {
- sendSuccess();
- mXmppConnectionService.getFileBackend().updateFileParams(message);
- mXmppConnectionService.databaseBackend.createMessage(message);
- mXmppConnectionService.markMessage(message,Message.STATUS_RECEIVED);
- if (acceptedAutomatically) {
- message.markUnread();
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- account.getPgpDecryptionService().decrypt(message, true);
- } else {
- JingleConnection.this.mXmppConnectionService.getNotificationService().push(message);
- }
- }
- } else {
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- account.getPgpDecryptionService().decrypt(message, false);
- }
- if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- file.delete();
- }
- }
- Log.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+file.getSha1Sum()+")");
- if (message.getEncryption() != Message.ENCRYPTION_PGP) {
- mXmppConnectionService.getFileBackend().updateMediaScanner(file);
- }
- }
-
- @Override
- public void onFileTransferAborted() {
- JingleConnection.this.sendCancel();
- JingleConnection.this.fail();
- }
- };
-
- public InputStream getFileInputStream() {
- return this.mFileInputStream;
- }
-
- public OutputStream getFileOutputStream() {
- return this.mFileOutputStream;
- }
-
- private OnProxyActivated onProxyActivated = new OnProxyActivated() {
-
- @Override
- public void success() {
- if (initiator.equals(account.getJid())) {
- Log.d(Config.LOGTAG, "we were initiating. sending file");
- transport.send(file, onFileTransmissionSatusChanged);
- } else {
- transport.receive(file, onFileTransmissionSatusChanged);
- Log.d(Config.LOGTAG, "we were responding. receiving file");
- }
- }
-
- @Override
- public void failed() {
- Log.d(Config.LOGTAG, "proxy activation failed");
- }
- };
-
- public JingleConnection(JingleConnectionManager mJingleConnectionManager) {
- this.mJingleConnectionManager = mJingleConnectionManager;
- this.mXmppConnectionService = mJingleConnectionManager
- .getXmppConnectionService();
- }
-
- public String getSessionId() {
- return this.sessionId;
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public Jid getCounterPart() {
- return this.message.getCounterpart();
- }
-
- public void deliverPacket(JinglePacket packet) {
- boolean returnResult = true;
- if (packet.isAction("session-terminate")) {
- Reason reason = packet.getReason();
- if (reason != null) {
- if (reason.hasChild("cancel")) {
- this.fail();
- } else if (reason.hasChild("success")) {
- this.receiveSuccess();
- } else {
- this.fail();
- }
- } else {
- this.fail();
- }
- } else if (packet.isAction("session-accept")) {
- returnResult = receiveAccept(packet);
- } else if (packet.isAction("transport-info")) {
- returnResult = receiveTransportInfo(packet);
- } else if (packet.isAction("transport-replace")) {
- if (packet.getJingleContent().hasIbbTransport()) {
- returnResult = this.receiveFallbackToIbb(packet);
- } else {
- returnResult = false;
- Log.d(Config.LOGTAG, "trying to fallback to something unknown"
- + packet.toString());
- }
- } else if (packet.isAction("transport-accept")) {
- returnResult = this.receiveTransportAccept(packet);
- } else {
- Log.d(Config.LOGTAG, "packet arrived in connection. action was "
- + packet.getAction());
- returnResult = false;
- }
- IqPacket response;
- if (returnResult) {
- response = packet.generateResponse(IqPacket.TYPE.RESULT);
-
- } else {
- response = packet.generateResponse(IqPacket.TYPE.ERROR);
- }
- mXmppConnectionService.sendIqPacket(account,response,null);
- }
-
- public void init(final Message message) {
- if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
- Conversation conversation = message.getConversation();
- conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, new OnMessageCreatedCallback() {
- @Override
- public void run(XmppAxolotlMessage xmppAxolotlMessage) {
- if (xmppAxolotlMessage != null) {
- init(message, xmppAxolotlMessage);
- } else {
- fail();
- }
- }
- });
- } else {
- init(message, null);
- }
- }
-
- private void init(Message message, XmppAxolotlMessage xmppAxolotlMessage) {
- this.mXmppAxolotlMessage = xmppAxolotlMessage;
- this.contentCreator = "initiator";
- this.contentName = this.mJingleConnectionManager.nextRandomId();
- this.message = message;
- this.account = message.getConversation().getAccount();
- upgradeNamespace();
- this.message.setTransferable(this);
- this.mStatus = Transferable.STATUS_UPLOADING;
- this.initiator = this.account.getJid();
- this.responder = this.message.getCounterpart();
- this.sessionId = this.mJingleConnectionManager.nextRandomId();
- this.transportId = this.mJingleConnectionManager.nextRandomId();
- if (this.candidates.size() > 0) {
- this.sendInitRequest();
- } else {
- this.mJingleConnectionManager.getPrimaryCandidate(account,
- new OnPrimaryCandidateFound() {
-
- @Override
- public void onPrimaryCandidateFound(boolean success,
- final JingleCandidate candidate) {
- if (success) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
- JingleConnection.this, candidate);
- connections.put(candidate.getCid(),
- socksConnection);
- socksConnection
- .connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d(Config.LOGTAG,
- "connection to our own primary candidete failed");
- sendInitRequest();
- }
-
- @Override
- public void established() {
- Log.d(Config.LOGTAG,
- "successfully connected to our own primary candidate");
- mergeCandidate(candidate);
- sendInitRequest();
- }
- });
- mergeCandidate(candidate);
- } else {
- Log.d(Config.LOGTAG, "no primary candidate of our own was found");
- sendInitRequest();
- }
- }
- });
- }
-
- }
-
- private void upgradeNamespace() {
- Jid jid = this.message.getCounterpart();
- String resource = jid != null ?jid.getResourcepart() : null;
- if (resource != null) {
- Presence presence = this.account.getRoster().getContact(jid).getPresences().getPresences().get(resource);
- ServiceDiscoveryResult result = presence != null ? presence.getServiceDiscoveryResult() : null;
- if (result != null) {
- List<String> features = result.getFeatures();
- if (features.contains(Content.Version.FT_4.getNamespace())) {
- this.ftVersion = Content.Version.FT_4;
- }
- }
- }
- }
-
- public void init(Account account, JinglePacket packet) {
- this.mJingleStatus = JINGLE_STATUS_INITIATED;
- Conversation conversation = this.mXmppConnectionService
- .findOrCreateConversation(account,
- packet.getFrom().toBareJid(), false);
- this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
- this.message.setStatus(Message.STATUS_RECEIVED);
- this.mStatus = Transferable.STATUS_OFFER;
- this.message.setTransferable(this);
+ }
+ }
+ };
+
+ final OnFileTransmissionStatusChanged onFileTransmissionSatusChanged = new OnFileTransmissionStatusChanged() {
+
+ @Override
+ public void onFileTransmitted(DownloadableFile file) {
+ if (responder.equals(account.getJid())) {
+ sendSuccess();
+ mXmppConnectionService.getFileBackend().updateFileParams(message);
+ mXmppConnectionService.databaseBackend.createMessage(message);
+ mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVED);
+ if (acceptedAutomatically) {
+ message.markUnread();
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ account.getPgpDecryptionService().decrypt(message, true);
+ } else {
+ JingleConnection.this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ }
+ } else {
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ account.getPgpDecryptionService().decrypt(message, false);
+ }
+ if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ file.delete();
+ }
+ }
+ Log.d(Config.LOGTAG, "successfully transmitted file:" + file.getAbsolutePath() + " (" + file.getSha1Sum() + ")");
+ if (message.getEncryption() != Message.ENCRYPTION_PGP) {
+ mXmppConnectionService.getFileBackend().updateMediaScanner(file);
+ }
+ }
+
+ @Override
+ public void onFileTransferAborted() {
+ JingleConnection.this.sendCancel();
+ JingleConnection.this.fail();
+ }
+ };
+
+ public InputStream getFileInputStream() {
+ return this.mFileInputStream;
+ }
+
+ public OutputStream getFileOutputStream() {
+ return this.mFileOutputStream;
+ }
+
+ private OnProxyActivated onProxyActivated = new OnProxyActivated() {
+
+ @Override
+ public void success() {
+ if (initiator.equals(account.getJid())) {
+ Log.d(Config.LOGTAG, "we were initiating. sending file");
+ transport.send(file, onFileTransmissionSatusChanged);
+ } else {
+ transport.receive(file, onFileTransmissionSatusChanged);
+ Log.d(Config.LOGTAG, "we were responding. receiving file");
+ }
+ }
+
+ @Override
+ public void failed() {
+ Log.d(Config.LOGTAG, "proxy activation failed");
+ }
+ };
+
+ public JingleConnection(JingleConnectionManager mJingleConnectionManager) {
+ this.mJingleConnectionManager = mJingleConnectionManager;
+ this.mXmppConnectionService = mJingleConnectionManager
+ .getXmppConnectionService();
+ }
+
+ public String getSessionId() {
+ return this.sessionId;
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public Jid getCounterPart() {
+ return this.message.getCounterpart();
+ }
+
+ public void deliverPacket(JinglePacket packet) {
+ boolean returnResult = true;
+ if (packet.isAction("session-terminate")) {
+ Reason reason = packet.getReason();
+ if (reason != null) {
+ if (reason.hasChild("cancel")) {
+ this.fail();
+ } else if (reason.hasChild("success")) {
+ this.receiveSuccess();
+ } else {
+ this.fail();
+ }
+ } else {
+ this.fail();
+ }
+ } else if (packet.isAction("session-accept")) {
+ returnResult = receiveAccept(packet);
+ } else if (packet.isAction("transport-info")) {
+ returnResult = receiveTransportInfo(packet);
+ } else if (packet.isAction("transport-replace")) {
+ if (packet.getJingleContent().hasIbbTransport()) {
+ returnResult = this.receiveFallbackToIbb(packet);
+ } else {
+ returnResult = false;
+ Log.d(Config.LOGTAG, "trying to fallback to something unknown"
+ + packet.toString());
+ }
+ } else if (packet.isAction("transport-accept")) {
+ returnResult = this.receiveTransportAccept(packet);
+ } else {
+ Log.d(Config.LOGTAG, "packet arrived in connection. action was "
+ + packet.getAction());
+ returnResult = false;
+ }
+ IqPacket response;
+ if (returnResult) {
+ response = packet.generateResponse(IqPacket.TYPE.RESULT);
+
+ } else {
+ response = packet.generateResponse(IqPacket.TYPE.ERROR);
+ }
+ mXmppConnectionService.sendIqPacket(account, response, null);
+ }
+
+ public void init(final Message message) {
+ if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ Conversation conversation = message.getConversation();
+ conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, new OnMessageCreatedCallback() {
+ @Override
+ public void run(XmppAxolotlMessage xmppAxolotlMessage) {
+ if (xmppAxolotlMessage != null) {
+ init(message, xmppAxolotlMessage);
+ } else {
+ fail();
+ }
+ }
+ });
+ } else {
+ init(message, null);
+ }
+ }
+
+ private void init(Message message, XmppAxolotlMessage xmppAxolotlMessage) {
+ this.mXmppAxolotlMessage = xmppAxolotlMessage;
+ this.contentCreator = "initiator";
+ this.contentName = this.mJingleConnectionManager.nextRandomId();
+ this.message = message;
+ this.account = message.getConversation().getAccount();
+ upgradeNamespace();
+ this.message.setTransferable(this);
+ this.mStatus = Transferable.STATUS_UPLOADING;
+ this.initiator = this.account.getJid();
+ this.responder = this.message.getCounterpart();
+ this.sessionId = this.mJingleConnectionManager.nextRandomId();
+ this.transportId = this.mJingleConnectionManager.nextRandomId();
+ if (this.candidates.size() > 0) {
+ this.sendInitRequest();
+ } else {
+ this.mJingleConnectionManager.getPrimaryCandidate(account,
+ new OnPrimaryCandidateFound() {
+
+ @Override
+ public void onPrimaryCandidateFound(boolean success,
+ final JingleCandidate candidate) {
+ if (success) {
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ JingleConnection.this, candidate);
+ connections.put(candidate.getCid(),
+ socksConnection);
+ socksConnection
+ .connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d(Config.LOGTAG,
+ "connection to our own primary candidete failed");
+ sendInitRequest();
+ }
+
+ @Override
+ public void established() {
+ Log.d(Config.LOGTAG,
+ "successfully connected to our own primary candidate");
+ mergeCandidate(candidate);
+ sendInitRequest();
+ }
+ });
+ mergeCandidate(candidate);
+ } else {
+ Log.d(Config.LOGTAG, "no primary candidate of our own was found");
+ sendInitRequest();
+ }
+ }
+ });
+ }
+
+ }
+
+ private void upgradeNamespace() {
+ Jid jid = this.message.getCounterpart();
+ String resource = jid != null ? jid.getResourcepart() : null;
+ if (resource != null) {
+ Presence presence = this.account.getRoster().getContact(jid).getPresences().getPresences().get(resource);
+ ServiceDiscoveryResult result = presence != null ? presence.getServiceDiscoveryResult() : null;
+ if (result != null) {
+ List<String> features = result.getFeatures();
+ if (features.contains(Content.Version.FT_4.getNamespace())) {
+ this.ftVersion = Content.Version.FT_4;
+ }
+ }
+ }
+ }
+
+ public void init(Account account, JinglePacket packet) {
+ this.mJingleStatus = JINGLE_STATUS_INITIATED;
+ Conversation conversation = this.mXmppConnectionService
+ .findOrCreateConversation(account,
+ packet.getFrom().toBareJid(), false);
+ this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
+ this.message.setStatus(Message.STATUS_RECEIVED);
+ this.mStatus = Transferable.STATUS_OFFER;
+ this.message.setTransferable(this);
final Jid from = packet.getFrom();
- this.message.setCounterpart(from);
- this.account = account;
- this.initiator = packet.getFrom();
- this.responder = this.account.getJid();
- this.sessionId = packet.getSessionId();
- Content content = packet.getJingleContent();
- this.contentCreator = content.getAttribute("creator");
- this.contentName = content.getAttribute("name");
- this.transportId = content.getTransportId();
- this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
- this.ftVersion = content.getVersion();
- if (ftVersion == null) {
- this.sendCancel();
- this.fail();
- return;
- }
- this.fileOffer = content.getFileOffer(this.ftVersion);
-
- mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null);
-
- if (fileOffer != null) {
- Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX);
- if (encrypted != null) {
- this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().toBareJid());
- }
- Element fileSize = fileOffer.findChild("size");
- Element fileNameElement = fileOffer.findChild("name");
- if (fileNameElement != null) {
- String[] filename = fileNameElement.getContent()
- .toLowerCase(Locale.US).toLowerCase().split("\\.");
- String filename_new = fileDateFormat.format(new Date(message.getTimeSent()))+"_"+message.getUuid().substring(0,4);
- String extension = filename[filename.length - 1];
- if (VALID_IMAGE_EXTENSIONS.contains(extension)) {
- message.setType(Message.TYPE_IMAGE);
- message.setRelativeFilePath(filename_new+"."+extension);
- } else if (VALID_CRYPTO_EXTENSIONS.contains(
- filename[filename.length - 1])) {
- if (filename.length == 3) {
- extension = filename[filename.length - 2];
- if (VALID_IMAGE_EXTENSIONS.contains(extension)) {
- message.setType(Message.TYPE_IMAGE);
- message.setRelativeFilePath(filename_new+"."+extension);
- } else {
- message.setType(Message.TYPE_FILE);
- }
- if (filename[filename.length - 1].equals("otr")) {
- message.setEncryption(Message.ENCRYPTION_OTR);
- } else {
- message.setEncryption(Message.ENCRYPTION_PGP);
- }
- }
- } else {
- message.setType(Message.TYPE_FILE);
- }
- if (message.getType() == Message.TYPE_FILE) {
- String suffix = "";
- if (!fileNameElement.getContent().isEmpty()) {
- String parts[] = fileNameElement.getContent().split("/");
- suffix = parts[parts.length - 1];
- if (message.getEncryption() == Message.ENCRYPTION_OTR && suffix.endsWith(".otr")) {
- suffix = suffix.substring(0,suffix.length() - 4);
- } else if (message.getEncryption() == Message.ENCRYPTION_PGP && (suffix.endsWith(".pgp") || suffix.endsWith(".gpg"))) {
- suffix = suffix.substring(0,suffix.length() - 4);
- }
- }
- message.setRelativeFilePath(filename_new+"_"+suffix);
- }
- long size = Long.parseLong(fileSize.getContent());
- message.setBody(Long.toString(size));
- conversation.add(message);
- mXmppConnectionService.updateConversationUi();
- if (mJingleConnectionManager.hasStoragePermission()
- && size < this.mJingleConnectionManager.getAutoAcceptFileSize()
- && mXmppConnectionService.isDataSaverDisabled()) {
- Log.d(Config.LOGTAG, "auto accepting file from "+ packet.getFrom());
- this.acceptedAutomatically = true;
- this.sendAccept();
- } else {
- message.markUnread();
- Log.d(Config.LOGTAG,
- "not auto accepting new file offer with size: "
- + size
- + " allowed size:"
- + this.mJingleConnectionManager
- .getAutoAcceptFileSize());
- this.mXmppConnectionService.getNotificationService().push(message);
- }
- this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
- if (mXmppAxolotlMessage != null) {
- XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage);
- if (transportMessage != null) {
- message.setEncryption(Message.ENCRYPTION_AXOLOTL);
- this.file.setKey(transportMessage.getKey());
- this.file.setIv(transportMessage.getIv());
- message.setFingerprint(transportMessage.getFingerprint());
- } else {
- Log.d(Config.LOGTAG,"could not process KeyTransportMessage");
- }
- } else if (message.getEncryption() == Message.ENCRYPTION_OTR) {
- byte[] key = conversation.getSymmetricKey();
- if (key == null) {
- this.sendCancel();
- this.fail();
- return;
- } else {
- this.file.setKeyAndIv(key);
- }
- }
- this.mFileOutputStream = AbstractConnectionManager.createOutputStream(this.file,message.getEncryption() == Message.ENCRYPTION_AXOLOTL);
- if (message.getEncryption() == Message.ENCRYPTION_OTR && Config.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE) {
- this.file.setExpectedSize((size / 16 + 1) * 16);
- } else {
- this.file.setExpectedSize(size);
- }
- Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
- } else {
- this.sendCancel();
- this.fail();
- }
- } else {
- this.sendCancel();
- this.fail();
- }
- }
-
- private void sendInitRequest() {
- JinglePacket packet = this.bootstrapPacket("session-initiate");
- Content content = new Content(this.contentCreator, this.contentName);
- if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
- content.setTransportId(this.transportId);
- this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
- Pair<InputStream,Integer> pair;
- try {
- if (message.getEncryption() == Message.ENCRYPTION_OTR) {
- Conversation conversation = this.message.getConversation();
- if (!this.mXmppConnectionService.renewSymmetricKey(conversation)) {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not set symmetric key");
- cancel();
- }
- this.file.setKeyAndIv(conversation.getSymmetricKey());
- pair = AbstractConnectionManager.createInputStream(this.file, false);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, true, this.ftVersion);
- } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
- this.file.setKey(mXmppAxolotlMessage.getInnerKey());
- this.file.setIv(mXmppAxolotlMessage.getIV());
- pair = AbstractConnectionManager.createInputStream(this.file, true);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement());
- } else {
- pair = AbstractConnectionManager.createInputStream(this.file, false);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, false, this.ftVersion);
- }
- } catch (FileNotFoundException e) {
- cancel();
- return;
- }
- this.mFileInputStream = pair.first;
- content.setTransportId(this.transportId);
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- this.sendJinglePacket(packet,new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() == IqPacket.TYPE.RESULT) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer");
- mJingleStatus = JINGLE_STATUS_INITIATED;
- mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
- } else {
- fail(IqParser.extractErrorMessage(packet));
- }
- }
- });
-
- }
- }
-
- private List<Element> getCandidatesAsElements() {
- List<Element> elements = new ArrayList<>();
- for (JingleCandidate c : this.candidates) {
- if (c.isOurs()) {
- elements.add(c.toElement());
- }
- }
- return elements;
- }
-
- private void sendAccept() {
- mJingleStatus = JINGLE_STATUS_ACCEPTED;
- this.mStatus = Transferable.STATUS_DOWNLOADING;
- mXmppConnectionService.updateConversationUi();
- this.mJingleConnectionManager.getPrimaryCandidate(this.account, new OnPrimaryCandidateFound() {
- @Override
- public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) {
- final JinglePacket packet = bootstrapPacket("session-accept");
- final Content content = new Content(contentCreator,contentName);
- content.setFileOffer(fileOffer, ftVersion);
- content.setTransportId(transportId);
- if (success && candidate != null && !equalCandidateExists(candidate)) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
- JingleConnection.this,
- candidate);
- connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d(Config.LOGTAG,"connection to our own primary candidate failed");
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- sendJinglePacket(packet);
- connectNextCandidate();
- }
-
- @Override
- public void established() {
- Log.d(Config.LOGTAG, "connected to primary candidate");
- mergeCandidate(candidate);
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- sendJinglePacket(packet);
- connectNextCandidate();
- }
- });
- } else {
- Log.d(Config.LOGTAG,"did not find a primary candidate for ourself");
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- sendJinglePacket(packet);
- connectNextCandidate();
- }
- }
- });
- }
-
- private JinglePacket bootstrapPacket(String action) {
- JinglePacket packet = new JinglePacket();
- packet.setAction(action);
- packet.setFrom(account.getJid());
- packet.setTo(this.message.getCounterpart());
- packet.setSessionId(this.sessionId);
- packet.setInitiator(this.initiator);
- return packet;
- }
-
- private void sendJinglePacket(JinglePacket packet) {
- mXmppConnectionService.sendIqPacket(account,packet,responseListener);
- }
-
- private void sendJinglePacket(JinglePacket packet, OnIqPacketReceived callback) {
- mXmppConnectionService.sendIqPacket(account,packet,callback);
- }
-
- private boolean receiveAccept(JinglePacket packet) {
- Content content = packet.getJingleContent();
- mergeCandidates(JingleCandidate.parse(content.socks5transport()
- .getChildren()));
- this.mJingleStatus = JINGLE_STATUS_ACCEPTED;
- mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
- this.connectNextCandidate();
- return true;
- }
-
- private boolean receiveTransportInfo(JinglePacket packet) {
- Content content = packet.getJingleContent();
- if (content.hasSocks5Transport()) {
- if (content.socks5transport().hasChild("activated")) {
- if ((this.transport != null) && (this.transport instanceof JingleSocks5Transport)) {
- onProxyActivated.success();
- } else {
- String cid = content.socks5transport().findChild("activated").getAttribute("cid");
- Log.d(Config.LOGTAG, "received proxy activated (" + cid
- + ")prior to choosing our own transport");
- JingleSocks5Transport connection = this.connections.get(cid);
- if (connection != null) {
- connection.setActivated(true);
- } else {
- Log.d(Config.LOGTAG, "activated connection not found");
- this.sendCancel();
- this.fail();
- }
- }
- return true;
- } else if (content.socks5transport().hasChild("proxy-error")) {
- onProxyActivated.failed();
- return true;
- } else if (content.socks5transport().hasChild("candidate-error")) {
- Log.d(Config.LOGTAG, "received candidate error");
- this.receivedCandidate = true;
- if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
- && (this.sentCandidate)) {
- this.connect();
- }
- return true;
- } else if (content.socks5transport().hasChild("candidate-used")) {
- String cid = content.socks5transport()
- .findChild("candidate-used").getAttribute("cid");
- if (cid != null) {
- Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid);
- JingleCandidate candidate = getCandidate(cid);
+ this.message.setCounterpart(from);
+ this.account = account;
+ this.initiator = packet.getFrom();
+ this.responder = this.account.getJid();
+ this.sessionId = packet.getSessionId();
+ Content content = packet.getJingleContent();
+ this.contentCreator = content.getAttribute("creator");
+ this.contentName = content.getAttribute("name");
+ this.transportId = content.getTransportId();
+ this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
+ this.ftVersion = content.getVersion();
+ if (ftVersion == null) {
+ this.sendCancel();
+ this.fail();
+ return;
+ }
+ this.fileOffer = content.getFileOffer(this.ftVersion);
+
+ mXmppConnectionService.sendIqPacket(account, packet.generateResponse(IqPacket.TYPE.RESULT), null);
+
+ if (fileOffer != null) {
+ Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX);
+ if (encrypted != null) {
+ this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().toBareJid());
+ }
+ Element fileSize = fileOffer.findChild("size");
+ Element fileNameElement = fileOffer.findChild("name");
+ if (fileNameElement != null) {
+ String[] filename = fileNameElement.getContent()
+ .toLowerCase(Locale.US).toLowerCase().split("\\.");
+ String filename_new = fileDateFormat.format(new Date(message.getTimeSent())) + "_" + message.getUuid().substring(0, 4);
+ String extension = filename[filename.length - 1];
+ if (VALID_IMAGE_EXTENSIONS.contains(extension)) {
+ message.setType(Message.TYPE_IMAGE);
+ message.setRelativeFilePath(filename_new + "." + extension);
+ } else if (VALID_CRYPTO_EXTENSIONS.contains(
+ filename[filename.length - 1])) {
+ if (filename.length == 3) {
+ extension = filename[filename.length - 2];
+ if (VALID_IMAGE_EXTENSIONS.contains(extension)) {
+ message.setType(Message.TYPE_IMAGE);
+ message.setRelativeFilePath(filename_new + "." + extension);
+ } else {
+ message.setType(Message.TYPE_FILE);
+ }
+ if (filename[filename.length - 1].equals("otr")) {
+ message.setEncryption(Message.ENCRYPTION_OTR);
+ } else {
+ message.setEncryption(Message.ENCRYPTION_PGP);
+ }
+ }
+ } else {
+ message.setType(Message.TYPE_FILE);
+ }
+ if (message.getType() == Message.TYPE_FILE) {
+ String suffix = "";
+ if (!fileNameElement.getContent().isEmpty()) {
+ String parts[] = fileNameElement.getContent().split("/");
+ suffix = parts[parts.length - 1];
+ if (message.getEncryption() == Message.ENCRYPTION_OTR && suffix.endsWith(".otr")) {
+ suffix = suffix.substring(0, suffix.length() - 4);
+ } else if (message.getEncryption() == Message.ENCRYPTION_PGP && (suffix.endsWith(".pgp") || suffix.endsWith(".gpg"))) {
+ suffix = suffix.substring(0, suffix.length() - 4);
+ }
+ }
+ message.setRelativeFilePath(filename_new + "_" + suffix);
+ }
+ long size = Long.parseLong(fileSize.getContent());
+ message.setBody(Long.toString(size));
+ conversation.add(message);
+ mXmppConnectionService.updateConversationUi();
+ if (mJingleConnectionManager.hasStoragePermission()
+ && size < this.mJingleConnectionManager.getAutoAcceptFileSize()
+ && mXmppConnectionService.isDataSaverDisabled()) {
+ Log.d(Config.LOGTAG, "auto accepting file from " + packet.getFrom());
+ this.acceptedAutomatically = true;
+ this.sendAccept();
+ } else {
+ message.markUnread();
+ Log.d(Config.LOGTAG,
+ "not auto accepting new file offer with size: "
+ + size
+ + " allowed size:"
+ + this.mJingleConnectionManager
+ .getAutoAcceptFileSize());
+ this.mXmppConnectionService.getNotificationService().push(message);
+ }
+ this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
+ if (mXmppAxolotlMessage != null) {
+ XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage);
+ if (transportMessage != null) {
+ message.setEncryption(Message.ENCRYPTION_AXOLOTL);
+ this.file.setKey(transportMessage.getKey());
+ this.file.setIv(transportMessage.getIv());
+ message.setFingerprint(transportMessage.getFingerprint());
+ } else {
+ Log.d(Config.LOGTAG, "could not process KeyTransportMessage");
+ }
+ } else if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ byte[] key = conversation.getSymmetricKey();
+ if (key == null) {
+ this.sendCancel();
+ this.fail();
+ return;
+ } else {
+ this.file.setKeyAndIv(key);
+ }
+ }
+ this.mFileOutputStream = AbstractConnectionManager.createOutputStream(this.file, message.getEncryption() == Message.ENCRYPTION_AXOLOTL);
+ if (message.getEncryption() == Message.ENCRYPTION_OTR && Config.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE) {
+ this.file.setExpectedSize((size / 16 + 1) * 16);
+ } else {
+ this.file.setExpectedSize(size);
+ }
+ Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
+ } else {
+ this.sendCancel();
+ this.fail();
+ }
+ } else {
+ this.sendCancel();
+ this.fail();
+ }
+ }
+
+ private void sendInitRequest() {
+ JinglePacket packet = this.bootstrapPacket("session-initiate");
+ Content content = new Content(this.contentCreator, this.contentName);
+ if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
+ content.setTransportId(this.transportId);
+ this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
+ Pair<InputStream, Integer> pair;
+ try {
+ if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ Conversation conversation = this.message.getConversation();
+ if (!this.mXmppConnectionService.renewSymmetricKey(conversation)) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not set symmetric key");
+ cancel();
+ }
+ this.file.setKeyAndIv(conversation.getSymmetricKey());
+ pair = AbstractConnectionManager.createInputStream(this.file, false);
+ this.file.setExpectedSize(pair.second);
+ content.setFileOffer(this.file, true, this.ftVersion);
+ } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ this.file.setKey(mXmppAxolotlMessage.getInnerKey());
+ this.file.setIv(mXmppAxolotlMessage.getIV());
+ pair = AbstractConnectionManager.createInputStream(this.file, true);
+ this.file.setExpectedSize(pair.second);
+ content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement());
+ } else {
+ pair = AbstractConnectionManager.createInputStream(this.file, false);
+ this.file.setExpectedSize(pair.second);
+ content.setFileOffer(this.file, false, this.ftVersion);
+ }
+ } catch (FileNotFoundException e) {
+ cancel();
+ return;
+ }
+ this.mFileInputStream = pair.first;
+ content.setTransportId(this.transportId);
+ content.socks5transport().setChildren(getCandidatesAsElements());
+ packet.setContent(content);
+ this.sendJinglePacket(packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": other party received offer");
+ mJingleStatus = JINGLE_STATUS_INITIATED;
+ mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
+ } else {
+ fail(IqParser.extractErrorMessage(packet));
+ }
+ }
+ });
+
+ }
+ }
+
+ private List<Element> getCandidatesAsElements() {
+ List<Element> elements = new ArrayList<>();
+ for (JingleCandidate c : this.candidates) {
+ if (c.isOurs()) {
+ elements.add(c.toElement());
+ }
+ }
+ return elements;
+ }
+
+ private void sendAccept() {
+ mJingleStatus = JINGLE_STATUS_ACCEPTED;
+ this.mStatus = Transferable.STATUS_DOWNLOADING;
+ mXmppConnectionService.updateConversationUi();
+ this.mJingleConnectionManager.getPrimaryCandidate(this.account, new OnPrimaryCandidateFound() {
+ @Override
+ public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) {
+ final JinglePacket packet = bootstrapPacket("session-accept");
+ final Content content = new Content(contentCreator, contentName);
+ content.setFileOffer(fileOffer, ftVersion);
+ content.setTransportId(transportId);
+ if (success && candidate != null && !equalCandidateExists(candidate)) {
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ JingleConnection.this,
+ candidate);
+ connections.put(candidate.getCid(), socksConnection);
+ socksConnection.connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d(Config.LOGTAG, "connection to our own primary candidate failed");
+ content.socks5transport().setChildren(getCandidatesAsElements());
+ packet.setContent(content);
+ sendJinglePacket(packet);
+ connectNextCandidate();
+ }
+
+ @Override
+ public void established() {
+ Log.d(Config.LOGTAG, "connected to primary candidate");
+ mergeCandidate(candidate);
+ content.socks5transport().setChildren(getCandidatesAsElements());
+ packet.setContent(content);
+ sendJinglePacket(packet);
+ connectNextCandidate();
+ }
+ });
+ } else {
+ Log.d(Config.LOGTAG, "did not find a primary candidate for ourself");
+ content.socks5transport().setChildren(getCandidatesAsElements());
+ packet.setContent(content);
+ sendJinglePacket(packet);
+ connectNextCandidate();
+ }
+ }
+ });
+ }
+
+ private JinglePacket bootstrapPacket(String action) {
+ JinglePacket packet = new JinglePacket();
+ packet.setAction(action);
+ packet.setFrom(account.getJid());
+ packet.setTo(this.message.getCounterpart());
+ packet.setSessionId(this.sessionId);
+ packet.setInitiator(this.initiator);
+ return packet;
+ }
+
+ private void sendJinglePacket(JinglePacket packet) {
+ mXmppConnectionService.sendIqPacket(account, packet, responseListener);
+ }
+
+ private void sendJinglePacket(JinglePacket packet, OnIqPacketReceived callback) {
+ mXmppConnectionService.sendIqPacket(account, packet, callback);
+ }
+
+ private boolean receiveAccept(JinglePacket packet) {
+ Content content = packet.getJingleContent();
+ mergeCandidates(JingleCandidate.parse(content.socks5transport()
+ .getChildren()));
+ this.mJingleStatus = JINGLE_STATUS_ACCEPTED;
+ mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
+ this.connectNextCandidate();
+ return true;
+ }
+
+ private boolean receiveTransportInfo(JinglePacket packet) {
+ Content content = packet.getJingleContent();
+ if (content.hasSocks5Transport()) {
+ if (content.socks5transport().hasChild("activated")) {
+ if ((this.transport != null) && (this.transport instanceof JingleSocks5Transport)) {
+ onProxyActivated.success();
+ } else {
+ String cid = content.socks5transport().findChild("activated").getAttribute("cid");
+ Log.d(Config.LOGTAG, "received proxy activated (" + cid
+ + ")prior to choosing our own transport");
+ JingleSocks5Transport connection = this.connections.get(cid);
+ if (connection != null) {
+ connection.setActivated(true);
+ } else {
+ Log.d(Config.LOGTAG, "activated connection not found");
+ this.sendCancel();
+ this.fail();
+ }
+ }
+ return true;
+ } else if (content.socks5transport().hasChild("proxy-error")) {
+ onProxyActivated.failed();
+ return true;
+ } else if (content.socks5transport().hasChild("candidate-error")) {
+ Log.d(Config.LOGTAG, "received candidate error");
+ this.receivedCandidate = true;
+ if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
+ && (this.sentCandidate)) {
+ this.connect();
+ }
+ return true;
+ } else if (content.socks5transport().hasChild("candidate-used")) {
+ String cid = content.socks5transport()
+ .findChild("candidate-used").getAttribute("cid");
+ if (cid != null) {
+ Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid);
+ JingleCandidate candidate = getCandidate(cid);
if (candidate == null) {
- Log.d(Config.LOGTAG,"could not find candidate with cid="+cid);
+ Log.d(Config.LOGTAG, "could not find candidate with cid=" + cid);
return false;
}
- candidate.flagAsUsedByCounterpart();
- this.receivedCandidate = true;
- if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
- && (this.sentCandidate)) {
- this.connect();
- } else {
- Log.d(Config.LOGTAG,
- "ignoring because file is already in transmission or we haven't sent our candidate yet");
- }
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- } else {
- return true;
- }
- }
-
- private void connect() {
- final JingleSocks5Transport connection = chooseConnection();
- this.transport = connection;
- if (connection == null) {
- Log.d(Config.LOGTAG, "could not find suitable candidate");
- this.disconnectSocks5Connections();
- if (this.initiator.equals(account.getJid())) {
- this.sendFallbackToIbb();
- }
- } else {
- this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
- if (connection.needsActivation()) {
- if (connection.getCandidate().isOurs()) {
- final String sid;
- if (ftVersion == Content.Version.FT_3) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": use session ID instead of transport ID to activate proxy");
- sid = getSessionId();
- } else {
- sid = getTransportId();
- }
- Log.d(Config.LOGTAG, "candidate "
- + connection.getCandidate().getCid()
- + " was our proxy. going to activate");
- IqPacket activation = new IqPacket(IqPacket.TYPE.SET);
- activation.setTo(connection.getCandidate().getJid());
- activation.query("http://jabber.org/protocol/bytestreams")
- .setAttribute("sid", sid);
- activation.query().addChild("activate")
- .setContent(this.getCounterPart().toString());
- mXmppConnectionService.sendIqPacket(account,activation,
- new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account,
- IqPacket packet) {
- if (packet.getType() != IqPacket.TYPE.RESULT) {
- onProxyActivated.failed();
- } else {
- onProxyActivated.success();
- sendProxyActivated(connection.getCandidate().getCid());
- }
- }
- });
- } else {
- Log.d(Config.LOGTAG,
- "candidate "
- + connection.getCandidate().getCid()
- + " was a proxy. waiting for other party to activate");
- }
- } else {
- if (initiator.equals(account.getJid())) {
- Log.d(Config.LOGTAG, "we were initiating. sending file");
- connection.send(file, onFileTransmissionSatusChanged);
- } else {
- Log.d(Config.LOGTAG, "we were responding. receiving file");
- connection.receive(file, onFileTransmissionSatusChanged);
- }
- }
- }
- }
-
- private JingleSocks5Transport chooseConnection() {
- JingleSocks5Transport connection = null;
- for (Entry<String, JingleSocks5Transport> cursor : connections
- .entrySet()) {
- JingleSocks5Transport currentConnection = cursor.getValue();
- // Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString());
- if (currentConnection.isEstablished()
- && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection
- .getCandidate().isOurs()))) {
- // Log.d(Config.LOGTAG,"is usable");
- if (connection == null) {
- connection = currentConnection;
- } else {
- if (connection.getCandidate().getPriority() < currentConnection
- .getCandidate().getPriority()) {
- connection = currentConnection;
- } else if (connection.getCandidate().getPriority() == currentConnection
- .getCandidate().getPriority()) {
- // Log.d(Config.LOGTAG,"found two candidates with same priority");
- if (initiator.equals(account.getJid())) {
- if (currentConnection.getCandidate().isOurs()) {
- connection = currentConnection;
- }
- } else {
- if (!currentConnection.getCandidate().isOurs()) {
- connection = currentConnection;
- }
- }
- }
- }
- }
- }
- return connection;
- }
-
- private void sendSuccess() {
- JinglePacket packet = bootstrapPacket("session-terminate");
- Reason reason = new Reason();
- reason.addChild("success");
- packet.setReason(reason);
- this.sendJinglePacket(packet);
- this.disconnectSocks5Connections();
- this.mJingleStatus = JINGLE_STATUS_FINISHED;
- this.message.setStatus(Message.STATUS_RECEIVED);
- this.message.setTransferable(null);
- this.mXmppConnectionService.updateMessage(message);
- this.mJingleConnectionManager.finishConnection(this);
- }
-
- private void sendFallbackToIbb() {
- Log.d(Config.LOGTAG, "sending fallback to ibb");
- JinglePacket packet = this.bootstrapPacket("transport-replace");
- Content content = new Content(this.contentCreator, this.contentName);
- this.transportId = this.mJingleConnectionManager.nextRandomId();
- content.setTransportId(this.transportId);
- content.ibbTransport().setAttribute("block-size",
- Integer.toString(this.ibbBlockSize));
- packet.setContent(content);
- this.sendJinglePacket(packet);
- }
-
- private boolean receiveFallbackToIbb(JinglePacket packet) {
- Log.d(Config.LOGTAG, "receiving fallack to ibb");
- String receivedBlockSize = packet.getJingleContent().ibbTransport()
- .getAttribute("block-size");
- if (receivedBlockSize != null) {
- int bs = Integer.parseInt(receivedBlockSize);
- if (bs > this.ibbBlockSize) {
- this.ibbBlockSize = bs;
- }
- }
- this.transportId = packet.getJingleContent().getTransportId();
- this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
- this.transport.receive(file, onFileTransmissionSatusChanged);
- JinglePacket answer = bootstrapPacket("transport-accept");
- Content content = new Content("initiator", "a-file-offer");
- content.setTransportId(this.transportId);
- content.ibbTransport().setAttribute("block-size",this.ibbBlockSize);
- answer.setContent(content);
- this.sendJinglePacket(answer);
- return true;
- }
-
- private boolean receiveTransportAccept(JinglePacket packet) {
- if (packet.getJingleContent().hasIbbTransport()) {
- String receivedBlockSize = packet.getJingleContent().ibbTransport()
- .getAttribute("block-size");
- if (receivedBlockSize != null) {
- int bs = Integer.parseInt(receivedBlockSize);
- if (bs > this.ibbBlockSize) {
- this.ibbBlockSize = bs;
- }
- }
- this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
- this.transport.connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d(Config.LOGTAG, "ibb open failed");
- }
-
- @Override
- public void established() {
- JingleConnection.this.transport.send(file,
- onFileTransmissionSatusChanged);
- }
- });
- return true;
- } else {
- return false;
- }
- }
-
- private void receiveSuccess() {
- this.mJingleStatus = JINGLE_STATUS_FINISHED;
- this.mXmppConnectionService.markMessage(this.message,Message.STATUS_SEND_RECEIVED);
- this.disconnectSocks5Connections();
- if (this.transport != null && this.transport instanceof JingleInbandTransport) {
- this.transport.disconnect();
- }
- this.message.setTransferable(null);
- this.mJingleConnectionManager.finishConnection(this);
- }
-
- public void cancel() {
- this.disconnectSocks5Connections();
- if (this.transport != null && this.transport instanceof JingleInbandTransport) {
- this.transport.disconnect();
- }
- this.sendCancel();
- this.mJingleConnectionManager.finishConnection(this);
- if (this.responder.equals(account.getJid())) {
- this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
- if (this.file!=null) {
- file.delete();
- }
- this.mXmppConnectionService.updateConversationUi();
- } else {
- this.mXmppConnectionService.markMessage(this.message,
- Message.STATUS_SEND_FAILED);
- this.message.setTransferable(null);
- }
- }
-
- private void fail() {
+ candidate.flagAsUsedByCounterpart();
+ this.receivedCandidate = true;
+ if ((mJingleStatus == JINGLE_STATUS_ACCEPTED)
+ && (this.sentCandidate)) {
+ this.connect();
+ } else {
+ Log.d(Config.LOGTAG,
+ "ignoring because file is already in transmission or we haven't sent our candidate yet");
+ }
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ private void connect() {
+ final JingleSocks5Transport connection = chooseConnection();
+ this.transport = connection;
+ if (connection == null) {
+ Log.d(Config.LOGTAG, "could not find suitable candidate");
+ this.disconnectSocks5Connections();
+ if (this.initiator.equals(account.getJid())) {
+ this.sendFallbackToIbb();
+ }
+ } else {
+ this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
+ if (connection.needsActivation()) {
+ if (connection.getCandidate().isOurs()) {
+ final String sid;
+ if (ftVersion == Content.Version.FT_3) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": use session ID instead of transport ID to activate proxy");
+ sid = getSessionId();
+ } else {
+ sid = getTransportId();
+ }
+ Log.d(Config.LOGTAG, "candidate "
+ + connection.getCandidate().getCid()
+ + " was our proxy. going to activate");
+ IqPacket activation = new IqPacket(IqPacket.TYPE.SET);
+ activation.setTo(connection.getCandidate().getJid());
+ activation.query("http://jabber.org/protocol/bytestreams")
+ .setAttribute("sid", sid);
+ activation.query().addChild("activate")
+ .setContent(this.getCounterPart().toString());
+ mXmppConnectionService.sendIqPacket(account, activation,
+ new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account,
+ IqPacket packet) {
+ if (packet.getType() != IqPacket.TYPE.RESULT) {
+ onProxyActivated.failed();
+ } else {
+ onProxyActivated.success();
+ sendProxyActivated(connection.getCandidate().getCid());
+ }
+ }
+ });
+ } else {
+ Log.d(Config.LOGTAG,
+ "candidate "
+ + connection.getCandidate().getCid()
+ + " was a proxy. waiting for other party to activate");
+ }
+ } else {
+ if (initiator.equals(account.getJid())) {
+ Log.d(Config.LOGTAG, "we were initiating. sending file");
+ connection.send(file, onFileTransmissionSatusChanged);
+ } else {
+ Log.d(Config.LOGTAG, "we were responding. receiving file");
+ connection.receive(file, onFileTransmissionSatusChanged);
+ }
+ }
+ }
+ }
+
+ private JingleSocks5Transport chooseConnection() {
+ JingleSocks5Transport connection = null;
+ for (Entry<String, JingleSocks5Transport> cursor : connections
+ .entrySet()) {
+ JingleSocks5Transport currentConnection = cursor.getValue();
+ // Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString());
+ if (currentConnection.isEstablished()
+ && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection
+ .getCandidate().isOurs()))) {
+ // Log.d(Config.LOGTAG,"is usable");
+ if (connection == null) {
+ connection = currentConnection;
+ } else {
+ if (connection.getCandidate().getPriority() < currentConnection
+ .getCandidate().getPriority()) {
+ connection = currentConnection;
+ } else if (connection.getCandidate().getPriority() == currentConnection
+ .getCandidate().getPriority()) {
+ // Log.d(Config.LOGTAG,"found two candidates with same priority");
+ if (initiator.equals(account.getJid())) {
+ if (currentConnection.getCandidate().isOurs()) {
+ connection = currentConnection;
+ }
+ } else {
+ if (!currentConnection.getCandidate().isOurs()) {
+ connection = currentConnection;
+ }
+ }
+ }
+ }
+ }
+ }
+ return connection;
+ }
+
+ private void sendSuccess() {
+ JinglePacket packet = bootstrapPacket("session-terminate");
+ Reason reason = new Reason();
+ reason.addChild("success");
+ packet.setReason(reason);
+ this.sendJinglePacket(packet);
+ this.disconnectSocks5Connections();
+ this.mJingleStatus = JINGLE_STATUS_FINISHED;
+ this.message.setStatus(Message.STATUS_RECEIVED);
+ this.message.setTransferable(null);
+ this.mXmppConnectionService.updateMessage(message);
+ this.mJingleConnectionManager.finishConnection(this);
+ }
+
+ private void sendFallbackToIbb() {
+ Log.d(Config.LOGTAG, "sending fallback to ibb");
+ JinglePacket packet = this.bootstrapPacket("transport-replace");
+ Content content = new Content(this.contentCreator, this.contentName);
+ this.transportId = this.mJingleConnectionManager.nextRandomId();
+ content.setTransportId(this.transportId);
+ content.ibbTransport().setAttribute("block-size",
+ Integer.toString(this.ibbBlockSize));
+ packet.setContent(content);
+ this.sendJinglePacket(packet);
+ }
+
+ private boolean receiveFallbackToIbb(JinglePacket packet) {
+ Log.d(Config.LOGTAG, "receiving fallack to ibb");
+ String receivedBlockSize = packet.getJingleContent().ibbTransport()
+ .getAttribute("block-size");
+ if (receivedBlockSize != null) {
+ int bs = Integer.parseInt(receivedBlockSize);
+ if (bs > this.ibbBlockSize) {
+ this.ibbBlockSize = bs;
+ }
+ }
+ this.transportId = packet.getJingleContent().getTransportId();
+ this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
+ this.transport.receive(file, onFileTransmissionSatusChanged);
+ JinglePacket answer = bootstrapPacket("transport-accept");
+ Content content = new Content("initiator", "a-file-offer");
+ content.setTransportId(this.transportId);
+ content.ibbTransport().setAttribute("block-size", this.ibbBlockSize);
+ answer.setContent(content);
+ this.sendJinglePacket(answer);
+ return true;
+ }
+
+ private boolean receiveTransportAccept(JinglePacket packet) {
+ if (packet.getJingleContent().hasIbbTransport()) {
+ String receivedBlockSize = packet.getJingleContent().ibbTransport()
+ .getAttribute("block-size");
+ if (receivedBlockSize != null) {
+ int bs = Integer.parseInt(receivedBlockSize);
+ if (bs > this.ibbBlockSize) {
+ this.ibbBlockSize = bs;
+ }
+ }
+ this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
+ this.transport.connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d(Config.LOGTAG, "ibb open failed");
+ }
+
+ @Override
+ public void established() {
+ JingleConnection.this.transport.send(file,
+ onFileTransmissionSatusChanged);
+ }
+ });
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void receiveSuccess() {
+ this.mJingleStatus = JINGLE_STATUS_FINISHED;
+ this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_RECEIVED);
+ this.disconnectSocks5Connections();
+ if (this.transport != null && this.transport instanceof JingleInbandTransport) {
+ this.transport.disconnect();
+ }
+ this.message.setTransferable(null);
+ this.mJingleConnectionManager.finishConnection(this);
+ }
+
+ public void cancel() {
+ this.disconnectSocks5Connections();
+ if (this.transport != null && this.transport instanceof JingleInbandTransport) {
+ this.transport.disconnect();
+ }
+ this.sendCancel();
+ this.mJingleConnectionManager.finishConnection(this);
+ if (this.responder.equals(account.getJid())) {
+ this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
+ if (this.file != null) {
+ file.delete();
+ }
+ this.mXmppConnectionService.updateConversationUi();
+ } else {
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND_FAILED);
+ this.message.setTransferable(null);
+ }
+ }
+
+ private void fail() {
fail(null);
}
private void fail(String errorMessage) {
- this.mJingleStatus = JINGLE_STATUS_FAILED;
- this.disconnectSocks5Connections();
- if (this.transport != null && this.transport instanceof JingleInbandTransport) {
- this.transport.disconnect();
- }
- FileBackend.close(mFileInputStream);
- FileBackend.close(mFileOutputStream);
- if (this.message != null) {
- if (this.responder.equals(account.getJid())) {
- this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
- if (this.file!=null) {
- file.delete();
- }
- this.mXmppConnectionService.updateConversationUi();
- } else {
- this.mXmppConnectionService.markMessage(this.message,
- Message.STATUS_SEND_FAILED,
+ this.mJingleStatus = JINGLE_STATUS_FAILED;
+ this.disconnectSocks5Connections();
+ if (this.transport != null && this.transport instanceof JingleInbandTransport) {
+ this.transport.disconnect();
+ }
+ FileBackend.close(mFileInputStream);
+ FileBackend.close(mFileOutputStream);
+ if (this.message != null) {
+ if (this.responder.equals(account.getJid())) {
+ this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
+ if (this.file != null) {
+ file.delete();
+ }
+ this.mXmppConnectionService.updateConversationUi();
+ } else {
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND_FAILED,
errorMessage);
- this.message.setTransferable(null);
- }
- }
- this.mJingleConnectionManager.finishConnection(this);
- }
-
- private void sendCancel() {
- JinglePacket packet = bootstrapPacket("session-terminate");
- Reason reason = new Reason();
- reason.addChild("cancel");
- packet.setReason(reason);
- this.sendJinglePacket(packet);
- }
-
- private void connectNextCandidate() {
- for (JingleCandidate candidate : this.candidates) {
- if ((!connections.containsKey(candidate.getCid()) && (!candidate
- .isOurs()))) {
- this.connectWithCandidate(candidate);
- return;
- }
- }
- this.sendCandidateError();
- }
-
- private void connectWithCandidate(final JingleCandidate candidate) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
- this, candidate);
- connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d(Config.LOGTAG,
- "connection failed with " + candidate.getHost() + ":"
- + candidate.getPort());
- connectNextCandidate();
- }
-
- @Override
- public void established() {
- Log.d(Config.LOGTAG,
- "established connection with " + candidate.getHost()
- + ":" + candidate.getPort());
- sendCandidateUsed(candidate.getCid());
- }
- });
- }
-
- private void disconnectSocks5Connections() {
- Iterator<Entry<String, JingleSocks5Transport>> it = this.connections
- .entrySet().iterator();
- while (it.hasNext()) {
- Entry<String, JingleSocks5Transport> pairs = it.next();
- pairs.getValue().disconnect();
- it.remove();
- }
- }
-
- private void sendProxyActivated(String cid) {
- JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator, this.contentName);
- content.setTransportId(this.transportId);
- content.socks5transport().addChild("activated")
- .setAttribute("cid", cid);
- packet.setContent(content);
- this.sendJinglePacket(packet);
- }
-
- private void sendCandidateUsed(final String cid) {
- JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator, this.contentName);
- content.setTransportId(this.transportId);
- content.socks5transport().addChild("candidate-used")
- .setAttribute("cid", cid);
- packet.setContent(content);
- this.sentCandidate = true;
- if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
- connect();
- }
- this.sendJinglePacket(packet);
- }
-
- private void sendCandidateError() {
- JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator, this.contentName);
- content.setTransportId(this.transportId);
- content.socks5transport().addChild("candidate-error");
- packet.setContent(content);
- this.sentCandidate = true;
- if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
- connect();
- }
- this.sendJinglePacket(packet);
- }
-
- public Jid getInitiator() {
- return this.initiator;
- }
-
- public Jid getResponder() {
- return this.responder;
- }
-
- public int getJingleStatus() {
- return this.mJingleStatus;
- }
-
- private boolean equalCandidateExists(JingleCandidate candidate) {
- for (JingleCandidate c : this.candidates) {
- if (c.equalValues(candidate)) {
- return true;
- }
- }
- return false;
- }
-
- private void mergeCandidate(JingleCandidate candidate) {
- for (JingleCandidate c : this.candidates) {
- if (c.equals(candidate)) {
- return;
- }
- }
- this.candidates.add(candidate);
- }
-
- private void mergeCandidates(List<JingleCandidate> candidates) {
- for (JingleCandidate c : candidates) {
- mergeCandidate(c);
- }
- }
-
- private JingleCandidate getCandidate(String cid) {
- for (JingleCandidate c : this.candidates) {
- if (c.getCid().equals(cid)) {
- return c;
- }
- }
- return null;
- }
-
- public void updateProgress(int i) {
- this.mProgress = i;
- mXmppConnectionService.updateConversationUi();
- }
-
- public String getTransportId() {
- return this.transportId;
- }
-
- public Content.Version getFtVersion() {
- return this.ftVersion;
- }
-
- interface OnProxyActivated {
- public void success();
-
- public void failed();
- }
-
- public boolean hasTransportId(String sid) {
- return sid.equals(this.transportId);
- }
-
- public JingleTransport getTransport() {
- return this.transport;
- }
-
- public boolean start() {
- if (account.getStatus() == Account.State.ONLINE) {
- if (mJingleStatus == JINGLE_STATUS_INITIATED) {
- new Thread(new Runnable() {
-
- @Override
- public void run() {
- sendAccept();
- }
- }).start();
- }
- return true;
- } else {
- return false;
- }
- }
-
- @Override
- public int getStatus() {
- return this.mStatus;
- }
-
- @Override
- public long getFileSize() {
- if (this.file != null) {
- return this.file.getExpectedSize();
- } else {
- return 0;
- }
- }
-
- @Override
- public int getProgress() {
- return this.mProgress;
- }
-
- public AbstractConnectionManager getConnectionManager() {
- return this.mJingleConnectionManager;
- }
+ this.message.setTransferable(null);
+ }
+ }
+ this.mJingleConnectionManager.finishConnection(this);
+ }
+
+ private void sendCancel() {
+ JinglePacket packet = bootstrapPacket("session-terminate");
+ Reason reason = new Reason();
+ reason.addChild("cancel");
+ packet.setReason(reason);
+ this.sendJinglePacket(packet);
+ }
+
+ private void connectNextCandidate() {
+ for (JingleCandidate candidate : this.candidates) {
+ if ((!connections.containsKey(candidate.getCid()) && (!candidate
+ .isOurs()))) {
+ this.connectWithCandidate(candidate);
+ return;
+ }
+ }
+ this.sendCandidateError();
+ }
+
+ private void connectWithCandidate(final JingleCandidate candidate) {
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ this, candidate);
+ connections.put(candidate.getCid(), socksConnection);
+ socksConnection.connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d(Config.LOGTAG,
+ "connection failed with " + candidate.getHost() + ":"
+ + candidate.getPort());
+ connectNextCandidate();
+ }
+
+ @Override
+ public void established() {
+ Log.d(Config.LOGTAG,
+ "established connection with " + candidate.getHost()
+ + ":" + candidate.getPort());
+ sendCandidateUsed(candidate.getCid());
+ }
+ });
+ }
+
+ private void disconnectSocks5Connections() {
+ Iterator<Entry<String, JingleSocks5Transport>> it = this.connections
+ .entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<String, JingleSocks5Transport> pairs = it.next();
+ pairs.getValue().disconnect();
+ it.remove();
+ }
+ }
+
+ private void sendProxyActivated(String cid) {
+ JinglePacket packet = bootstrapPacket("transport-info");
+ Content content = new Content(this.contentCreator, this.contentName);
+ content.setTransportId(this.transportId);
+ content.socks5transport().addChild("activated")
+ .setAttribute("cid", cid);
+ packet.setContent(content);
+ this.sendJinglePacket(packet);
+ }
+
+ private void sendCandidateUsed(final String cid) {
+ JinglePacket packet = bootstrapPacket("transport-info");
+ Content content = new Content(this.contentCreator, this.contentName);
+ content.setTransportId(this.transportId);
+ content.socks5transport().addChild("candidate-used")
+ .setAttribute("cid", cid);
+ packet.setContent(content);
+ this.sentCandidate = true;
+ if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
+ connect();
+ }
+ this.sendJinglePacket(packet);
+ }
+
+ private void sendCandidateError() {
+ JinglePacket packet = bootstrapPacket("transport-info");
+ Content content = new Content(this.contentCreator, this.contentName);
+ content.setTransportId(this.transportId);
+ content.socks5transport().addChild("candidate-error");
+ packet.setContent(content);
+ this.sentCandidate = true;
+ if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) {
+ connect();
+ }
+ this.sendJinglePacket(packet);
+ }
+
+ public Jid getInitiator() {
+ return this.initiator;
+ }
+
+ public Jid getResponder() {
+ return this.responder;
+ }
+
+ public int getJingleStatus() {
+ return this.mJingleStatus;
+ }
+
+ private boolean equalCandidateExists(JingleCandidate candidate) {
+ for (JingleCandidate c : this.candidates) {
+ if (c.equalValues(candidate)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void mergeCandidate(JingleCandidate candidate) {
+ for (JingleCandidate c : this.candidates) {
+ if (c.equals(candidate)) {
+ return;
+ }
+ }
+ this.candidates.add(candidate);
+ }
+
+ private void mergeCandidates(List<JingleCandidate> candidates) {
+ for (JingleCandidate c : candidates) {
+ mergeCandidate(c);
+ }
+ }
+
+ private JingleCandidate getCandidate(String cid) {
+ for (JingleCandidate c : this.candidates) {
+ if (c.getCid().equals(cid)) {
+ return c;
+ }
+ }
+ return null;
+ }
+
+ public void updateProgress(int i) {
+ this.mProgress = i;
+ mXmppConnectionService.updateConversationUi();
+ }
+
+ public String getTransportId() {
+ return this.transportId;
+ }
+
+ public Content.Version getFtVersion() {
+ return this.ftVersion;
+ }
+
+ interface OnProxyActivated {
+ public void success();
+
+ public void failed();
+ }
+
+ public boolean hasTransportId(String sid) {
+ return sid.equals(this.transportId);
+ }
+
+ public JingleTransport getTransport() {
+ return this.transport;
+ }
+
+ public boolean start() {
+ if (account.getStatus() == Account.State.ONLINE) {
+ if (mJingleStatus == JINGLE_STATUS_INITIATED) {
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ sendAccept();
+ }
+ }).start();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int getStatus() {
+ return this.mStatus;
+ }
+
+ @Override
+ public long getFileSize() {
+ if (this.file != null) {
+ return this.file.getExpectedSize();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int getProgress() {
+ return this.mProgress;
+ }
+
+ public AbstractConnectionManager getConnectionManager() {
+ return this.mJingleConnectionManager;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java
index b8f96bded..d36bc52b0 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnectionManager.java
@@ -23,149 +23,149 @@ import de.pixart.messenger.xmpp.jingle.stanzas.JinglePacket;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class JingleConnectionManager extends AbstractConnectionManager {
- private List<JingleConnection> connections = new CopyOnWriteArrayList<>();
-
- private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
-
- @SuppressLint("TrulyRandom")
- private SecureRandom random = new SecureRandom();
-
- public JingleConnectionManager(XmppConnectionService service) {
- super(service);
- }
-
- public void deliverPacket(Account account, JinglePacket packet) {
- if (packet.isAction("session-initiate")) {
- JingleConnection connection = new JingleConnection(this);
- connection.init(account, packet);
- connections.add(connection);
- } else {
- for (JingleConnection connection : connections) {
- if (connection.getAccount() == account
- && connection.getSessionId().equals(
- packet.getSessionId())
- && connection.getCounterPart().equals(packet.getFrom())) {
- connection.deliverPacket(packet);
- return;
- }
- }
- IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
- Element error = response.addChild("error");
- error.setAttribute("type", "cancel");
- error.addChild("item-not-found",
- "urn:ietf:params:xml:ns:xmpp-stanzas");
- error.addChild("unknown-session", "urn:xmpp:jingle:errors:1");
- account.getXmppConnection().sendIqPacket(response, null);
- }
- }
-
- public JingleConnection createNewConnection(Message message) {
- Transferable old = message.getTransferable();
- if (old != null) {
- old.cancel();
- }
- JingleConnection connection = new JingleConnection(this);
- mXmppConnectionService.markMessage(message,Message.STATUS_WAITING);
- connection.init(message);
- this.connections.add(connection);
- return connection;
- }
-
- public JingleConnection createNewConnection(final JinglePacket packet) {
- JingleConnection connection = new JingleConnection(this);
- this.connections.add(connection);
- return connection;
- }
-
- public void finishConnection(JingleConnection connection) {
- this.connections.remove(connection);
- }
-
- public void getPrimaryCandidate(Account account,
- final OnPrimaryCandidateFound listener) {
- if (Config.DISABLE_PROXY_LOOKUP) {
- listener.onPrimaryCandidateFound(false, null);
- return;
- }
- if (!this.primaryCandidates.containsKey(account.getJid().toBareJid())) {
- final Jid proxy = account.getXmppConnection().findDiscoItemByFeature(Xmlns.BYTE_STREAMS);
- if (proxy != null) {
- IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
- iq.setTo(proxy);
- iq.query(Xmlns.BYTE_STREAMS);
- account.getXmppConnection().sendIqPacket(iq,new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element streamhost = packet.query().findChild("streamhost",Xmlns.BYTE_STREAMS);
- final String host = streamhost == null ? null : streamhost.getAttribute("host");
- final String port = streamhost == null ? null : streamhost.getAttribute("port");
- if (host != null && port != null) {
- try {
- JingleCandidate candidate = new JingleCandidate(nextRandomId(), true);
- candidate.setHost(host);
- candidate.setPort(Integer.parseInt(port));
- candidate.setType(JingleCandidate.TYPE_PROXY);
- candidate.setJid(proxy);
- candidate.setPriority(655360 + 65535);
- primaryCandidates.put(account.getJid().toBareJid(),candidate);
- listener.onPrimaryCandidateFound(true,candidate);
- } catch (final NumberFormatException e) {
- listener.onPrimaryCandidateFound(false,null);
- return;
- }
- } else {
- listener.onPrimaryCandidateFound(false,null);
- }
- }
- });
- } else {
- listener.onPrimaryCandidateFound(false, null);
- }
-
- } else {
- listener.onPrimaryCandidateFound(true,
- this.primaryCandidates.get(account.getJid().toBareJid()));
- }
- }
-
- public String nextRandomId() {
- return new BigInteger(50, random).toString(32);
- }
-
- public void deliverIbbPacket(Account account, IqPacket packet) {
- String sid = null;
- Element payload = null;
- if (packet.hasChild("open", "http://jabber.org/protocol/ibb")) {
- payload = packet.findChild("open", "http://jabber.org/protocol/ibb");
- sid = payload.getAttribute("sid");
- } else if (packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
- payload = packet.findChild("data", "http://jabber.org/protocol/ibb");
- sid = payload.getAttribute("sid");
- }
- if (sid != null) {
- for (JingleConnection connection : connections) {
- if (connection.getAccount() == account
- && connection.hasTransportId(sid)) {
- JingleTransport transport = connection.getTransport();
- if (transport instanceof JingleInbandTransport) {
- JingleInbandTransport inbandTransport = (JingleInbandTransport) transport;
- inbandTransport.deliverPayload(packet, payload);
- return;
- }
- }
- }
- Log.d(Config.LOGTAG,"couldn't deliver payload: " + payload.toString());
- } else {
- Log.d(Config.LOGTAG, "no sid found in incoming ibb packet");
- }
- }
-
- public void cancelInTransmission() {
- for (JingleConnection connection : this.connections) {
- if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
- connection.cancel();
- }
- }
- }
+ private List<JingleConnection> connections = new CopyOnWriteArrayList<>();
+
+ private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
+
+ @SuppressLint("TrulyRandom")
+ private SecureRandom random = new SecureRandom();
+
+ public JingleConnectionManager(XmppConnectionService service) {
+ super(service);
+ }
+
+ public void deliverPacket(Account account, JinglePacket packet) {
+ if (packet.isAction("session-initiate")) {
+ JingleConnection connection = new JingleConnection(this);
+ connection.init(account, packet);
+ connections.add(connection);
+ } else {
+ for (JingleConnection connection : connections) {
+ if (connection.getAccount() == account
+ && connection.getSessionId().equals(
+ packet.getSessionId())
+ && connection.getCounterPart().equals(packet.getFrom())) {
+ connection.deliverPacket(packet);
+ return;
+ }
+ }
+ IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
+ Element error = response.addChild("error");
+ error.setAttribute("type", "cancel");
+ error.addChild("item-not-found",
+ "urn:ietf:params:xml:ns:xmpp-stanzas");
+ error.addChild("unknown-session", "urn:xmpp:jingle:errors:1");
+ account.getXmppConnection().sendIqPacket(response, null);
+ }
+ }
+
+ public JingleConnection createNewConnection(Message message) {
+ Transferable old = message.getTransferable();
+ if (old != null) {
+ old.cancel();
+ }
+ JingleConnection connection = new JingleConnection(this);
+ mXmppConnectionService.markMessage(message, Message.STATUS_WAITING);
+ connection.init(message);
+ this.connections.add(connection);
+ return connection;
+ }
+
+ public JingleConnection createNewConnection(final JinglePacket packet) {
+ JingleConnection connection = new JingleConnection(this);
+ this.connections.add(connection);
+ return connection;
+ }
+
+ public void finishConnection(JingleConnection connection) {
+ this.connections.remove(connection);
+ }
+
+ public void getPrimaryCandidate(Account account,
+ final OnPrimaryCandidateFound listener) {
+ if (Config.DISABLE_PROXY_LOOKUP) {
+ listener.onPrimaryCandidateFound(false, null);
+ return;
+ }
+ if (!this.primaryCandidates.containsKey(account.getJid().toBareJid())) {
+ final Jid proxy = account.getXmppConnection().findDiscoItemByFeature(Xmlns.BYTE_STREAMS);
+ if (proxy != null) {
+ IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
+ iq.setTo(proxy);
+ iq.query(Xmlns.BYTE_STREAMS);
+ account.getXmppConnection().sendIqPacket(iq, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element streamhost = packet.query().findChild("streamhost", Xmlns.BYTE_STREAMS);
+ final String host = streamhost == null ? null : streamhost.getAttribute("host");
+ final String port = streamhost == null ? null : streamhost.getAttribute("port");
+ if (host != null && port != null) {
+ try {
+ JingleCandidate candidate = new JingleCandidate(nextRandomId(), true);
+ candidate.setHost(host);
+ candidate.setPort(Integer.parseInt(port));
+ candidate.setType(JingleCandidate.TYPE_PROXY);
+ candidate.setJid(proxy);
+ candidate.setPriority(655360 + 65535);
+ primaryCandidates.put(account.getJid().toBareJid(), candidate);
+ listener.onPrimaryCandidateFound(true, candidate);
+ } catch (final NumberFormatException e) {
+ listener.onPrimaryCandidateFound(false, null);
+ return;
+ }
+ } else {
+ listener.onPrimaryCandidateFound(false, null);
+ }
+ }
+ });
+ } else {
+ listener.onPrimaryCandidateFound(false, null);
+ }
+
+ } else {
+ listener.onPrimaryCandidateFound(true,
+ this.primaryCandidates.get(account.getJid().toBareJid()));
+ }
+ }
+
+ public String nextRandomId() {
+ return new BigInteger(50, random).toString(32);
+ }
+
+ public void deliverIbbPacket(Account account, IqPacket packet) {
+ String sid = null;
+ Element payload = null;
+ if (packet.hasChild("open", "http://jabber.org/protocol/ibb")) {
+ payload = packet.findChild("open", "http://jabber.org/protocol/ibb");
+ sid = payload.getAttribute("sid");
+ } else if (packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
+ payload = packet.findChild("data", "http://jabber.org/protocol/ibb");
+ sid = payload.getAttribute("sid");
+ }
+ if (sid != null) {
+ for (JingleConnection connection : connections) {
+ if (connection.getAccount() == account
+ && connection.hasTransportId(sid)) {
+ JingleTransport transport = connection.getTransport();
+ if (transport instanceof JingleInbandTransport) {
+ JingleInbandTransport inbandTransport = (JingleInbandTransport) transport;
+ inbandTransport.deliverPayload(packet, payload);
+ return;
+ }
+ }
+ }
+ Log.d(Config.LOGTAG, "couldn't deliver payload: " + payload.toString());
+ } else {
+ Log.d(Config.LOGTAG, "no sid found in incoming ibb packet");
+ }
+ }
+
+ public void cancelInTransmission() {
+ for (JingleConnection connection : this.connections) {
+ if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
+ connection.cancel();
+ }
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java
index 4e45c2a03..b19910c9f 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleInbandTransport.java
@@ -22,218 +22,218 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class JingleInbandTransport extends JingleTransport {
- private Account account;
- private Jid counterpart;
- private int blockSize;
- private int seq = 0;
- private String sessionId;
-
- private boolean established = false;
-
- private boolean connected = true;
-
- private DownloadableFile file;
- private JingleConnection connection;
-
- private InputStream fileInputStream = null;
- private OutputStream fileOutputStream = null;
- private long remainingSize = 0;
- private long fileSize = 0;
- private MessageDigest digest;
-
- private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged;
-
- private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (connected && packet.getType() == IqPacket.TYPE.RESULT) {
- sendNextBlock();
- }
- }
- };
-
- public JingleInbandTransport(final JingleConnection connection, final String sid, final int blocksize) {
- this.connection = connection;
- this.account = connection.getAccount();
- this.counterpart = connection.getCounterPart();
- this.blockSize = blocksize;
- this.sessionId = sid;
- }
-
- public void connect(final OnTransportConnected callback) {
- IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.setTo(this.counterpart);
- Element open = iq.addChild("open", "http://jabber.org/protocol/ibb");
- open.setAttribute("sid", this.sessionId);
- open.setAttribute("stanza", "iq");
- open.setAttribute("block-size", Integer.toString(this.blockSize));
- this.connected = true;
- this.account.getXmppConnection().sendIqPacket(iq,
- new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account,
- IqPacket packet) {
- if (packet.getType() != IqPacket.TYPE.RESULT) {
- callback.failed();
- } else {
- callback.established();
- }
- }
- });
- }
-
- @Override
- public void receive(DownloadableFile file,
- OnFileTransmissionStatusChanged callback) {
- this.onFileTransmissionStatusChanged = callback;
- this.file = file;
- try {
- this.digest = MessageDigest.getInstance("SHA-1");
- digest.reset();
- file.getParentFile().mkdirs();
- file.createNewFile();
- this.fileOutputStream = connection.getFileOutputStream();
- if (this.fileOutputStream == null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream");
- callback.onFileTransferAborted();
- return;
- }
- this.remainingSize = this.fileSize = file.getExpectedSize();
- } catch (final NoSuchAlgorithmException | IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage());
- callback.onFileTransferAborted();
- }
+ private Account account;
+ private Jid counterpart;
+ private int blockSize;
+ private int seq = 0;
+ private String sessionId;
+
+ private boolean established = false;
+
+ private boolean connected = true;
+
+ private DownloadableFile file;
+ private JingleConnection connection;
+
+ private InputStream fileInputStream = null;
+ private OutputStream fileOutputStream = null;
+ private long remainingSize = 0;
+ private long fileSize = 0;
+ private MessageDigest digest;
+
+ private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged;
+
+ private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (connected && packet.getType() == IqPacket.TYPE.RESULT) {
+ sendNextBlock();
+ }
+ }
+ };
+
+ public JingleInbandTransport(final JingleConnection connection, final String sid, final int blocksize) {
+ this.connection = connection;
+ this.account = connection.getAccount();
+ this.counterpart = connection.getCounterPart();
+ this.blockSize = blocksize;
+ this.sessionId = sid;
}
- @Override
- public void send(DownloadableFile file,
- OnFileTransmissionStatusChanged callback) {
- this.onFileTransmissionStatusChanged = callback;
- this.file = file;
- try {
- this.remainingSize = this.file.getExpectedSize();
- this.fileSize = this.remainingSize;
- this.digest = MessageDigest.getInstance("SHA-1");
- this.digest.reset();
- fileInputStream = connection.getFileInputStream();
- if (fileInputStream == null) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream");
- callback.onFileTransferAborted();
- return;
- }
- if (this.connected) {
- this.sendNextBlock();
- }
- } catch (NoSuchAlgorithmException e) {
- callback.onFileTransferAborted();
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
- }
- }
-
- @Override
- public void disconnect() {
- this.connected = false;
- if (this.fileOutputStream != null) {
- try {
- this.fileOutputStream.close();
- } catch (IOException e) {
-
- }
- }
- if (this.fileInputStream != null) {
- try {
- this.fileInputStream.close();
- } catch (IOException e) {
-
- }
- }
- }
-
- private void sendNextBlock() {
- byte[] buffer = new byte[this.blockSize];
- try {
- int count = fileInputStream.read(buffer);
- if (count == -1) {
- file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- this.onFileTransmissionStatusChanged.onFileTransmitted(file);
- fileInputStream.close();
- return;
- } else if (count != buffer.length) {
- int rem = fileInputStream.read(buffer,count,buffer.length-count);
- if (rem > 0) {
- count += rem;
- }
- }
- this.remainingSize -= count;
- this.digest.update(buffer,0,count);
- String base64 = Base64.encodeToString(buffer,0,count, Base64.NO_WRAP);
- IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.setTo(this.counterpart);
- Element data = iq.addChild("data", "http://jabber.org/protocol/ibb");
- data.setAttribute("seq", Integer.toString(this.seq));
- data.setAttribute("block-size", Integer.toString(this.blockSize));
- data.setAttribute("sid", this.sessionId);
- data.setContent(base64);
- this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived);
- this.account.getXmppConnection().r(); //don't fill up stanza queue too much
- this.seq++;
- if (this.remainingSize > 0) {
- connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
- } else {
- file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- this.onFileTransmissionStatusChanged.onFileTransmitted(file);
- fileInputStream.close();
- }
- } catch (IOException e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
- FileBackend.close(fileInputStream);
- this.onFileTransmissionStatusChanged.onFileTransferAborted();
- }
- }
-
- private void receiveNextBlock(String data) {
- try {
- byte[] buffer = Base64.decode(data, Base64.NO_WRAP);
- if (this.remainingSize < buffer.length) {
- buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize);
- }
- this.remainingSize -= buffer.length;
- this.fileOutputStream.write(buffer);
- this.digest.update(buffer);
- if (this.remainingSize <= 0) {
- file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- fileOutputStream.flush();
- fileOutputStream.close();
- this.onFileTransmissionStatusChanged.onFileTransmitted(file);
- } else {
- connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
- }
- } catch (Exception e) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
- FileBackend.close(fileOutputStream);
- this.onFileTransmissionStatusChanged.onFileTransferAborted();
- }
- }
-
- public void deliverPayload(IqPacket packet, Element payload) {
- if (payload.getName().equals("open")) {
- if (!established) {
- established = true;
- connected = true;
- this.receiveNextBlock("");
- this.account.getXmppConnection().sendIqPacket(
- packet.generateResponse(IqPacket.TYPE.RESULT), null);
- } else {
- this.account.getXmppConnection().sendIqPacket(
- packet.generateResponse(IqPacket.TYPE.ERROR), null);
- }
- } else if (connected && payload.getName().equals("data")) {
- this.receiveNextBlock(payload.getContent());
- this.account.getXmppConnection().sendIqPacket(
- packet.generateResponse(IqPacket.TYPE.RESULT), null);
- } else {
- // TODO some sort of exception
- }
- }
+ public void connect(final OnTransportConnected callback) {
+ IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.setTo(this.counterpart);
+ Element open = iq.addChild("open", "http://jabber.org/protocol/ibb");
+ open.setAttribute("sid", this.sessionId);
+ open.setAttribute("stanza", "iq");
+ open.setAttribute("block-size", Integer.toString(this.blockSize));
+ this.connected = true;
+ this.account.getXmppConnection().sendIqPacket(iq,
+ new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account,
+ IqPacket packet) {
+ if (packet.getType() != IqPacket.TYPE.RESULT) {
+ callback.failed();
+ } else {
+ callback.established();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void receive(DownloadableFile file,
+ OnFileTransmissionStatusChanged callback) {
+ this.onFileTransmissionStatusChanged = callback;
+ this.file = file;
+ try {
+ this.digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ this.fileOutputStream = connection.getFileOutputStream();
+ if (this.fileOutputStream == null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not create output stream");
+ callback.onFileTransferAborted();
+ return;
+ }
+ this.remainingSize = this.fileSize = file.getExpectedSize();
+ } catch (final NoSuchAlgorithmException | IOException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + " " + e.getMessage());
+ callback.onFileTransferAborted();
+ }
+ }
+
+ @Override
+ public void send(DownloadableFile file,
+ OnFileTransmissionStatusChanged callback) {
+ this.onFileTransmissionStatusChanged = callback;
+ this.file = file;
+ try {
+ this.remainingSize = this.file.getExpectedSize();
+ this.fileSize = this.remainingSize;
+ this.digest = MessageDigest.getInstance("SHA-1");
+ this.digest.reset();
+ fileInputStream = connection.getFileInputStream();
+ if (fileInputStream == null) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could no create input stream");
+ callback.onFileTransferAborted();
+ return;
+ }
+ if (this.connected) {
+ this.sendNextBlock();
+ }
+ } catch (NoSuchAlgorithmException e) {
+ callback.onFileTransferAborted();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": " + e.getMessage());
+ }
+ }
+
+ @Override
+ public void disconnect() {
+ this.connected = false;
+ if (this.fileOutputStream != null) {
+ try {
+ this.fileOutputStream.close();
+ } catch (IOException e) {
+
+ }
+ }
+ if (this.fileInputStream != null) {
+ try {
+ this.fileInputStream.close();
+ } catch (IOException e) {
+
+ }
+ }
+ }
+
+ private void sendNextBlock() {
+ byte[] buffer = new byte[this.blockSize];
+ try {
+ int count = fileInputStream.read(buffer);
+ if (count == -1) {
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ this.onFileTransmissionStatusChanged.onFileTransmitted(file);
+ fileInputStream.close();
+ return;
+ } else if (count != buffer.length) {
+ int rem = fileInputStream.read(buffer, count, buffer.length - count);
+ if (rem > 0) {
+ count += rem;
+ }
+ }
+ this.remainingSize -= count;
+ this.digest.update(buffer, 0, count);
+ String base64 = Base64.encodeToString(buffer, 0, count, Base64.NO_WRAP);
+ IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.setTo(this.counterpart);
+ Element data = iq.addChild("data", "http://jabber.org/protocol/ibb");
+ data.setAttribute("seq", Integer.toString(this.seq));
+ data.setAttribute("block-size", Integer.toString(this.blockSize));
+ data.setAttribute("sid", this.sessionId);
+ data.setContent(base64);
+ this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived);
+ this.account.getXmppConnection().r(); //don't fill up stanza queue too much
+ this.seq++;
+ if (this.remainingSize > 0) {
+ connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
+ } else {
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ this.onFileTransmissionStatusChanged.onFileTransmitted(file);
+ fileInputStream.close();
+ }
+ } catch (IOException e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": " + e.getMessage());
+ FileBackend.close(fileInputStream);
+ this.onFileTransmissionStatusChanged.onFileTransferAborted();
+ }
+ }
+
+ private void receiveNextBlock(String data) {
+ try {
+ byte[] buffer = Base64.decode(data, Base64.NO_WRAP);
+ if (this.remainingSize < buffer.length) {
+ buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize);
+ }
+ this.remainingSize -= buffer.length;
+ this.fileOutputStream.write(buffer);
+ this.digest.update(buffer);
+ if (this.remainingSize <= 0) {
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ fileOutputStream.flush();
+ fileOutputStream.close();
+ this.onFileTransmissionStatusChanged.onFileTransmitted(file);
+ } else {
+ connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
+ }
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": " + e.getMessage());
+ FileBackend.close(fileOutputStream);
+ this.onFileTransmissionStatusChanged.onFileTransferAborted();
+ }
+ }
+
+ public void deliverPayload(IqPacket packet, Element payload) {
+ if (payload.getName().equals("open")) {
+ if (!established) {
+ established = true;
+ connected = true;
+ this.receiveNextBlock("");
+ this.account.getXmppConnection().sendIqPacket(
+ packet.generateResponse(IqPacket.TYPE.RESULT), null);
+ } else {
+ this.account.getXmppConnection().sendIqPacket(
+ packet.generateResponse(IqPacket.TYPE.ERROR), null);
+ }
+ } else if (connected && payload.getName().equals("data")) {
+ this.receiveNextBlock(payload.getContent());
+ this.account.getXmppConnection().sendIqPacket(
+ packet.generateResponse(IqPacket.TYPE.RESULT), null);
+ } else {
+ // TODO some sort of exception
+ }
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java
index 4f3e78394..57d1afeba 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java
@@ -20,191 +20,191 @@ import de.pixart.messenger.utils.SocksSocketFactory;
import de.pixart.messenger.xmpp.jingle.stanzas.Content;
public class JingleSocks5Transport extends JingleTransport {
- private JingleCandidate candidate;
- private JingleConnection connection;
- private String destination;
- private OutputStream outputStream;
- private InputStream inputStream;
- private boolean isEstablished = false;
- private boolean activated = false;
- protected Socket socket;
-
- public JingleSocks5Transport(JingleConnection jingleConnection,
- JingleCandidate candidate) {
- this.candidate = candidate;
- this.connection = jingleConnection;
- try {
- MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
- StringBuilder destBuilder = new StringBuilder();
- if (jingleConnection.getFtVersion() == Content.Version.FT_3) {
- Log.d(Config.LOGTAG,this.connection.getAccount().getJid().toBareJid()+": using session Id instead of transport Id for proxy destination");
- destBuilder.append(jingleConnection.getSessionId());
- } else {
- destBuilder.append(jingleConnection.getTransportId());
- }
- if (candidate.isOurs()) {
- destBuilder.append(jingleConnection.getAccount().getJid());
- destBuilder.append(jingleConnection.getCounterPart());
- } else {
- destBuilder.append(jingleConnection.getCounterPart());
- destBuilder.append(jingleConnection.getAccount().getJid());
- }
- mDigest.reset();
- this.destination = CryptoHelper.bytesToHex(mDigest
- .digest(destBuilder.toString().getBytes()));
- } catch (NoSuchAlgorithmException e) {
-
- }
- }
-
- public void connect(final OnTransportConnected callback) {
- new Thread(new Runnable() {
-
- @Override
- public void run() {
- try {
- final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect();
- if (useTor) {
- socket = SocksSocketFactory.createSocketOverTor(candidate.getHost(),candidate.getPort());
- } else {
- socket = new Socket();
- SocketAddress address = new InetSocketAddress(candidate.getHost(),candidate.getPort());
- socket.connect(address,Config.SOCKET_TIMEOUT * 1000);
- }
- inputStream = socket.getInputStream();
- outputStream = socket.getOutputStream();
- SocksSocketFactory.createSocksConnection(socket,destination,0);
- isEstablished = true;
- callback.established();
- } catch (IOException e) {
- callback.failed();
- }
- }
- }).start();
-
- }
-
- public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
- new Thread(new Runnable() {
-
- @Override
- public void run() {
- InputStream fileInputStream = null;
- final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_"+connection.getSessionId());
- try {
- wakeLock.acquire();
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- digest.reset();
- fileInputStream = connection.getFileInputStream();
- if (fileInputStream == null) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
- callback.onFileTransferAborted();
- return;
- }
- long size = file.getExpectedSize();
- long transmitted = 0;
- int count;
- byte[] buffer = new byte[8192];
- while ((count = fileInputStream.read(buffer)) > 0) {
- outputStream.write(buffer, 0, count);
- digest.update(buffer, 0, count);
- transmitted += count;
- connection.updateProgress((int) ((((double) transmitted) / size) * 100));
- }
- outputStream.flush();
- file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- if (callback != null) {
- callback.onFileTransmitted(file);
- }
- } catch (Exception e) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
- callback.onFileTransferAborted();
- } finally {
- FileBackend.close(fileInputStream);
- wakeLock.release();
- }
- }
- }).start();
-
- }
-
- public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
- new Thread(new Runnable() {
-
- @Override
- public void run() {
- OutputStream fileOutputStream = null;
- final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_"+connection.getSessionId());
- try {
- wakeLock.acquire();
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- digest.reset();
- //inputStream.skip(45);
- socket.setSoTimeout(30000);
- file.getParentFile().mkdirs();
- file.createNewFile();
- fileOutputStream = connection.getFileOutputStream();
- if (fileOutputStream == null) {
- callback.onFileTransferAborted();
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
- return;
- }
- double size = file.getExpectedSize();
- long remainingSize = file.getExpectedSize();
- byte[] buffer = new byte[8192];
- int count;
- while (remainingSize > 0) {
- count = inputStream.read(buffer);
- if (count == -1) {
- callback.onFileTransferAborted();
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining");
- return;
- } else {
- fileOutputStream.write(buffer, 0, count);
- digest.update(buffer, 0, count);
- remainingSize -= count;
- }
- connection.updateProgress((int) (((size - remainingSize) / size) * 100));
- }
- fileOutputStream.flush();
- fileOutputStream.close();
- file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- callback.onFileTransmitted(file);
- } catch (Exception e) {
- Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
- callback.onFileTransferAborted();
- } finally {
- wakeLock.release();
- FileBackend.close(fileOutputStream);
- FileBackend.close(inputStream);
- }
- }
- }).start();
- }
-
- public boolean isProxy() {
- return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
- }
-
- public boolean needsActivation() {
- return (this.isProxy() && !this.activated);
- }
-
- public void disconnect() {
- FileBackend.close(inputStream);
- FileBackend.close(outputStream);
- FileBackend.close(socket);
- }
-
- public boolean isEstablished() {
- return this.isEstablished;
- }
-
- public JingleCandidate getCandidate() {
- return this.candidate;
- }
-
- public void setActivated(boolean activated) {
- this.activated = activated;
- }
+ private JingleCandidate candidate;
+ private JingleConnection connection;
+ private String destination;
+ private OutputStream outputStream;
+ private InputStream inputStream;
+ private boolean isEstablished = false;
+ private boolean activated = false;
+ protected Socket socket;
+
+ public JingleSocks5Transport(JingleConnection jingleConnection,
+ JingleCandidate candidate) {
+ this.candidate = candidate;
+ this.connection = jingleConnection;
+ try {
+ MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
+ StringBuilder destBuilder = new StringBuilder();
+ if (jingleConnection.getFtVersion() == Content.Version.FT_3) {
+ Log.d(Config.LOGTAG, this.connection.getAccount().getJid().toBareJid() + ": using session Id instead of transport Id for proxy destination");
+ destBuilder.append(jingleConnection.getSessionId());
+ } else {
+ destBuilder.append(jingleConnection.getTransportId());
+ }
+ if (candidate.isOurs()) {
+ destBuilder.append(jingleConnection.getAccount().getJid());
+ destBuilder.append(jingleConnection.getCounterPart());
+ } else {
+ destBuilder.append(jingleConnection.getCounterPart());
+ destBuilder.append(jingleConnection.getAccount().getJid());
+ }
+ mDigest.reset();
+ this.destination = CryptoHelper.bytesToHex(mDigest
+ .digest(destBuilder.toString().getBytes()));
+ } catch (NoSuchAlgorithmException e) {
+
+ }
+ }
+
+ public void connect(final OnTransportConnected callback) {
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect();
+ if (useTor) {
+ socket = SocksSocketFactory.createSocketOverTor(candidate.getHost(), candidate.getPort());
+ } else {
+ socket = new Socket();
+ SocketAddress address = new InetSocketAddress(candidate.getHost(), candidate.getPort());
+ socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
+ }
+ inputStream = socket.getInputStream();
+ outputStream = socket.getOutputStream();
+ SocksSocketFactory.createSocksConnection(socket, destination, 0);
+ isEstablished = true;
+ callback.established();
+ } catch (IOException e) {
+ callback.failed();
+ }
+ }
+ }).start();
+
+ }
+
+ public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ InputStream fileInputStream = null;
+ final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId());
+ try {
+ wakeLock.acquire();
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ fileInputStream = connection.getFileInputStream();
+ if (fileInputStream == null) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
+ callback.onFileTransferAborted();
+ return;
+ }
+ long size = file.getExpectedSize();
+ long transmitted = 0;
+ int count;
+ byte[] buffer = new byte[8192];
+ while ((count = fileInputStream.read(buffer)) > 0) {
+ outputStream.write(buffer, 0, count);
+ digest.update(buffer, 0, count);
+ transmitted += count;
+ connection.updateProgress((int) ((((double) transmitted) / size) * 100));
+ }
+ outputStream.flush();
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ if (callback != null) {
+ callback.onFileTransmitted(file);
+ }
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": " + e.getMessage());
+ callback.onFileTransferAborted();
+ } finally {
+ FileBackend.close(fileInputStream);
+ wakeLock.release();
+ }
+ }
+ }).start();
+
+ }
+
+ public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ OutputStream fileOutputStream = null;
+ final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_" + connection.getSessionId());
+ try {
+ wakeLock.acquire();
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ //inputStream.skip(45);
+ socket.setSoTimeout(30000);
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ fileOutputStream = connection.getFileOutputStream();
+ if (fileOutputStream == null) {
+ callback.onFileTransferAborted();
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
+ return;
+ }
+ double size = file.getExpectedSize();
+ long remainingSize = file.getExpectedSize();
+ byte[] buffer = new byte[8192];
+ int count;
+ while (remainingSize > 0) {
+ count = inputStream.read(buffer);
+ if (count == -1) {
+ callback.onFileTransferAborted();
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with " + remainingSize + " bytes remaining");
+ return;
+ } else {
+ fileOutputStream.write(buffer, 0, count);
+ digest.update(buffer, 0, count);
+ remainingSize -= count;
+ }
+ connection.updateProgress((int) (((size - remainingSize) / size) * 100));
+ }
+ fileOutputStream.flush();
+ fileOutputStream.close();
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ callback.onFileTransmitted(file);
+ } catch (Exception e) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": " + e.getMessage());
+ callback.onFileTransferAborted();
+ } finally {
+ wakeLock.release();
+ FileBackend.close(fileOutputStream);
+ FileBackend.close(inputStream);
+ }
+ }
+ }).start();
+ }
+
+ public boolean isProxy() {
+ return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
+ }
+
+ public boolean needsActivation() {
+ return (this.isProxy() && !this.activated);
+ }
+
+ public void disconnect() {
+ FileBackend.close(inputStream);
+ FileBackend.close(outputStream);
+ FileBackend.close(socket);
+ }
+
+ public boolean isEstablished() {
+ return this.isEstablished;
+ }
+
+ public JingleCandidate getCandidate() {
+ return this.candidate;
+ }
+
+ public void setActivated(boolean activated) {
+ this.activated = activated;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleTransport.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleTransport.java
index 1ee97b415..b9adb4632 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleTransport.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleTransport.java
@@ -3,13 +3,13 @@ package de.pixart.messenger.xmpp.jingle;
import de.pixart.messenger.entities.DownloadableFile;
public abstract class JingleTransport {
- public abstract void connect(final OnTransportConnected callback);
+ public abstract void connect(final OnTransportConnected callback);
- public abstract void receive(final DownloadableFile file,
- final OnFileTransmissionStatusChanged callback);
+ public abstract void receive(final DownloadableFile file,
+ final OnFileTransmissionStatusChanged callback);
- public abstract void send(final DownloadableFile file,
- final OnFileTransmissionStatusChanged callback);
+ public abstract void send(final DownloadableFile file,
+ final OnFileTransmissionStatusChanged callback);
- public abstract void disconnect();
+ public abstract void disconnect();
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/OnFileTransmissionStatusChanged.java b/src/main/java/de/pixart/messenger/xmpp/jingle/OnFileTransmissionStatusChanged.java
index 6bbf7111a..65f9726d8 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/OnFileTransmissionStatusChanged.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/OnFileTransmissionStatusChanged.java
@@ -3,7 +3,7 @@ package de.pixart.messenger.xmpp.jingle;
import de.pixart.messenger.entities.DownloadableFile;
public interface OnFileTransmissionStatusChanged {
- void onFileTransmitted(DownloadableFile file);
+ void onFileTransmitted(DownloadableFile file);
- void onFileTransferAborted();
+ void onFileTransferAborted();
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/OnJinglePacketReceived.java b/src/main/java/de/pixart/messenger/xmpp/jingle/OnJinglePacketReceived.java
index 82dfefa5e..4cacac74c 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/OnJinglePacketReceived.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/OnJinglePacketReceived.java
@@ -5,5 +5,5 @@ import de.pixart.messenger.xmpp.PacketReceived;
import de.pixart.messenger.xmpp.jingle.stanzas.JinglePacket;
public interface OnJinglePacketReceived extends PacketReceived {
- void onJinglePacketReceived(Account account, JinglePacket packet);
+ void onJinglePacketReceived(Account account, JinglePacket packet);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/OnPrimaryCandidateFound.java b/src/main/java/de/pixart/messenger/xmpp/jingle/OnPrimaryCandidateFound.java
index fad7bb4d4..b1fb0b02a 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/OnPrimaryCandidateFound.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/OnPrimaryCandidateFound.java
@@ -1,5 +1,5 @@
package de.pixart.messenger.xmpp.jingle;
public interface OnPrimaryCandidateFound {
- void onPrimaryCandidateFound(boolean success, JingleCandidate canditate);
+ void onPrimaryCandidateFound(boolean success, JingleCandidate canditate);
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/OnTransportConnected.java b/src/main/java/de/pixart/messenger/xmpp/jingle/OnTransportConnected.java
index 10efd76d7..11a3eed5e 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/OnTransportConnected.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/OnTransportConnected.java
@@ -1,7 +1,7 @@
package de.pixart.messenger.xmpp.jingle;
public interface OnTransportConnected {
- public void failed();
+ public void failed();
- public void established();
+ public void established();
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Content.java b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Content.java
index bef32926f..fc7231e30 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Content.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Content.java
@@ -5,125 +5,125 @@ import de.pixart.messenger.xml.Element;
public class Content extends Element {
- public enum Version {
- FT_3("urn:xmpp:jingle:apps:file-transfer:3"),
- FT_4("urn:xmpp:jingle:apps:file-transfer:4");
-
- private final String namespace;
-
- Version(String namespace) {
- this.namespace = namespace;
- }
-
- public String getNamespace() {
- return namespace;
- }
- }
-
- private String transportId;
-
- public Content() {
- super("content");
- }
-
- public Content(String creator, String name) {
- super("content");
- this.setAttribute("creator", creator);
- this.setAttribute("name", name);
- }
-
- public Version getVersion() {
- if (hasChild("description", Version.FT_3.namespace)) {
- return Version.FT_3;
- } else if (hasChild("description" , Version.FT_4.namespace)) {
- return Version.FT_4;
- }
- return null;
- }
-
- public void setTransportId(String sid) {
- this.transportId = sid;
- }
-
- public Element setFileOffer(DownloadableFile actualFile, boolean otr, Version version) {
- Element description = this.addChild("description", version.namespace);
- Element file;
- if (version == Version.FT_3) {
- Element offer = description.addChild("offer");
- file = offer.addChild("file");
- } else {
- file = description.addChild("file");
- }
- file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize()));
- if (otr) {
- file.addChild("name").setContent(actualFile.getName() + ".otr");
- } else {
- file.addChild("name").setContent(actualFile.getName());
- }
- return file;
- }
-
- public Element getFileOffer(Version version) {
- Element description = this.findChild("description", version.namespace);
- if (description == null) {
- return null;
- }
- if (version == Version.FT_3) {
- Element offer = description.findChild("offer");
- if (offer == null) {
- return null;
- }
- return offer.findChild("file");
- } else {
- return description.findChild("file");
- }
- }
-
- public void setFileOffer(Element fileOffer, Version version) {
- Element description = this.addChild("description", version.namespace);
- if (version == Version.FT_3) {
- description.addChild("offer").addChild(fileOffer);
- } else {
- description.addChild(fileOffer);
- }
- }
-
- public String getTransportId() {
- if (hasSocks5Transport()) {
- this.transportId = socks5transport().getAttribute("sid");
- } else if (hasIbbTransport()) {
- this.transportId = ibbTransport().getAttribute("sid");
- }
- return this.transportId;
- }
-
- public Element socks5transport() {
- Element transport = this.findChild("transport",
- "urn:xmpp:jingle:transports:s5b:1");
- if (transport == null) {
- transport = this.addChild("transport",
- "urn:xmpp:jingle:transports:s5b:1");
- transport.setAttribute("sid", this.transportId);
- }
- return transport;
- }
-
- public Element ibbTransport() {
- Element transport = this.findChild("transport",
- "urn:xmpp:jingle:transports:ibb:1");
- if (transport == null) {
- transport = this.addChild("transport",
- "urn:xmpp:jingle:transports:ibb:1");
- transport.setAttribute("sid", this.transportId);
- }
- return transport;
- }
-
- public boolean hasSocks5Transport() {
- return this.hasChild("transport", "urn:xmpp:jingle:transports:s5b:1");
- }
-
- public boolean hasIbbTransport() {
- return this.hasChild("transport", "urn:xmpp:jingle:transports:ibb:1");
- }
+ public enum Version {
+ FT_3("urn:xmpp:jingle:apps:file-transfer:3"),
+ FT_4("urn:xmpp:jingle:apps:file-transfer:4");
+
+ private final String namespace;
+
+ Version(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+ }
+
+ private String transportId;
+
+ public Content() {
+ super("content");
+ }
+
+ public Content(String creator, String name) {
+ super("content");
+ this.setAttribute("creator", creator);
+ this.setAttribute("name", name);
+ }
+
+ public Version getVersion() {
+ if (hasChild("description", Version.FT_3.namespace)) {
+ return Version.FT_3;
+ } else if (hasChild("description", Version.FT_4.namespace)) {
+ return Version.FT_4;
+ }
+ return null;
+ }
+
+ public void setTransportId(String sid) {
+ this.transportId = sid;
+ }
+
+ public Element setFileOffer(DownloadableFile actualFile, boolean otr, Version version) {
+ Element description = this.addChild("description", version.namespace);
+ Element file;
+ if (version == Version.FT_3) {
+ Element offer = description.addChild("offer");
+ file = offer.addChild("file");
+ } else {
+ file = description.addChild("file");
+ }
+ file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize()));
+ if (otr) {
+ file.addChild("name").setContent(actualFile.getName() + ".otr");
+ } else {
+ file.addChild("name").setContent(actualFile.getName());
+ }
+ return file;
+ }
+
+ public Element getFileOffer(Version version) {
+ Element description = this.findChild("description", version.namespace);
+ if (description == null) {
+ return null;
+ }
+ if (version == Version.FT_3) {
+ Element offer = description.findChild("offer");
+ if (offer == null) {
+ return null;
+ }
+ return offer.findChild("file");
+ } else {
+ return description.findChild("file");
+ }
+ }
+
+ public void setFileOffer(Element fileOffer, Version version) {
+ Element description = this.addChild("description", version.namespace);
+ if (version == Version.FT_3) {
+ description.addChild("offer").addChild(fileOffer);
+ } else {
+ description.addChild(fileOffer);
+ }
+ }
+
+ public String getTransportId() {
+ if (hasSocks5Transport()) {
+ this.transportId = socks5transport().getAttribute("sid");
+ } else if (hasIbbTransport()) {
+ this.transportId = ibbTransport().getAttribute("sid");
+ }
+ return this.transportId;
+ }
+
+ public Element socks5transport() {
+ Element transport = this.findChild("transport",
+ "urn:xmpp:jingle:transports:s5b:1");
+ if (transport == null) {
+ transport = this.addChild("transport",
+ "urn:xmpp:jingle:transports:s5b:1");
+ transport.setAttribute("sid", this.transportId);
+ }
+ return transport;
+ }
+
+ public Element ibbTransport() {
+ Element transport = this.findChild("transport",
+ "urn:xmpp:jingle:transports:ibb:1");
+ if (transport == null) {
+ transport = this.addChild("transport",
+ "urn:xmpp:jingle:transports:ibb:1");
+ transport.setAttribute("sid", this.transportId);
+ }
+ return transport;
+ }
+
+ public boolean hasSocks5Transport() {
+ return this.hasChild("transport", "urn:xmpp:jingle:transports:s5b:1");
+ }
+
+ public boolean hasIbbTransport() {
+ return this.hasChild("transport", "urn:xmpp:jingle:transports:ibb:1");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/JinglePacket.java b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/JinglePacket.java
index 971ab8e1b..de856772a 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/JinglePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/JinglePacket.java
@@ -5,92 +5,92 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class JinglePacket extends IqPacket {
- Content content = null;
- Reason reason = null;
- Element jingle = new Element("jingle");
+ Content content = null;
+ Reason reason = null;
+ Element jingle = new Element("jingle");
- @Override
- public Element addChild(Element child) {
- if ("jingle".equals(child.getName())) {
- Element contentElement = child.findChild("content");
- if (contentElement != null) {
- this.content = new Content();
- this.content.setChildren(contentElement.getChildren());
- this.content.setAttributes(contentElement.getAttributes());
- }
- Element reasonElement = child.findChild("reason");
- if (reasonElement != null) {
- this.reason = new Reason();
- this.reason.setChildren(reasonElement.getChildren());
- this.reason.setAttributes(reasonElement.getAttributes());
- }
- this.jingle.setAttributes(child.getAttributes());
- }
- return child;
- }
+ @Override
+ public Element addChild(Element child) {
+ if ("jingle".equals(child.getName())) {
+ Element contentElement = child.findChild("content");
+ if (contentElement != null) {
+ this.content = new Content();
+ this.content.setChildren(contentElement.getChildren());
+ this.content.setAttributes(contentElement.getAttributes());
+ }
+ Element reasonElement = child.findChild("reason");
+ if (reasonElement != null) {
+ this.reason = new Reason();
+ this.reason.setChildren(reasonElement.getChildren());
+ this.reason.setAttributes(reasonElement.getAttributes());
+ }
+ this.jingle.setAttributes(child.getAttributes());
+ }
+ return child;
+ }
- public JinglePacket setContent(Content content) {
- this.content = content;
- return this;
- }
+ public JinglePacket setContent(Content content) {
+ this.content = content;
+ return this;
+ }
- public Content getJingleContent() {
- if (this.content == null) {
- this.content = new Content();
- }
- return this.content;
- }
+ public Content getJingleContent() {
+ if (this.content == null) {
+ this.content = new Content();
+ }
+ return this.content;
+ }
- public JinglePacket setReason(Reason reason) {
- this.reason = reason;
- return this;
- }
+ public JinglePacket setReason(Reason reason) {
+ this.reason = reason;
+ return this;
+ }
- public Reason getReason() {
- return this.reason;
- }
+ public Reason getReason() {
+ return this.reason;
+ }
- private void build() {
- this.children.clear();
- this.jingle.clearChildren();
- this.jingle.setAttribute("xmlns", "urn:xmpp:jingle:1");
- if (this.content != null) {
- jingle.addChild(this.content);
- }
- if (this.reason != null) {
- jingle.addChild(this.reason);
- }
- this.children.add(jingle);
- this.setAttribute("type", "set");
- }
+ private void build() {
+ this.children.clear();
+ this.jingle.clearChildren();
+ this.jingle.setAttribute("xmlns", "urn:xmpp:jingle:1");
+ if (this.content != null) {
+ jingle.addChild(this.content);
+ }
+ if (this.reason != null) {
+ jingle.addChild(this.reason);
+ }
+ this.children.add(jingle);
+ this.setAttribute("type", "set");
+ }
- public String getSessionId() {
- return this.jingle.getAttribute("sid");
- }
+ public String getSessionId() {
+ return this.jingle.getAttribute("sid");
+ }
- public void setSessionId(String sid) {
- this.jingle.setAttribute("sid", sid);
- }
+ public void setSessionId(String sid) {
+ this.jingle.setAttribute("sid", sid);
+ }
- @Override
- public String toString() {
- this.build();
- return super.toString();
- }
+ @Override
+ public String toString() {
+ this.build();
+ return super.toString();
+ }
- public void setAction(String action) {
- this.jingle.setAttribute("action", action);
- }
+ public void setAction(String action) {
+ this.jingle.setAttribute("action", action);
+ }
- public String getAction() {
- return this.jingle.getAttribute("action");
- }
+ public String getAction() {
+ return this.jingle.getAttribute("action");
+ }
- public void setInitiator(final Jid initiator) {
- this.jingle.setAttribute("initiator", initiator.toString());
- }
+ public void setInitiator(final Jid initiator) {
+ this.jingle.setAttribute("initiator", initiator.toString());
+ }
- public boolean isAction(String action) {
- return action.equalsIgnoreCase(this.getAction());
- }
+ public boolean isAction(String action) {
+ return action.equalsIgnoreCase(this.getAction());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Reason.java b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Reason.java
index 3c77a91a6..19f27bc40 100644
--- a/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Reason.java
+++ b/src/main/java/de/pixart/messenger/xmpp/jingle/stanzas/Reason.java
@@ -3,11 +3,11 @@ package de.pixart.messenger.xmpp.jingle.stanzas;
import de.pixart.messenger.xml.Element;
public class Reason extends Element {
- private Reason(String name) {
- super(name);
- }
+ private Reason(String name) {
+ super(name);
+ }
- public Reason() {
- super("reason");
- }
+ public Reason() {
+ super("reason");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/pep/Avatar.java b/src/main/java/de/pixart/messenger/xmpp/pep/Avatar.java
index 1f28cd92d..655eabfc8 100644
--- a/src/main/java/de/pixart/messenger/xmpp/pep/Avatar.java
+++ b/src/main/java/de/pixart/messenger/xmpp/pep/Avatar.java
@@ -7,96 +7,98 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class Avatar {
- public enum Origin { PEP, VCARD };
+ public enum Origin {PEP, VCARD}
- public String type;
- public String sha1sum;
- public String image;
- public int height;
- public int width;
- public long size;
- public Jid owner;
- public Origin origin = Origin.PEP; //default to maintain compat
+ ;
- public byte[] getImageAsBytes() {
- return Base64.decode(image, Base64.DEFAULT);
- }
+ public String type;
+ public String sha1sum;
+ public String image;
+ public int height;
+ public int width;
+ public long size;
+ public Jid owner;
+ public Origin origin = Origin.PEP; //default to maintain compat
- public String getFilename() {
- return sha1sum;
- }
+ public byte[] getImageAsBytes() {
+ return Base64.decode(image, Base64.DEFAULT);
+ }
- public static Avatar parseMetadata(Element items) {
- Element item = items.findChild("item");
- if (item == null) {
- return null;
- }
- Element metadata = item.findChild("metadata");
- if (metadata == null) {
- return null;
- }
- String primaryId = item.getAttribute("id");
- if (primaryId == null) {
- return null;
- }
- for (Element child : metadata.getChildren()) {
- if (child.getName().equals("info")
- && primaryId.equals(child.getAttribute("id"))) {
- Avatar avatar = new Avatar();
- String height = child.getAttribute("height");
- String width = child.getAttribute("width");
- String size = child.getAttribute("bytes");
- try {
- if (height != null) {
- avatar.height = Integer.parseInt(height);
- }
- if (width != null) {
- avatar.width = Integer.parseInt(width);
- }
- if (size != null) {
- avatar.size = Long.parseLong(size);
- }
- } catch (NumberFormatException e) {
- return null;
- }
- avatar.type = child.getAttribute("type");
- String hash = child.getAttribute("id");
- if (!isValidSHA1(hash)) {
- return null;
- }
- avatar.sha1sum = hash;
- avatar.origin = Origin.PEP;
- return avatar;
- }
- }
- return null;
- }
+ public String getFilename() {
+ return sha1sum;
+ }
- @Override
- public boolean equals(Object object) {
- if (object != null && object instanceof Avatar) {
- Avatar other = (Avatar) object;
- return other.getFilename().equals(this.getFilename());
- } else {
- return false;
- }
- }
+ public static Avatar parseMetadata(Element items) {
+ Element item = items.findChild("item");
+ if (item == null) {
+ return null;
+ }
+ Element metadata = item.findChild("metadata");
+ if (metadata == null) {
+ return null;
+ }
+ String primaryId = item.getAttribute("id");
+ if (primaryId == null) {
+ return null;
+ }
+ for (Element child : metadata.getChildren()) {
+ if (child.getName().equals("info")
+ && primaryId.equals(child.getAttribute("id"))) {
+ Avatar avatar = new Avatar();
+ String height = child.getAttribute("height");
+ String width = child.getAttribute("width");
+ String size = child.getAttribute("bytes");
+ try {
+ if (height != null) {
+ avatar.height = Integer.parseInt(height);
+ }
+ if (width != null) {
+ avatar.width = Integer.parseInt(width);
+ }
+ if (size != null) {
+ avatar.size = Long.parseLong(size);
+ }
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ avatar.type = child.getAttribute("type");
+ String hash = child.getAttribute("id");
+ if (!isValidSHA1(hash)) {
+ return null;
+ }
+ avatar.sha1sum = hash;
+ avatar.origin = Origin.PEP;
+ return avatar;
+ }
+ }
+ return null;
+ }
- public static Avatar parsePresence(Element x) {
- String hash = x == null ? null : x.findChildContent("photo");
- if (hash == null) {
- return null;
- }
- if (!isValidSHA1(hash)) {
- return null;
- }
- Avatar avatar = new Avatar();
- avatar.sha1sum = hash;
- avatar.origin = Origin.VCARD;
- return avatar;
- }
+ @Override
+ public boolean equals(Object object) {
+ if (object != null && object instanceof Avatar) {
+ Avatar other = (Avatar) object;
+ return other.getFilename().equals(this.getFilename());
+ } else {
+ return false;
+ }
+ }
- private static boolean isValidSHA1(String s) {
- return s != null && s.matches("[a-fA-F0-9]{40}");
- }
+ public static Avatar parsePresence(Element x) {
+ String hash = x == null ? null : x.findChildContent("photo");
+ if (hash == null) {
+ return null;
+ }
+ if (!isValidSHA1(hash)) {
+ return null;
+ }
+ Avatar avatar = new Avatar();
+ avatar.sha1sum = hash;
+ avatar.origin = Origin.VCARD;
+ return avatar;
+ }
+
+ private static boolean isValidSHA1(String s) {
+ return s != null && s.matches("[a-fA-F0-9]{40}");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractAcknowledgeableStanza.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractAcknowledgeableStanza.java
index 615c4dfbe..22d035e6e 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractAcknowledgeableStanza.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractAcknowledgeableStanza.java
@@ -4,28 +4,28 @@ import de.pixart.messenger.xml.Element;
abstract public class AbstractAcknowledgeableStanza extends AbstractStanza {
- protected AbstractAcknowledgeableStanza(String name) {
- super(name);
- }
+ protected AbstractAcknowledgeableStanza(String name) {
+ super(name);
+ }
- public String getId() {
- return this.getAttribute("id");
- }
+ public String getId() {
+ return this.getAttribute("id");
+ }
- public void setId(final String id) {
- setAttribute("id", id);
- }
+ public void setId(final String id) {
+ setAttribute("id", id);
+ }
- public Element getError() {
- Element error = findChild("error");
- if (error != null) {
- for(Element element : error.getChildren()) {
- if (!element.getName().equals("text")) {
- return element;
- }
- }
- }
- return null;
- }
+ public Element getError() {
+ Element error = findChild("error");
+ if (error != null) {
+ for (Element element : error.getChildren()) {
+ if (!element.getName().equals("text")) {
+ return element;
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractStanza.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractStanza.java
index a21d8b56d..8d1b24ef8 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractStanza.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/AbstractStanza.java
@@ -6,45 +6,45 @@ import de.pixart.messenger.xmpp.jid.Jid;
public class AbstractStanza extends Element {
- protected AbstractStanza(final String name) {
- super(name);
- }
-
- public Jid getTo() {
- return getAttributeAsJid("to");
- }
-
- public Jid getFrom() {
- return getAttributeAsJid("from");
- }
-
- public void setTo(final Jid to) {
- if (to != null) {
- setAttribute("to", to.toString());
- }
- }
-
- public void setFrom(final Jid from) {
- if (from != null) {
- setAttribute("from", from.toString());
- }
- }
-
- public boolean fromServer(final Account account) {
- return getFrom() == null
- || getFrom().equals(account.getServer())
- || getFrom().equals(account.getJid().toBareJid())
- || getFrom().equals(account.getJid());
- }
-
- public boolean toServer(final Account account) {
- return getTo() == null
- || getTo().equals(account.getServer())
- || getTo().equals(account.getJid().toBareJid())
- || getTo().equals(account.getJid());
- }
-
- public boolean fromAccount(final Account account) {
- return getFrom() != null && getFrom().toBareJid().equals(account.getJid().toBareJid());
- }
+ protected AbstractStanza(final String name) {
+ super(name);
+ }
+
+ public Jid getTo() {
+ return getAttributeAsJid("to");
+ }
+
+ public Jid getFrom() {
+ return getAttributeAsJid("from");
+ }
+
+ public void setTo(final Jid to) {
+ if (to != null) {
+ setAttribute("to", to.toString());
+ }
+ }
+
+ public void setFrom(final Jid from) {
+ if (from != null) {
+ setAttribute("from", from.toString());
+ }
+ }
+
+ public boolean fromServer(final Account account) {
+ return getFrom() == null
+ || getFrom().equals(account.getServer())
+ || getFrom().equals(account.getJid().toBareJid())
+ || getFrom().equals(account.getJid());
+ }
+
+ public boolean toServer(final Account account) {
+ return getTo() == null
+ || getTo().equals(account.getServer())
+ || getTo().equals(account.getJid().toBareJid())
+ || getTo().equals(account.getJid());
+ }
+
+ public boolean fromAccount(final Account account) {
+ return getFrom() != null && getFrom().toBareJid().equals(account.getJid().toBareJid());
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/IqPacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/IqPacket.java
index b0a22c314..2efc948f4 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/IqPacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/IqPacket.java
@@ -4,66 +4,66 @@ import de.pixart.messenger.xml.Element;
public class IqPacket extends AbstractAcknowledgeableStanza {
- public enum TYPE {
- ERROR,
- SET,
- RESULT,
- GET,
- INVALID,
- TIMEOUT
- }
+ public enum TYPE {
+ ERROR,
+ SET,
+ RESULT,
+ GET,
+ INVALID,
+ TIMEOUT
+ }
- public IqPacket(final TYPE type) {
- super("iq");
- if (type != TYPE.INVALID) {
- this.setAttribute("type", type.toString().toLowerCase());
- }
- }
+ public IqPacket(final TYPE type) {
+ super("iq");
+ if (type != TYPE.INVALID) {
+ this.setAttribute("type", type.toString().toLowerCase());
+ }
+ }
- public IqPacket() {
- super("iq");
- }
+ public IqPacket() {
+ super("iq");
+ }
- public Element query() {
- Element query = findChild("query");
- if (query == null) {
- query = addChild("query");
- }
- return query;
- }
+ public Element query() {
+ Element query = findChild("query");
+ if (query == null) {
+ query = addChild("query");
+ }
+ return query;
+ }
- public Element query(final String xmlns) {
- final Element query = query();
- query.setAttribute("xmlns", xmlns);
- return query();
- }
+ public Element query(final String xmlns) {
+ final Element query = query();
+ query.setAttribute("xmlns", xmlns);
+ return query();
+ }
- public TYPE getType() {
- final String type = getAttribute("type");
- if (type == null) {
- return TYPE.INVALID;
- }
- switch (type) {
- case "error":
- return TYPE.ERROR;
- case "result":
- return TYPE.RESULT;
- case "set":
- return TYPE.SET;
- case "get":
- return TYPE.GET;
- case "timeout":
- return TYPE.TIMEOUT;
- default:
- return TYPE.INVALID;
- }
- }
+ public TYPE getType() {
+ final String type = getAttribute("type");
+ if (type == null) {
+ return TYPE.INVALID;
+ }
+ switch (type) {
+ case "error":
+ return TYPE.ERROR;
+ case "result":
+ return TYPE.RESULT;
+ case "set":
+ return TYPE.SET;
+ case "get":
+ return TYPE.GET;
+ case "timeout":
+ return TYPE.TIMEOUT;
+ default:
+ return TYPE.INVALID;
+ }
+ }
- public IqPacket generateResponse(final TYPE type) {
- final IqPacket packet = new IqPacket(type);
- packet.setTo(this.getFrom());
- packet.setId(this.getId());
- return packet;
- }
+ public IqPacket generateResponse(final TYPE type) {
+ final IqPacket packet = new IqPacket(type);
+ packet.setTo(this.getFrom());
+ packet.setId(this.getId());
+ return packet;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/MessagePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/MessagePacket.java
index 5af2d701b..2f7d7e071 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/MessagePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/MessagePacket.java
@@ -6,94 +6,94 @@ import de.pixart.messenger.parser.AbstractParser;
import de.pixart.messenger.xml.Element;
public class MessagePacket extends AbstractAcknowledgeableStanza {
- public static final int TYPE_CHAT = 0;
- public static final int TYPE_NORMAL = 2;
- public static final int TYPE_GROUPCHAT = 3;
- public static final int TYPE_ERROR = 4;
- public static final int TYPE_HEADLINE = 5;
+ public static final int TYPE_CHAT = 0;
+ public static final int TYPE_NORMAL = 2;
+ public static final int TYPE_GROUPCHAT = 3;
+ public static final int TYPE_ERROR = 4;
+ public static final int TYPE_HEADLINE = 5;
- public MessagePacket() {
- super("message");
- }
+ public MessagePacket() {
+ super("message");
+ }
- public String getBody() {
- return findChildContent("body");
- }
+ public String getBody() {
+ return findChildContent("body");
+ }
- public void setBody(String text) {
- this.children.remove(findChild("body"));
- Element body = new Element("body");
- body.setContent(text);
- this.children.add(0, body);
- }
+ public void setBody(String text) {
+ this.children.remove(findChild("body"));
+ Element body = new Element("body");
+ body.setContent(text);
+ this.children.add(0, body);
+ }
- public void setAxolotlMessage(Element axolotlMessage) {
- this.children.remove(findChild("body"));
- this.children.add(0, axolotlMessage);
- }
+ public void setAxolotlMessage(Element axolotlMessage) {
+ this.children.remove(findChild("body"));
+ this.children.add(0, axolotlMessage);
+ }
- public void setType(int type) {
- switch (type) {
- case TYPE_CHAT:
- this.setAttribute("type", "chat");
- break;
- case TYPE_GROUPCHAT:
- this.setAttribute("type", "groupchat");
- break;
- case TYPE_NORMAL:
- break;
- case TYPE_ERROR:
- this.setAttribute("type","error");
- break;
- default:
- this.setAttribute("type", "chat");
- break;
- }
- }
+ public void setType(int type) {
+ switch (type) {
+ case TYPE_CHAT:
+ this.setAttribute("type", "chat");
+ break;
+ case TYPE_GROUPCHAT:
+ this.setAttribute("type", "groupchat");
+ break;
+ case TYPE_NORMAL:
+ break;
+ case TYPE_ERROR:
+ this.setAttribute("type", "error");
+ break;
+ default:
+ this.setAttribute("type", "chat");
+ break;
+ }
+ }
- public int getType() {
- String type = getAttribute("type");
- if (type == null) {
- return TYPE_NORMAL;
- } else if (type.equals("normal")) {
- return TYPE_NORMAL;
- } else if (type.equals("chat")) {
- return TYPE_CHAT;
- } else if (type.equals("groupchat")) {
- return TYPE_GROUPCHAT;
- } else if (type.equals("error")) {
- return TYPE_ERROR;
- } else if (type.equals("headline")) {
- return TYPE_HEADLINE;
- } else {
- return TYPE_NORMAL;
- }
- }
+ public int getType() {
+ String type = getAttribute("type");
+ if (type == null) {
+ return TYPE_NORMAL;
+ } else if (type.equals("normal")) {
+ return TYPE_NORMAL;
+ } else if (type.equals("chat")) {
+ return TYPE_CHAT;
+ } else if (type.equals("groupchat")) {
+ return TYPE_GROUPCHAT;
+ } else if (type.equals("error")) {
+ return TYPE_ERROR;
+ } else if (type.equals("headline")) {
+ return TYPE_HEADLINE;
+ } else {
+ return TYPE_NORMAL;
+ }
+ }
- public Pair<MessagePacket,Long> getForwardedMessagePacket(String name, String namespace) {
- Element wrapper = findChild(name, namespace);
- if (wrapper == null) {
- return null;
- }
- Element forwarded = wrapper.findChild("forwarded", "urn:xmpp:forward:0");
- if (forwarded == null) {
- return null;
- }
- MessagePacket packet = create(forwarded.findChild("message"));
- if (packet == null) {
- return null;
- }
- Long timestamp = AbstractParser.parseTimestamp(forwarded, null);
- return new Pair(packet,timestamp);
- }
+ public Pair<MessagePacket, Long> getForwardedMessagePacket(String name, String namespace) {
+ Element wrapper = findChild(name, namespace);
+ if (wrapper == null) {
+ return null;
+ }
+ Element forwarded = wrapper.findChild("forwarded", "urn:xmpp:forward:0");
+ if (forwarded == null) {
+ return null;
+ }
+ MessagePacket packet = create(forwarded.findChild("message"));
+ if (packet == null) {
+ return null;
+ }
+ Long timestamp = AbstractParser.parseTimestamp(forwarded, null);
+ return new Pair(packet, timestamp);
+ }
- public static MessagePacket create(Element element) {
- if (element == null) {
- return null;
- }
- MessagePacket packet = new MessagePacket();
- packet.setAttributes(element.getAttributes());
- packet.setChildren(element.getChildren());
- return packet;
- }
+ public static MessagePacket create(Element element) {
+ if (element == null) {
+ return null;
+ }
+ MessagePacket packet = new MessagePacket();
+ packet.setAttributes(element.getAttributes());
+ packet.setChildren(element.getChildren());
+ return packet;
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/PresencePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/PresencePacket.java
index 6c816cac3..6c57de807 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/PresencePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/PresencePacket.java
@@ -2,7 +2,7 @@ package de.pixart.messenger.xmpp.stanzas;
public class PresencePacket extends AbstractAcknowledgeableStanza {
- public PresencePacket() {
- super("presence");
- }
+ public PresencePacket() {
+ super("presence");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/ActivePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/ActivePacket.java
index 10abb3499..5d1cb61c3 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/ActivePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/ActivePacket.java
@@ -3,8 +3,8 @@ package de.pixart.messenger.xmpp.stanzas.csi;
import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class ActivePacket extends AbstractStanza {
- public ActivePacket() {
- super("active");
- setAttribute("xmlns", "urn:xmpp:csi:0");
- }
+ public ActivePacket() {
+ super("active");
+ setAttribute("xmlns", "urn:xmpp:csi:0");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/InactivePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/InactivePacket.java
index ebaad8205..9e840190d 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/InactivePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/csi/InactivePacket.java
@@ -3,8 +3,8 @@ package de.pixart.messenger.xmpp.stanzas.csi;
import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class InactivePacket extends AbstractStanza {
- public InactivePacket() {
- super("inactive");
- setAttribute("xmlns", "urn:xmpp:csi:0");
- }
+ public InactivePacket() {
+ super("inactive");
+ setAttribute("xmlns", "urn:xmpp:csi:0");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/AckPacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/AckPacket.java
index 5c8131878..f98707fbe 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/AckPacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/AckPacket.java
@@ -4,10 +4,10 @@ import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class AckPacket extends AbstractStanza {
- public AckPacket(int sequence, int smVersion) {
- super("a");
- this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
- this.setAttribute("h", Integer.toString(sequence));
- }
+ public AckPacket(int sequence, int smVersion) {
+ super("a");
+ this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
+ this.setAttribute("h", Integer.toString(sequence));
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/EnablePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/EnablePacket.java
index 2beca0f20..d4c8ab242 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/EnablePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/EnablePacket.java
@@ -4,10 +4,10 @@ import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class EnablePacket extends AbstractStanza {
- public EnablePacket(int smVersion) {
- super("enable");
- this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
- this.setAttribute("resume", "true");
- }
+ public EnablePacket(int smVersion) {
+ super("enable");
+ this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
+ this.setAttribute("resume", "true");
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/RequestPacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/RequestPacket.java
index 19880f3c6..cbd82eed6 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/RequestPacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/RequestPacket.java
@@ -4,9 +4,9 @@ import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class RequestPacket extends AbstractStanza {
- public RequestPacket(int smVersion) {
- super("r");
- this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
- }
+ public RequestPacket(int smVersion) {
+ super("r");
+ this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
+ }
}
diff --git a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/ResumePacket.java b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/ResumePacket.java
index 1c7f8d08c..e47fc1b95 100644
--- a/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/ResumePacket.java
+++ b/src/main/java/de/pixart/messenger/xmpp/stanzas/streammgmt/ResumePacket.java
@@ -4,11 +4,11 @@ import de.pixart.messenger.xmpp.stanzas.AbstractStanza;
public class ResumePacket extends AbstractStanza {
- public ResumePacket(String id, int sequence, int smVersion) {
- super("resume");
- this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
- this.setAttribute("previd", id);
- this.setAttribute("h", Integer.toString(sequence));
- }
+ public ResumePacket(String id, int sequence, int smVersion) {
+ super("resume");
+ this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
+ this.setAttribute("previd", id);
+ this.setAttribute("h", Integer.toString(sequence));
+ }
}