Detailed changes
@@ -27,13 +27,13 @@ public class AppSettings {
public static final String BLIND_TRUST_BEFORE_VERIFICATION = "btbv";
public static final String AUTOMATIC_MESSAGE_DELETION = "automatic_message_deletion";
public static final String BROADCAST_LAST_ACTIVITY = "last_activity";
+ public static final String SEND_CHAT_STATES = "chat_states";
public static final String THEME = "theme";
public static final String DYNAMIC_COLORS = "dynamic_colors";
public static final String SHOW_DYNAMIC_TAGS = "show_dynamic_tags";
public static final String OMEMO = "omemo";
public static final String ALLOW_SCREENSHOTS = "allow_screenshots";
public static final String RINGTONE = "call_ringtone";
- public static final String BTBV = "btbv";
public static final String CONFIRM_MESSAGES = "confirm_messages";
public static final String ALLOW_MESSAGE_CORRECTION = "allow_message_correction";
@@ -57,6 +57,7 @@ public class AppSettings {
public static final String AUTO_ACCEPT_FILE_SIZE = "auto_accept_file_size";
private static final String ACCEPT_INVITES_FROM_STRANGERS = "accept_invites_from_strangers";
+ private static final String NOTIFICATIONS_FROM_STRANGERS = "notifications_from_strangers";
private static final String INSTALLATION_ID = "im.conversations.android.install_id";
private static final String EXTERNAL_STORAGE_AUTHORITY =
@@ -102,7 +103,7 @@ public class AppSettings {
}
public boolean isBTBVEnabled() {
- return getBooleanPreference(BTBV, R.bool.btbv);
+ return getBooleanPreference(BLIND_TRUST_BEFORE_VERIFICATION, R.bool.btbv);
}
public boolean isTrustSystemCAStore() {
@@ -145,11 +146,37 @@ public class AppSettings {
return getBooleanPreference(BROADCAST_LAST_ACTIVITY, R.bool.last_activity);
}
+ public boolean isUserManagedAvailability() {
+ return getBooleanPreference(MANUALLY_CHANGE_PRESENCE, R.bool.manually_change_presence);
+ }
+
+ public boolean isAutomaticAvailability() {
+ return !isUserManagedAvailability();
+ }
+
+ public boolean isDndOnSilentMode() {
+ return getBooleanPreference(AppSettings.DND_ON_SILENT_MODE, R.bool.dnd_on_silent_mode);
+ }
+
+ public boolean isTreatVibrateAsSilent() {
+ return getBooleanPreference(
+ AppSettings.TREAT_VIBRATE_AS_SILENT, R.bool.treat_vibrate_as_silent);
+ }
+
+ public boolean isAwayWhenScreenLocked() {
+ return getBooleanPreference(
+ AppSettings.AWAY_WHEN_SCREEN_IS_OFF, R.bool.away_when_screen_off);
+ }
+
public boolean isUseTor() {
return QuickConversationsService.isConversations()
&& getBooleanPreference(USE_TOR, R.bool.use_tor);
}
+ public boolean isSendChatStates() {
+ return getBooleanPreference(SEND_CHAT_STATES, R.bool.chat_states);
+ }
+
public boolean isExtendedConnectionOptions() {
return QuickConversationsService.isConversations()
&& getBooleanPreference(
@@ -161,6 +188,11 @@ public class AppSettings {
ACCEPT_INVITES_FROM_STRANGERS, R.bool.accept_invites_from_strangers);
}
+ public boolean isNotificationsFromStrangers() {
+ return getBooleanPreference(
+ NOTIFICATIONS_FROM_STRANGERS, R.bool.notifications_from_strangers);
+ }
+
public boolean isKeepForegroundService() {
return Compatibility.twentySix()
|| getBooleanPreference(KEEP_FOREGROUND_SERVICE, R.bool.enable_foreground_service);
@@ -0,0 +1,80 @@
+package eu.siacs.conversations.android;
+
+import android.app.KeyguardManager;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.Build;
+import android.os.PowerManager;
+import android.util.Log;
+import eu.siacs.conversations.Config;
+
+public class Device {
+
+ private final Context context;
+
+ public Device(final Context context) {
+ this.context = context;
+ }
+
+ public boolean isScreenLocked() {
+ final var keyguardManager = context.getSystemService(KeyguardManager.class);
+ final var powerManager = context.getSystemService(PowerManager.class);
+ final var locked = keyguardManager != null && keyguardManager.isKeyguardLocked();
+ final boolean interactive;
+ try {
+ interactive = powerManager != null && powerManager.isInteractive();
+ } catch (final Exception e) {
+ return false;
+ }
+ return locked || !interactive;
+ }
+
+ public boolean isPhoneSilenced(final boolean vibrateIsSilent) {
+ final var notificationManager = context.getSystemService(NotificationManager.class);
+ final int filter =
+ notificationManager == null
+ ? NotificationManager.INTERRUPTION_FILTER_UNKNOWN
+ : notificationManager.getCurrentInterruptionFilter();
+ final boolean notificationDnd = filter >= NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+ final var audioManager = context.getSystemService(AudioManager.class);
+ final int ringerMode =
+ audioManager == null
+ ? AudioManager.RINGER_MODE_NORMAL
+ : audioManager.getRingerMode();
+ try {
+ if (vibrateIsSilent) {
+ return notificationDnd || ringerMode != AudioManager.RINGER_MODE_NORMAL;
+ } else {
+ return notificationDnd || ringerMode == AudioManager.RINGER_MODE_SILENT;
+ }
+ } catch (final Throwable throwable) {
+ Log.d(Config.LOGTAG, "platform bug in isPhoneSilenced", throwable);
+ return notificationDnd;
+ }
+ }
+
+ public boolean isPhysicalDevice() {
+ return !isEmulator();
+ }
+
+ private static boolean isEmulator() {
+ return (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
+ || Build.FINGERPRINT.startsWith("generic")
+ || Build.FINGERPRINT.startsWith("unknown")
+ || Build.HARDWARE.contains("goldfish")
+ || Build.HARDWARE.contains("ranchu")
+ || Build.MODEL.contains("google_sdk")
+ || Build.MODEL.contains("Emulator")
+ || Build.MODEL.contains("Android SDK built for x86")
+ || Build.MANUFACTURER.contains("Genymotion")
+ || Build.PRODUCT.contains("sdk_google")
+ || Build.PRODUCT.contains("google_sdk")
+ || Build.PRODUCT.contains("sdk")
+ || Build.PRODUCT.contains("sdk_x86")
+ || Build.PRODUCT.contains("sdk_gphone64_arm64")
+ || Build.PRODUCT.contains("vbox86p")
+ || Build.PRODUCT.contains("emulator")
+ || Build.PRODUCT.contains("simulator");
+ }
+}
@@ -12,11 +12,6 @@ public class PresenceGenerator extends AbstractGenerator {
super(service);
}
- public im.conversations.android.xmpp.model.stanza.Presence selfPresence(
- Account account, Presence.Availability status) {
- return selfPresence(account, status, true);
- }
-
public im.conversations.android.xmpp.model.stanza.Presence selfPresence(
final Account account, final Presence.Availability status, final boolean personal) {
final var connection = account.getXmppConnection();
@@ -50,6 +50,7 @@ import com.google.common.primitives.Ints;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
+import eu.siacs.conversations.android.Device;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
@@ -390,11 +391,12 @@ public class NotificationService {
}
private boolean notifyMessage(final Message message) {
+ final var appSettings = new AppSettings(mXmppConnectionService.getApplicationContext());
final Conversation conversation = (Conversation) message.getConversation();
return message.getStatus() == Message.STATUS_RECEIVED
&& !conversation.isMuted()
&& (conversation.alwaysNotify() || wasHighlightedOrPrivate(message))
- && (!conversation.isWithStranger() || notificationsFromStrangers())
+ && (!conversation.isWithStranger() || appSettings.isNotificationsFromStrangers())
&& message.getType() != Message.TYPE_RTP_SESSION;
}
@@ -403,11 +405,6 @@ public class NotificationService {
&& message.getStatus() == Message.STATUS_RECEIVED;
}
- public boolean notificationsFromStrangers() {
- return mXmppConnectionService.getBooleanPreference(
- "notifications_from_strangers", R.bool.notifications_from_strangers);
- }
-
public void pushFromBacklog(final Message message) {
if (notifyMessage(message)) {
synchronized (notifications) {
@@ -513,9 +510,8 @@ public class NotificationService {
public void pushFailedDelivery(final Message message) {
final Conversation conversation = (Conversation) message.getConversation();
- final boolean isScreenLocked = !mXmppConnectionService.isScreenLocked();
if (this.mIsInForeground
- && isScreenLocked
+ && !new Device(mXmppConnectionService).isScreenLocked()
&& this.mOpenConversation == message.getConversation()) {
Log.d(
Config.LOGTAG,
@@ -755,7 +751,7 @@ public class NotificationService {
+ ": suppressing notification because turned off");
return;
}
- final boolean isScreenLocked = mXmppConnectionService.isScreenLocked();
+ final boolean isScreenLocked = new Device(mXmppConnectionService).isScreenLocked();
if (this.mIsInForeground
&& !isScreenLocked
&& this.mOpenConversation == message.getConversation()) {
@@ -29,8 +29,8 @@ import eu.siacs.conversations.receiver.UnifiedPushDistributor;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
+import eu.siacs.conversations.xmpp.manager.PresenceManager;
import im.conversations.android.xmpp.model.stanza.Iq;
-import im.conversations.android.xmpp.model.stanza.Presence;
import im.conversations.android.xmpp.model.up.Push;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
@@ -69,7 +69,10 @@ public class UnifiedPushBroker {
if (transportAccount != null && transportAccount.getUuid().equals(account.getUuid())) {
final UnifiedPushDatabase database = UnifiedPushDatabase.getInstance(service);
if (database.hasEndpoints(transport)) {
- sendDirectedPresence(transportAccount, transport.transport);
+ transportAccount
+ .getXmppConnection()
+ .getManager(PresenceManager.class)
+ .available(transport.transport);
}
Log.d(
Config.LOGTAG,
@@ -79,12 +82,6 @@ public class UnifiedPushBroker {
}
}
- private void sendDirectedPresence(final Account account, Jid to) {
- final var presence = new Presence();
- presence.setTo(to);
- service.sendPresencePacket(account, presence);
- }
-
public void renewUnifiedPushEndpoints() {
renewUnifiedPushEndpoints(null);
}
@@ -5,7 +5,6 @@ import static eu.siacs.conversations.utils.Compatibility.s;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.AlarmManager;
-import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -42,7 +41,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
-import androidx.annotation.BoolRes;
import androidx.annotation.IntegerRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -706,7 +704,7 @@ public class XmppConnectionService extends Service {
});
case AudioManager.RINGER_MODE_CHANGED_ACTION:
case NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED:
- if (dndOnSilentMode()) {
+ if (appSettings.isDndOnSilentMode() && appSettings.isAutomaticAvailability()) {
refreshAllPresences();
}
break;
@@ -714,7 +712,7 @@ public class XmppConnectionService extends Service {
deactivateGracePeriod();
case Intent.ACTION_USER_PRESENT:
case Intent.ACTION_SCREEN_OFF:
- if (awayWhenScreenLocked()) {
+ if (appSettings.isAwayWhenScreenLocked() && appSettings.isAutomaticAvailability()) {
refreshAllPresences();
}
break;
@@ -1052,25 +1050,6 @@ public class XmppConnectionService extends Service {
}
}
- private boolean dndOnSilentMode() {
- return getBooleanPreference(AppSettings.DND_ON_SILENT_MODE, R.bool.dnd_on_silent_mode);
- }
-
- private boolean manuallyChangePresence() {
- return getBooleanPreference(
- AppSettings.MANUALLY_CHANGE_PRESENCE, R.bool.manually_change_presence);
- }
-
- private boolean treatVibrateAsSilent() {
- return getBooleanPreference(
- AppSettings.TREAT_VIBRATE_AS_SILENT, R.bool.treat_vibrate_as_silent);
- }
-
- private boolean awayWhenScreenLocked() {
- return getBooleanPreference(
- AppSettings.AWAY_WHEN_SCREEN_IS_OFF, R.bool.away_when_screen_off);
- }
-
private String getCompressPicturesPreference() {
return getPreferences()
.getString(
@@ -1078,55 +1057,6 @@ public class XmppConnectionService extends Service {
getResources().getString(R.string.picture_compression));
}
- private im.conversations.android.xmpp.model.stanza.Presence.Availability getTargetPresence() {
- if (dndOnSilentMode() && isPhoneSilenced()) {
- return im.conversations.android.xmpp.model.stanza.Presence.Availability.DND;
- } else if (awayWhenScreenLocked() && isScreenLocked()) {
- return im.conversations.android.xmpp.model.stanza.Presence.Availability.AWAY;
- } else {
- return im.conversations.android.xmpp.model.stanza.Presence.Availability.ONLINE;
- }
- }
-
- public boolean isScreenLocked() {
- final KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
- final PowerManager powerManager = getSystemService(PowerManager.class);
- final boolean locked = keyguardManager != null && keyguardManager.isKeyguardLocked();
- final boolean interactive;
- try {
- interactive = powerManager != null && powerManager.isInteractive();
- } catch (final Exception e) {
- return false;
- }
- return locked || !interactive;
- }
-
- private boolean isPhoneSilenced() {
- final NotificationManager notificationManager = getSystemService(NotificationManager.class);
- final int filter =
- notificationManager == null
- ? NotificationManager.INTERRUPTION_FILTER_UNKNOWN
- : notificationManager.getCurrentInterruptionFilter();
- final boolean notificationDnd = filter >= NotificationManager.INTERRUPTION_FILTER_PRIORITY;
- final AudioManager audioManager = getSystemService(AudioManager.class);
- final int ringerMode =
- audioManager == null
- ? AudioManager.RINGER_MODE_NORMAL
- : audioManager.getRingerMode();
- try {
- if (treatVibrateAsSilent()) {
- return notificationDnd || ringerMode != AudioManager.RINGER_MODE_NORMAL;
- } else {
- return notificationDnd || ringerMode == AudioManager.RINGER_MODE_SILENT;
- }
- } catch (final Throwable throwable) {
- Log.d(
- Config.LOGTAG,
- "platform bug in isPhoneSilenced (" + throwable.getMessage() + ")");
- return notificationDnd;
- }
- }
-
private void resetAllAttemptCounts(boolean reallyAll, boolean retryImmediately) {
Log.d(Config.LOGTAG, "resetting all attempt counts");
for (Account account : accounts) {
@@ -1433,7 +1363,7 @@ public class XmppConnectionService extends Service {
}
public void toggleScreenEventReceiver() {
- if (awayWhenScreenLocked() && !manuallyChangePresence()) {
+ if (appSettings.isAwayWhenScreenLocked() && appSettings.isAutomaticAvailability()) {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -1655,8 +1585,8 @@ public class XmppConnectionService extends Service {
return connection;
}
- public void sendChatState(Conversation conversation) {
- if (sendChatStates()) {
+ public void sendChatState(final Conversation conversation) {
+ if (appSettings.isSendChatStates()) {
final var packet = mMessageGenerator.generateChatState(conversation);
sendMessagePacket(conversation.getAccount(), packet);
}
@@ -1848,7 +1778,7 @@ public class XmppConnectionService extends Service {
mMessageGenerator.addDelay(packet, message.getTimeSent());
}
if (conversation.setOutgoingChatState(Config.DEFAULT_CHAT_STATE)) {
- if (this.sendChatStates()) {
+ if (this.appSettings.isSendChatStates()) {
packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
}
}
@@ -3112,7 +3042,7 @@ public class XmppConnectionService extends Service {
private void switchToForeground() {
toggleSoftDisabled(false);
- final boolean broadcastLastActivity = broadcastLastActivity();
+ final boolean broadcastLastActivity = appSettings.isBroadcastLastActivity();
for (Conversation conversation : getConversations()) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
conversation.getMucOptions().resetChatState();
@@ -3120,45 +3050,41 @@ public class XmppConnectionService extends Service {
conversation.setIncomingChatState(Config.DEFAULT_CHAT_STATE);
}
}
- 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
- }
- }
+ for (final var account : getAccounts()) {
+ if (account.getStatus() != Account.State.ONLINE) {
+ continue;
+ }
+ account.deactivateGracePeriod();
+ final XmppConnection connection = account.getXmppConnection();
+ if (connection.getFeatures().csi()) {
+ connection.sendActive();
+ }
+ if (broadcastLastActivity) {
+ // send new presence but don't include idle because we are not
+ connection.getManager(PresenceManager.class).available(false);
}
}
Log.d(Config.LOGTAG, "app switched into foreground");
}
private void switchToBackground() {
- final boolean broadcastLastActivity = broadcastLastActivity();
+ final boolean broadcastLastActivity = appSettings.isBroadcastLastActivity();
if (broadcastLastActivity) {
mLastActivity = System.currentTimeMillis();
final SharedPreferences.Editor editor = getPreferences().edit();
editor.putLong(SETTING_LAST_ACTIVITY_TS, mLastActivity);
editor.apply();
}
- for (Account account : getAccounts()) {
- if (account.getStatus() == Account.State.ONLINE) {
- XmppConnection connection = account.getXmppConnection();
- if (connection != null) {
- if (broadcastLastActivity) {
- sendPresence(account, true);
- }
- if (connection.getFeatures().csi()) {
- connection.sendInactive();
- }
- }
+ for (final var account : getAccounts()) {
+ if (account.getStatus() != Account.State.ONLINE) {
+ continue;
+ }
+ final var connection = account.getXmppConnection();
+ if (broadcastLastActivity) {
+ connection.getManager(PresenceManager.class).available(true);
+ }
+ if (connection.getFeatures().csi()) {
+ connection.sendInactive();
}
}
this.mNotificationService.setIsInForeground(false);
@@ -4218,7 +4144,7 @@ public class XmppConnectionService extends Service {
}
}
}
- sendOfflinePresence(account);
+ connection.getManager(PresenceManager.class).unavailable();
}
connection.disconnect(force);
}
@@ -4549,10 +4475,6 @@ public class XmppConnectionService extends Service {
}
}
- public boolean getBooleanPreference(String name, @BoolRes int res) {
- return getPreferences().getBoolean(name, getResources().getBoolean(res));
- }
-
public boolean confirmMessages() {
return appSettings.isConfirmMessages();
}
@@ -4561,18 +4483,10 @@ public class XmppConnectionService extends Service {
return appSettings.isAllowMessageCorrection();
}
- public boolean sendChatStates() {
- return getBooleanPreference("chat_states", R.bool.chat_states);
- }
-
public boolean useTorToConnect() {
return appSettings.isUseTor();
}
- public boolean broadcastLastActivity() {
- return appSettings.isBroadcastLastActivity();
- }
-
public int unreadCount() {
int count = 0;
for (Conversation conversation : getConversations()) {
@@ -5014,27 +4928,6 @@ public class XmppConnectionService extends Service {
}
}
- public void sendPresence(final Account account) {
- sendPresence(account, checkListeners() && broadcastLastActivity());
- }
-
- private void sendPresence(final Account account, final boolean includeIdleTimestamp) {
- final im.conversations.android.xmpp.model.stanza.Presence.Availability status;
- if (manuallyChangePresence()) {
- status = account.getPresenceStatus();
- } else {
- status = getTargetPresence();
- }
- final var packet = mPresenceGenerator.selfPresence(account, status);
- if (mLastActivity > 0 && includeIdleTimestamp) {
- long since =
- Math.min(mLastActivity, System.currentTimeMillis()); // don't send future dates
- packet.addChild("idle", Namespace.IDLE)
- .setAttribute("since", AbstractGenerator.getTimestamp(since));
- }
- sendPresencePacket(account, packet);
- }
-
private void deactivateGracePeriod() {
for (Account account : getAccounts()) {
account.deactivateGracePeriod();
@@ -5042,10 +4935,13 @@ public class XmppConnectionService extends Service {
}
public void refreshAllPresences() {
- boolean includeIdleTimestamp = checkListeners() && broadcastLastActivity();
- for (Account account : getAccounts()) {
+ final boolean includeIdleTimestamp =
+ checkListeners() && appSettings.isBroadcastLastActivity();
+ for (final var account : getAccounts()) {
if (account.isConnectionEnabled()) {
- sendPresence(account, includeIdleTimestamp);
+ account.getXmppConnection()
+ .getManager(PresenceManager.class)
+ .available(includeIdleTimestamp);
}
}
}
@@ -5058,11 +4954,6 @@ public class XmppConnectionService extends Service {
}
}
- private void sendOfflinePresence(final Account account) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending offline presence");
- sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account));
- }
-
public MessageGenerator getMessageGenerator() {
return this.mMessageGenerator;
}
@@ -5247,7 +5138,8 @@ public class XmppConnectionService extends Service {
return mPushManagementService;
}
- public void changeStatus(Account account, PresenceTemplate template, String signature) {
+ public void changeStatus(
+ final Account account, final PresenceTemplate template, final String signature) {
if (!template.getStatusMessage().isEmpty()) {
databaseBackend.insertPresenceTemplate(template);
}
@@ -5255,7 +5147,7 @@ public class XmppConnectionService extends Service {
account.setPresenceStatus(template.getStatus());
account.setPresenceStatusMessage(template.getStatusMessage());
databaseBackend.updateAccount(account);
- sendPresence(account);
+ account.getXmppConnection().getManager(PresenceManager.class).available();
}
public List<PresenceTemplate> getPresenceTemplates(Account account) {
@@ -5345,6 +5237,10 @@ public class XmppConnectionService extends Service {
}
}
+ public long getLastActivity() {
+ return this.mLastActivity;
+ }
+
public interface OnMamPreferencesFetched {
void onPreferencesFetched(Element prefs);
@@ -82,6 +82,7 @@ import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.XmppConnection.Features;
import eu.siacs.conversations.xmpp.manager.CarbonsManager;
import eu.siacs.conversations.xmpp.manager.HttpUploadManager;
+import eu.siacs.conversations.xmpp.manager.PresenceManager;
import eu.siacs.conversations.xmpp.manager.RegistrationManager;
import im.conversations.android.xmpp.model.data.Data;
import im.conversations.android.xmpp.model.stanza.Presence;
@@ -1518,7 +1519,7 @@ public class EditAccountActivity extends OmemoActivity
mAccount.setPgpSignId(0);
mAccount.unsetPgpSignature();
xmppConnectionService.databaseBackend.updateAccount(mAccount);
- xmppConnectionService.sendPresence(mAccount);
+ mAccount.getXmppConnection().getManager(PresenceManager.class).available();
refreshUiReal();
});
builder.create().show();
@@ -828,7 +828,9 @@ public abstract class XmppActivity extends ActionBarActivity {
public void success(String signature) {
account.setPgpSignature(signature);
xmppConnectionService.databaseBackend.updateAccount(account);
- xmppConnectionService.sendPresence(account);
+ account.getXmppConnection()
+ .getManager(PresenceManager.class)
+ .available();
if (conversation != null) {
conversation.setNextEncryption(Message.ENCRYPTION_PGP);
xmppConnectionService.updateConversation(conversation);
@@ -6,12 +6,9 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
-import android.os.Build;
import android.provider.ContactsContract.Profile;
import android.provider.Settings;
-
import com.google.common.base.Strings;
-
import eu.siacs.conversations.services.QuickConversationsService;
public class PhoneHelper {
@@ -23,9 +20,8 @@ public class PhoneHelper {
public static Uri getProfilePictureUri(final Context context) {
if (!QuickConversationsService.isContactListIntegration(context)
- || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
- && context.checkSelfPermission(Manifest.permission.READ_CONTACTS)
- != PackageManager.PERMISSION_GRANTED)) {
+ || context.checkSelfPermission(Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
return null;
}
final String[] projection = new String[] {Profile._ID, Profile.PHOTO_URI};
@@ -42,24 +38,4 @@ public class PhoneHelper {
}
return null;
}
-
- public static boolean isEmulator() {
- return (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
- || Build.FINGERPRINT.startsWith("generic")
- || Build.FINGERPRINT.startsWith("unknown")
- || Build.HARDWARE.contains("goldfish")
- || Build.HARDWARE.contains("ranchu")
- || Build.MODEL.contains("google_sdk")
- || Build.MODEL.contains("Emulator")
- || Build.MODEL.contains("Android SDK built for x86")
- || Build.MANUFACTURER.contains("Genymotion")
- || Build.PRODUCT.contains("sdk_google")
- || Build.PRODUCT.contains("google_sdk")
- || Build.PRODUCT.contains("sdk")
- || Build.PRODUCT.contains("sdk_x86")
- || Build.PRODUCT.contains("sdk_gphone64_arm64")
- || Build.PRODUCT.contains("vbox86p")
- || Build.PRODUCT.contains("emulator")
- || Build.PRODUCT.contains("simulator");
- }
}
@@ -14,6 +14,7 @@ import eu.siacs.conversations.xmpp.manager.EntityTimeManager;
import eu.siacs.conversations.xmpp.manager.HttpUploadManager;
import eu.siacs.conversations.xmpp.manager.LegacyBookmarkManager;
import eu.siacs.conversations.xmpp.manager.MessageDisplayedSynchronizationManager;
+import eu.siacs.conversations.xmpp.manager.MultiUserChatManager;
import eu.siacs.conversations.xmpp.manager.NickManager;
import eu.siacs.conversations.xmpp.manager.OfflineMessagesManager;
import eu.siacs.conversations.xmpp.manager.PepManager;
@@ -48,6 +49,7 @@ public class Managers {
.put(
MessageDisplayedSynchronizationManager.class,
new MessageDisplayedSynchronizationManager(context, connection))
+ .put(MultiUserChatManager.class, new MultiUserChatManager(context, connection))
.put(NickManager.class, new NickManager(context, connection))
.put(OfflineMessagesManager.class, new OfflineMessagesManager(context, connection))
.put(PepManager.class, new PepManager(context, connection))
@@ -31,6 +31,7 @@ import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.BuildConfig;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
+import eu.siacs.conversations.android.Device;
import eu.siacs.conversations.crypto.PgpDecryptionService;
import eu.siacs.conversations.crypto.XmppDomainVerifier;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@@ -54,7 +55,6 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.CryptoHelper;
-import eu.siacs.conversations.utils.PhoneHelper;
import eu.siacs.conversations.utils.Resolver;
import eu.siacs.conversations.utils.SSLSockets;
import eu.siacs.conversations.utils.SocksSocketFactory;
@@ -1799,7 +1799,7 @@ public class XmppConnection implements Runnable {
account, appSettings.getInstallationId())));
userAgent.setSoftware(
String.format("%s %s", BuildConfig.APP_NAME, BuildConfig.VERSION_NAME));
- if (!PhoneHelper.isEmulator()) {
+ if (new Device(mXmppConnectionService).isPhysicalDevice()) {
userAgent.setDevice(String.format("%s %s", Build.MANUFACTURER, Build.MODEL));
}
// do not include bind if 'inlineStreamManagement' is missing and we have a streamId
@@ -13,6 +13,7 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Collections2;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableSet;
+import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
@@ -249,7 +250,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
private boolean isWithStrangerAndStrangerNotificationsAreOff(final Account account, Jid with) {
final boolean notifyForStrangers =
- mXmppConnectionService.getNotificationService().notificationsFromStrangers();
+ new AppSettings(mXmppConnectionService.getApplicationContext())
+ .isNotificationsFromStrangers();
if (notifyForStrangers) {
return false;
}
@@ -28,13 +28,13 @@ import com.google.common.util.concurrent.SettableFuture;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
+import eu.siacs.conversations.android.Device;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.Compatibility;
-import eu.siacs.conversations.utils.PhoneHelper;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.XmppConnection;
@@ -622,7 +622,9 @@ public class AvatarManager extends AbstractManager {
resizeAndStoreAvatarAsync(
image, Config.AVATAR_FULL_SIZE, ImageFormat.JPEG, autoAcceptFileSize);
- if (Compatibility.twentyEight() && !PhoneHelper.isEmulator()) {
+ final var device = new Device(context);
+
+ if (Compatibility.twentyEight() && device.isPhysicalDevice()) {
final var avatarHeifFuture =
resizeAndStoreAvatarAsync(
image,
@@ -634,7 +636,7 @@ public class AvatarManager extends AbstractManager {
avatarHeifFuture, this::upload, MoreExecutors.directExecutor());
avatarFutures.add(avatarHeifWithUrlFuture);
}
- if (Compatibility.thirtyFour() && !PhoneHelper.isEmulator()) {
+ if (Compatibility.thirtyFour() && device.isPhysicalDevice()) {
final var avatarAvifFuture =
resizeAndStoreAvatarAsync(
image,
@@ -0,0 +1,14 @@
+package eu.siacs.conversations.xmpp.manager;
+
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.xmpp.XmppConnection;
+
+public class MultiUserChatManager extends AbstractManager {
+
+ private final XmppConnectionService service;
+
+ public MultiUserChatManager(final XmppConnectionService service, XmppConnection connection) {
+ super(service.getApplicationContext(), connection);
+ this.service = service;
+ }
+}
@@ -1,12 +1,19 @@
package eu.siacs.conversations.xmpp.manager;
-import android.content.Context;
+import android.util.Log;
import com.google.common.base.Strings;
+import eu.siacs.conversations.AppSettings;
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.android.Device;
+import eu.siacs.conversations.generator.AbstractGenerator;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.XmppConnection;
import im.conversations.android.xmpp.EntityCapabilities;
import im.conversations.android.xmpp.EntityCapabilities2;
import im.conversations.android.xmpp.ServiceDescription;
+import im.conversations.android.xmpp.model.Extension;
import im.conversations.android.xmpp.model.capabilties.Capabilities;
import im.conversations.android.xmpp.model.capabilties.LegacyCapabilities;
import im.conversations.android.xmpp.model.nick.Nick;
@@ -18,11 +25,16 @@ import java.util.Map;
public class PresenceManager extends AbstractManager {
+ private final XmppConnectionService service;
+ private final AppSettings appSettings;
+
private final Map<EntityCapabilities.Hash, ServiceDescription> serviceDescriptions =
new HashMap<>();
- public PresenceManager(Context context, XmppConnection connection) {
- super(context, connection);
+ public PresenceManager(final XmppConnectionService service, final XmppConnection connection) {
+ super(service.getApplicationContext(), connection);
+ this.appSettings = new AppSettings(service.getApplicationContext());
+ this.service = service;
}
public void subscribe(final Jid address) {
@@ -62,6 +74,85 @@ public class PresenceManager extends AbstractManager {
this.connection.sendPresencePacket(presence);
}
+ public void available() {
+ available(service.checkListeners() && appSettings.isBroadcastLastActivity());
+ }
+
+ public void available(final boolean withIdle) {
+ final var account = connection.getAccount();
+ final var serviceDiscoveryFeatures = getManager(DiscoManager.class).getServiceDescription();
+ final var infoQuery = serviceDiscoveryFeatures.asInfoQuery();
+ final var capsHash = EntityCapabilities.hash(infoQuery);
+ final var caps2Hash = EntityCapabilities2.hash(infoQuery);
+ serviceDescriptions.put(capsHash, serviceDiscoveryFeatures);
+ serviceDescriptions.put(caps2Hash, serviceDiscoveryFeatures);
+ final var capabilities = new Capabilities();
+ capabilities.setHash(caps2Hash);
+ final var legacyCapabilities = new LegacyCapabilities();
+ legacyCapabilities.setNode(DiscoManager.CAPABILITY_NODE);
+ legacyCapabilities.setHash(capsHash);
+ final var presence = new Presence();
+ presence.addExtension(capabilities);
+ presence.addExtension(legacyCapabilities);
+ final String pgpSignature = account.getPgpSignature();
+ final String message = account.getPresenceStatusMessage();
+ final Presence.Availability availability;
+ if (appSettings.isUserManagedAvailability()) {
+ availability = account.getPresenceStatus();
+ } else {
+ availability = getTargetPresence();
+ }
+ presence.setAvailability(availability);
+ presence.setStatus(message);
+ if (pgpSignature != null) {
+ final var signed = new Signed();
+ signed.setContent(pgpSignature);
+ presence.addExtension(signed);
+ }
+
+ final var lastActivity = service.getLastActivity();
+ if (lastActivity > 0 && withIdle) {
+ final long since =
+ Math.min(lastActivity, System.currentTimeMillis()); // don't send future dates
+ presence.addChild("idle", Namespace.IDLE)
+ .setAttribute("since", AbstractGenerator.getTimestamp(since));
+ }
+ Log.d(Config.LOGTAG, "--> " + presence);
+ connection.sendPresencePacket(presence);
+ }
+
+ public void unavailable() {
+ var presence = new Presence(Presence.Type.UNAVAILABLE);
+ this.connection.sendPresencePacket(presence);
+ }
+
+ public void available(final Jid to, final Extension... extensions) {
+ final var presence = new Presence();
+ presence.setTo(to);
+ for (final var extension : extensions) {
+ presence.addExtension(extension);
+ }
+ connection.sendPresencePacket(presence);
+ }
+
+ public void unavailable(final Jid to) {
+ final var presence = new Presence(Presence.Type.UNAVAILABLE);
+ presence.setTo(to);
+ connection.sendPresencePacket(presence);
+ }
+
+ private im.conversations.android.xmpp.model.stanza.Presence.Availability getTargetPresence() {
+ final var device = new Device(context);
+ if (appSettings.isDndOnSilentMode()
+ && device.isPhoneSilenced(appSettings.isTreatVibrateAsSilent())) {
+ return im.conversations.android.xmpp.model.stanza.Presence.Availability.DND;
+ } else if (appSettings.isAwayWhenScreenLocked() && device.isScreenLocked()) {
+ return im.conversations.android.xmpp.model.stanza.Presence.Availability.AWAY;
+ } else {
+ return im.conversations.android.xmpp.model.stanza.Presence.Availability.ONLINE;
+ }
+ }
+
public Presence getPresence(final Presence.Availability availability, final boolean personal) {
final var account = connection.getAccount();
final var serviceDiscoveryFeatures = getManager(DiscoManager.class).getServiceDescription();
@@ -16,6 +16,7 @@ import eu.siacs.conversations.xmpp.manager.LegacyBookmarkManager;
import eu.siacs.conversations.xmpp.manager.MessageDisplayedSynchronizationManager;
import eu.siacs.conversations.xmpp.manager.NickManager;
import eu.siacs.conversations.xmpp.manager.OfflineMessagesManager;
+import eu.siacs.conversations.xmpp.manager.PresenceManager;
import eu.siacs.conversations.xmpp.manager.PrivateStorageManager;
import eu.siacs.conversations.xmpp.manager.RosterManager;
@@ -115,7 +116,7 @@ public class BindProcessor extends XmppConnection.Delegate implements Runnable {
} else {
trackOfflineMessageRetrieval = true;
}
- service.sendPresence(account);
+ getManager(PresenceManager.class).available();
connection.trackOfflineMessageRetrieval(trackOfflineMessageRetrieval);
if (service.getPushManagementService().available(account)) {
service.getPushManagementService().registerPushTokenOnServer(account);