From 19c634f3d2d089b4ed5eefcfda36cc0dd4d11c93 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 14 Jan 2024 10:58:00 +0100 Subject: [PATCH 001/363] use call integration via MANAGE_OWN_CALLS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit better integrate calls into the system via 'Build a calling app'¹ a few hooks like onAnswer/onReject and automatic PhoneAccount creation are still missing ¹: https://developer.android.com/develop/connectivity/telecom/selfManaged --- build.gradle | 2 +- src/main/AndroidManifest.xml | 13 +- .../services/AppRTCAudioManager.java | 100 ++--- .../services/CallIntegration.java | 408 ++++++++++++++++++ .../CallIntegrationConnectionService.java | 255 +++++++++++ .../services/XmppConnectionService.java | 30 +- .../ui/ConversationFragment.java | 6 +- .../conversations/ui/RtpSessionActivity.java | 57 ++- .../xmpp/jingle/JingleConnectionManager.java | 87 ++-- .../xmpp/jingle/JingleRtpConnection.java | 116 ++++- .../xmpp/jingle/RtpEndUserState.java | 2 +- .../xmpp/jingle/ToneManager.java | 3 +- .../xmpp/jingle/WebRTCWrapper.java | 42 +- 13 files changed, 909 insertions(+), 212 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/services/CallIntegration.java create mode 100644 src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java diff --git a/build.gradle b/build.gradle index b0e6227544aecc3f059584390b76aab6247a1619..e3fb55a76b02645758345a297c1964a6a4f2206f 100644 --- a/build.gradle +++ b/build.gradle @@ -95,7 +95,7 @@ android { compileSdk 34 defaultConfig { - minSdkVersion 21 + minSdkVersion 23 targetSdkVersion 34 versionCode 42094 versionName "2.13.4" diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index c01009862191e65e6f10cf106d0a85a885632ecc..6233770df0d66e352b7cf7ae61d8d0f75eabf3d2 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -5,9 +5,6 @@ - @@ -50,6 +47,8 @@ + + @@ -133,6 +132,14 @@ + + + + + + diff --git a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java index 3bed4eaba32604bd91ef082027a5f5e225fedb20..a472445d32bef97f697e32604a431051176a7e82 100644 --- a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java +++ b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java @@ -58,18 +58,18 @@ public class AppRTCAudioManager { private boolean hasWiredHeadset; // Default audio device; speaker phone for video calls or earpiece for audio // only calls. - private AudioDevice defaultAudioDevice; + private CallIntegration.AudioDevice defaultAudioDevice; // Contains the currently selected audio device. // This device is changed automatically using a certain scheme where e.g. // a wired headset "wins" over speaker phone. It is also possible for a // user to explicitly select a device (and overrid any predefined scheme). // See |userSelectedAudioDevice| for details. - private AudioDevice selectedAudioDevice; + private CallIntegration.AudioDevice selectedAudioDevice; // Contains the user-selected audio device which overrides the predefined // selection scheme. // TODO(henrika): always set to AudioDevice.NONE today. Add support for // explicit selection based on choice by userSelectedAudioDevice. - private AudioDevice userSelectedAudioDevice; + private CallIntegration.AudioDevice userSelectedAudioDevice; // Proximity sensor object. It measures the proximity of an object in cm // relative to the view screen of a device and can therefore be used to // assist device switching (close to ear <=> use headset earpiece if @@ -78,26 +78,25 @@ public class AppRTCAudioManager { private AppRTCProximitySensor proximitySensor; // Contains a list of available audio devices. A Set collection is used to // avoid duplicate elements. - private Set audioDevices = new HashSet<>(); + private Set audioDevices = new HashSet<>(); // Broadcast receiver for wired headset intent broadcasts. private final BroadcastReceiver wiredHeadsetReceiver; // Callback method for changes in audio focus. @Nullable private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener; - private AppRTCAudioManager(Context context, final SpeakerPhonePreference speakerPhonePreference) { - Log.d(Config.LOGTAG, "ctor"); + public AppRTCAudioManager(final Context context) { ThreadUtils.checkIsOnMainThread(); apprtcContext = context; audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE)); bluetoothManager = AppRTCBluetoothManager.create(context, this); wiredHeadsetReceiver = new WiredHeadsetReceiver(); amState = AudioManagerState.UNINITIALIZED; - this.speakerPhonePreference = speakerPhonePreference; - if (speakerPhonePreference == SpeakerPhonePreference.EARPIECE && hasEarpiece()) { - defaultAudioDevice = AudioDevice.EARPIECE; + // CallIntegration / Connection uses Earpiece as default too + if (hasEarpiece()) { + defaultAudioDevice = CallIntegration.AudioDevice.EARPIECE; } else { - defaultAudioDevice = AudioDevice.SPEAKER_PHONE; + defaultAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE; } // Create and initialize the proximity sensor. // Tablet devices (e.g. Nexus 7) does not support proximity sensors. @@ -114,20 +113,13 @@ public class AppRTCAudioManager { public void switchSpeakerPhonePreference(final SpeakerPhonePreference speakerPhonePreference) { this.speakerPhonePreference = speakerPhonePreference; if (speakerPhonePreference == SpeakerPhonePreference.EARPIECE && hasEarpiece()) { - defaultAudioDevice = AudioDevice.EARPIECE; + defaultAudioDevice = CallIntegration.AudioDevice.EARPIECE; } else { - defaultAudioDevice = AudioDevice.SPEAKER_PHONE; + defaultAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE; } updateAudioDeviceState(); } - /** - * Construction. - */ - public static AppRTCAudioManager create(Context context, SpeakerPhonePreference speakerPhonePreference) { - return new AppRTCAudioManager(context, speakerPhonePreference); - } - public static boolean isMicrophoneAvailable() { microphoneLatch = new CountDownLatch(1); AudioRecord audioRecord = null; @@ -174,16 +166,16 @@ public class AppRTCAudioManager { } // The proximity sensor should only be activated when there are exactly two // available audio devices. - if (audioDevices.size() == 2 && audioDevices.contains(AppRTCAudioManager.AudioDevice.EARPIECE) - && audioDevices.contains(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE)) { + if (audioDevices.size() == 2 && audioDevices.contains(CallIntegration.AudioDevice.EARPIECE) + && audioDevices.contains(CallIntegration.AudioDevice.SPEAKER_PHONE)) { if (proximitySensor.sensorReportsNearState()) { // Sensor reports that a "handset is being held up to a person's ear", // or "something is covering the light sensor". - setAudioDeviceInternal(AppRTCAudioManager.AudioDevice.EARPIECE); + setAudioDeviceInternal(CallIntegration.AudioDevice.EARPIECE); } else { // Sensor reports that a "handset is removed from a person's ear", or // "the light sensor is no longer covered". - setAudioDeviceInternal(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE); + setAudioDeviceInternal(CallIntegration.AudioDevice.SPEAKER_PHONE); } } } @@ -258,8 +250,8 @@ public class AppRTCAudioManager { // Always disable microphone mute during a WebRTC call. setMicrophoneMute(false); // Set initial device states. - userSelectedAudioDevice = AudioDevice.NONE; - selectedAudioDevice = AudioDevice.NONE; + userSelectedAudioDevice = CallIntegration.AudioDevice.NONE; + selectedAudioDevice = CallIntegration.AudioDevice.NONE; audioDevices.clear(); // Initialize and start Bluetooth if a BT device is available or initiate // detection of new (enabled) BT devices. @@ -315,7 +307,7 @@ public class AppRTCAudioManager { /** * Changes selection of the currently active audio device. */ - private void setAudioDeviceInternal(AudioDevice device) { + private void setAudioDeviceInternal(CallIntegration.AudioDevice device) { Log.d(Config.LOGTAG, "setAudioDeviceInternal(device=" + device + ")"); AppRTCUtils.assertIsTrue(audioDevices.contains(device)); switch (device) { @@ -338,7 +330,7 @@ public class AppRTCAudioManager { * Changes default audio device. * TODO(henrika): add usage of this method in the AppRTCMobile client. */ - public void setDefaultAudioDevice(AudioDevice defaultDevice) { + public void setDefaultAudioDevice(CallIntegration.AudioDevice defaultDevice) { ThreadUtils.checkIsOnMainThread(); switch (defaultDevice) { case SPEAKER_PHONE: @@ -348,7 +340,7 @@ public class AppRTCAudioManager { if (hasEarpiece()) { defaultAudioDevice = defaultDevice; } else { - defaultAudioDevice = AudioDevice.SPEAKER_PHONE; + defaultAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE; } break; default: @@ -362,7 +354,7 @@ public class AppRTCAudioManager { /** * Changes selection of the currently active audio device. */ - public void selectAudioDevice(AudioDevice device) { + public void selectAudioDevice(CallIntegration.AudioDevice device) { ThreadUtils.checkIsOnMainThread(); if (!audioDevices.contains(device)) { Log.e(Config.LOGTAG, "Can not select " + device + " from available " + audioDevices); @@ -374,7 +366,7 @@ public class AppRTCAudioManager { /** * Returns current set of available/selectable audio devices. */ - public Set getAudioDevices() { + public Set getAudioDevices() { ThreadUtils.checkIsOnMainThread(); return Collections.unmodifiableSet(new HashSet<>(audioDevices)); } @@ -382,7 +374,7 @@ public class AppRTCAudioManager { /** * Returns the currently selected audio device. */ - public AudioDevice getSelectedAudioDevice() { + public CallIntegration.AudioDevice getSelectedAudioDevice() { ThreadUtils.checkIsOnMainThread(); return selectedAudioDevice; } @@ -479,21 +471,21 @@ public class AppRTCAudioManager { bluetoothManager.updateDevice(); } // Update the set of available audio devices. - Set newAudioDevices = new HashSet<>(); + Set newAudioDevices = new HashSet<>(); if (bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED || bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTING || bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE) { - newAudioDevices.add(AudioDevice.BLUETOOTH); + newAudioDevices.add(CallIntegration.AudioDevice.BLUETOOTH); } if (hasWiredHeadset) { // If a wired headset is connected, then it is the only possible option. - newAudioDevices.add(AudioDevice.WIRED_HEADSET); + newAudioDevices.add(CallIntegration.AudioDevice.WIRED_HEADSET); } else { // No wired headset, hence the audio-device list can contain speaker // phone (on a tablet), or speaker phone and earpiece (on mobile phone). - newAudioDevices.add(AudioDevice.SPEAKER_PHONE); + newAudioDevices.add(CallIntegration.AudioDevice.SPEAKER_PHONE); if (hasEarpiece()) { - newAudioDevices.add(AudioDevice.EARPIECE); + newAudioDevices.add(CallIntegration.AudioDevice.EARPIECE); } } // Store state which is set to true if the device list has changed. @@ -502,33 +494,33 @@ public class AppRTCAudioManager { audioDevices = newAudioDevices; // Correct user selected audio devices if needed. if (bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_UNAVAILABLE - && userSelectedAudioDevice == AudioDevice.BLUETOOTH) { + && userSelectedAudioDevice == CallIntegration.AudioDevice.BLUETOOTH) { // If BT is not available, it can't be the user selection. - userSelectedAudioDevice = AudioDevice.NONE; + userSelectedAudioDevice = CallIntegration.AudioDevice.NONE; } - if (hasWiredHeadset && userSelectedAudioDevice == AudioDevice.SPEAKER_PHONE) { + if (hasWiredHeadset && userSelectedAudioDevice == CallIntegration.AudioDevice.SPEAKER_PHONE) { // If user selected speaker phone, but then plugged wired headset then make // wired headset as user selected device. - userSelectedAudioDevice = AudioDevice.WIRED_HEADSET; + userSelectedAudioDevice = CallIntegration.AudioDevice.WIRED_HEADSET; } - if (!hasWiredHeadset && userSelectedAudioDevice == AudioDevice.WIRED_HEADSET) { + if (!hasWiredHeadset && userSelectedAudioDevice == CallIntegration.AudioDevice.WIRED_HEADSET) { // If user selected wired headset, but then unplugged wired headset then make // speaker phone as user selected device. - userSelectedAudioDevice = AudioDevice.SPEAKER_PHONE; + userSelectedAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE; } // Need to start Bluetooth if it is available and user either selected it explicitly or // user did not select any output device. boolean needBluetoothAudioStart = bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE - && (userSelectedAudioDevice == AudioDevice.NONE - || userSelectedAudioDevice == AudioDevice.BLUETOOTH); + && (userSelectedAudioDevice == CallIntegration.AudioDevice.NONE + || userSelectedAudioDevice == CallIntegration.AudioDevice.BLUETOOTH); // Need to stop Bluetooth audio if user selected different device and // Bluetooth SCO connection is established or in the process. boolean needBluetoothAudioStop = (bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED || bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTING) - && (userSelectedAudioDevice != AudioDevice.NONE - && userSelectedAudioDevice != AudioDevice.BLUETOOTH); + && (userSelectedAudioDevice != CallIntegration.AudioDevice.NONE + && userSelectedAudioDevice != CallIntegration.AudioDevice.BLUETOOTH); if (bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE || bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTING || bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED) { @@ -545,21 +537,21 @@ public class AppRTCAudioManager { // Attempt to start Bluetooth SCO audio (takes a few second to start). if (!bluetoothManager.startScoAudio()) { // Remove BLUETOOTH from list of available devices since SCO failed. - audioDevices.remove(AudioDevice.BLUETOOTH); + audioDevices.remove(CallIntegration.AudioDevice.BLUETOOTH); audioDeviceSetUpdated = true; } } // Update selected audio device. - final AudioDevice newAudioDevice; + final CallIntegration.AudioDevice newAudioDevice; if (bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED) { // If a Bluetooth is connected, then it should be used as output audio // device. Note that it is not sufficient that a headset is available; // an active SCO channel must also be up and running. - newAudioDevice = AudioDevice.BLUETOOTH; + newAudioDevice = CallIntegration.AudioDevice.BLUETOOTH; } else if (hasWiredHeadset) { // If a wired headset is connected, but Bluetooth is not, then wired headset is used as // audio device. - newAudioDevice = AudioDevice.WIRED_HEADSET; + newAudioDevice = CallIntegration.AudioDevice.WIRED_HEADSET; } else { // No wired headset and no Bluetooth, hence the audio-device list can contain speaker // phone (on a tablet), or speaker phone and earpiece (on mobile phone). @@ -582,12 +574,6 @@ public class AppRTCAudioManager { Log.d(Config.LOGTAG, "--- updateAudioDeviceState done"); } - /** - * AudioDevice is the names of possible audio devices that we currently - * support. - */ - public enum AudioDevice {SPEAKER_PHONE, WIRED_HEADSET, EARPIECE, BLUETOOTH, NONE} - /** * AudioManager state. */ @@ -615,7 +601,7 @@ public class AppRTCAudioManager { public interface AudioManagerEvents { // Callback fired once audio device is changed or list of available audio devices changed. void onAudioDeviceChanged( - AudioDevice selectedAudioDevice, Set availableAudioDevices); + CallIntegration.AudioDevice selectedAudioDevice, Set availableAudioDevices); } /* Receiver which handles changes in wired headset availability. */ diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java new file mode 100644 index 0000000000000000000000000000000000000000..bf12f5f4bd378a08a63245370b111a412b2e5eb0 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -0,0 +1,408 @@ +package eu.siacs.conversations.services; + +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.telecom.CallAudioState; +import android.telecom.CallEndpoint; +import android.telecom.Connection; +import android.telecom.DisconnectCause; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.ui.util.MainThreadExecutor; +import eu.siacs.conversations.xmpp.Jid; +import eu.siacs.conversations.xmpp.jingle.Media; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +public class CallIntegration extends Connection { + + private final AppRTCAudioManager appRTCAudioManager; + private AudioDevice initialAudioDevice = null; + private final AtomicBoolean initialAudioDeviceConfigured = new AtomicBoolean(false); + + private List availableEndpoints = Collections.emptyList(); + + private Callback callback = null; + + public CallIntegration(final Context context) { + if (selfManaged()) { + setConnectionProperties(Connection.PROPERTY_SELF_MANAGED); + this.appRTCAudioManager = null; + } else { + this.appRTCAudioManager = new AppRTCAudioManager(context); + this.appRTCAudioManager.start(this::onAudioDeviceChanged); + // TODO WebRTCWrapper would issue one call to eventCallback.onAudioDeviceChanged + } + setRingbackRequested(true); + } + + public void setCallback(final Callback callback) { + this.callback = callback; + } + + @Override + public void onShowIncomingCallUi() { + Log.d(Config.LOGTAG, "onShowIncomingCallUi"); + this.callback.onCallIntegrationShowIncomingCallUi(); + } + + @Override + public void onAnswer() { + Log.d(Config.LOGTAG, "onAnswer()"); + } + + @Override + public void onDisconnect() { + Log.d(Config.LOGTAG, "onDisconnect()"); + this.callback.onCallIntegrationDisconnect(); + } + + @Override + public void onReject() { + Log.d(Config.LOGTAG, "onReject()"); + } + + @Override + public void onReject(final String replyMessage) { + Log.d(Config.LOGTAG, "onReject(" + replyMessage + ")"); + } + + @RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Override + public void onAvailableCallEndpointsChanged(@NonNull List availableEndpoints) { + Log.d(Config.LOGTAG, "onAvailableCallEndpointsChanged(" + availableEndpoints + ")"); + this.availableEndpoints = availableEndpoints; + this.onAudioDeviceChanged( + getAudioDeviceUpsideDownCake(getCurrentCallEndpoint()), + ImmutableSet.copyOf( + Lists.transform( + availableEndpoints, + CallIntegration::getAudioDeviceUpsideDownCake))); + } + + @RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Override + public void onCallEndpointChanged(@NonNull final CallEndpoint callEndpoint) { + Log.d(Config.LOGTAG, "onCallEndpointChanged()"); + this.onAudioDeviceChanged( + getAudioDeviceUpsideDownCake(callEndpoint), + ImmutableSet.copyOf( + Lists.transform( + this.availableEndpoints, + CallIntegration::getAudioDeviceUpsideDownCake))); + } + + @Override + public void onCallAudioStateChanged(final CallAudioState state) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + Log.d(Config.LOGTAG, "ignoring onCallAudioStateChange() on Upside Down Cake"); + return; + } + Log.d(Config.LOGTAG, "onCallAudioStateChange(" + state + ")"); + this.onAudioDeviceChanged(getAudioDeviceOreo(state), getAudioDevicesOreo(state)); + } + + public Set getAudioDevices() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + return getAudioDevicesUpsideDownCake(); + } else if (selfManaged()) { + return getAudioDevicesOreo(); + } else { + return getAudioDevicesFallback(); + } + } + + public AudioDevice getSelectedAudioDevice() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + return getAudioDeviceUpsideDownCake(); + } else if (selfManaged()) { + return getAudioDeviceOreo(); + } else { + return getAudioDeviceFallback(); + } + } + + public void setAudioDevice(final AudioDevice audioDevice) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + setAudioDeviceUpsideDownCake(audioDevice); + } else if (selfManaged()) { + setAudioDeviceOreo(audioDevice); + } else { + setAudioDeviceFallback(audioDevice); + } + } + + @RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + private Set getAudioDevicesUpsideDownCake() { + return ImmutableSet.copyOf( + Lists.transform( + this.availableEndpoints, CallIntegration::getAudioDeviceUpsideDownCake)); + } + + @RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + private AudioDevice getAudioDeviceUpsideDownCake() { + return getAudioDeviceUpsideDownCake(getCurrentCallEndpoint()); + } + + @RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + private static AudioDevice getAudioDeviceUpsideDownCake(final CallEndpoint callEndpoint) { + if (callEndpoint == null) { + return AudioDevice.NONE; + } + final var endpointType = callEndpoint.getEndpointType(); + return switch (endpointType) { + case CallEndpoint.TYPE_BLUETOOTH -> AudioDevice.BLUETOOTH; + case CallEndpoint.TYPE_EARPIECE -> AudioDevice.EARPIECE; + case CallEndpoint.TYPE_SPEAKER -> AudioDevice.SPEAKER_PHONE; + case CallEndpoint.TYPE_WIRED_HEADSET -> AudioDevice.WIRED_HEADSET; + case CallEndpoint.TYPE_STREAMING -> AudioDevice.STREAMING; + case CallEndpoint.TYPE_UNKNOWN -> AudioDevice.NONE; + default -> throw new IllegalStateException("Unknown endpoint type " + endpointType); + }; + } + + @RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + private void setAudioDeviceUpsideDownCake(final AudioDevice audioDevice) { + final var callEndpointOptional = + Iterables.tryFind( + this.availableEndpoints, + e -> getAudioDeviceUpsideDownCake(e) == audioDevice); + if (callEndpointOptional.isPresent()) { + final var endpoint = callEndpointOptional.get(); + requestCallEndpointChange( + endpoint, + MainThreadExecutor.getInstance(), + result -> Log.d(Config.LOGTAG, "switched to endpoint " + endpoint)); + } else { + Log.w(Config.LOGTAG, "no endpoint found matching " + audioDevice); + } + } + + private Set getAudioDevicesOreo() { + final var audioState = getCallAudioState(); + if (audioState == null) { + Log.d( + Config.LOGTAG, + "no CallAudioState available. returning empty set for audio devices"); + return Collections.emptySet(); + } + return getAudioDevicesOreo(audioState); + } + + private static Set getAudioDevicesOreo(final CallAudioState callAudioState) { + final ImmutableSet.Builder supportedAudioDevicesBuilder = + new ImmutableSet.Builder<>(); + final var supportedRouteMask = callAudioState.getSupportedRouteMask(); + if ((supportedRouteMask & CallAudioState.ROUTE_BLUETOOTH) + == CallAudioState.ROUTE_BLUETOOTH) { + supportedAudioDevicesBuilder.add(AudioDevice.BLUETOOTH); + } + if ((supportedRouteMask & CallAudioState.ROUTE_EARPIECE) == CallAudioState.ROUTE_EARPIECE) { + supportedAudioDevicesBuilder.add(AudioDevice.EARPIECE); + } + if ((supportedRouteMask & CallAudioState.ROUTE_SPEAKER) == CallAudioState.ROUTE_SPEAKER) { + supportedAudioDevicesBuilder.add(AudioDevice.SPEAKER_PHONE); + } + if ((supportedRouteMask & CallAudioState.ROUTE_WIRED_HEADSET) + == CallAudioState.ROUTE_WIRED_HEADSET) { + supportedAudioDevicesBuilder.add(AudioDevice.WIRED_HEADSET); + } + return supportedAudioDevicesBuilder.build(); + } + + private AudioDevice getAudioDeviceOreo() { + final var audioState = getCallAudioState(); + if (audioState == null) { + Log.d(Config.LOGTAG, "no CallAudioState available. returning NONE as audio device"); + return AudioDevice.NONE; + } + return getAudioDeviceOreo(audioState); + } + + private static AudioDevice getAudioDeviceOreo(final CallAudioState audioState) { + // technically we get a mask here; maybe we should query the mask instead + return switch (audioState.getRoute()) { + case CallAudioState.ROUTE_BLUETOOTH -> AudioDevice.BLUETOOTH; + case CallAudioState.ROUTE_EARPIECE -> AudioDevice.EARPIECE; + case CallAudioState.ROUTE_SPEAKER -> AudioDevice.SPEAKER_PHONE; + case CallAudioState.ROUTE_WIRED_HEADSET -> AudioDevice.WIRED_HEADSET; + default -> AudioDevice.NONE; + }; + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private void setAudioDeviceOreo(final AudioDevice audioDevice) { + switch (audioDevice) { + case EARPIECE -> setAudioRoute(CallAudioState.ROUTE_EARPIECE); + case BLUETOOTH -> setAudioRoute(CallAudioState.ROUTE_BLUETOOTH); + case WIRED_HEADSET -> setAudioRoute(CallAudioState.ROUTE_WIRED_HEADSET); + case SPEAKER_PHONE -> setAudioRoute(CallAudioState.ROUTE_SPEAKER); + } + } + + private Set getAudioDevicesFallback() { + return requireAppRtcAudioManager().getAudioDevices(); + } + + private AudioDevice getAudioDeviceFallback() { + return requireAppRtcAudioManager().getSelectedAudioDevice(); + } + + private void setAudioDeviceFallback(final AudioDevice audioDevice) { + requireAppRtcAudioManager().setDefaultAudioDevice(audioDevice); + } + + @NonNull + private AppRTCAudioManager requireAppRtcAudioManager() { + if (this.appRTCAudioManager == null) { + throw new IllegalStateException( + "You are trying to access the fallback audio manager on a modern device"); + } + return this.appRTCAudioManager; + } + + @Override + public void onStateChanged(final int state) { + Log.d(Config.LOGTAG, "onStateChanged(" + state + ")"); + if (state == STATE_DISCONNECTED) { + final var audioManager = this.appRTCAudioManager; + if (audioManager != null) { + audioManager.stop(); + } + } + } + + public void success() { + Log.d(Config.LOGTAG, "CallIntegration.success()"); + this.destroyWith(new DisconnectCause(DisconnectCause.LOCAL, null)); + } + + public void accepted() { + Log.d(Config.LOGTAG, "CallIntegration.accepted()"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { + this.destroyWith(new DisconnectCause(DisconnectCause.ANSWERED_ELSEWHERE, null)); + } else { + this.destroyWith(new DisconnectCause(DisconnectCause.CANCELED, null)); + } + } + + public void error() { + Log.d(Config.LOGTAG, "CallIntegration.error()"); + this.destroyWith(new DisconnectCause(DisconnectCause.ERROR, null)); + } + + public void retracted() { + Log.d(Config.LOGTAG, "CallIntegration.retracted()"); + // an alternative cause would be LOCAL + this.destroyWith(new DisconnectCause(DisconnectCause.CANCELED, null)); + } + + public void rejected() { + Log.d(Config.LOGTAG, "CallIntegration.rejected()"); + this.destroyWith(new DisconnectCause(DisconnectCause.REJECTED, null)); + } + + public void busy() { + Log.d(Config.LOGTAG, "CallIntegration.busy()"); + this.destroyWith(new DisconnectCause(DisconnectCause.BUSY, null)); + } + + private void destroyWith(final DisconnectCause disconnectCause) { + if (this.getState() == STATE_DISCONNECTED) { + Log.d(Config.LOGTAG, "CallIntegration has already been destroyed"); + return; + } + this.setDisconnected(disconnectCause); + this.destroy(); + } + + public static Uri address(final Jid contact) { + return Uri.parse(String.format("xmpp:%s", contact.toEscapedString())); + } + + public void verifyDisconnected() { + if (this.getState() == STATE_DISCONNECTED) { + return; + } + throw new AssertionError("CallIntegration has not been disconnected"); + } + + private void onAudioDeviceChanged( + final CallIntegration.AudioDevice selectedAudioDevice, + final Set availableAudioDevices) { + if (this.initialAudioDevice != null + && this.initialAudioDeviceConfigured.compareAndSet(false, true)) { + if (availableAudioDevices.contains(this.initialAudioDevice)) { + setAudioDevice(this.initialAudioDevice); + Log.d(Config.LOGTAG, "configured initial audio device"); + } else { + Log.d( + Config.LOGTAG, + "initial audio device not available. available devices: " + + availableAudioDevices); + } + } + final var callback = this.callback; + if (callback == null) { + return; + } + callback.onAudioDeviceChanged(selectedAudioDevice, availableAudioDevices); + } + + public static boolean selfManaged() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + } + + public void setInitialAudioDevice(final AudioDevice audioDevice) { + Log.d(Config.LOGTAG, "setInitialAudioDevice(" + audioDevice + ")"); + this.initialAudioDevice = audioDevice; + if (CallIntegration.selfManaged()) { + // once the 'CallIntegration' gets added to the system we receive calls to update audio + // state + return; + } + final var audioManager = requireAppRtcAudioManager(); + this.onAudioDeviceChanged( + audioManager.getSelectedAudioDevice(), audioManager.getAudioDevices()); + } + + /** AudioDevice is the names of possible audio devices that we currently support. */ + public enum AudioDevice { + NONE, + SPEAKER_PHONE, + WIRED_HEADSET, + EARPIECE, + BLUETOOTH, + STREAMING + } + + public static AudioDevice initialAudioDevice(final Set media) { + if (Media.audioOnly(media)) { + return AudioDevice.EARPIECE; + } else { + return AudioDevice.SPEAKER_PHONE; + } + } + + public interface Callback { + void onCallIntegrationShowIncomingCallUi(); + + void onCallIntegrationDisconnect(); + + void onAudioDeviceChanged( + CallIntegration.AudioDevice selectedAudioDevice, + Set availableAudioDevices); + } +} diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java new file mode 100644 index 0000000000000000000000000000000000000000..cfd6ae603c91bc5e1bcdc8e724972b3571faa201 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -0,0 +1,255 @@ +package eu.siacs.conversations.services; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.telecom.Connection; +import android.telecom.ConnectionRequest; +import android.telecom.ConnectionService; +import android.telecom.DisconnectCause; +import android.telecom.PhoneAccount; +import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; +import android.telecom.VideoProfile; +import android.util.Log; + +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.ui.RtpSessionActivity; +import eu.siacs.conversations.xmpp.Jid; +import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection; +import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; +import eu.siacs.conversations.xmpp.jingle.Media; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class CallIntegrationConnectionService extends ConnectionService { + + private ListenableFuture serviceFuture; + + @Override + public void onCreate() { + super.onCreate(); + this.serviceFuture = ServiceConnectionService.bindService(this); + } + + @Override + public void onDestroy() { + Log.d(Config.LOGTAG, "destroying CallIntegrationConnectionService"); + super.onDestroy(); + final ServiceConnection serviceConnection; + try { + serviceConnection = serviceFuture.get().serviceConnection; + } catch (final Exception e) { + Log.d(Config.LOGTAG, "could not fetch service connection", e); + return; + } + this.unbindService(serviceConnection); + } + + @Override + public Connection onCreateOutgoingConnection( + final PhoneAccountHandle phoneAccountHandle, final ConnectionRequest request) { + Log.d(Config.LOGTAG, "onCreateOutgoingConnection(" + request.getAddress() + ")"); + final var uri = request.getAddress(); + final var jid = Jid.ofEscaped(uri.getSchemeSpecificPart()); + final var extras = request.getExtras(); + final int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE); + final Set media = + videoState == VideoProfile.STATE_AUDIO_ONLY + ? ImmutableSet.of(Media.AUDIO) + : ImmutableSet.of(Media.AUDIO, Media.VIDEO); + Log.d(Config.LOGTAG, "jid=" + jid); + Log.d(Config.LOGTAG, "phoneAccountHandle:" + phoneAccountHandle.getId()); + Log.d(Config.LOGTAG, "media " + media); + final var service = ServiceConnectionService.get(this.serviceFuture); + if (service == null) { + return Connection.createFailedConnection( + new DisconnectCause(DisconnectCause.ERROR, "service connection not found")); + } + final Account account = service.findAccountByUuid(phoneAccountHandle.getId()); + final Intent intent = new Intent(this, RtpSessionActivity.class); + intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.getJid().toEscapedString()); + intent.putExtra(RtpSessionActivity.EXTRA_WITH, jid.toEscapedString()); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + final CallIntegration callIntegration; + if (jid.isBareJid()) { + final var proposal = + service.getJingleConnectionManager() + .proposeJingleRtpSession(account, jid, media); + + if (Media.audioOnly(media)) { + intent.setAction(RtpSessionActivity.ACTION_MAKE_VOICE_CALL); + } else { + intent.setAction(RtpSessionActivity.ACTION_MAKE_VIDEO_CALL); + } + callIntegration = proposal.getCallIntegration(); + } else { + final JingleRtpConnection jingleRtpConnection = + service.getJingleConnectionManager().initializeRtpSession(account, jid, media); + final String sessionId = jingleRtpConnection.getId().sessionId; + intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, sessionId); + callIntegration = jingleRtpConnection.getCallIntegration(); + } + Log.d(Config.LOGTAG, "start activity!"); + startActivity(intent); + return callIntegration; + } + + public Connection onCreateIncomingConnection( + final PhoneAccountHandle phoneAccountHandle, final ConnectionRequest request) { + final var service = ServiceConnectionService.get(this.serviceFuture); + final Bundle extras = request.getExtras(); + final Bundle extraExtras = extras.getBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS); + final String incomingCallAddress = + extras.getString(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS); + final String sid = extraExtras == null ? null : extraExtras.getString("sid"); + Log.d(Config.LOGTAG, "sid " + sid); + final Uri uri = incomingCallAddress == null ? null : Uri.parse(incomingCallAddress); + Log.d(Config.LOGTAG, "uri=" + uri); + if (uri == null || sid == null) { + return Connection.createFailedConnection( + new DisconnectCause( + DisconnectCause.ERROR, + "connection request is missing required information")); + } + if (service == null) { + return Connection.createFailedConnection( + new DisconnectCause(DisconnectCause.ERROR, "service connection not found")); + } + final var jid = Jid.ofEscaped(uri.getSchemeSpecificPart()); + final Account account = service.findAccountByUuid(phoneAccountHandle.getId()); + final var weakReference = + service.getJingleConnectionManager().findJingleRtpConnection(account, jid, sid); + if (weakReference == null) { + Log.d(Config.LOGTAG, "no connection found for " + jid + " and sid=" + sid); + return Connection.createFailedConnection( + new DisconnectCause(DisconnectCause.ERROR, "no incoming connection found")); + } + final var jingleRtpConnection = weakReference.get(); + if (jingleRtpConnection == null) { + Log.d(Config.LOGTAG, "connection has been terminated"); + return Connection.createFailedConnection( + new DisconnectCause(DisconnectCause.ERROR, "connection has been terminated")); + } + Log.d(Config.LOGTAG, "registering call integration for incoming call"); + return jingleRtpConnection.getCallIntegration(); + } + + public static void registerPhoneAccount(final Context context, final Account account) { + final var builder = + PhoneAccount.builder(getHandle(context, account), account.getJid().asBareJid()); + builder.setSupportedUriSchemes(Collections.singletonList("xmpp")); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + builder.setCapabilities( + PhoneAccount.CAPABILITY_SELF_MANAGED + | PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING); + } + final var phoneAccount = builder.build(); + + context.getSystemService(TelecomManager.class).registerPhoneAccount(phoneAccount); + } + + public static void registerPhoneAccounts( + final Context context, final Collection accounts) { + for (final Account account : accounts) { + registerPhoneAccount(context, account); + } + } + + public static PhoneAccountHandle getHandle(final Context context, final Account account) { + final var competentName = + new ComponentName(context, CallIntegrationConnectionService.class); + return new PhoneAccountHandle(competentName, account.getUuid()); + } + + public static void placeCall( + final Context context, final Account account, final Jid with, final Set media) { + Log.d(Config.LOGTAG, "place call media=" + media); + final var extras = new Bundle(); + extras.putParcelable( + TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, getHandle(context, account)); + extras.putInt( + TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, + Media.audioOnly(media) + ? VideoProfile.STATE_AUDIO_ONLY + : VideoProfile.STATE_BIDIRECTIONAL); + context.getSystemService(TelecomManager.class) + .placeCall(CallIntegration.address(with), extras); + } + + public static void addNewIncomingCall( + final Context context, final AbstractJingleConnection.Id id) { + final var phoneAccountHandle = + CallIntegrationConnectionService.getHandle(context, id.account); + final var bundle = new Bundle(); + bundle.putString( + TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, + CallIntegration.address(id.with).toString()); + final var extras = new Bundle(); + extras.putString("sid", id.sessionId); + bundle.putBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras); + context.getSystemService(TelecomManager.class) + .addNewIncomingCall(phoneAccountHandle, bundle); + } + + public static class ServiceConnectionService { + private final ServiceConnection serviceConnection; + private final XmppConnectionService service; + + public ServiceConnectionService( + final ServiceConnection serviceConnection, final XmppConnectionService service) { + this.serviceConnection = serviceConnection; + this.service = service; + } + + public static XmppConnectionService get( + final ListenableFuture future) { + try { + return future.get(2, TimeUnit.SECONDS).service; + } catch (final ExecutionException | InterruptedException | TimeoutException e) { + return null; + } + } + + public static ListenableFuture bindService( + final Context context) { + final SettableFuture serviceConnectionFuture = + SettableFuture.create(); + final var intent = new Intent(context, XmppConnectionService.class); + intent.setAction(XmppConnectionService.ACTION_CALL_INTEGRATION_SERVICE_STARTED); + final var serviceConnection = + new ServiceConnection() { + + @Override + public void onServiceConnected( + final ComponentName name, final IBinder iBinder) { + final XmppConnectionService.XmppConnectionBinder binder = + (XmppConnectionService.XmppConnectionBinder) iBinder; + serviceConnectionFuture.set( + new ServiceConnectionService(this, binder.getService())); + } + + @Override + public void onServiceDisconnected(final ComponentName name) {} + }; + context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + return serviceConnectionFuture; + } + } +} diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 58423715684bd1b42aa7a1dc63cf32b93d718b0d..5f48fca58e9516a182b831b45166e4b70c05b2ee 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -198,6 +198,7 @@ public class XmppConnectionService extends Service { public static final String ACTION_DISMISS_CALL = "dismiss_call"; public static final String ACTION_END_CALL = "end_call"; public static final String ACTION_PROVISION_ACCOUNT = "provision_account"; + public static final String ACTION_CALL_INTEGRATION_SERVICE_STARTED = "call_integration_service_started"; private static final String ACTION_POST_CONNECTIVITY_CHANGE = "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE"; public static final String ACTION_RENEW_UNIFIED_PUSH_ENDPOINTS = "eu.siacs.conversations.UNIFIED_PUSH_RENEW"; public static final String ACTION_QUICK_LOG = "eu.siacs.conversations.QUICK_LOG"; @@ -303,16 +304,6 @@ public class XmppConnectionService extends Service { return false; } }; - private final AtomicBoolean isPhoneInCall = new AtomicBoolean(false); - private final PhoneStateListener phoneStateListener = new PhoneStateListener() { - @Override - public void onCallStateChanged(final int state, final String phoneNumber) { - isPhoneInCall.set(state != TelephonyManager.CALL_STATE_IDLE); - if (state == TelephonyManager.CALL_STATE_OFFHOOK) { - mJingleConnectionManager.notifyPhoneCallStarted(); - } - } - }; private boolean destroyed = false; @@ -1288,6 +1279,8 @@ public class XmppConnectionService extends Service { toggleSetProfilePictureActivity(hasEnabledAccounts); reconfigurePushDistributor(); + CallIntegrationConnectionService.registerPhoneAccounts(this, this.accounts); + restoreFromDatabase(); if (QuickConversationsService.isContactListIntegration(this) @@ -1351,23 +1344,10 @@ public class XmppConnectionService extends Service { ContextCompat.RECEIVER_EXPORTED); mForceDuringOnCreate.set(false); toggleForegroundService(); - setupPhoneStateListener(); internalPingExecutor.scheduleAtFixedRate(this::manageAccountConnectionStatesInternal,10,10,TimeUnit.SECONDS); } - private void setupPhoneStateListener() { - final TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); - if (telephonyManager == null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - return; - } - telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); - } - - public boolean isPhoneInCall() { - return isPhoneInCall.get(); - } - private void checkForDeletedFiles() { if (destroyed) { Log.d(Config.LOGTAG, "Do not check for deleted files because service has been destroyed"); @@ -4413,7 +4393,7 @@ public class XmppConnectionService extends Service { } } - public void notifyJingleRtpConnectionUpdate(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set availableAudioDevices) { + public void notifyJingleRtpConnectionUpdate(CallIntegration.AudioDevice selectedAudioDevice, Set availableAudioDevices) { for (OnJingleRtpConnectionUpdate listener : threadSafeList(this.onJingleRtpConnectionUpdate)) { listener.onAudioDeviceChanged(selectedAudioDevice, availableAudioDevices); } @@ -5110,7 +5090,7 @@ public class XmppConnectionService extends Service { public interface OnJingleRtpConnectionUpdate { void onJingleRtpConnectionUpdate(final Account account, final Jid with, final String sessionId, final RtpEndUserState state); - void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set availableAudioDevices); + void onAudioDeviceChanged(CallIntegration.AudioDevice selectedAudioDevice, Set availableAudioDevices); } public interface OnAccountUpdate { diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index d19bc4a55cba0e01326013afb0ba68b9ada633a4..ccb08f95f006ab53d2db1b251883b2b0e313994b 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -86,6 +86,7 @@ import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.TransferablePlaceholder; import eu.siacs.conversations.http.HttpDownloadConnection; import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.services.CallIntegrationConnectionService; import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.QuickConversationsService; import eu.siacs.conversations.services.XmppConnectionService; @@ -1652,13 +1653,14 @@ public class ConversationFragment extends XmppFragment } private void triggerRtpSession(final Account account, final Jid with, final String action) { - final Intent intent = new Intent(activity, RtpSessionActivity.class); + CallIntegrationConnectionService.placeCall(requireActivity(),account,with,RtpSessionActivity.actionToMedia(action)); + /*final Intent intent = new Intent(activity, RtpSessionActivity.class); intent.setAction(action); intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.getJid().toEscapedString()); intent.putExtra(RtpSessionActivity.EXTRA_WITH, with.toEscapedString()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(intent); + startActivity(intent);*/ } private void handleAttachmentSelection(MenuItem item) { diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 5b5c82baefb554d883afcc30092b02f87085d756..8f546918ab0a3fdbf58eb13e7dea93493e978284 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -49,6 +49,8 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.services.AppRTCAudioManager; +import eu.siacs.conversations.services.CallIntegration; +import eu.siacs.conversations.services.CallIntegrationConnectionService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.util.AvatarWorkerTask; import eu.siacs.conversations.ui.util.MainThreadExecutor; @@ -133,7 +135,7 @@ public class RtpSessionActivity extends XmppActivity } }; - private static Set actionToMedia(final String action) { + public static Set actionToMedia(final String action) { if (ACTION_MAKE_VIDEO_CALL.equals(action)) { return ImmutableSet.of(Media.AUDIO, Media.VIDEO); } else { @@ -416,11 +418,11 @@ public class RtpSessionActivity extends XmppActivity if (Media.audioOnly(media)) { final JingleRtpConnection rtpConnection = rtpConnectionReference != null ? rtpConnectionReference.get() : null; - final AppRTCAudioManager audioManager = - rtpConnection == null ? null : rtpConnection.getAudioManager(); - if (audioManager == null - || audioManager.getSelectedAudioDevice() - == AppRTCAudioManager.AudioDevice.EARPIECE) { + final CallIntegration callIntegration = + rtpConnection == null ? null : rtpConnection.getCallIntegration(); + if (callIntegration == null + || callIntegration.getSelectedAudioDevice() + == CallIntegration.AudioDevice.EARPIECE) { acquireProximityWakeLock(); } } @@ -466,8 +468,8 @@ public class RtpSessionActivity extends XmppActivity } private void putProximityWakeLockInProperState( - final AppRTCAudioManager.AudioDevice audioDevice) { - if (audioDevice == AppRTCAudioManager.AudioDevice.EARPIECE) { + final CallIntegration.AudioDevice audioDevice) { + if (audioDevice == CallIntegration.AudioDevice.EARPIECE) { acquireProximityWakeLock(); } else { releaseProximityWakeLock(); @@ -581,12 +583,7 @@ public class RtpSessionActivity extends XmppActivity .getJingleConnectionManager() .proposeJingleRtpSession(account, with, media); } else { - final String sessionId = - xmppConnectionService - .getJingleConnectionManager() - .initializeRtpSession(account, with, media); - initializeActivityWithRunningRtpSession(account, with, sessionId); - resetIntent(account, with, sessionId); + throw new IllegalStateException("We should not be initializing direct calls from the RtpSessionActivity. Go through CallIntegrationConnectionService.placeCall instead!"); } putScreenInCallMode(media); } @@ -1032,10 +1029,10 @@ public class RtpSessionActivity extends XmppActivity updateInCallButtonConfigurationVideo( rtpConnection.isVideoEnabled(), rtpConnection.isCameraSwitchable()); } else { - final AppRTCAudioManager audioManager = requireRtpConnection().getAudioManager(); + final CallIntegration callIntegration = requireRtpConnection().getCallIntegration(); updateInCallButtonConfigurationSpeaker( - audioManager.getSelectedAudioDevice(), - audioManager.getAudioDevices().size()); + callIntegration.getSelectedAudioDevice(), + callIntegration.getAudioDevices().size()); this.binding.inCallActionFarRight.setVisibility(View.GONE); } if (media.contains(Media.AUDIO)) { @@ -1053,7 +1050,7 @@ public class RtpSessionActivity extends XmppActivity @SuppressLint("RestrictedApi") private void updateInCallButtonConfigurationSpeaker( - final AppRTCAudioManager.AudioDevice selectedAudioDevice, final int numberOfChoices) { + final CallIntegration.AudioDevice selectedAudioDevice, final int numberOfChoices) { switch (selectedAudioDevice) { case EARPIECE -> { this.binding.inCallActionRight.setImageResource( @@ -1294,19 +1291,19 @@ public class RtpSessionActivity extends XmppActivity private void switchToEarpiece(View view) { requireRtpConnection() - .getAudioManager() - .setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.EARPIECE); + .getCallIntegration() + .setAudioDevice(CallIntegration.AudioDevice.EARPIECE); acquireProximityWakeLock(); } private void switchToSpeaker(View view) { requireRtpConnection() - .getAudioManager() - .setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE); + .getCallIntegration() + .setAudioDevice(CallIntegration.AudioDevice.SPEAKER_PHONE); releaseProximityWakeLock(); } - private void retry(View view) { + private void retry(final View view) { final Intent intent = getIntent(); final Account account = extractAccount(intent); final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH)); @@ -1315,7 +1312,7 @@ public class RtpSessionActivity extends XmppActivity final Set media = actionToMedia(lastAction == null ? action : lastAction); this.rtpConnectionReference = null; Log.d(Config.LOGTAG, "attempting retry with " + with.toEscapedString()); - proposeJingleRtpSession(account, with, media); + CallIntegrationConnectionService.placeCall(this,account,with,media); } private void exit(final View view) { @@ -1411,8 +1408,8 @@ public class RtpSessionActivity extends XmppActivity @Override public void onAudioDeviceChanged( - final AppRTCAudioManager.AudioDevice selectedAudioDevice, - final Set availableAudioDevices) { + final CallIntegration.AudioDevice selectedAudioDevice, + final Set availableAudioDevices) { Log.d( Config.LOGTAG, "onAudioDeviceChanged in activity: selected:" @@ -1428,11 +1425,11 @@ public class RtpSessionActivity extends XmppActivity "onAudioDeviceChanged() nothing to do because end card has been reached"); } else { if (Media.audioOnly(media) && endUserState == RtpEndUserState.CONNECTED) { - final AppRTCAudioManager audioManager = - requireRtpConnection().getAudioManager(); + final CallIntegration callIntegration = + requireRtpConnection().getCallIntegration(); updateInCallButtonConfigurationSpeaker( - audioManager.getSelectedAudioDevice(), - audioManager.getAudioDevices().size()); + callIntegration.getSelectedAudioDevice(), + callIntegration.getAudioDevices().size()); } Log.d( Config.LOGTAG, diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 23d4f175b951d9a2905d68685536c72a6cb759c6..d4c9189ebe9766e029b397d8e7550b9f80d043b6 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -1,5 +1,7 @@ package eu.siacs.conversations.xmpp.jingle; +import android.os.Bundle; +import android.telecom.TelecomManager; import android.util.Base64; import android.util.Log; @@ -21,6 +23,8 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.RtpSessionStatus; import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.services.AbstractConnectionManager; +import eu.siacs.conversations.services.CallIntegration; +import eu.siacs.conversations.services.CallIntegrationConnectionService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Namespace; @@ -135,6 +139,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { return; } connections.put(id, connection); + + CallIntegrationConnectionService.addNewIncomingCall(getXmppConnectionService(), id); + mXmppConnectionService.updateConversationUi(); connection.deliverPacket(packet); } else { @@ -148,12 +155,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { } public boolean isBusy() { - if (mXmppConnectionService.isPhoneInCall()) { - return true; - } for (AbstractJingleConnection connection : this.connections.values()) { if (connection instanceof JingleRtpConnection) { - if (((JingleRtpConnection) connection).isTerminated()) { + if (connection.isTerminated()) { continue; } return true; @@ -181,17 +185,6 @@ public class JingleConnectionManager extends AbstractConnectionManager { return false; } - public void notifyPhoneCallStarted() { - for (AbstractJingleConnection connection : connections.values()) { - if (connection instanceof JingleRtpConnection rtpConnection) { - if (rtpConnection.isTerminated()) { - continue; - } - rtpConnection.notifyPhoneCall(); - } - } - } - private Optional findMatchingSessionProposal( final Account account, final Jid with, final Set media) { synchronized (this.rtpSessionProposals) { @@ -390,6 +383,8 @@ public class JingleConnectionManager extends AbstractConnectionManager { this.connections.put(id, rtpConnection); rtpConnection.setProposedMedia(ImmutableSet.copyOf(media)); rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp); + + CallIntegrationConnectionService.addNewIncomingCall(getXmppConnectionService(), id); // TODO actually do the automatic accept?! } else { Log.d( @@ -439,6 +434,8 @@ public class JingleConnectionManager extends AbstractConnectionManager { this.connections.put(id, rtpConnection); rtpConnection.setProposedMedia(ImmutableSet.copyOf(media)); rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp); + + CallIntegrationConnectionService.addNewIncomingCall(getXmppConnectionService(), id); } } else { Log.d( @@ -457,7 +454,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { if (proposal != null) { rtpSessionProposals.remove(proposal); final JingleRtpConnection rtpConnection = - new JingleRtpConnection(this, id, account.getJid()); + new JingleRtpConnection(this, id, account.getJid(), proposal.callIntegration); rtpConnection.setProposedMedia(proposal.media); this.connections.put(id, rtpConnection); rtpConnection.transitionOrThrow(AbstractJingleConnection.State.PROPOSED); @@ -490,6 +487,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { getRtpSessionProposal(account, from.asBareJid(), sessionId); synchronized (rtpSessionProposals) { if (proposal != null && rtpSessionProposals.remove(proposal) != null) { + proposal.callIntegration.busy(); writeLogMissedOutgoing( account, proposal.with, proposal.sessionId, serverMsgId, timestamp); toneManager.transition(RtpEndUserState.DECLINED_OR_BUSY, proposal.media); @@ -628,10 +626,6 @@ public class JingleConnectionManager extends AbstractConnectionManager { return Optional.absent(); } - void finishConnection(final AbstractJingleConnection connection) { - this.connections.remove(connection.getId()); - } - void finishConnectionOrThrow(final AbstractJingleConnection connection) { final AbstractJingleConnection.Id id = connection.getId(); if (this.connections.remove(id) == null) { @@ -680,6 +674,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { + ": retracting rtp session proposal with " + rtpSessionProposal.with); this.rtpSessionProposals.remove(rtpSessionProposal); + rtpSessionProposal.callIntegration.retracted(); final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionRetract(rtpSessionProposal); writeLogMissedOutgoing( @@ -691,7 +686,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { mXmppConnectionService.sendMessagePacket(account, messagePacket); } - public String initializeRtpSession( + public JingleRtpConnection initializeRtpSession( final Account account, final Jid with, final Set media) { final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, with); final JingleRtpConnection rtpConnection = @@ -699,15 +694,15 @@ public class JingleConnectionManager extends AbstractConnectionManager { rtpConnection.setProposedMedia(media); this.connections.put(id, rtpConnection); rtpConnection.sendSessionInitiate(); - return id.sessionId; + return rtpConnection; } - public void proposeJingleRtpSession( + public RtpSessionProposal proposeJingleRtpSession( final Account account, final Jid with, final Set media) { synchronized (this.rtpSessionProposals) { - for (Map.Entry entry : + for (final Map.Entry entry : this.rtpSessionProposals.entrySet()) { - RtpSessionProposal proposal = entry.getKey(); + final RtpSessionProposal proposal = entry.getKey(); if (proposal.account == account && with.asBareJid().equals(proposal.with)) { final DeviceDiscoveryState preexistingState = entry.getValue(); if (preexistingState != null @@ -716,7 +711,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { toneManager.transition(endUserState, media); mXmppConnectionService.notifyJingleRtpConnectionUpdate( account, with, proposal.sessionId, endUserState); - return; + return proposal; } } } @@ -725,19 +720,23 @@ public class JingleConnectionManager extends AbstractConnectionManager { Log.d( Config.LOGTAG, "ignoring request to propose jingle session because the other party already created one for us"); - return; + // TODO return something that we can parse the connection of of + return null; } throw new IllegalStateException( "There is already a running RTP session. This should have been caught by the UI"); } + final CallIntegration callIntegration = new CallIntegration(mXmppConnectionService.getApplicationContext()); + callIntegration.setInitialAudioDevice(CallIntegration.initialAudioDevice(media)); final RtpSessionProposal proposal = - RtpSessionProposal.of(account, with.asBareJid(), media); + RtpSessionProposal.of(account, with.asBareJid(), media, callIntegration); this.rtpSessionProposals.put(proposal, DeviceDiscoveryState.SEARCHING); mXmppConnectionService.notifyJingleRtpConnectionUpdate( account, proposal.with, proposal.sessionId, RtpEndUserState.FINDING_DEVICE); final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionProposal(proposal); mXmppConnectionService.sendMessagePacket(account, messagePacket); + return proposal; } } @@ -826,6 +825,21 @@ public class JingleConnectionManager extends AbstractConnectionManager { return null; } + public JingleRtpConnection findJingleRtpConnection(final Account account, final Jid with) { + for (final AbstractJingleConnection connection : this.connections.values()) { + if (connection instanceof JingleRtpConnection rtpConnection) { + if (rtpConnection.isTerminated()) { + continue; + } + final var id = rtpConnection.getId(); + if (id.account == account && account.getJid().equals(with)) { + return rtpConnection; + } + } + } + return null; + } + private void resendSessionProposals(final Account account) { synchronized (this.rtpSessionProposals) { for (final Map.Entry entry : @@ -865,7 +879,10 @@ public class JingleConnectionManager extends AbstractConnectionManager { } this.rtpSessionProposals.put(sessionProposal, target); final RtpEndUserState endUserState = target.toEndUserState(); - toneManager.transition(endUserState, sessionProposal.media); + if (endUserState == RtpEndUserState.RINGING) { + sessionProposal.callIntegration.setDialing(); + } + //toneManager.transition(endUserState, sessionProposal.media); mXmppConnectionService.notifyJingleRtpConnectionUpdate( account, sessionProposal.with, sessionProposal.sessionId, endUserState); Log.d( @@ -994,16 +1011,18 @@ public class JingleConnectionManager extends AbstractConnectionManager { public final String sessionId; public final Set media; private final Account account; + private final CallIntegration callIntegration; - private RtpSessionProposal(Account account, Jid with, String sessionId, Set media) { + private RtpSessionProposal(Account account, Jid with, String sessionId, Set media, final CallIntegration callIntegration) { this.account = account; this.with = with; this.sessionId = sessionId; this.media = media; + this.callIntegration = callIntegration; } - public static RtpSessionProposal of(Account account, Jid with, Set media) { - return new RtpSessionProposal(account, with, nextRandomId(), media); + public static RtpSessionProposal of(Account account, Jid with, Set media, final CallIntegration callIntegration) { + return new RtpSessionProposal(account, with, nextRandomId(), media,callIntegration); } @Override @@ -1035,5 +1054,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { public String getSessionId() { return sessionId; } + + public CallIntegration getCallIntegration() { + return this.callIntegration; + } } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 80d7d2118b803323c8606b2486bb2e89aab02861..8d5aa8dfd00ca772078889690f532b535e137756 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -1,5 +1,7 @@ package eu.siacs.conversations.xmpp.jingle; +import android.telecom.Call; +import android.telecom.TelecomManager; import android.util.Log; import androidx.annotation.NonNull; @@ -12,13 +14,11 @@ import com.google.common.base.Stopwatch; import com.google.common.base.Strings; import com.google.common.base.Throwables; import com.google.common.collect.Collections2; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.google.common.primitives.Ints; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -34,7 +34,7 @@ import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.RtpSessionStatus; import eu.siacs.conversations.services.AppRTCAudioManager; -import eu.siacs.conversations.utils.IP; +import eu.siacs.conversations.services.CallIntegration; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xmpp.Jid; @@ -67,7 +67,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class JingleRtpConnection extends AbstractJingleConnection - implements WebRTCWrapper.EventCallback { + implements WebRTCWrapper.EventCallback, CallIntegration.Callback { public static final List STATES_SHOWING_ONGOING_CALL = Arrays.asList( @@ -78,6 +78,7 @@ public class JingleRtpConnection extends AbstractJingleConnection private final Queue>> pendingIceCandidates = new LinkedList<>(); private final OmemoVerification omemoVerification = new OmemoVerification(); + private final CallIntegration callIntegration; private final Message message; private Set proposedMedia; @@ -90,7 +91,13 @@ public class JingleRtpConnection extends AbstractJingleConnection private final Queue stateHistory = new LinkedList<>(); private ScheduledFuture ringingTimeoutFuture; - JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) { + JingleRtpConnection(final JingleConnectionManager jingleConnectionManager, final Id id, final Jid initiator) { + this(jingleConnectionManager, id, initiator, new CallIntegration(jingleConnectionManager.getXmppConnectionService().getApplicationContext())); + this.callIntegration.setAddress(CallIntegration.address(id.with.asBareJid()), TelecomManager.PRESENTATION_ALLOWED); + this.callIntegration.setInitialized(); + } + + JingleRtpConnection(final JingleConnectionManager jingleConnectionManager, final Id id, final Jid initiator, final CallIntegration callIntegration) { super(jingleConnectionManager, id, initiator); final Conversation conversation = jingleConnectionManager @@ -102,6 +109,8 @@ public class JingleRtpConnection extends AbstractJingleConnection isInitiator() ? Message.STATUS_SEND : Message.STATUS_RECEIVED, Message.TYPE_RTP_SESSION, id.sessionId); + this.callIntegration = callIntegration; + this.callIntegration.setCallback(this); } @Override @@ -1158,6 +1167,7 @@ public class JingleRtpConnection extends AbstractJingleConnection target = State.SESSION_INITIALIZED_PRE_APPROVED; } else { target = State.SESSION_INITIALIZED; + setProposedMedia(contentMap.getMedia()); } if (transition(target, () -> this.initiatorRtpContentMap = contentMap)) { respondOk(jinglePacket); @@ -1628,7 +1638,7 @@ public class JingleRtpConnection extends AbstractJingleConnection + from + " for " + media); - this.proposedMedia = Sets.newHashSet(media); + this.setProposedMedia(Sets.newHashSet(media)); })) { if (serverMsgId != null) { this.message.setServerMsgId(serverMsgId); @@ -1648,6 +1658,7 @@ public class JingleRtpConnection extends AbstractJingleConnection } private void startRinging() { + this.callIntegration.setRinging(); Log.d( Config.LOGTAG, id.account.getJid().asBareJid() @@ -1657,6 +1668,9 @@ public class JingleRtpConnection extends AbstractJingleConnection ringingTimeoutFuture = jingleConnectionManager.schedule( this::ringingTimeout, BUSY_TIME_OUT, TimeUnit.SECONDS); + if (CallIntegration.selfManaged()) { + return; + } xmppConnectionService.getNotificationService().startRinging(id, getMedia()); } @@ -2054,6 +2068,56 @@ public class JingleRtpConnection extends AbstractJingleConnection }; } + private boolean isPeerConnectionConnected() { + try { + return webRTCWrapper.getState() == PeerConnection.PeerConnectionState.CONNECTED; + } catch (final WebRTCWrapper.PeerConnectionNotInitialized e) { + return false; + } + } + + private void updateCallIntegrationState() { + switch (this.state) { + case NULL, PROPOSED, SESSION_INITIALIZED -> { + if (isInitiator()) { + this.callIntegration.setDialing(); + } else { + this.callIntegration.setRinging(); + } + } + case PROCEED, SESSION_INITIALIZED_PRE_APPROVED -> { + if (isInitiator()) { + this.callIntegration.setDialing(); + } else { + this.callIntegration.setInitialized(); + } + } + case SESSION_ACCEPTED -> { + if (isPeerConnectionConnected()) { + this.callIntegration.setActive(); + } else { + this.callIntegration.setInitialized(); + } + } + case REJECTED, REJECTED_RACED, TERMINATED_DECLINED_OR_BUSY -> { + if (isInitiator()) { + this.callIntegration.busy(); + } else { + this.callIntegration.rejected(); + } + } + case TERMINATED_SUCCESS -> this.callIntegration.success(); + case ACCEPTED -> this.callIntegration.accepted(); + case RETRACTED, RETRACTED_RACED, TERMINATED_CANCEL_OR_TIMEOUT -> this.callIntegration + .retracted(); + case TERMINATED_CONNECTIVITY_ERROR, + TERMINATED_APPLICATION_FAILURE, + TERMINATED_SECURITY_ERROR -> this.callIntegration.error(); + default -> throw new IllegalStateException( + String.format("%s is not handled", this.state)); + } + } + public ContentAddition getPendingContentAddition() { final RtpContentMap in = this.incomingContentAdd; final RtpContentMap out = this.outgoingContentAdd; @@ -2135,15 +2199,6 @@ public class JingleRtpConnection extends AbstractJingleConnection } } - public void notifyPhoneCall() { - Log.d(Config.LOGTAG, "a phone call has just been started. killing jingle rtp connections"); - if (Arrays.asList(State.PROPOSED, State.SESSION_INITIALIZED).contains(this.state)) { - rejectCall(); - } else { - endCall(); - } - } - public synchronized void rejectCall() { if (isTerminated()) { Log.w( @@ -2537,8 +2592,7 @@ public class JingleRtpConnection extends AbstractJingleConnection private void modifyLocalContentMap(final RtpContentMap rtpContentMap) { final RtpContentMap activeContents = rtpContentMap.activeContents(); setLocalContentMap(activeContents); - this.webRTCWrapper.switchSpeakerPhonePreference( - AppRTCAudioManager.SpeakerPhonePreference.of(activeContents.getMedia())); + // TODO change audio device on callIntegration was (`switchSpeakerPhonePreference(AppRTCAudioManager.SpeakerPhonePreference.of(activeContents.getMedia())`) updateEndUserState(); } @@ -2571,8 +2625,9 @@ public class JingleRtpConnection extends AbstractJingleConnection return this.sessionDuration.elapsed(TimeUnit.MILLISECONDS); } - public AppRTCAudioManager getAudioManager() { - return webRTCWrapper.getAudioManager(); + + public CallIntegration getCallIntegration() { + return this.callIntegration; } public boolean isMicrophoneEnabled() { @@ -2603,10 +2658,26 @@ public class JingleRtpConnection extends AbstractJingleConnection return webRTCWrapper.switchCamera(); } + @Override + public void onCallIntegrationShowIncomingCallUi() { + xmppConnectionService.getNotificationService().startRinging(id, getMedia()); + } + + @Override + public void onCallIntegrationDisconnect() { + Log.d(Config.LOGTAG, "a phone call has just been started. killing jingle rtp connections"); + if (Arrays.asList(State.PROPOSED, State.SESSION_INITIALIZED).contains(this.state)) { + rejectCall(); + } else { + endCall(); + } + } + @Override public void onAudioDeviceChanged( - AppRTCAudioManager.AudioDevice selectedAudioDevice, - Set availableAudioDevices) { + final CallIntegration.AudioDevice selectedAudioDevice, + final Set availableAudioDevices) { + Log.d(Config.LOGTAG,"onAudioDeviceChanged("+selectedAudioDevice+","+availableAudioDevices+")"); xmppConnectionService.notifyJingleRtpConnectionUpdate( selectedAudioDevice, availableAudioDevices); } @@ -2614,6 +2685,7 @@ public class JingleRtpConnection extends AbstractJingleConnection private void updateEndUserState() { final RtpEndUserState endUserState = getEndUserState(); jingleConnectionManager.toneManager.transition(isInitiator(), endUserState, getMedia()); + this.updateCallIntegrationState(); xmppConnectionService.notifyJingleRtpConnectionUpdate( id.account, id.with, id.sessionId, endUserState); } @@ -2670,6 +2742,7 @@ public class JingleRtpConnection extends AbstractJingleConnection protected void finish() { if (isTerminated()) { this.cancelRingingTimeout(); + this.callIntegration.verifyDisconnected(); this.webRTCWrapper.verifyClosed(); this.jingleConnectionManager.setTerminalSessionState(id, getEndUserState(), getMedia()); super.finish(); @@ -2724,6 +2797,7 @@ public class JingleRtpConnection extends AbstractJingleConnection void setProposedMedia(final Set media) { this.proposedMedia = media; + this.callIntegration.setInitialAudioDevice(CallIntegration.initialAudioDevice(media)); } public void fireStateUpdate() { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java index 24ed790ddadc1b72ee560555f2fe7abea2023b43..885820460a60897eb0853549d8631852cf07ab64 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java @@ -9,7 +9,7 @@ public enum RtpEndUserState { FINDING_DEVICE, //'propose' has been sent out; no 184 ack yet RINGING, //'propose' has been sent out and it has been 184 acked ACCEPTING_CALL, //'proceed' message has been sent; but no session-initiate has been received - ENDING_CALL, //libwebrt says 'closed' but session-terminate hasnt gone through + ENDING_CALL, //libwebrt says 'closed' but session-terminate has not gone through ENDED, //close UI DECLINED_OR_BUSY, //other party declined; no retry button CONNECTIVITY_ERROR, //network error; retry button diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java index da5b9ab2bc0e9899e0b19a9e8a837c2265c0e9d2..fb82b7219254589c30a5c3c4434479e83032939b 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java @@ -89,7 +89,8 @@ class ToneManager { } switch (state) { case RINGING: - scheduleWaitingTone(); + // ringing can be removed as this is now handled by 'CallIntegration' + //scheduleWaitingTone(); break; case CONNECTED: scheduleConnected(); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java index cb0c8579d046f4ec9bfaf530a752a7da89ec1137..fa504ed191e5b149c040cf569cec20feba487106 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java @@ -16,6 +16,7 @@ import com.google.common.util.concurrent.SettableFuture; import eu.siacs.conversations.Config; import eu.siacs.conversations.services.AppRTCAudioManager; +import eu.siacs.conversations.services.CallIntegration; import eu.siacs.conversations.services.XmppConnectionService; import org.webrtc.AudioSource; @@ -83,16 +84,6 @@ public class WebRTCWrapper { private final EventCallback eventCallback; private final AtomicBoolean readyToReceivedIceCandidates = new AtomicBoolean(false); private final Queue iceCandidates = new LinkedList<>(); - private final AppRTCAudioManager.AudioManagerEvents audioManagerEvents = - new AppRTCAudioManager.AudioManagerEvents() { - @Override - public void onAudioDeviceChanged( - AppRTCAudioManager.AudioDevice selectedAudioDevice, - Set availableAudioDevices) { - eventCallback.onAudioDeviceChanged(selectedAudioDevice, availableAudioDevices); - } - }; - private final Handler mainHandler = new Handler(Looper.getMainLooper()); private TrackWrapper localAudioTrack = null; private TrackWrapper localVideoTrack = null; private VideoTrack remoteVideoTrack = null; @@ -214,7 +205,6 @@ public class WebRTCWrapper { }; @Nullable private PeerConnectionFactory peerConnectionFactory = null; @Nullable private PeerConnection peerConnection = null; - private AppRTCAudioManager appRTCAudioManager = null; private ToneManager toneManager = null; private Context context = null; private EglBase eglBase = null; @@ -251,15 +241,6 @@ public class WebRTCWrapper { } this.context = service; this.toneManager = service.getJingleConnectionManager().toneManager; - mainHandler.post( - () -> { - appRTCAudioManager = AppRTCAudioManager.create(service, speakerPhonePreference); - toneManager.setAppRtcAudioManagerHasControl(true); - appRTCAudioManager.start(audioManagerEvents); - eventCallback.onAudioDeviceChanged( - appRTCAudioManager.getSelectedAudioDevice(), - appRTCAudioManager.getAudioDevices()); - }); } synchronized void initializePeerConnection( @@ -462,16 +443,11 @@ public class WebRTCWrapper { final PeerConnection peerConnection = this.peerConnection; final PeerConnectionFactory peerConnectionFactory = this.peerConnectionFactory; final VideoSourceWrapper videoSourceWrapper = this.videoSourceWrapper; - final AppRTCAudioManager audioManager = this.appRTCAudioManager; final EglBase eglBase = this.eglBase; if (peerConnection != null) { this.peerConnection = null; dispose(peerConnection); } - if (audioManager != null) { - toneManager.setAppRtcAudioManagerHasControl(false); - mainHandler.post(audioManager::stop); - } this.localVideoTrack = null; this.remoteVideoTrack = null; if (videoSourceWrapper != null) { @@ -498,8 +474,8 @@ public class WebRTCWrapper { || this.eglBase != null || this.localVideoTrack != null || this.remoteVideoTrack != null) { - final IllegalStateException e = - new IllegalStateException("WebRTCWrapper hasn't been closed properly"); + final AssertionError e = + new AssertionError("WebRTCWrapper hasn't been closed properly"); Log.e(Config.LOGTAG, "verifyClosed() failed. Going to throw", e); throw e; } @@ -750,27 +726,15 @@ public class WebRTCWrapper { return context; } - AppRTCAudioManager getAudioManager() { - return appRTCAudioManager; - } - void execute(final Runnable command) { this.executorService.execute(command); } - public void switchSpeakerPhonePreference(AppRTCAudioManager.SpeakerPhonePreference preference) { - mainHandler.post(() -> appRTCAudioManager.switchSpeakerPhonePreference(preference)); - } - public interface EventCallback { void onIceCandidate(IceCandidate iceCandidate); void onConnectionChange(PeerConnection.PeerConnectionState newState); - void onAudioDeviceChanged( - AppRTCAudioManager.AudioDevice selectedAudioDevice, - Set availableAudioDevices); - void onRenegotiationNeeded(); } From 4378f8931bbe13656ee7be28a702b86bf7813075 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 14 Jan 2024 11:55:11 +0100 Subject: [PATCH 002/363] add Config flag to debug direct call init --- .../java/eu/siacs/conversations/Config.java | 139 +++++++++--------- .../ui/ConversationFragment.java | 9 +- 2 files changed, 70 insertions(+), 78 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 982bd478118cfadbc5177830797cf866ab330fc4..6c9e5dd68891651cfc4a4e6dd85038198ee61904 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -3,15 +3,15 @@ package eu.siacs.conversations; import android.graphics.Bitmap; import android.net.Uri; +import eu.siacs.conversations.crypto.XmppDomainVerifier; +import eu.siacs.conversations.xmpp.Jid; +import eu.siacs.conversations.xmpp.chatstate.ChatState; + import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; -import eu.siacs.conversations.crypto.XmppDomainVerifier; -import eu.siacs.conversations.xmpp.Jid; -import eu.siacs.conversations.xmpp.chatstate.ChatState; - public final class Config { private static final int UNENCRYPTED = 1; private static final int OPENPGP = 2; @@ -46,34 +46,32 @@ public final class Config { public static final Jid BUG_REPORTS = Jid.of("bugs@conversations.im"); public static final Uri HELP = Uri.parse("https://help.conversations.im"); - - public static final String DOMAIN_LOCK = null; //only allow account creation for this domain + public static final String DOMAIN_LOCK = null; // only allow account creation for this domain public static final String MAGIC_CREATE_DOMAIN = "conversations.im"; public static final Jid QUICKSY_DOMAIN = Jid.of("quicksy.im"); public static final String CHANNEL_DISCOVERY = "https://search.jabber.network"; - public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox + public static final boolean DISALLOW_REGISTRATION_IN_UI = false; // hide the register checkbox public static final boolean USE_RANDOM_RESOURCE_ON_EVERY_BIND = false; - public static final boolean ALLOW_NON_TLS_CONNECTIONS = false; //very dangerous. you should have a good reason to set this to true + public static final boolean ALLOW_NON_TLS_CONNECTIONS = + false; // very dangerous. you should have a good reason to set this to true public static final long CONTACT_SYNC_RETRY_INTERVAL = 1000L * 60 * 5; - public static final boolean QUICKSTART_ENABLED = true; - //Notification settings + // Notification settings public static final boolean HIDE_MESSAGE_TEXT_IN_NOTIFICATION = false; public static final boolean ALWAYS_NOTIFY_BY_DEFAULT = false; public static final boolean SUPPRESS_ERROR_NOTIFICATION = false; - public static final boolean DISABLE_BAN = false; // disables the ability to ban users from rooms public static final int PING_MAX_INTERVAL = 300; - public static final int IDLE_PING_INTERVAL = 600; //540 is minimum according to docs; + public static final int IDLE_PING_INTERVAL = 600; // 540 is minimum according to docs; public static final int PING_MIN_INTERVAL = 30; public static final int LOW_PING_TIMEOUT = 1; // used after push received public static final int PING_TIMEOUT = 15; @@ -83,10 +81,10 @@ public final class Config { public static final int CONNECT_DISCO_TIMEOUT = 20; public static final int MINI_GRACE_PERIOD = 750; - public static final boolean XEP_0392 = true; //enables XEP-0392 v0.6.0 - + public static final boolean XEP_0392 = true; // enables XEP-0392 v0.6.0 - // media file formats. Homogenous Android or Conversations only deployments can switch to opus and webp + // media file formats. Homogenous Android or Conversations only deployments can switch to opus + // and webp public static final int AVATAR_SIZE = 192; public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.JPEG; public static final int AVATAR_CHAR_LIMIT = 9400; @@ -106,30 +104,39 @@ public final class Config { public static final int REFRESH_UI_INTERVAL = 500; public static final int MAX_DISPLAY_MESSAGE_CHARS = 4096; - public static final int MAX_STORAGE_MESSAGE_CHARS = 2 * 1024 * 1024; //2MB + public static final int MAX_STORAGE_MESSAGE_CHARS = 2 * 1024 * 1024; // 2MB public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000; - //remove *other* omemo devices from *your* device list announcement after not seeing any activity from them for 42 days. They will automatically add themselves after coming back online. + // remove *other* omemo devices from *your* device list announcement after not seeing any + // activity from them for 42 days. They will automatically add themselves after coming back + // online. public static final long OMEMO_AUTO_EXPIRY = 42 * MILLISECONDS_IN_DAY; public static final boolean REMOVE_BROKEN_DEVICES = false; public static final boolean OMEMO_PADDING = false; public static final boolean PUT_AUTH_TAG_INTO_KEY = true; public static final boolean AUTOMATICALLY_COMPLETE_SESSIONS = true; - public static final boolean DISABLE_PROXY_LOOKUP = false; //disables STUN/TURN and Proxy65 look up (useful to debug IBB fallback) + public static final boolean DISABLE_PROXY_LOOKUP = + false; // disables STUN/TURN and Proxy65 look up (useful to debug IBB fallback) public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true; + public static final boolean USE_JINGLE_DIRECT_INIT = true; public static final boolean DISABLE_HTTP_UPLOAD = false; public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts - public static final boolean BACKGROUND_STANZA_LOGGING = false; //log all stanzas that were received while the app is in background - public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption + public static final boolean BACKGROUND_STANZA_LOGGING = + false; // log all stanzas that were received while the app is in background + public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = + true; // setting to true might increase power consumption public static final boolean ENCRYPT_ON_HTTP_UPLOADED = false; - public static final boolean X509_VERIFICATION = false; //use x509 certificates to verify OMEMO keys - public static final boolean REQUIRE_RTP_VERIFICATION = false; //require a/v calls to be verified with OMEMO + public static final boolean X509_VERIFICATION = + false; // use x509 certificates to verify OMEMO keys + public static final boolean REQUIRE_RTP_VERIFICATION = + false; // require a/v calls to be verified with OMEMO - public static final boolean ONLY_INTERNAL_STORAGE = false; //use internal storage instead of sdcard to save attachments + public static final boolean ONLY_INTERNAL_STORAGE = + false; // use internal storage instead of sdcard to save attachments public static final boolean IGNORE_ID_REWRITE_IN_MUC = true; public static final boolean MUC_LEAVE_BEFORE_JOIN = false; @@ -145,69 +152,61 @@ public final class Config { public static final int EXPIRY_INTERVAL = 30 * 60 * 1000; // 30 minutes public static final String[] ENABLED_CIPHERS = { - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - - "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_DHE_RSA_WITH_AES_128_GCM_SHA384", - "TLS_DHE_RSA_WITH_AES_256_GCM_SHA256", - "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", - - "TLS_DHE_RSA_WITH_CAMELLIA_256_SHA", - - // Fallback. - "TLS_RSA_WITH_AES_128_GCM_SHA256", - "TLS_RSA_WITH_AES_128_GCM_SHA384", - "TLS_RSA_WITH_AES_256_GCM_SHA256", - "TLS_RSA_WITH_AES_256_GCM_SHA384", - "TLS_RSA_WITH_AES_128_CBC_SHA256", - "TLS_RSA_WITH_AES_128_CBC_SHA384", - "TLS_RSA_WITH_AES_256_CBC_SHA256", - "TLS_RSA_WITH_AES_256_CBC_SHA384", - "TLS_RSA_WITH_AES_128_CBC_SHA", - "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA384", + "TLS_DHE_RSA_WITH_AES_256_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_DHE_RSA_WITH_CAMELLIA_256_SHA", + + // Fallback. + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_128_GCM_SHA384", + "TLS_RSA_WITH_AES_256_GCM_SHA256", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_AES_128_CBC_SHA384", + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA384", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA", }; public static final String[] WEAK_CIPHER_PATTERNS = { - "_NULL_", - "_EXPORT_", - "_anon_", - "_RC4_", - "_DES_", - "_MD5", + "_NULL_", "_EXPORT_", "_anon_", "_RC4_", "_DES_", "_MD5", }; public static class OMEMO_EXCEPTIONS { - //if the own account matches one of the following domains OMEMO won’t be turned on automatically + // if the own account matches one of the following domains OMEMO won’t be turned on + // automatically public static final List ACCOUNT_DOMAINS = Collections.singletonList("s.ms"); - //if the contacts domain matches one of the following domains OMEMO won’t be turned on automatically - //can be used for well known, widely used gateways - private static final List CONTACT_DOMAINS = Arrays.asList( - "cheogram.com", - "*.covid.monal.im" - ); + // if the contacts domain matches one of the following domains OMEMO won’t be turned on + // automatically + // can be used for well known, widely used gateways + private static final List CONTACT_DOMAINS = + Arrays.asList("cheogram.com", "*.covid.monal.im"); public static boolean matchesContactDomain(final String domain) { return XmppDomainVerifier.matchDomain(domain, CONTACT_DOMAINS); } } - private Config() { - } + private Config() {} public static final class Map { - public final static double INITIAL_ZOOM_LEVEL = 4; - public final static double FINAL_ZOOM_LEVEL = 15; - public final static int MY_LOCATION_INDICATOR_SIZE = 10; - public final static int MY_LOCATION_INDICATOR_OUTLINE_SIZE = 3; - public final static long LOCATION_FIX_TIME_DELTA = 1000 * 10; // ms - public final static float LOCATION_FIX_SPACE_DELTA = 10; // m - public final static int LOCATION_FIX_SIGNIFICANT_TIME_DELTA = 1000 * 60 * 2; // ms + public static final double INITIAL_ZOOM_LEVEL = 4; + public static final double FINAL_ZOOM_LEVEL = 15; + public static final int MY_LOCATION_INDICATOR_SIZE = 10; + public static final int MY_LOCATION_INDICATOR_OUTLINE_SIZE = 3; + public static final long LOCATION_FIX_TIME_DELTA = 1000 * 10; // ms + public static final float LOCATION_FIX_SPACE_DELTA = 10; // m + public static final int LOCATION_FIX_SIGNIFICANT_TIME_DELTA = 1000 * 60 * 2; // ms } // How deep nested quotes should be displayed. '2' means one quote nested in another. diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index ccb08f95f006ab53d2db1b251883b2b0e313994b..01932a04e94cff5baa81708a5b219158b798d9e2 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1633,7 +1633,7 @@ public class ConversationFragment extends XmppFragment activity.xmppConnectionService.updateAccount(account); } final Contact contact = conversation.getContact(); - if (RtpCapability.jmiSupport(contact)) { + if (Config.USE_JINGLE_DIRECT_INIT && RtpCapability.jmiSupport(contact)) { triggerRtpSession(contact.getAccount(), contact.getJid().asBareJid(), action); } else { final RtpCapability.Capability capability; @@ -1654,13 +1654,6 @@ public class ConversationFragment extends XmppFragment private void triggerRtpSession(final Account account, final Jid with, final String action) { CallIntegrationConnectionService.placeCall(requireActivity(),account,with,RtpSessionActivity.actionToMedia(action)); - /*final Intent intent = new Intent(activity, RtpSessionActivity.class); - intent.setAction(action); - intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.getJid().toEscapedString()); - intent.putExtra(RtpSessionActivity.EXTRA_WITH, with.toEscapedString()); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(intent);*/ } private void handleAttachmentSelection(MenuItem item) { From d20cc87bda0b82ea8bf27c76dfdfbf4a93f31785 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 14 Jan 2024 12:09:56 +0100 Subject: [PATCH 003/363] retract proposal when accepting other call --- .../xmpp/jingle/JingleConnectionManager.java | 65 +++++++++++++++---- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index d4c9189ebe9766e029b397d8e7550b9f80d043b6..5edd461fba841222dc712f158972849b7caf56b2 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -1,7 +1,5 @@ package eu.siacs.conversations.xmpp.jingle; -import android.os.Bundle; -import android.telecom.TelecomManager; import android.util.Base64; import android.util.Log; @@ -31,9 +29,7 @@ import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.jingle.stanzas.Content; -import eu.siacs.conversations.xmpp.jingle.stanzas.FileTransferDescription; import eu.siacs.conversations.xmpp.jingle.stanzas.GenericDescription; -import eu.siacs.conversations.xmpp.jingle.stanzas.IbbTransportInfo; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.Propose; import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; @@ -384,7 +380,8 @@ public class JingleConnectionManager extends AbstractConnectionManager { rtpConnection.setProposedMedia(ImmutableSet.copyOf(media)); rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp); - CallIntegrationConnectionService.addNewIncomingCall(getXmppConnectionService(), id); + CallIntegrationConnectionService.addNewIncomingCall( + getXmppConnectionService(), id); // TODO actually do the automatic accept?! } else { Log.d( @@ -435,7 +432,8 @@ public class JingleConnectionManager extends AbstractConnectionManager { rtpConnection.setProposedMedia(ImmutableSet.copyOf(media)); rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp); - CallIntegrationConnectionService.addNewIncomingCall(getXmppConnectionService(), id); + CallIntegrationConnectionService.addNewIncomingCall( + getXmppConnectionService(), id); } } else { Log.d( @@ -454,7 +452,8 @@ public class JingleConnectionManager extends AbstractConnectionManager { if (proposal != null) { rtpSessionProposals.remove(proposal); final JingleRtpConnection rtpConnection = - new JingleRtpConnection(this, id, account.getJid(), proposal.callIntegration); + new JingleRtpConnection( + this, id, account.getJid(), proposal.callIntegration); rtpConnection.setProposedMedia(proposal.media); this.connections.put(id, rtpConnection); rtpConnection.transitionOrThrow(AbstractJingleConnection.State.PROPOSED); @@ -726,10 +725,12 @@ public class JingleConnectionManager extends AbstractConnectionManager { throw new IllegalStateException( "There is already a running RTP session. This should have been caught by the UI"); } - final CallIntegration callIntegration = new CallIntegration(mXmppConnectionService.getApplicationContext()); + final CallIntegration callIntegration = + new CallIntegration(mXmppConnectionService.getApplicationContext()); callIntegration.setInitialAudioDevice(CallIntegration.initialAudioDevice(media)); final RtpSessionProposal proposal = RtpSessionProposal.of(account, with.asBareJid(), media, callIntegration); + callIntegration.setCallback(new ProposalStateCallback(proposal)); this.rtpSessionProposals.put(proposal, DeviceDiscoveryState.SEARCHING); mXmppConnectionService.notifyJingleRtpConnectionUpdate( account, proposal.with, proposal.sessionId, RtpEndUserState.FINDING_DEVICE); @@ -775,7 +776,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { sid = null; } if (sid == null) { - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": unable to deliver ibb packet. missing sid"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": unable to deliver ibb packet. missing sid"); account.getXmppConnection() .sendIqPacket(packet.generateResponse(IqPacket.TYPE.ERROR), null); return; @@ -799,7 +802,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } } - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": unable to deliver ibb packet with sid="+sid); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": unable to deliver ibb packet with sid=" + sid); account.getXmppConnection() .sendIqPacket(packet.generateResponse(IqPacket.TYPE.ERROR), null); } @@ -882,7 +887,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { if (endUserState == RtpEndUserState.RINGING) { sessionProposal.callIntegration.setDialing(); } - //toneManager.transition(endUserState, sessionProposal.media); + // toneManager.transition(endUserState, sessionProposal.media); mXmppConnectionService.notifyJingleRtpConnectionUpdate( account, sessionProposal.with, sessionProposal.sessionId, endUserState); Log.d( @@ -1013,7 +1018,12 @@ public class JingleConnectionManager extends AbstractConnectionManager { private final Account account; private final CallIntegration callIntegration; - private RtpSessionProposal(Account account, Jid with, String sessionId, Set media, final CallIntegration callIntegration) { + private RtpSessionProposal( + Account account, + Jid with, + String sessionId, + Set media, + final CallIntegration callIntegration) { this.account = account; this.with = with; this.sessionId = sessionId; @@ -1021,8 +1031,12 @@ public class JingleConnectionManager extends AbstractConnectionManager { this.callIntegration = callIntegration; } - public static RtpSessionProposal of(Account account, Jid with, Set media, final CallIntegration callIntegration) { - return new RtpSessionProposal(account, with, nextRandomId(), media,callIntegration); + public static RtpSessionProposal of( + Account account, + Jid with, + Set media, + final CallIntegration callIntegration) { + return new RtpSessionProposal(account, with, nextRandomId(), media, callIntegration); } @Override @@ -1059,4 +1073,27 @@ public class JingleConnectionManager extends AbstractConnectionManager { return this.callIntegration; } } + + public class ProposalStateCallback implements CallIntegration.Callback { + + private final RtpSessionProposal proposal; + + public ProposalStateCallback(final RtpSessionProposal proposal) { + this.proposal = proposal; + } + + @Override + public void onCallIntegrationShowIncomingCallUi() {} + + @Override + public void onCallIntegrationDisconnect() { + Log.d(Config.LOGTAG, "a phone call has just been started. retracting proposal"); + retractSessionProposal(this.proposal); + } + + @Override + public void onAudioDeviceChanged( + CallIntegration.AudioDevice selectedAudioDevice, + Set availableAudioDevices) {} + } } From a44ad6015dd60d255cd7a5a1000a7483d11769ea Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 14 Jan 2024 18:05:43 +0100 Subject: [PATCH 004/363] update UI with correct state after UI gets invoked with ACTION_VIEW --- .../services/CallIntegrationConnectionService.java | 13 +++++++++++-- .../siacs/conversations/ui/RtpSessionActivity.java | 8 ++++---- .../xmpp/jingle/JingleConnectionManager.java | 13 ++++++++++--- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index cfd6ae603c91bc5e1bcdc8e724972b3571faa201..28f59a06008ed913964ff2a997054f8bc3311679 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -29,6 +29,7 @@ import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection; import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.jingle.Media; +import eu.siacs.conversations.xmpp.jingle.RtpEndUserState; import java.util.Collection; import java.util.Collections; @@ -83,6 +84,7 @@ public class CallIntegrationConnectionService extends ConnectionService { } final Account account = service.findAccountByUuid(phoneAccountHandle.getId()); final Intent intent = new Intent(this, RtpSessionActivity.class); + intent.setAction(Intent.ACTION_VIEW); intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.getJid().toEscapedString()); intent.putExtra(RtpSessionActivity.EXTRA_WITH, jid.toEscapedString()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -93,10 +95,17 @@ public class CallIntegrationConnectionService extends ConnectionService { service.getJingleConnectionManager() .proposeJingleRtpSession(account, jid, media); + intent.putExtra( + RtpSessionActivity.EXTRA_LAST_REPORTED_STATE, + RtpEndUserState.FINDING_DEVICE.toString()); if (Media.audioOnly(media)) { - intent.setAction(RtpSessionActivity.ACTION_MAKE_VOICE_CALL); + intent.putExtra( + RtpSessionActivity.EXTRA_LAST_ACTION, + RtpSessionActivity.ACTION_MAKE_VOICE_CALL); } else { - intent.setAction(RtpSessionActivity.ACTION_MAKE_VIDEO_CALL); + intent.putExtra( + RtpSessionActivity.EXTRA_LAST_ACTION, + RtpSessionActivity.ACTION_MAKE_VIDEO_CALL); } callIntegration = proposal.getCallIntegration(); } else { diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 8f546918ab0a3fdbf58eb13e7dea93493e978284..5982324677dfb15086934f699aa84144b7d9f45e 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -467,8 +467,7 @@ public class RtpSessionActivity extends XmppActivity } } - private void putProximityWakeLockInProperState( - final CallIntegration.AudioDevice audioDevice) { + private void putProximityWakeLockInProperState(final CallIntegration.AudioDevice audioDevice) { if (audioDevice == CallIntegration.AudioDevice.EARPIECE) { acquireProximityWakeLock(); } else { @@ -583,7 +582,8 @@ public class RtpSessionActivity extends XmppActivity .getJingleConnectionManager() .proposeJingleRtpSession(account, with, media); } else { - throw new IllegalStateException("We should not be initializing direct calls from the RtpSessionActivity. Go through CallIntegrationConnectionService.placeCall instead!"); + throw new IllegalStateException( + "We should not be initializing direct calls from the RtpSessionActivity. Go through CallIntegrationConnectionService.placeCall instead!"); } putScreenInCallMode(media); } @@ -1312,7 +1312,7 @@ public class RtpSessionActivity extends XmppActivity final Set media = actionToMedia(lastAction == null ? action : lastAction); this.rtpConnectionReference = null; Log.d(Config.LOGTAG, "attempting retry with " + with.toEscapedString()); - CallIntegrationConnectionService.placeCall(this,account,with,media); + CallIntegrationConnectionService.placeCall(this, account, with, media); } private void exit(final View view) { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 5edd461fba841222dc712f158972849b7caf56b2..5a5117a4c5ecfdd4788d84644b498be0ebe43cbd 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -636,17 +636,16 @@ public class JingleConnectionManager extends AbstractConnectionManager { } public boolean fireJingleRtpConnectionStateUpdates() { - boolean firedUpdates = false; for (final AbstractJingleConnection connection : this.connections.values()) { if (connection instanceof JingleRtpConnection jingleRtpConnection) { if (jingleRtpConnection.isTerminated()) { continue; } jingleRtpConnection.fireStateUpdate(); - firedUpdates = true; + return true; } } - return firedUpdates; + return false; } public void retractSessionProposal(final Account account, final Jid with) { @@ -745,8 +744,16 @@ public class JingleConnectionManager extends AbstractConnectionManager { synchronized (this.rtpSessionProposals) { for (Map.Entry entry : this.rtpSessionProposals.entrySet()) { + final var state = entry.getValue(); final RtpSessionProposal proposal = entry.getKey(); if (proposal.account == account && with.asBareJid().equals(proposal.with)) { + // CallIntegrationConnectionService starts RtpSessionActivity with ACTION_VIEW + // and an EXTRA_LAST_REPORTED_STATE of DISCOVERING devices. however due to + // possible race conditions the state might have already moved on so we are going + // to update the UI + final RtpEndUserState endUserState = state.toEndUserState(); + mXmppConnectionService.notifyJingleRtpConnectionUpdate( + account, proposal.with, proposal.sessionId, endUserState); return true; } } From dfa389f61f3e6c04ad67098838ff9f8c0d6320e2 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 14 Jan 2024 18:17:55 +0100 Subject: [PATCH 005/363] update UI in case proposal gets retracted by system --- .../xmpp/jingle/JingleConnectionManager.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 5a5117a4c5ecfdd4788d84644b498be0ebe43cbd..a4781870c020c947a1c0c29f11accd04c0b86665 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -663,7 +663,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } - private void retractSessionProposal(RtpSessionProposal rtpSessionProposal) { + private void retractSessionProposal(final RtpSessionProposal rtpSessionProposal) { final Account account = rtpSessionProposal.account; toneManager.transition(RtpEndUserState.ENDED, rtpSessionProposal.media); Log.d( @@ -673,6 +673,11 @@ public class JingleConnectionManager extends AbstractConnectionManager { + rtpSessionProposal.with); this.rtpSessionProposals.remove(rtpSessionProposal); rtpSessionProposal.callIntegration.retracted(); + mXmppConnectionService.notifyJingleRtpConnectionUpdate( + account, + rtpSessionProposal.with, + rtpSessionProposal.sessionId, + RtpEndUserState.RETRACTED); final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionRetract(rtpSessionProposal); writeLogMissedOutgoing( @@ -749,7 +754,8 @@ public class JingleConnectionManager extends AbstractConnectionManager { if (proposal.account == account && with.asBareJid().equals(proposal.with)) { // CallIntegrationConnectionService starts RtpSessionActivity with ACTION_VIEW // and an EXTRA_LAST_REPORTED_STATE of DISCOVERING devices. however due to - // possible race conditions the state might have already moved on so we are going + // possible race conditions the state might have already moved on so we are + // going // to update the UI final RtpEndUserState endUserState = state.toEndUserState(); mXmppConnectionService.notifyJingleRtpConnectionUpdate( From b9e4296321581f396af52e29e2eb3b340c88e73a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 15 Jan 2024 11:48:56 +0100 Subject: [PATCH 006/363] reformat debug info --- .../conversations/xmpp/jingle/JingleConnectionManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index a4781870c020c947a1c0c29f11accd04c0b86665..8153dc254f028d68c4f9f85099fb5b0bd8d2a291 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -512,8 +512,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { Log.d( Config.LOGTAG, account.getJid() - + ": retrieved out of order jingle message from " + + ": received out of order jingle message from=" + from + + ", message=" + message + ", addressedDirectly=" + addressedDirectly); From f119c36bffa318ca16a78728120fbaf222aeef92 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 15 Jan 2024 12:50:50 +0100 Subject: [PATCH 007/363] (un)register phone account on xmpp account creation/deletion --- .../services/CallIntegrationConnectionService.java | 4 ++++ .../siacs/conversations/services/XmppConnectionService.java | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index 28f59a06008ed913964ff2a997054f8bc3311679..01f24684436203d6350ce565207974e38518409e 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -181,6 +181,10 @@ public class CallIntegrationConnectionService extends ConnectionService { } } + public static void unregisterPhoneAccount(final Context context, final Account account) { + context.getSystemService(TelecomManager.class).unregisterPhoneAccount(getHandle(context, account)); + } + public static PhoneAccountHandle getHandle(final Context context, final Account account) { final var competentName = new ComponentName(context, CallIntegrationConnectionService.class); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 5f48fca58e9516a182b831b45166e4b70c05b2ee..41c467b155f5522f49f0f17d5e52b7fe064b40b6 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -2460,6 +2460,7 @@ public class XmppConnectionService extends Service { public void createAccount(final Account account) { account.initAccountServices(this); databaseBackend.createAccount(account); + CallIntegrationConnectionService.registerPhoneAccount(this, account); this.accounts.add(account); this.reconnectAccountInBackground(account); updateAccountUi(); @@ -2644,6 +2645,7 @@ public class XmppConnectionService extends Service { }; mDatabaseWriterExecutor.execute(runnable); this.accounts.remove(account); + CallIntegrationConnectionService.unregisterPhoneAccount(this, account); this.mRosterSyncTaskManager.clear(account); updateAccountUi(); mNotificationService.updateErrorNotification(); From 6975299a28e7915c0f88e1e2f59f66eea7fae8bc Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 15 Jan 2024 13:20:39 +0100 Subject: [PATCH 008/363] hook into onAnswer/onReject of CallIntegration the Operating System shows a notification on our behalf if there is currently a call going on that can not be put on hold (For example a Quicksy call is going on while a Conversations call is coming on) --- .../services/CallIntegration.java | 9 ++- .../xmpp/jingle/JingleConnectionManager.java | 10 +++ .../xmpp/jingle/JingleRtpConnection.java | 81 ++++++++++++++----- 3 files changed, 78 insertions(+), 22 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index bf12f5f4bd378a08a63245370b111a412b2e5eb0..fbd3e8c2ca5292efa12e49534bc4c5b585d68fb3 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -60,7 +60,7 @@ public class CallIntegration extends Connection { @Override public void onAnswer() { - Log.d(Config.LOGTAG, "onAnswer()"); + this.callback.onCallIntegrationAnswer(); } @Override @@ -71,12 +71,13 @@ public class CallIntegration extends Connection { @Override public void onReject() { - Log.d(Config.LOGTAG, "onReject()"); + this.callback.onCallIntegrationReject(); } @Override public void onReject(final String replyMessage) { Log.d(Config.LOGTAG, "onReject(" + replyMessage + ")"); + this.callback.onCallIntegrationReject(); } @RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @@ -404,5 +405,9 @@ public class CallIntegration extends Connection { void onAudioDeviceChanged( CallIntegration.AudioDevice selectedAudioDevice, Set availableAudioDevices); + + void onCallIntegrationReject(); + + void onCallIntegrationAnswer(); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 8153dc254f028d68c4f9f85099fb5b0bd8d2a291..9101b939a0223ccdd492538da427e6db0d08cc5c 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -1109,5 +1109,15 @@ public class JingleConnectionManager extends AbstractConnectionManager { public void onAudioDeviceChanged( CallIntegration.AudioDevice selectedAudioDevice, Set availableAudioDevices) {} + + @Override + public void onCallIntegrationReject() { + + } + + @Override + public void onCallIntegrationAnswer() { + + } } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 8d5aa8dfd00ca772078889690f532b535e137756..2ecd91253b84292cfe0f05828db386d235cb214b 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -1,6 +1,6 @@ package eu.siacs.conversations.xmpp.jingle; -import android.telecom.Call; +import android.content.Intent; import android.telecom.TelecomManager; import android.util.Log; @@ -35,6 +35,7 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.RtpSessionStatus; import eu.siacs.conversations.services.AppRTCAudioManager; import eu.siacs.conversations.services.CallIntegration; +import eu.siacs.conversations.ui.RtpSessionActivity; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xmpp.Jid; @@ -75,7 +76,8 @@ public class JingleRtpConnection extends AbstractJingleConnection private static final long BUSY_TIME_OUT = 30; private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this); - private final Queue>> + private final Queue< + Map.Entry>> pendingIceCandidates = new LinkedList<>(); private final OmemoVerification omemoVerification = new OmemoVerification(); private final CallIntegration callIntegration; @@ -91,13 +93,28 @@ public class JingleRtpConnection extends AbstractJingleConnection private final Queue stateHistory = new LinkedList<>(); private ScheduledFuture ringingTimeoutFuture; - JingleRtpConnection(final JingleConnectionManager jingleConnectionManager, final Id id, final Jid initiator) { - this(jingleConnectionManager, id, initiator, new CallIntegration(jingleConnectionManager.getXmppConnectionService().getApplicationContext())); - this.callIntegration.setAddress(CallIntegration.address(id.with.asBareJid()), TelecomManager.PRESENTATION_ALLOWED); + JingleRtpConnection( + final JingleConnectionManager jingleConnectionManager, + final Id id, + final Jid initiator) { + this( + jingleConnectionManager, + id, + initiator, + new CallIntegration( + jingleConnectionManager + .getXmppConnectionService() + .getApplicationContext())); + this.callIntegration.setAddress( + CallIntegration.address(id.with.asBareJid()), TelecomManager.PRESENTATION_ALLOWED); this.callIntegration.setInitialized(); } - JingleRtpConnection(final JingleConnectionManager jingleConnectionManager, final Id id, final Jid initiator, final CallIntegration callIntegration) { + JingleRtpConnection( + final JingleConnectionManager jingleConnectionManager, + final Id id, + final Jid initiator, + final CallIntegration callIntegration) { super(jingleConnectionManager, id, initiator); final Conversation conversation = jingleConnectionManager @@ -231,8 +248,8 @@ public class JingleRtpConnection extends AbstractJingleConnection private void receiveTransportInfo( final JinglePacket jinglePacket, final RtpContentMap contentMap) { - final Set>> candidates = - contentMap.contents.entrySet(); + final Set>> + candidates = contentMap.contents.entrySet(); final RtpContentMap remote = getRemoteContentMap(); final Set remoteContentIds = remote == null ? Collections.emptySet() : remote.contents.keySet(); @@ -1004,14 +1021,17 @@ public class JingleRtpConnection extends AbstractJingleConnection } private void processCandidates( - final Set>> contents) { - for (final Map.Entry> content : contents) { + final Set>> + contents) { + for (final Map.Entry> + content : contents) { processCandidate(content); } } private void processCandidate( - final Map.Entry> content) { + final Map.Entry> + content) { final RtpContentMap rtpContentMap = getRemoteContentMap(); final List indices = toIdentificationTags(rtpContentMap); final String sdpMid = content.getKey(); // aka content name @@ -1382,7 +1402,7 @@ public class JingleRtpConnection extends AbstractJingleConnection } private void addIceCandidatesFromBlackLog() { - Map.Entry> foo; + Map.Entry> foo; while ((foo = this.pendingIceCandidates.poll()) != null) { processCandidate(foo); Log.d( @@ -1960,12 +1980,10 @@ public class JingleRtpConnection extends AbstractJingleConnection sendSessionTerminate(reason, null); } - protected void sendSessionTerminate(final Reason reason, final String text) { - sendSessionTerminate(reason,text, this::writeLogMessage); + sendSessionTerminate(reason, text, this::writeLogMessage); } - private void sendTransportInfo( final String contentName, IceUdpTransportInfo.Candidate candidate) { final RtpContentMap transportInfo; @@ -2351,7 +2369,6 @@ public class JingleRtpConnection extends AbstractJingleConnection sendSessionAccept(); } - @Override protected synchronized boolean transition(final State target, final Runnable runnable) { if (super.transition(target, runnable)) { @@ -2592,7 +2609,6 @@ public class JingleRtpConnection extends AbstractJingleConnection private void modifyLocalContentMap(final RtpContentMap rtpContentMap) { final RtpContentMap activeContents = rtpContentMap.activeContents(); setLocalContentMap(activeContents); - // TODO change audio device on callIntegration was (`switchSpeakerPhonePreference(AppRTCAudioManager.SpeakerPhonePreference.of(activeContents.getMedia())`) updateEndUserState(); } @@ -2625,7 +2641,6 @@ public class JingleRtpConnection extends AbstractJingleConnection return this.sessionDuration.elapsed(TimeUnit.MILLISECONDS); } - public CallIntegration getCallIntegration() { return this.callIntegration; } @@ -2673,11 +2688,37 @@ public class JingleRtpConnection extends AbstractJingleConnection } } + @Override + public void onCallIntegrationReject() { + Log.d(Config.LOGTAG, "rejecting call from system notification / call integration"); + try { + rejectCall(); + } catch (final IllegalStateException e) { + Log.w(Config.LOGTAG, "race condition on rejecting call from notification", e); + } + } + + @Override + public void onCallIntegrationAnswer() { + // we need to start the UI to a) show it and b) be able to ask for permissions + final Intent intent = new Intent(xmppConnectionService, RtpSessionActivity.class); + intent.setAction(RtpSessionActivity.ACTION_ACCEPT_CALL); + intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().toEscapedString()); + intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString()); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId); + Log.d(Config.LOGTAG, "start activity to accept call from call integration"); + xmppConnectionService.startActivity(intent); + } + @Override public void onAudioDeviceChanged( final CallIntegration.AudioDevice selectedAudioDevice, final Set availableAudioDevices) { - Log.d(Config.LOGTAG,"onAudioDeviceChanged("+selectedAudioDevice+","+availableAudioDevices+")"); + Log.d( + Config.LOGTAG, + "onAudioDeviceChanged(" + selectedAudioDevice + "," + availableAudioDevices + ")"); xmppConnectionService.notifyJingleRtpConnectionUpdate( selectedAudioDevice, availableAudioDevices); } @@ -2732,7 +2773,7 @@ public class JingleRtpConnection extends AbstractJingleConnection onIceServersDiscovered.onIceServersDiscovered(Collections.emptyList()); } } - + @Override protected void terminateTransport() { this.webRTCWrapper.close(); From ebb48e9320d07459d898556e3f48fca786b7201b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 15 Jan 2024 13:54:26 +0100 Subject: [PATCH 009/363] set correct video state for calls --- .../xmpp/jingle/JingleConnectionManager.java | 13 +++++++------ .../xmpp/jingle/JingleRtpConnection.java | 5 +++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 9101b939a0223ccdd492538da427e6db0d08cc5c..fd5c4c9af7220206de6ac72c409a597d0efc83c9 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.xmpp.jingle; +import android.telecom.VideoProfile; import android.util.Base64; import android.util.Log; @@ -732,6 +733,10 @@ public class JingleConnectionManager extends AbstractConnectionManager { } final CallIntegration callIntegration = new CallIntegration(mXmppConnectionService.getApplicationContext()); + callIntegration.setVideoState( + Media.audioOnly(media) + ? VideoProfile.STATE_AUDIO_ONLY + : VideoProfile.STATE_BIDIRECTIONAL); callIntegration.setInitialAudioDevice(CallIntegration.initialAudioDevice(media)); final RtpSessionProposal proposal = RtpSessionProposal.of(account, with.asBareJid(), media, callIntegration); @@ -1111,13 +1116,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { Set availableAudioDevices) {} @Override - public void onCallIntegrationReject() { - - } + public void onCallIntegrationReject() {} @Override - public void onCallIntegrationAnswer() { - - } + public void onCallIntegrationAnswer() {} } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 2ecd91253b84292cfe0f05828db386d235cb214b..dc0980e1b2c4f4717a9e5f24d3bc4884106dda6a 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.xmpp.jingle; import android.content.Intent; import android.telecom.TelecomManager; +import android.telecom.VideoProfile; import android.util.Log; import androidx.annotation.NonNull; @@ -2838,6 +2839,10 @@ public class JingleRtpConnection extends AbstractJingleConnection void setProposedMedia(final Set media) { this.proposedMedia = media; + this.callIntegration.setVideoState( + Media.audioOnly(media) + ? VideoProfile.STATE_AUDIO_ONLY + : VideoProfile.STATE_BIDIRECTIONAL); this.callIntegration.setInitialAudioDevice(CallIntegration.initialAudioDevice(media)); } From 6ba9208eea5a12ecbaf8dcbeb6a0453e5ec5995a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 16 Jan 2024 13:56:39 +0100 Subject: [PATCH 010/363] switch audio device when switching to video --- .../eu/siacs/conversations/services/CallIntegration.java | 7 +++++++ .../conversations/xmpp/jingle/JingleRtpConnection.java | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index fbd3e8c2ca5292efa12e49534bc4c5b585d68fb3..a4dffffaa478470d9424f1594486cf634339f0c1 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -145,6 +145,13 @@ public class CallIntegration extends Connection { } } + public void setAudioDeviceWhenAvailable(final AudioDevice audioDevice) { + final var available = getAudioDevices(); + if (available.contains(audioDevice)) { + this.setAudioDevice(audioDevice); + } + } + @RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) private Set getAudioDevicesUpsideDownCake() { return ImmutableSet.copyOf( diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index dc0980e1b2c4f4717a9e5f24d3bc4884106dda6a..e9d75031909414d2bf27a177c81581621dc9200e 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -2610,6 +2610,8 @@ public class JingleRtpConnection extends AbstractJingleConnection private void modifyLocalContentMap(final RtpContentMap rtpContentMap) { final RtpContentMap activeContents = rtpContentMap.activeContents(); setLocalContentMap(activeContents); + this.callIntegration.setAudioDeviceWhenAvailable( + CallIntegration.initialAudioDevice(activeContents.getMedia())); updateEndUserState(); } From d79fc1bb790ef70476886167575349035f2ff9a0 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 16 Jan 2024 18:50:40 +0100 Subject: [PATCH 011/363] run some AppRTCAudioManager actions on main thread --- .../services/AppRTCAudioManager.java | 77 +------- .../services/AppRTCBluetoothManager.java | 2 - .../services/AppRTCProximitySensor.java | 171 ------------------ .../services/CallIntegration.java | 23 ++- .../CallIntegrationConnectionService.java | 4 +- .../xmpp/jingle/JingleRtpConnection.java | 3 +- .../xmpp/jingle/WebRTCWrapper.java | 4 +- 7 files changed, 30 insertions(+), 254 deletions(-) delete mode 100644 src/main/java/eu/siacs/conversations/services/AppRTCProximitySensor.java diff --git a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java index a472445d32bef97f697e32604a431051176a7e82..894b2ace64286b913f9a56e588cc9dbcdbd95ad2 100644 --- a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java +++ b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java @@ -23,6 +23,7 @@ import android.os.Build; import android.util.Log; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import org.webrtc.ThreadUtils; @@ -44,8 +45,6 @@ public class AppRTCAudioManager { private final Context apprtcContext; // Contains speakerphone setting: auto, true or false - @Nullable - private SpeakerPhonePreference speakerPhonePreference; // Handles all tasks related to Bluetooth headset devices. private final AppRTCBluetoothManager bluetoothManager; @Nullable @@ -70,12 +69,7 @@ public class AppRTCAudioManager { // TODO(henrika): always set to AudioDevice.NONE today. Add support for // explicit selection based on choice by userSelectedAudioDevice. private CallIntegration.AudioDevice userSelectedAudioDevice; - // Proximity sensor object. It measures the proximity of an object in cm - // relative to the view screen of a device and can therefore be used to - // assist device switching (close to ear <=> use headset earpiece if - // available, far from ear <=> use speaker phone). - @Nullable - private AppRTCProximitySensor proximitySensor; + // Contains a list of available audio devices. A Set collection is used to // avoid duplicate elements. private Set audioDevices = new HashSet<>(); @@ -86,7 +80,6 @@ public class AppRTCAudioManager { private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener; public AppRTCAudioManager(final Context context) { - ThreadUtils.checkIsOnMainThread(); apprtcContext = context; audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE)); bluetoothManager = AppRTCBluetoothManager.create(context, this); @@ -98,28 +91,10 @@ public class AppRTCAudioManager { } else { defaultAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE; } - // Create and initialize the proximity sensor. - // Tablet devices (e.g. Nexus 7) does not support proximity sensors. - // Note that, the sensor will not be active until start() has been called. - proximitySensor = AppRTCProximitySensor.create(context, - // This method will be called each time a state change is detected. - // Example: user holds his hand over the device (closer than ~5 cm), - // or removes his hand from the device. - this::onProximitySensorChangedState); Log.d(Config.LOGTAG, "defaultAudioDevice: " + defaultAudioDevice); AppRTCUtils.logDeviceInfo(Config.LOGTAG); } - public void switchSpeakerPhonePreference(final SpeakerPhonePreference speakerPhonePreference) { - this.speakerPhonePreference = speakerPhonePreference; - if (speakerPhonePreference == SpeakerPhonePreference.EARPIECE && hasEarpiece()) { - defaultAudioDevice = CallIntegration.AudioDevice.EARPIECE; - } else { - defaultAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE; - } - updateAudioDeviceState(); - } - public static boolean isMicrophoneAvailable() { microphoneLatch = new CountDownLatch(1); AudioRecord audioRecord = null; @@ -156,30 +131,6 @@ public class AppRTCAudioManager { } } - /** - * This method is called when the proximity sensor reports a state change, - * e.g. from "NEAR to FAR" or from "FAR to NEAR". - */ - private void onProximitySensorChangedState() { - if (speakerPhonePreference != SpeakerPhonePreference.AUTO) { - return; - } - // The proximity sensor should only be activated when there are exactly two - // available audio devices. - if (audioDevices.size() == 2 && audioDevices.contains(CallIntegration.AudioDevice.EARPIECE) - && audioDevices.contains(CallIntegration.AudioDevice.SPEAKER_PHONE)) { - if (proximitySensor.sensorReportsNearState()) { - // Sensor reports that a "handset is being held up to a person's ear", - // or "something is covering the light sensor". - setAudioDeviceInternal(CallIntegration.AudioDevice.EARPIECE); - } else { - // Sensor reports that a "handset is removed from a person's ear", or - // "the light sensor is no longer covered". - setAudioDeviceInternal(CallIntegration.AudioDevice.SPEAKER_PHONE); - } - } - } - @SuppressWarnings("deprecation") public void start(AudioManagerEvents audioManagerEvents) { Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".start()"); @@ -280,6 +231,7 @@ public class AppRTCAudioManager { @SuppressWarnings("deprecation") public void stop() { + Log.d(Config.LOGTAG,"appRtpAudioManager.stop()"); Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".stop()"); ThreadUtils.checkIsOnMainThread(); if (amState != AudioManagerState.RUNNING) { @@ -296,12 +248,8 @@ public class AppRTCAudioManager { // Abandon audio focus. Gives the previous focus owner, if any, focus. audioManager.abandonAudioFocus(audioFocusChangeListener); audioFocusChangeListener = null; - Log.d(Config.LOGTAG, "Abandoned audio focus for VOICE_CALL streams"); - if (proximitySensor != null) { - proximitySensor.stop(); - proximitySensor = null; - } audioManagerEvents = null; + Log.d(Config.LOGTAG,"appRtpAudioManager.stopped()"); } /** @@ -375,7 +323,6 @@ public class AppRTCAudioManager { * Returns the currently selected audio device. */ public CallIntegration.AudioDevice getSelectedAudioDevice() { - ThreadUtils.checkIsOnMainThread(); return selectedAudioDevice; } @@ -574,6 +521,10 @@ public class AppRTCAudioManager { Log.d(Config.LOGTAG, "--- updateAudioDeviceState done"); } + public void executeOnMain(final Runnable runnable) { + ContextCompat.getMainExecutor(apprtcContext).execute(runnable); + } + /** * AudioManager state. */ @@ -583,18 +534,6 @@ public class AppRTCAudioManager { RUNNING, } - public enum SpeakerPhonePreference { - AUTO, EARPIECE, SPEAKER; - - public static SpeakerPhonePreference of(final Set media) { - if (media.contains(Media.VIDEO)) { - return SPEAKER; - } else { - return EARPIECE; - } - } - } - /** * Selected audio device change event. */ diff --git a/src/main/java/eu/siacs/conversations/services/AppRTCBluetoothManager.java b/src/main/java/eu/siacs/conversations/services/AppRTCBluetoothManager.java index 484072605174ab852628cf501086145d9a14dddf..d915459a4d582cf00badfabdcadae19518ed3362 100644 --- a/src/main/java/eu/siacs/conversations/services/AppRTCBluetoothManager.java +++ b/src/main/java/eu/siacs/conversations/services/AppRTCBluetoothManager.java @@ -68,8 +68,6 @@ public class AppRTCBluetoothManager { }; protected AppRTCBluetoothManager(Context context, AppRTCAudioManager audioManager) { - Log.d(Config.LOGTAG, "ctor"); - ThreadUtils.checkIsOnMainThread(); apprtcContext = context; apprtcAudioManager = audioManager; this.audioManager = getAudioManager(context); diff --git a/src/main/java/eu/siacs/conversations/services/AppRTCProximitySensor.java b/src/main/java/eu/siacs/conversations/services/AppRTCProximitySensor.java deleted file mode 100644 index 2f24787c0ff020fa99107f74163ccdfc84e8e2d5..0000000000000000000000000000000000000000 --- a/src/main/java/eu/siacs/conversations/services/AppRTCProximitySensor.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2014 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -package eu.siacs.conversations.services; - -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.Build; -import android.util.Log; - -import androidx.annotation.Nullable; - -import org.webrtc.ThreadUtils; - -import eu.siacs.conversations.Config; -import eu.siacs.conversations.utils.AppRTCUtils; - -/** - * AppRTCProximitySensor manages functions related to the proximity sensor in - * the AppRTC demo. - * On most device, the proximity sensor is implemented as a boolean-sensor. - * It returns just two values "NEAR" or "FAR". Thresholding is done on the LUX - * value i.e. the LUX value of the light sensor is compared with a threshold. - * A LUX-value more than the threshold means the proximity sensor returns "FAR". - * Anything less than the threshold value and the sensor returns "NEAR". - */ -public class AppRTCProximitySensor implements SensorEventListener { - // This class should be created, started and stopped on one thread - // (e.g. the main thread). We use |nonThreadSafe| to ensure that this is - // the case. Only active when |DEBUG| is set to true. - private final ThreadUtils.ThreadChecker threadChecker = new ThreadUtils.ThreadChecker(); - private final Runnable onSensorStateListener; - private final SensorManager sensorManager; - @Nullable - private Sensor proximitySensor; - private boolean lastStateReportIsNear; - - private AppRTCProximitySensor(Context context, Runnable sensorStateListener) { - Log.d(Config.LOGTAG, "AppRTCProximitySensor" + AppRTCUtils.getThreadInfo()); - onSensorStateListener = sensorStateListener; - sensorManager = ((SensorManager) context.getSystemService(Context.SENSOR_SERVICE)); - } - - /** - * Construction - */ - static AppRTCProximitySensor create(Context context, Runnable sensorStateListener) { - return new AppRTCProximitySensor(context, sensorStateListener); - } - - /** - * Activate the proximity sensor. Also do initialization if called for the - * first time. - */ - public boolean start() { - threadChecker.checkIsOnValidThread(); - Log.d(Config.LOGTAG, "start" + AppRTCUtils.getThreadInfo()); - if (!initDefaultSensor()) { - // Proximity sensor is not supported on this device. - return false; - } - sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); - return true; - } - - /** - * Deactivate the proximity sensor. - */ - public void stop() { - threadChecker.checkIsOnValidThread(); - Log.d(Config.LOGTAG, "stop" + AppRTCUtils.getThreadInfo()); - if (proximitySensor == null) { - return; - } - sensorManager.unregisterListener(this, proximitySensor); - } - - /** - * Getter for last reported state. Set to true if "near" is reported. - */ - public boolean sensorReportsNearState() { - threadChecker.checkIsOnValidThread(); - return lastStateReportIsNear; - } - - @Override - public final void onAccuracyChanged(Sensor sensor, int accuracy) { - threadChecker.checkIsOnValidThread(); - AppRTCUtils.assertIsTrue(sensor.getType() == Sensor.TYPE_PROXIMITY); - if (accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) { - Log.e(Config.LOGTAG, "The values returned by this sensor cannot be trusted"); - } - } - - @Override - public final void onSensorChanged(SensorEvent event) { - threadChecker.checkIsOnValidThread(); - AppRTCUtils.assertIsTrue(event.sensor.getType() == Sensor.TYPE_PROXIMITY); - // As a best practice; do as little as possible within this method and - // avoid blocking. - float distanceInCentimeters = event.values[0]; - if (distanceInCentimeters < proximitySensor.getMaximumRange()) { - Log.d(Config.LOGTAG, "Proximity sensor => NEAR state"); - lastStateReportIsNear = true; - } else { - Log.d(Config.LOGTAG, "Proximity sensor => FAR state"); - lastStateReportIsNear = false; - } - // Report about new state to listening client. Client can then call - // sensorReportsNearState() to query the current state (NEAR or FAR). - if (onSensorStateListener != null) { - onSensorStateListener.run(); - } - Log.d(Config.LOGTAG, "onSensorChanged" + AppRTCUtils.getThreadInfo() + ": " - + "accuracy=" + event.accuracy + ", timestamp=" + event.timestamp + ", distance=" - + event.values[0]); - } - - /** - * Get default proximity sensor if it exists. Tablet devices (e.g. Nexus 7) - * does not support this type of sensor and false will be returned in such - * cases. - */ - private boolean initDefaultSensor() { - if (proximitySensor != null) { - return true; - } - proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); - if (proximitySensor == null) { - return false; - } - logProximitySensorInfo(); - return true; - } - - /** - * Helper method for logging information about the proximity sensor. - */ - private void logProximitySensorInfo() { - if (proximitySensor == null) { - return; - } - StringBuilder info = new StringBuilder("Proximity sensor: "); - info.append("name=").append(proximitySensor.getName()); - info.append(", vendor: ").append(proximitySensor.getVendor()); - info.append(", power: ").append(proximitySensor.getPower()); - info.append(", resolution: ").append(proximitySensor.getResolution()); - info.append(", max range: ").append(proximitySensor.getMaximumRange()); - info.append(", min delay: ").append(proximitySensor.getMinDelay()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { - // Added in API level 20. - info.append(", type: ").append(proximitySensor.getStringType()); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // Added in API level 21. - info.append(", max delay: ").append(proximitySensor.getMaxDelay()); - info.append(", reporting mode: ").append(proximitySensor.getReportingMode()); - info.append(", isWakeUpSensor: ").append(proximitySensor.isWakeUpSensor()); - } - Log.d(Config.LOGTAG, info.toString()); - } -} \ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index a4dffffaa478470d9424f1594486cf634339f0c1..051a7de9af367a3989a763cdce164d4702f679f3 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -11,6 +11,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +import androidx.core.content.ContextCompat; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -42,8 +43,8 @@ public class CallIntegration extends Connection { this.appRTCAudioManager = null; } else { this.appRTCAudioManager = new AppRTCAudioManager(context); - this.appRTCAudioManager.start(this::onAudioDeviceChanged); - // TODO WebRTCWrapper would issue one call to eventCallback.onAudioDeviceChanged + ContextCompat.getMainExecutor(context) + .execute(() -> this.appRTCAudioManager.start(this::onAudioDeviceChanged)); } setRingbackRequested(true); } @@ -149,6 +150,12 @@ public class CallIntegration extends Connection { final var available = getAudioDevices(); if (available.contains(audioDevice)) { this.setAudioDevice(audioDevice); + } else { + Log.d( + Config.LOGTAG, + "application requested to switch to " + + audioDevice + + " but device was not available"); } } @@ -269,7 +276,8 @@ public class CallIntegration extends Connection { } private void setAudioDeviceFallback(final AudioDevice audioDevice) { - requireAppRtcAudioManager().setDefaultAudioDevice(audioDevice); + final var audioManager = requireAppRtcAudioManager(); + audioManager.executeOnMain(() -> audioManager.setDefaultAudioDevice(audioDevice)); } @NonNull @@ -287,7 +295,7 @@ public class CallIntegration extends Connection { if (state == STATE_DISCONNECTED) { final var audioManager = this.appRTCAudioManager; if (audioManager != null) { - audioManager.stop(); + audioManager.executeOnMain(audioManager::stop); } } } @@ -382,8 +390,11 @@ public class CallIntegration extends Connection { return; } final var audioManager = requireAppRtcAudioManager(); - this.onAudioDeviceChanged( - audioManager.getSelectedAudioDevice(), audioManager.getAudioDevices()); + audioManager.executeOnMain( + () -> + this.onAudioDeviceChanged( + audioManager.getSelectedAudioDevice(), + audioManager.getAudioDevices())); } /** AudioDevice is the names of possible audio devices that we currently support. */ diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index 01f24684436203d6350ce565207974e38518409e..e1a9b338391b5479a744803a12cdbaf897ecf3c2 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -122,6 +122,7 @@ public class CallIntegrationConnectionService extends ConnectionService { public Connection onCreateIncomingConnection( final PhoneAccountHandle phoneAccountHandle, final ConnectionRequest request) { + Log.d(Config.LOGTAG, "onCreateIncomingConnection()"); final var service = ServiceConnectionService.get(this.serviceFuture); final Bundle extras = request.getExtras(); final Bundle extraExtras = extras.getBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS); @@ -182,7 +183,8 @@ public class CallIntegrationConnectionService extends ConnectionService { } public static void unregisterPhoneAccount(final Context context, final Account account) { - context.getSystemService(TelecomManager.class).unregisterPhoneAccount(getHandle(context, account)); + context.getSystemService(TelecomManager.class) + .unregisterPhoneAccount(getHandle(context, account)); } public static PhoneAccountHandle getHandle(final Context context, final Account account) { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index e9d75031909414d2bf27a177c81581621dc9200e..2cbd8c739f947b47b821e9b4486d58fef98a3c87 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -2300,8 +2300,7 @@ public class JingleRtpConnection extends AbstractJingleConnection final boolean trickle) throws WebRTCWrapper.InitializationException { this.jingleConnectionManager.ensureConnectionIsRegistered(this); - this.webRTCWrapper.setup( - this.xmppConnectionService, AppRTCAudioManager.SpeakerPhonePreference.of(media)); + this.webRTCWrapper.setup(this.xmppConnectionService); this.webRTCWrapper.initializePeerConnection(media, iceServers, trickle); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java index fa504ed191e5b149c040cf569cec20feba487106..128a35bf03114d25bcc7ac8a86efe2ab61d9d0ee 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java @@ -222,9 +222,7 @@ public class WebRTCWrapper { } } - public void setup( - final XmppConnectionService service, - @Nonnull final AppRTCAudioManager.SpeakerPhonePreference speakerPhonePreference) + public void setup(final XmppConnectionService service) throws InitializationException { try { PeerConnectionFactory.initialize( From 0ffc2958886691a8bafc45824e7c8c49d33ac55a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 17 Jan 2024 09:18:24 +0100 Subject: [PATCH 012/363] provide alternative method to create calls for Android <8 --- .../services/CallIntegration.java | 4 + .../CallIntegrationConnectionService.java | 113 ++++++++++++------ .../ui/ConversationFragment.java | 2 +- .../conversations/ui/RtpSessionActivity.java | 2 +- 4 files changed, 84 insertions(+), 37 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index 051a7de9af367a3989a763cdce164d4702f679f3..b9b21e578d778ab0e02bb67a7f28b07ed1d52cd9 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -381,6 +381,10 @@ public class CallIntegration extends Connection { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; } + public static boolean notSelfManaged() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.O; + } + public void setInitialAudioDevice(final AudioDevice audioDevice) { Log.d(Config.LOGTAG, "setInitialAudioDevice(" + audioDevice + ")"); this.initialAudioDevice = audioDevice; diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index e1a9b338391b5479a744803a12cdbaf897ecf3c2..71ea897c2afe832d6b80ec4d07775a8056cabd4e 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -18,6 +18,8 @@ import android.telecom.TelecomManager; import android.telecom.VideoProfile; import android.util.Log; +import androidx.annotation.NonNull; + import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; @@ -44,6 +46,7 @@ public class CallIntegrationConnectionService extends ConnectionService { @Override public void onCreate() { + Log.d(Config.LOGTAG, "CallIntegrationService.onCreate()"); super.onCreate(); this.serviceFuture = ServiceConnectionService.bindService(this); } @@ -62,38 +65,39 @@ public class CallIntegrationConnectionService extends ConnectionService { this.unbindService(serviceConnection); } - @Override - public Connection onCreateOutgoingConnection( - final PhoneAccountHandle phoneAccountHandle, final ConnectionRequest request) { - Log.d(Config.LOGTAG, "onCreateOutgoingConnection(" + request.getAddress() + ")"); - final var uri = request.getAddress(); - final var jid = Jid.ofEscaped(uri.getSchemeSpecificPart()); - final var extras = request.getExtras(); - final int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE); - final Set media = - videoState == VideoProfile.STATE_AUDIO_ONLY - ? ImmutableSet.of(Media.AUDIO) - : ImmutableSet.of(Media.AUDIO, Media.VIDEO); - Log.d(Config.LOGTAG, "jid=" + jid); - Log.d(Config.LOGTAG, "phoneAccountHandle:" + phoneAccountHandle.getId()); - Log.d(Config.LOGTAG, "media " + media); - final var service = ServiceConnectionService.get(this.serviceFuture); + private static Connection createOutgoingRtpConnection( + final XmppConnectionService service, + final String phoneAccountHandle, + final Jid with, + final Set media) { if (service == null) { + Log.d( + Config.LOGTAG, + "CallIntegrationConnection service was unable to bind to XmppConnectionService"); return Connection.createFailedConnection( new DisconnectCause(DisconnectCause.ERROR, "service connection not found")); } - final Account account = service.findAccountByUuid(phoneAccountHandle.getId()); - final Intent intent = new Intent(this, RtpSessionActivity.class); + final var account = service.findAccountByUuid(phoneAccountHandle); + return createOutgoingRtpConnection(service, account, with, media); + } + + private static Connection createOutgoingRtpConnection( + @NonNull final XmppConnectionService service, + @NonNull final Account account, + final Jid with, + final Set media) { + Log.d(Config.LOGTAG, "create outgoing rtp connection!"); + final Intent intent = new Intent(service, RtpSessionActivity.class); intent.setAction(Intent.ACTION_VIEW); intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.getJid().toEscapedString()); - intent.putExtra(RtpSessionActivity.EXTRA_WITH, jid.toEscapedString()); + intent.putExtra(RtpSessionActivity.EXTRA_WITH, with.toEscapedString()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); final CallIntegration callIntegration; - if (jid.isBareJid()) { + if (with.isBareJid()) { final var proposal = service.getJingleConnectionManager() - .proposeJingleRtpSession(account, jid, media); + .proposeJingleRtpSession(account, with, media); intent.putExtra( RtpSessionActivity.EXTRA_LAST_REPORTED_STATE, @@ -110,16 +114,35 @@ public class CallIntegrationConnectionService extends ConnectionService { callIntegration = proposal.getCallIntegration(); } else { final JingleRtpConnection jingleRtpConnection = - service.getJingleConnectionManager().initializeRtpSession(account, jid, media); + service.getJingleConnectionManager().initializeRtpSession(account, with, media); final String sessionId = jingleRtpConnection.getId().sessionId; intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, sessionId); callIntegration = jingleRtpConnection.getCallIntegration(); } - Log.d(Config.LOGTAG, "start activity!"); - startActivity(intent); + service.startActivity(intent); return callIntegration; } + @Override + public Connection onCreateOutgoingConnection( + final PhoneAccountHandle phoneAccountHandle, final ConnectionRequest request) { + Log.d(Config.LOGTAG, "onCreateOutgoingConnection(" + request.getAddress() + ")"); + final var uri = request.getAddress(); + final var jid = Jid.ofEscaped(uri.getSchemeSpecificPart()); + final var extras = request.getExtras(); + final int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE); + final Set media = + videoState == VideoProfile.STATE_AUDIO_ONLY + ? ImmutableSet.of(Media.AUDIO) + : ImmutableSet.of(Media.AUDIO, Media.VIDEO); + Log.d(Config.LOGTAG, "jid=" + jid); + Log.d(Config.LOGTAG, "phoneAccountHandle:" + phoneAccountHandle.getId()); + Log.d(Config.LOGTAG, "media " + media); + final var service = ServiceConnectionService.get(this.serviceFuture); + return createOutgoingRtpConnection(service, phoneAccountHandle.getId(), jid, media); + } + + @Override public Connection onCreateIncomingConnection( final PhoneAccountHandle phoneAccountHandle, final ConnectionRequest request) { Log.d(Config.LOGTAG, "onCreateIncomingConnection()"); @@ -194,22 +217,42 @@ public class CallIntegrationConnectionService extends ConnectionService { } public static void placeCall( - final Context context, final Account account, final Jid with, final Set media) { + final XmppConnectionService service, + final Account account, + final Jid with, + final Set media) { Log.d(Config.LOGTAG, "place call media=" + media); - final var extras = new Bundle(); - extras.putParcelable( - TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, getHandle(context, account)); - extras.putInt( - TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, - Media.audioOnly(media) - ? VideoProfile.STATE_AUDIO_ONLY - : VideoProfile.STATE_BIDIRECTIONAL); - context.getSystemService(TelecomManager.class) - .placeCall(CallIntegration.address(with), extras); + if (CallIntegration.selfManaged()) { + final var extras = new Bundle(); + extras.putParcelable( + TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, getHandle(service, account)); + extras.putInt( + TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, + Media.audioOnly(media) + ? VideoProfile.STATE_AUDIO_ONLY + : VideoProfile.STATE_BIDIRECTIONAL); + service.getSystemService(TelecomManager.class) + .placeCall(CallIntegration.address(with), extras); + } else { + final var connection = createOutgoingRtpConnection(service, account, with, media); + if (connection != null) { + Log.d( + Config.LOGTAG, + "not adding outgoing call to TelecomManager on Android " + + Build.VERSION.RELEASE); + } + } } public static void addNewIncomingCall( final Context context, final AbstractJingleConnection.Id id) { + if (CallIntegration.notSelfManaged()) { + Log.d( + Config.LOGTAG, + "not adding incoming call to TelecomManager on Android " + + Build.VERSION.RELEASE); + return; + } final var phoneAccountHandle = CallIntegrationConnectionService.getHandle(context, id.account); final var bundle = new Bundle(); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 01932a04e94cff5baa81708a5b219158b798d9e2..a00a472097d90decf43f6e7e4b19e0f92213e59f 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1653,7 +1653,7 @@ public class ConversationFragment extends XmppFragment } private void triggerRtpSession(final Account account, final Jid with, final String action) { - CallIntegrationConnectionService.placeCall(requireActivity(),account,with,RtpSessionActivity.actionToMedia(action)); + CallIntegrationConnectionService.placeCall(activity.xmppConnectionService, account,with,RtpSessionActivity.actionToMedia(action)); } private void handleAttachmentSelection(MenuItem item) { diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 5982324677dfb15086934f699aa84144b7d9f45e..2e4576f57181e544ad30a8038a0e0827800d768b 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -1312,7 +1312,7 @@ public class RtpSessionActivity extends XmppActivity final Set media = actionToMedia(lastAction == null ? action : lastAction); this.rtpConnectionReference = null; Log.d(Config.LOGTAG, "attempting retry with " + with.toEscapedString()); - CallIntegrationConnectionService.placeCall(this, account, with, media); + CallIntegrationConnectionService.placeCall(xmppConnectionService, account, with, media); } private void exit(final View view) { From 66cd50e1638905c76e9d508de9e6282eabca1ade Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 17 Jan 2024 09:32:49 +0100 Subject: [PATCH 013/363] add permission check to placeCall method --- .../CallIntegrationConnectionService.java | 11 ++++++- .../ui/ConversationFragment.java | 30 ++++++++----------- src/main/res/values/strings.xml | 1 + 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index 71ea897c2afe832d6b80ec4d07775a8056cabd4e..99f565ff197bfe763ede7853042fd4bb53f45e2c 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -1,9 +1,11 @@ package eu.siacs.conversations.services; +import android.Manifest; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -17,6 +19,7 @@ import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telecom.VideoProfile; import android.util.Log; +import android.widget.Toast; import androidx.annotation.NonNull; @@ -25,6 +28,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.ui.RtpSessionActivity; import eu.siacs.conversations.xmpp.Jid; @@ -221,7 +225,6 @@ public class CallIntegrationConnectionService extends ConnectionService { final Account account, final Jid with, final Set media) { - Log.d(Config.LOGTAG, "place call media=" + media); if (CallIntegration.selfManaged()) { final var extras = new Bundle(); extras.putParcelable( @@ -231,6 +234,12 @@ public class CallIntegrationConnectionService extends ConnectionService { Media.audioOnly(media) ? VideoProfile.STATE_AUDIO_ONLY : VideoProfile.STATE_BIDIRECTIONAL); + if (service.checkSelfPermission(Manifest.permission.MANAGE_OWN_CALLS) + != PackageManager.PERMISSION_GRANTED) { + Toast.makeText(service, R.string.no_permission_to_place_call, Toast.LENGTH_SHORT) + .show(); + return; + } service.getSystemService(TelecomManager.class) .placeCall(CallIntegration.address(with), extras); } else { diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index a00a472097d90decf43f6e7e4b19e0f92213e59f..6eea73d395746ab005b6dc46d81ef9de5f475adf 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1981,26 +1981,22 @@ public class ConversationFragment extends XmppFragment } private boolean hasPermissions(int requestCode, List permissions) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - final List missingPermissions = new ArrayList<>(); - for (String permission : permissions) { - if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU || Config.ONLY_INTERNAL_STORAGE) && permission.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - continue; - } - if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { - missingPermissions.add(permission); - } + final List missingPermissions = new ArrayList<>(); + for (String permission : permissions) { + if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU || Config.ONLY_INTERNAL_STORAGE) && permission.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + continue; } - if (missingPermissions.size() == 0) { - return true; - } else { - requestPermissions( - missingPermissions.toArray(new String[0]), - requestCode); - return false; + if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { + missingPermissions.add(permission); } - } else { + } + if (missingPermissions.size() == 0) { return true; + } else { + requestPermissions( + missingPermissions.toArray(new String[0]), + requestCode); + return false; } } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 3ee875fcf49c8cae6110f0728d627e76e1f045a7..996977eab39ed51affba9e5db9f0f830491d64ea 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -595,6 +595,7 @@ Me Contact asks for presence subscription Allow + No permission to place phone call No permission to access %s Remote server not found Remote server timeout From d31b24d05a2dd24373867819a9133e6e967a700b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 17 Jan 2024 12:18:47 +0100 Subject: [PATCH 014/363] get rid of ToneManager and play sounds in CallIntegration instead CallIntegration takes care of audio routing so it makes sense to play and sounds here too --- .../java/eu/siacs/conversations/Config.java | 2 +- .../services/CallIntegration.java | 35 ++- .../ui/ConversationFragment.java | 2 +- .../xmpp/jingle/JingleConnectionManager.java | 7 +- .../xmpp/jingle/JingleRtpConnection.java | 1 - .../xmpp/jingle/ToneManager.java | 238 ------------------ .../xmpp/jingle/WebRTCWrapper.java | 10 +- 7 files changed, 35 insertions(+), 260 deletions(-) delete mode 100644 src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 6c9e5dd68891651cfc4a4e6dd85038198ee61904..febe12b1488da86f7f150d96634127f16c5daa4b 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -120,7 +120,7 @@ public final class Config { public static final boolean DISABLE_PROXY_LOOKUP = false; // disables STUN/TURN and Proxy65 look up (useful to debug IBB fallback) public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true; - public static final boolean USE_JINGLE_DIRECT_INIT = true; + public static final boolean USE_JINGLE_MESSAGE_INIT = true; public static final boolean DISABLE_HTTP_UPLOAD = false; public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts public static final boolean BACKGROUND_STANZA_LOGGING = diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index b9b21e578d778ab0e02bb67a7f28b07ed1d52cd9..548a1507d28551873dd0c1dffc2bdda64a7fc889 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -1,6 +1,8 @@ package eu.siacs.conversations.services; import android.content.Context; +import android.media.AudioManager; +import android.media.ToneGenerator; import android.net.Uri; import android.os.Build; import android.telecom.CallAudioState; @@ -20,11 +22,13 @@ import com.google.common.collect.Lists; import eu.siacs.conversations.Config; import eu.siacs.conversations.ui.util.MainThreadExecutor; import eu.siacs.conversations.xmpp.Jid; +import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; import eu.siacs.conversations.xmpp.jingle.Media; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class CallIntegration extends Connection { @@ -32,6 +36,7 @@ public class CallIntegration extends Connection { private final AppRTCAudioManager appRTCAudioManager; private AudioDevice initialAudioDevice = null; private final AtomicBoolean initialAudioDeviceConfigured = new AtomicBoolean(false); + private final AtomicBoolean delayedDestructionInitiated = new AtomicBoolean(false); private List availableEndpoints = Collections.emptyList(); @@ -302,7 +307,9 @@ public class CallIntegration extends Connection { public void success() { Log.d(Config.LOGTAG, "CallIntegration.success()"); - this.destroyWith(new DisconnectCause(DisconnectCause.LOCAL, null)); + final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 100); + toneGenerator.startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375); + this.destroyWithDelay(new DisconnectCause(DisconnectCause.LOCAL, null), 375); } public void accepted() { @@ -316,6 +323,9 @@ public class CallIntegration extends Connection { public void error() { Log.d(Config.LOGTAG, "CallIntegration.error()"); + final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 80); + toneGenerator.startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375); + this.destroyWithDelay(new DisconnectCause(DisconnectCause.ERROR, null), 375); this.destroyWith(new DisconnectCause(DisconnectCause.ERROR, null)); } @@ -332,16 +342,33 @@ public class CallIntegration extends Connection { public void busy() { Log.d(Config.LOGTAG, "CallIntegration.busy()"); - this.destroyWith(new DisconnectCause(DisconnectCause.BUSY, null)); + final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 80); + toneGenerator.startTone(ToneGenerator.TONE_CDMA_NETWORK_BUSY, 2500); + this.destroyWithDelay(new DisconnectCause(DisconnectCause.BUSY, null), 2500); + } + + private void destroyWithDelay(final DisconnectCause disconnectCause, final int delay) { + if (this.delayedDestructionInitiated.compareAndSet(false, true)) { + JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule( + () -> { + this.setDisconnected(disconnectCause); + this.destroy(); + }, + delay, + TimeUnit.MILLISECONDS); + } else { + Log.w(Config.LOGTAG, "CallIntegration destruction has already been scheduled!"); + } } private void destroyWith(final DisconnectCause disconnectCause) { - if (this.getState() == STATE_DISCONNECTED) { + if (this.getState() == STATE_DISCONNECTED || this.delayedDestructionInitiated.get()) { Log.d(Config.LOGTAG, "CallIntegration has already been destroyed"); return; } this.setDisconnected(disconnectCause); this.destroy(); + Log.d(Config.LOGTAG, "destroyed!"); } public static Uri address(final Jid contact) { @@ -349,7 +376,7 @@ public class CallIntegration extends Connection { } public void verifyDisconnected() { - if (this.getState() == STATE_DISCONNECTED) { + if (this.getState() == STATE_DISCONNECTED || this.delayedDestructionInitiated.get()) { return; } throw new AssertionError("CallIntegration has not been disconnected"); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 6eea73d395746ab005b6dc46d81ef9de5f475adf..da281617e95e3442d6d019502116c29ec95f4c49 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1633,7 +1633,7 @@ public class ConversationFragment extends XmppFragment activity.xmppConnectionService.updateAccount(account); } final Contact contact = conversation.getContact(); - if (Config.USE_JINGLE_DIRECT_INIT && RtpCapability.jmiSupport(contact)) { + if (Config.USE_JINGLE_MESSAGE_INIT && RtpCapability.jmiSupport(contact)) { triggerRtpSession(contact.getAccount(), contact.getJid().asBareJid(), action); } else { final RtpCapability.Capability capability; diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index fd5c4c9af7220206de6ac72c409a597d0efc83c9..e90a35a0c7fc01d9cd47ba28b10c086d9af98563 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -54,9 +54,8 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class JingleConnectionManager extends AbstractConnectionManager { - static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = + public static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor(); - final ToneManager toneManager; private final HashMap rtpSessionProposals = new HashMap<>(); private final ConcurrentHashMap @@ -67,7 +66,6 @@ public class JingleConnectionManager extends AbstractConnectionManager { public JingleConnectionManager(XmppConnectionService service) { super(service); - this.toneManager = new ToneManager(service); } static String nextRandomId() { @@ -490,7 +488,6 @@ public class JingleConnectionManager extends AbstractConnectionManager { proposal.callIntegration.busy(); writeLogMissedOutgoing( account, proposal.with, proposal.sessionId, serverMsgId, timestamp); - toneManager.transition(RtpEndUserState.DECLINED_OR_BUSY, proposal.media); mXmppConnectionService.notifyJingleRtpConnectionUpdate( account, proposal.with, @@ -667,7 +664,6 @@ public class JingleConnectionManager extends AbstractConnectionManager { private void retractSessionProposal(final RtpSessionProposal rtpSessionProposal) { final Account account = rtpSessionProposal.account; - toneManager.transition(RtpEndUserState.ENDED, rtpSessionProposal.media); Log.d( Config.LOGTAG, account.getJid().asBareJid() @@ -713,7 +709,6 @@ public class JingleConnectionManager extends AbstractConnectionManager { if (preexistingState != null && preexistingState != DeviceDiscoveryState.FAILED) { final RtpEndUserState endUserState = preexistingState.toEndUserState(); - toneManager.transition(endUserState, media); mXmppConnectionService.notifyJingleRtpConnectionUpdate( account, with, proposal.sessionId, endUserState); return proposal; diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 2cbd8c739f947b47b821e9b4486d58fef98a3c87..0368f1a5180504319055c7d2b3972d903b7b16d8 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -2727,7 +2727,6 @@ public class JingleRtpConnection extends AbstractJingleConnection private void updateEndUserState() { final RtpEndUserState endUserState = getEndUserState(); - jingleConnectionManager.toneManager.transition(isInitiator(), endUserState, getMedia()); this.updateCallIntegrationState(); xmppConnectionService.notifyJingleRtpConnectionUpdate( id.account, id.with, id.sessionId, endUserState); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java deleted file mode 100644 index fb82b7219254589c30a5c3c4434479e83032939b..0000000000000000000000000000000000000000 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java +++ /dev/null @@ -1,238 +0,0 @@ -package eu.siacs.conversations.xmpp.jingle; - -import android.content.Context; -import android.media.AudioManager; -import android.media.ToneGenerator; -import android.os.Build; -import android.util.Log; - -import java.util.Arrays; -import java.util.Set; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import eu.siacs.conversations.Config; - -import static java.util.Arrays.asList; - -import androidx.core.content.ContextCompat; - -class ToneManager { - - private ToneGenerator toneGenerator; - private final Context context; - - private ToneState state = null; - private RtpEndUserState endUserState = null; - private ScheduledFuture currentTone; - private ScheduledFuture currentResetFuture; - private boolean appRtcAudioManagerHasControl = false; - - ToneManager(final Context context) { - this.context = context; - } - - private static ToneState of(final boolean isInitiator, final RtpEndUserState state, final Set media) { - if (isInitiator) { - if (asList(RtpEndUserState.FINDING_DEVICE, RtpEndUserState.RINGING, RtpEndUserState.CONNECTING).contains(state)) { - return ToneState.RINGING; - } - if (state == RtpEndUserState.DECLINED_OR_BUSY) { - return ToneState.BUSY; - } - } - if (state == RtpEndUserState.ENDING_CALL) { - if (media.contains(Media.VIDEO)) { - return ToneState.NULL; - } else { - return ToneState.ENDING_CALL; - } - } - if (Arrays.asList( - RtpEndUserState.CONNECTED, - RtpEndUserState.RECONNECTING, - RtpEndUserState.INCOMING_CONTENT_ADD) - .contains(state)) { - if (media.contains(Media.VIDEO)) { - return ToneState.NULL; - } else { - return ToneState.CONNECTED; - } - } - return ToneState.NULL; - } - - void transition(final RtpEndUserState state, final Set media) { - transition(state, of(true, state, media), media); - } - - void transition(final boolean isInitiator, final RtpEndUserState state, final Set media) { - transition(state, of(isInitiator, state, media), media); - } - - private synchronized void transition(final RtpEndUserState endUserState, final ToneState state, final Set media) { - final RtpEndUserState normalizeEndUserState = normalize(endUserState); - if (this.endUserState == normalizeEndUserState) { - return; - } - this.endUserState = normalizeEndUserState; - if (this.state == state) { - return; - } - if (state == ToneState.NULL && this.state == ToneState.ENDING_CALL) { - return; - } - cancelCurrentTone(); - Log.d(Config.LOGTAG, getClass().getName() + ".transition(" + state + ")"); - if (state != ToneState.NULL) { - configureAudioManagerForCall(media); - } - switch (state) { - case RINGING: - // ringing can be removed as this is now handled by 'CallIntegration' - //scheduleWaitingTone(); - break; - case CONNECTED: - scheduleConnected(); - break; - case BUSY: - scheduleBusy(); - break; - case ENDING_CALL: - scheduleEnding(); - break; - case NULL: - if (noResetScheduled()) { - resetAudioManager(); - } - break; - default: - throw new IllegalStateException("Unable to handle transition to "+state); - } - this.state = state; - } - - private static RtpEndUserState normalize(final RtpEndUserState endUserState) { - if (Arrays.asList( - RtpEndUserState.CONNECTED, - RtpEndUserState.RECONNECTING, - RtpEndUserState.INCOMING_CONTENT_ADD) - .contains(endUserState)) { - return RtpEndUserState.CONNECTED; - } else { - return endUserState; - } - } - - void setAppRtcAudioManagerHasControl(final boolean appRtcAudioManagerHasControl) { - this.appRtcAudioManagerHasControl = appRtcAudioManagerHasControl; - } - - private void scheduleConnected() { - this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> { - startTone(ToneGenerator.TONE_PROP_PROMPT, 200); - }, 0, TimeUnit.SECONDS); - } - - private void scheduleEnding() { - this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> { - startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375); - }, 0, TimeUnit.SECONDS); - this.currentResetFuture = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(this::resetAudioManager, 375, TimeUnit.MILLISECONDS); - } - - private void scheduleBusy() { - this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> { - startTone(ToneGenerator.TONE_CDMA_NETWORK_BUSY, 2500); - }, 0, TimeUnit.SECONDS); - this.currentResetFuture = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(this::resetAudioManager, 2500, TimeUnit.MILLISECONDS); - } - - private void scheduleWaitingTone() { - this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate(() -> { - startTone(ToneGenerator.TONE_CDMA_DIAL_TONE_LITE, 750); - }, 0, 3, TimeUnit.SECONDS); - } - - private boolean noResetScheduled() { - return this.currentResetFuture == null || this.currentResetFuture.isDone(); - } - - private void cancelCurrentTone() { - if (currentTone != null) { - currentTone.cancel(true); - } - stopTone(toneGenerator); - } - - private static void stopTone(final ToneGenerator toneGenerator) { - if (toneGenerator == null) { - return; - } - try { - toneGenerator.stopTone(); - } catch (final RuntimeException e) { - Log.w(Config.LOGTAG,"tone has already stopped"); - } - } - - private void startTone(final int toneType, final int durationMs) { - if (this.toneGenerator != null) { - this.toneGenerator.release();; - - } - final AudioManager audioManager = ContextCompat.getSystemService(context, AudioManager.class); - final boolean ringerModeNormal = audioManager == null || audioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL; - this.toneGenerator = getToneGenerator(ringerModeNormal); - if (toneGenerator != null) { - this.toneGenerator.startTone(toneType, durationMs); - } - } - - private static ToneGenerator getToneGenerator(final boolean ringerModeNormal) { - try { - // when silent and on Android 12+ use STREAM_MUSIC - if (ringerModeNormal || Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { - return new ToneGenerator(AudioManager.STREAM_VOICE_CALL,60); - } else { - return new ToneGenerator(AudioManager.STREAM_MUSIC,100); - } - } catch (final Exception e) { - Log.d(Config.LOGTAG,"could not create tone generator",e); - return null; - } - } - - private void configureAudioManagerForCall(final Set media) { - if (appRtcAudioManagerHasControl) { - Log.d(Config.LOGTAG, ToneManager.class.getName() + ": do not configure audio manager because RTC has control"); - return; - } - final AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - if (audioManager == null) { - return; - } - final boolean isSpeakerPhone = media.contains(Media.VIDEO); - Log.d(Config.LOGTAG, ToneManager.class.getName() + ": putting AudioManager into communication mode. speaker=" + isSpeakerPhone); - audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); - audioManager.setSpeakerphoneOn(isSpeakerPhone); - } - - private void resetAudioManager() { - if (appRtcAudioManagerHasControl) { - Log.d(Config.LOGTAG, ToneManager.class.getName() + ": do not reset audio manager because RTC has control"); - return; - } - final AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - if (audioManager == null) { - return; - } - Log.d(Config.LOGTAG, ToneManager.class.getName() + ": putting AudioManager back into normal mode"); - audioManager.setMode(AudioManager.MODE_NORMAL); - audioManager.setSpeakerphoneOn(false); - } - - private enum ToneState { - NULL, RINGING, CONNECTED, BUSY, ENDING_CALL - } -} diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java index 128a35bf03114d25bcc7ac8a86efe2ab61d9d0ee..8bc7c6f6f0c7c7265a7451d16f0081b77704eeeb 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java @@ -2,8 +2,6 @@ package eu.siacs.conversations.xmpp.jingle; import android.content.Context; import android.os.Build; -import android.os.Handler; -import android.os.Looper; import android.util.Log; import com.google.common.base.Optional; @@ -15,8 +13,6 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; import eu.siacs.conversations.Config; -import eu.siacs.conversations.services.AppRTCAudioManager; -import eu.siacs.conversations.services.CallIntegration; import eu.siacs.conversations.services.XmppConnectionService; import org.webrtc.AudioSource; @@ -52,7 +48,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nonnull; import javax.annotation.Nullable; -@SuppressWarnings("UnstableApiUsage") public class WebRTCWrapper { private static final String EXTENDED_LOGGING_TAG = WebRTCWrapper.class.getSimpleName(); @@ -205,7 +200,6 @@ public class WebRTCWrapper { }; @Nullable private PeerConnectionFactory peerConnectionFactory = null; @Nullable private PeerConnection peerConnection = null; - private ToneManager toneManager = null; private Context context = null; private EglBase eglBase = null; private VideoSourceWrapper videoSourceWrapper; @@ -222,8 +216,7 @@ public class WebRTCWrapper { } } - public void setup(final XmppConnectionService service) - throws InitializationException { + public void setup(final XmppConnectionService service) throws InitializationException { try { PeerConnectionFactory.initialize( PeerConnectionFactory.InitializationOptions.builder(service) @@ -238,7 +231,6 @@ public class WebRTCWrapper { throw new InitializationException("Unable to create EGL base", e); } this.context = service; - this.toneManager = service.getJingleConnectionManager().toneManager; } synchronized void initializePeerConnection( From 7f9d836f1a2df2af4346675d32028a0760111ee2 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 17 Jan 2024 13:53:12 +0100 Subject: [PATCH 015/363] play connected sound from sound file --- .../services/CallIntegration.java | 20 +++++++++++++++--- src/main/res/raw/connected.ogg | Bin 0 -> 10581 bytes 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 src/main/res/raw/connected.ogg diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index 548a1507d28551873dd0c1dffc2bdda64a7fc889..489d71256422c50df4933b53c9ec41e380b2cda9 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -20,6 +20,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; import eu.siacs.conversations.ui.util.MainThreadExecutor; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; @@ -33,6 +34,10 @@ import java.util.concurrent.atomic.AtomicBoolean; public class CallIntegration extends Connection { + private static final int DEFAULT_VOLUME = 80; + + private final Context context; + private final AppRTCAudioManager appRTCAudioManager; private AudioDevice initialAudioDevice = null; private final AtomicBoolean initialAudioDeviceConfigured = new AtomicBoolean(false); @@ -43,6 +48,7 @@ public class CallIntegration extends Connection { private Callback callback = null; public CallIntegration(final Context context) { + this.context = context.getApplicationContext(); if (selfManaged()) { setConnectionProperties(Connection.PROPERTY_SELF_MANAGED); this.appRTCAudioManager = null; @@ -297,7 +303,9 @@ public class CallIntegration extends Connection { @Override public void onStateChanged(final int state) { Log.d(Config.LOGTAG, "onStateChanged(" + state + ")"); - if (state == STATE_DISCONNECTED) { + if (state == STATE_ACTIVE) { + playConnectedSound(); + } else if (state == STATE_DISCONNECTED) { final var audioManager = this.appRTCAudioManager; if (audioManager != null) { audioManager.executeOnMain(audioManager::stop); @@ -305,9 +313,15 @@ public class CallIntegration extends Connection { } } + private void playConnectedSound() { + final var mediaPlayer = MediaPlayer.create(context, R.raw.connected); + mediaPlayer.setVolume(DEFAULT_VOLUME / 100f, DEFAULT_VOLUME / 100f); + mediaPlayer.start(); + } + public void success() { Log.d(Config.LOGTAG, "CallIntegration.success()"); - final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 100); + final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, DEFAULT_VOLUME); toneGenerator.startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375); this.destroyWithDelay(new DisconnectCause(DisconnectCause.LOCAL, null), 375); } @@ -323,7 +337,7 @@ public class CallIntegration extends Connection { public void error() { Log.d(Config.LOGTAG, "CallIntegration.error()"); - final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 80); + final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, DEFAULT_VOLUME); toneGenerator.startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375); this.destroyWithDelay(new DisconnectCause(DisconnectCause.ERROR, null), 375); this.destroyWith(new DisconnectCause(DisconnectCause.ERROR, null)); diff --git a/src/main/res/raw/connected.ogg b/src/main/res/raw/connected.ogg new file mode 100644 index 0000000000000000000000000000000000000000..29e9fabba379b3d86fcd7205318e8c6039ef60b2 GIT binary patch literal 10581 zcmb_>c|4SF_y0|{Y)O)2%@WF*eF=l?VeDgzvBX$Hwyb4I42dyzg&9joXtHG~8EbZf ztRcIkEM@)Pqv!KH&-eGo@AdoRcfanLIoEa0b*^*X=UnHy?t2VeT#Nxy;9pmxP^<@$ z9H(Y3AmJeK_wsRY^(A(YwAc`T0N{xo$?0zii2-rszc1oQBB!UYiT9lP$$ww7Xa4Gu z1lbm@_wR`q__%PnyE>SiYUk4Ck`R*+6O$2>*!&t1T)7AudJYG;9K);Y#O zIv@2XnF7QC7&dc5A8p~KY~KPF^B(VIE|B`h<=>3sx=a*voa z2NIoFf?LnyU#>< zI?fRQsO}2H704zoSR`XDb19=@%1D6-03ZY9Xf-RD^tX8Xek%6;`akc#({cd-@L0f+ z<;*`JpyFn|td;|1w5Dc;u$@<9hxDqIU?XP*qxs;2#$~Eh+##Dvd(*+2e?!0k0QO9> zv5(q$skoyYn69aN7jl8rH(rabpk-dFsYnJ$ZFX>X9XR3?3ed+Et|$fwCm z!NPQhIKg%{-yZRHu1H#m<(DaUiQQ=M@&6NcRlQOJ!VFnW_Fz>Mx1QWPE$A!8-k6^sNes-PhF2710oU^K-9roD-okb zeHxr35`Y1SD3Fbt*8<~_$Y+_%?}~N_LKg*P7C+@U6#)YSq6S_O`KMvywL}-X<`xly z{pwX*VacmkZkdIy|M#0TkGm8A&H?~?yEmJrH(R?m(W_I0xy}Gw0H9{Xrr~p=Jy}v0 z^l?bNZ`;~P+RxbBU1QD$GHh&jj04nu&91dGK70z1p4GDNr5Pa*(me zmP!xNVUI%T=m?`xu%7IcK~+s`WD-=H?_Dw!$(K5&qa%(+Ik*eszc`dhfg}Dhgx|4R zA*D2ts-P-xax8I<5*%n1s7juaFBRo*gAbPuGC)<7+}LEOJ0EroB3S;HG3L;N!;QHK zl(Z4&z_M5&aqq_5)-i9Hp-(|`9pWnq1Xy_WMGuaiUw0Ki8U zEK6iR7X}4qV2wgK&_l3H@Luf_lmiGu1{_QtK6tMj6<;)ijy4|_j4mJ61g!6m&HySA zbeW9tXP8*47y+uY02c!^PJD?Al?>H657CpO;EsYq-GsqVFS11$>gbf4>WPCCsHFs_ zBaY7o%a5QS4h@!LVxJ(EAhGzj4?7H_1WS+(I1vH1MyyDJSj;dCaoUH)pkVRhfBTZ5 z2yrx&I2Hv(2;-qTf<#KUye%Ch!LeW;i!n%nV=LQO;2;D9a4en$q5lsBF9(Y|n=Dw6 zSyZoGHf6RiID3gB;xuKAK%~TD#Xqt5B&v42RPPl(n-SF^r-@BT);^n;N{|rDOHGC? zfO%%rRg6MITr)6(uzPs`@a7C9_@T+`Pf7(;H~`>L5)=vg2ttF;_N5oZvVdR~vA}!4 zy~r3Tz6fqeL zS6YV{s0PFu1+FP75GsK-mQzT5=}((5Jj(+{BI@)I0V0q{_Ndb)ivlPNA}8)oj0u+D zaN`taaRQ7eOaPY&h#Z)+FoaGCjs;$ZBai|u7ubLZYIh6vub6u`-kkbK0kkjxpjp5f zdT^uUM?E^NJ|#r>h>k}=S+S>aEs@lb1LF#Xc4em+f}sLK53V=R?D9x3I|OmeaLPOs z3d|ThJw%AuOkh7vAUum2D9j8;;$n#T1cp!el#5%05u2=t&tOtVQ4mqTVx}o8$VziV z(vc5Hq6Ad9K7o6Y+!K@o@gSfECS5uaZI-`igT{fUhQB8SBIDoZg2aD^;XeWC1?#!c zeXtU~F>>^M{QTvqk6-`#*bc%AbxVH?qRofthZLj}Lj^)9v5di(fO+3WbQA-Hf)rSz zi0Oy~AwNw~2PBT@yl|TZy!_PLavTeSju-}aaY81zw!th$RuVFG!Lgu7-J&(<@;eJe zxfEC?z|dqOdK-%f4nz^(?~MSELP-e}lU;GK(3Zc>MkjKL!xbQc>l5f7arY%|?x&0N zltMvuFyK=EhYRA5we1TX)Kp(B8eVw68+oIPb=AAr^1?^*=qR!&b0e;KGj zqAD=9r%gWM9RFfKb^bE~GLBjwvGp2Em072h&@y15*v2zv$o$ z5Xzh=Cbwd~!WeNX?gzxQdS~HFk%s{*q%hth^0N|{YG5S=h2<{IgH;_n>`;nZVncpE zE!o6;``&o3UmINeFyHS=ly-_>c>vQ-iINj3mXAU^lvNVgLeAf2G1Ac$#%Bh9Ze)Q+ z=?LOs!M$}$*2wd>u*~q+b$c8kA|HRxfqTc^T;#c1RQAXu@x?LUAB~sb$a5b%tbIj3 zvdqVeef-VkUS3COhXnP(LpG&;zYG@tNX3H$zovW#G=76C_BM-M6bVpwo*sy$xy=Z5 zxopPjJ%5(vY7)5iNC0uLW`k!DOcDq-Slj>Cu`~O$k^(>r8F8n(K9*_;!x>EuYuj$C z&R&prxGAv!6>|2uy8N8gt&(xQpW5>W>1ingE(5><28DtNvqHL5U8Q0>xW4hW@m_@| zJsx~qlq4Rj-Ng>4<|Z%k?-!3|Dqw|DXF#dlUdhEIiWiGt`wBgar2sRB0pQ}|1zG^r z543=otO|=9!!i}u{d;EsTJ;XSyM|PB^b#bqBv&CqfR>&iO5}km)p<-jS3IRicJ0yLA!JZkXBg4X#nyy}j;rvfU#EO?ycCb^oQkx}}l zwxy$gYE8#)2*J}XfcOAO^jV->0n5Utg3SH;11bPtBB0Z_bn+g5*&c_zCtRWMyTU+cq}VjoaEy!|a-V zb_MtYDuqjvJ4aKEZjidh%-E|pf3EIsRa))l^==+XyfuF)e>E(1)ctCKxZhfU&e_se zrtS|G51f=s+uQMR2VTchWwz`JY!}8WpHH-RN4>qFnR9=py*3moPbXaU*+DUb4os1p zVCv4wO+T&m&rc+He;>Ax@<}&b2+VsU zz-IM$avn;ONZv?6&7Kj;L4D#dgmsvPwv(1@vVc5DQq|hU`_T z9~$`XU-TZnzgqXNRcYrP=vB*n72kHimX?*&@UAu-4eV9r=B$(pRA?LscXopBB{s*^ z$$)J%q4ERz&f4KOjuMiB(_Ux%nV?L$ekQ z-IsG$Hs%wC$3$daam+XRfN`x#t9$dSY4T-fM&UyKNd1v@bP4JB$mIiZKx~$EnaeG0 zcGKx;x<&~!2>NS#Y*#a^!}c0;DpLr-AwIzVu|lPCS;xhyC;^6U!CC8io{#`M&j<0$ z2i&~cQw2;SZi+`y>I`*jHlHe8@Uy%uQF=O_(&Lx!CMk&RsbOoaJMdZK;WGVSAIs|a z3`bqI(3=lOjCiWU&}AR`VXD`?%XgV(|rI2M6a@+<6mxOQk)8YUreU`||lOtN5?+qlp}9uF~^AA~W_1LK@Rq%@^Q-SFgJ0 z!1;kK=L%Zbm+Q0Bx-{tB-j{E^>kS4F6_qkYJ1Ey%=UOFLqh0V;A`}JsKhCiB(hFoPE(`zsen{zh zTCt5}hS4}Ome~7+@)DU>Bj5Dpi|Nf)7=kybg zI@fp{yCP+fSNoXegh*44h{qj~7Ca@9W zHGRus#t3G7&|Y0Mu{)-)AdT!sG&>v*TJ9c>{PI2gaZxJd$r11P@IE7V#t56`;ft-; z1-tWM%OZvqD4ASCJN;UG^-|WYua6-iE9HY3HJzM7pfNywQC+RSW`J}#?}3|VXS^ps z(PBu?HBf(rRIAJ|;q`JsEszl0GKQsj=`v+@vC)IDJ%YJaGd=xLV`e3N+RD4(70+f> z6gy{_pY&UGoWZh~RFuKVkz-h0tUSJn7bI-DubP^__bV7{E~S3mcAosmB*mm+in zr8zej)?}CGe)7!3(hE8ze;{1R^CGTazA(lI(#t>ZsJpqt(aM+hI3hyb`r+BANP*y) zG{YzUg32^d3!^51T2#MCM0O0xX*wWoPIsOuqv~tf&$%O1>;@7ItdAsc5l;I)| zvRQber5gxnn8jD^N1V-2BDlxQprv@ES>Ty0-I=331DQPMLMtmFlP8Gtn&e5JjsQR~ z>bjv`{i2dWYjc-{QsCms`{sA$j*V4>g`CCS>hBhvCTdh#MS_X!FM@Qd=A-62hh`}j zF}u=aUHLM%%Jl%T(4LqyEfUh}ZTu(^vOS7)L;7=1Qm)TS0oE@feikpjR|_4=`pT5v z!+&k2cnJ1P*;blk?k!0l-q{*7ufX;upsiqwul1G$tS#OqJ{S*Ka;oEqUauk_=d4;B zmscW(OjDDA?~++xE*yIUKx>4*$%FN|ILUXYSD2Z)7@W&M<-$Xg+G!6v&zNosEvL+H zd|D-{tO>KnDrbLcflsdyQ#|oTWQfCWrKy)GyaFYi63}l%KYIm`REnykNZJhs|9)rj zl;;WuI(({s@}7k~tD`w^?aduI(-TJbQW3Z+zk|{j65GYVSpvS|X~B?ux9Icq`UV?m zo*Skz2F>IbY%h^$PjVUn%O5hMlD*~ufFfdD>1$lsn%gJN)~(volg7!S$sF$q1E&pZ zZ)2U7F`ekqI|5bAg}uQSah{euI>w(E8qL9@9Z!SxNu1@wU$~W4j_Yhbx6w<^oZ+^& zSl1RUg4gV}sPiS&xrFtQFpmYrW6k@mCWS!l^H88EHSh=2;gf9(q3ILTYnLVL`mhc4 zQe6$|HDU7=$y2mbXV*TzC@-p^Gb`T4 zL_dc#V3-JdgV^1Xine^LD(=4hVe-nIOw1o@K*ZzSmSbJrVf*pErs+}RZ=c4)8@l*u z3YpQ=Df#j3z{-`G_*5M{vD>GvnBIxjWt z?ziW5oFeuUgGRDx`I`^RrG`~fR9uD0Rm!SAn>MiYS#Er4-D@?+E;VWHg}UYlOchnZ z?ZTKCA=CpR^mkZoR7Zu(XpJz}Is8hMc#Vz{t+U;PHfOwVmIAZ*qwhjS?Yi3NmQkgi zYI}|aD;dg2XU#Z;oF6~0-up6X7{N2;&Zp8x`(dlNHPEdwDpd%*E)o`+@(!NqpZ;8i z37Af;U3qnvI`hsxw7LHvMOpR@g%7<8_luN1#+MtU+jq5Bs3--LG#pC=w}Nado5qLo zZ{=n(U8*lpG%(qCY=&85h*T}p$Sh8@^1n&ouqm;NuhqTq_<>n&dF7lZa_^nK)6C5f z>=d(|m1`g6kY{!4eX)v>n7Avdl44EnD2w7hFF9wF^M{(MS4XeCxomYT{HMG|h?iSB z2};^+Lx9oT-y{bZtWphu-`;;7%@{BP)6unm2rY7%+KW|Br-B?3x*v6H@31Ng|Fk4E zIfw30F%luCZPh&gVufch_dr*Am_pd8aSNMk9)584tF?a*t3?z4l^WO}PXIG>o4W(1 zt<2|euwrSqsEy02o6c`-^36x(nVjtkA*i&)*c{TWKp7|9wLNBlK!#09DLEZ~-jk>& zNZcun%pJV3N3KF|g&V^5Q(rKT;?2=f9X-tQexk-vrb61X9aEtI0E+ZLGx(GmCRO`K z-7k$Sso_S&ttPt|BXnD`RKfUFgKlP%hjmP`M%gUL=36W_LsmZ?{-!KRsj)w)({#kD zJseqbmcp;Rr;gN1>ngGp-A!@7nIiwrb45obRYafvf)btgG1BK=++FXu zdu-vjxp&7~`3B`y>_LO@M9H})N4w_nk7$L|0I@UI^tdMSi3(Sq zp_9H~H~QoS6A-cginPU?6)aOuwSjHf@5x%QH@2UD^gV0iWPy&}qp{aP0s6qVdO!e@ zJv=YEivN5ebs%8q{+s**qj$;8Ggp?(9Yplh zPY4Zkk;Hx7z-l8v?ah8&fDJVGOdvDay>0OM9tiWRUFmT)UgkQ0z7Lza)z@ixGu7s} zvV~uc`JMt0z+QOiEbx8#=JC&X)~STO#WAZOmTWIG%uF1Mcvlu3x1MJzYk}%KTZ-7) zpI--_L*`6W$M=QADyD8U(wkAQkd0XR@-XH2nHzM#5#EgWYSXjxGsdu1=wV6s{LJ_l z#7@PyF+ywXRP;5|zR2@MI9ex?M>3R5QVH7=Mj17S|J-pSR7d;JS(M|MYr zX3nNqKHhH{+BUfkq(Zu_BzL!MU_tFPl}8L7AM|B=_VGVAZgXH1bgGYZl5WNg%pFZL zOcm?NoD^)Pc2TIfllQzEwr5c0*lQ}Y6xkSe4offyIj|Al-Qsjo7`5F?>#I#q7{(j1 zuQ>!|n|>(FVK`SbQ9rNAeBAqHe8BofSge zPcCrDog_v3HKXXc7FMJI+EEN?OWjbKGTVbqW9Pz|OLN)@=+Kl#o-9vmeN-Z~^}44K z-Xtgfp)*za=$o+ni1HA#Z%DN_<4P8j<*p~9x(aRn#bL+`u2yqi76xW^OB8O0E0|V$ zjUf)s@6u~nv-P#N^z&PRn^~V{RDfLD^v(n+4kS+0Q?wu(`RlX%OS_6QiYF&e&jYu} z182#sb;YNU9+ySx#OSyYJJw|x$(u9x~&0Lk=tQR1mLY-8L}kX`}x*XH_5Yy zb_J#jW*ejkLeHFl>CTf$9jYk;CkuDf3!CGoXwRd z)RBn2lrIcs?%%Qp3%{Fmc22SEP{rT>NLeGo%WsUfF+lH7WZ6*&A2ir@%VOOpcXmX& z=yw}wxLknpS8K*x$7Hpx=aSQ?^JjjilQmZ|hLc<*{FL!;VqVjz5m=W>_ZJRHPDSCtx$&E5|#p9l8 zgxc19>voxmA2$~oJWhJM_LsiiiH$uwJrVtgW#d-e$h#1Mt`P?gunXJhUcWYtF_rm7UmK@+;A9M51hVJAmh~_9-x5J zcM4~~U#eKMH2TVSMTM_%Z~FQNM+V-v9y8Vc`6O7T|KO*Hc!2G#;&Aaydip*78YxS( zN`pum9z)`X%qU2+$$bHpxW3Pjbs=cLQmeAD>#cU@fl%yCK!*_@Y*1uA@T@)FvJbYq z)L^C{JJO3De(UjMsVjPfVQ=(!;r$`X^7_dnd@1XbXQSoiY=XA#>aFsoYnJglE7{iE zMgiQHSE;|$&3nXqk|{cgqdn+rJ)+bZRd}Tn_-7Pa$E=fdVH3d1+gk>}EHfC9Ag|~T zF%E3ot<5JEnEGB^*?48^2laL)(gP+@2MtDKJBsORKN(6&d$c+35M}n z|MSpul<5sm68B$AjviU%C!YrO}jWhUN@Zlt_IWE<+0Y-K`P1J?Q>Kw9Aq6Sn(N>dqSe51kA&6Iu1VuU)c{!`@n+{#dNQ7QQ6 zY9+R?uX32g1nn>QgHE#|2`|zo`)j)SYJd+DX1d5kO}@P2iD{*N5TBy(h7nC4r1X}O zDtmR_^VU)w`w%g6^)il=f<2$!E_CP4pLC~s$(RRHbaLOufsAwX|846=k*h<_Z98`SYQ90KmFDV&`Kq=c<3CD?6CMFX>;C0@Gx&Z|^DniDj!Z zII+D*qmr}Bs>nntNn%o{OEU8Q@V6GF*NH=s!Rw0cFKZcy8s?H7{h1NtB0Spt;$)Ra zkz*P4O-awX_@5}0y_I=#Zr%$w``FL9Wm{`i)1^-Sj8kUDucUM`s(SePFk~(TcdDVP z{LDLSb{pYop+ThmqOoC9M%^$`#N&tWy7}}O-ctag(N@6h%m*@^$0R-PLJsJD1u{kO zY8B}6BzOml0S(d;;uvMcI7*)P0XvHLcGg^E_S8gT<_ zGCaW|5F?S-;bJ6ykSiusMF?}1D`!FW8fO4(iC;e1@I6_COaml=H~NN`@zb$J>F2uQ1pVV zVZC)LNtgPUL&?_e?(c(~{!&MKCY2}mHcX4cvwQsw%5L7wGtPs}x_aCJj-JM(y zuAY`&Q{XY6*8B5t{@Ukz)t167{O=CI;A`pK17?I?U)sszx{_ioE!G>VtiI#afb`A< zrX?*dCm5n0lcm-D`u^J3O5<8Jl07i05BPG2>pD(1-L(|0BQ@z#cD*~JN$}5O3hFh2b6K{=Aju`3p-%VM&<0uhXvB&z7j4y&^#g%Mu z3ucVZN+G%r3kT*bh5a*7YxkV1cq|uhm0ybe=sGek2kY&t5A`exIB!t z$*Zq$KMscD{cv$wqtX`{joFqZIc5^cTcTh8Dj3kz&Y|Q~u2K?F4nO+DJ-l=x8Vo*2 z9ZP29_Q$)6*}vGapLDNmu8~EcIqO$LLsrUQL%A!0d}baEo%5c9ulKE^L?q&6c7q2B z^YI-$N#+v5zp5-BREKZG4;xhJc7z&VS{v58<;7P|(C0jN+iN}xcBJL9nQ~a_aAUk= zl3#RbE7?G_f)(7v^qG7eY5#=wi$`27>dvdTGYGSBBOKHmuJw5+vMh?RyxGtlRJ#z9 z=}}%p_X{_!$^RmBnRm1?Mo{Rd|pbd!pNk**QQZxc*MbLauAfwHFNd4T52~aH1P-67H_l zp)-E0A;Isl6&@4BF2lFBk#l{a5`C4X+2lrQnSq#|TW=4)S61W>!?fu1?crUxvcZ5N z<-plL?%{?@*3S6bE;}E;ytSKO@Swt7Xtz-iWg4olPc~pnd0)fwB8=^wOiF2uQp3Ia zvXM&1x6>w5xo%|}&h8LlXw#M0uW#7fGvq!r+f2!*{-P_3-#H+tWffjqk2%sj(Wpw0 zz4si9(Ds;oSNC9yFTeHvA6T$Q-dwuSz^1Un3VGFgMQoI}2twIE5X#iBq3`D&wg)X& zV7Z#I!@4H8uxR9ouBaP_VT=!UT>|+%y6+*C6g|7IW*3LHehS>SvR9V3Q|R5O^)|fb zY+`<3^+yF3(#bp;Yh^7T?|6((ef}vhc(+quO^zE?BKPJOSxJ)qdzns?JzX2RgfGE! zJcR>SHuJpR&AdpvHOP5#usyGvo9@5rIm%DZF16xgj{DrSIU!L|>jaxaAr32#rNSpX zWbDJtWtYI$YPbB-*TuZ*u4-BDcduP@LpxcbozNPtKNK+(%X)pu54B^{Tm8>mhzW{T zIv(w*uo^wU(sP?X|DKHOA71c{yUiDxt=P%yN)KyeNEOdIe$+ADHzt|8JVJZ1H>Bsq zuR!6aQWGAR8y_|nZTClsSSN@u+fLnyacM~S#^TtZ=<+@I$AQM;y+HpfA1cOYoa5yO zooSXH%u7!kTXb`7+kGpy6aKQGVDM=+)blycQcw-&EMt-DCcF0RsrRqEL9gi@B8Fv<;S}8H#7;i&&L_!r``9cH8ul<;%{-@ z&UMqetG{hv_Bp>tsIQPzZqhhyGR>=zaXyQ;Y4X#pzVPo~KXT3#t7cwFljg%+)GzZb zI-kB#nwas!b1B2Z*4*Bd264p!F=u!7uoC$g71%ZnoP%?J&C}7YAYCNBc%3G*P^v#D zKYZ22?7L4Z($pwsu7cP@G8k0k=cxKS%F}im46Wk{u{D~eMRyD`7AxQB80pq(qT5@C zIz!x+MR*UM(_pp=Y<9epM{pYpy2w4d@{)@Ep+AbIij(($WEbTOuQttnp*%o~mP!)Z zwGX%8-#o*^ofOMbfB)>RpU^-YCqZ(2UJdXPR$Rlvf9{Ozbn*mT#Am=l_l2>9XCM8t ltuz#cnlOB}KZk Date: Wed, 17 Jan 2024 19:02:30 +0100 Subject: [PATCH 016/363] fix regression with screen not being put in right state --- .../siacs/conversations/ui/RtpSessionActivity.java | 13 +++++++++---- .../xmpp/jingle/JingleRtpConnection.java | 6 ++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 2e4576f57181e544ad30a8038a0e0827800d768b..c40a49a7239640746fb452cea60d3e1209c7a795 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -532,6 +532,8 @@ public class RtpSessionActivity extends XmppActivity setWith(account.getRoster().getContact(with), null); } else if (Intent.ACTION_VIEW.equals(action)) { final String extraLastState = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE); + final String lastAction = intent.getStringExtra(EXTRA_LAST_ACTION); + final Set media = actionToMedia(lastAction); final RtpEndUserState state = extraLastState == null ? null : RtpEndUserState.valueOf(extraLastState); if (state != null) { @@ -548,10 +550,13 @@ public class RtpSessionActivity extends XmppActivity .fireJingleRtpConnectionStateUpdates()) { return; } - if (END_CARD.contains(state) - || xmppConnectionService - .getJingleConnectionManager() - .hasMatchingProposal(account, with)) { + if (END_CARD.contains(state)) { + return; + } + if (xmppConnectionService + .getJingleConnectionManager() + .hasMatchingProposal(account, with)) { + putScreenInCallMode(media); return; } Log.d(Config.LOGTAG, "restored state (" + state + ") was not an end card. finishing"); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 0368f1a5180504319055c7d2b3972d903b7b16d8..edb58656585a6dff33bae0523fc79e5cbd4b18d1 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -34,7 +34,6 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.RtpSessionStatus; -import eu.siacs.conversations.services.AppRTCAudioManager; import eu.siacs.conversations.services.CallIntegration; import eu.siacs.conversations.ui.RtpSessionActivity; import eu.siacs.conversations.xml.Element; @@ -73,7 +72,10 @@ public class JingleRtpConnection extends AbstractJingleConnection public static final List STATES_SHOWING_ONGOING_CALL = Arrays.asList( - State.PROCEED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED); + State.PROPOSED, + State.PROCEED, + State.SESSION_INITIALIZED_PRE_APPROVED, + State.SESSION_ACCEPTED); private static final long BUSY_TIME_OUT = 30; private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this); From 1090b2edd33b969cdae715f56c71aeace635dbf4 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 18 Jan 2024 13:09:04 +0100 Subject: [PATCH 017/363] add optional strict offline checking for calls --- .../java/eu/siacs/conversations/Config.java | 2 ++ .../conversations/entities/Presences.java | 6 ++++ .../services/CallIntegration.java | 1 + .../CallIntegrationConnectionService.java | 28 ++++++++++----- .../conversations/ui/RtpSessionActivity.java | 5 ++- .../xmpp/jingle/RtpEndUserState.java | 35 ++++++++++--------- src/main/res/values/strings.xml | 1 + 7 files changed, 52 insertions(+), 26 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index febe12b1488da86f7f150d96634127f16c5daa4b..da3628b6fcb4953a0dfdefd77ecfcb21d72662df 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -121,6 +121,8 @@ public final class Config { false; // disables STUN/TURN and Proxy65 look up (useful to debug IBB fallback) public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true; public static final boolean USE_JINGLE_MESSAGE_INIT = true; + + public static final boolean JINGLE_MESSAGE_INIT_STRICT_OFFLINE_CHECK = false; public static final boolean DISABLE_HTTP_UPLOAD = false; public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts public static final boolean BACKGROUND_STANZA_LOGGING = diff --git a/src/main/java/eu/siacs/conversations/entities/Presences.java b/src/main/java/eu/siacs/conversations/entities/Presences.java index 59480b0cee53c0a29e2baafbc940b6d0f6efe375..d3bd706f87e42e3263c7693c77a063863468b1b5 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presences.java +++ b/src/main/java/eu/siacs/conversations/entities/Presences.java @@ -83,6 +83,12 @@ public class Presences { } } + public boolean isEmpty() { + synchronized (this.presences) { + return this.presences.isEmpty(); + } + } + public String[] toResourceArray() { synchronized (this.presences) { final String[] presencesArray = new String[presences.size()]; diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index 489d71256422c50df4933b53c9ec41e380b2cda9..f07ebe28da991a58493c59a4bdddbe521219b507 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -303,6 +303,7 @@ public class CallIntegration extends Connection { @Override public void onStateChanged(final int state) { Log.d(Config.LOGTAG, "onStateChanged(" + state + ")"); + // TODO devices before selfManaged() will likely have to play their own ringback sound if (state == STATE_ACTIVE) { playConnectedSound(); } else if (state == STATE_DISCONNECTED) { diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index 99f565ff197bfe763ede7853042fd4bb53f45e2c..de39abb518aec58b24317003d01b2af276b2e61f 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -97,15 +97,26 @@ public class CallIntegrationConnectionService extends ConnectionService { intent.putExtra(RtpSessionActivity.EXTRA_WITH, with.toEscapedString()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - final CallIntegration callIntegration; + final Connection callIntegration; if (with.isBareJid()) { - final var proposal = - service.getJingleConnectionManager() - .proposeJingleRtpSession(account, with, media); - - intent.putExtra( - RtpSessionActivity.EXTRA_LAST_REPORTED_STATE, - RtpEndUserState.FINDING_DEVICE.toString()); + final var contact = account.getRoster().getContact(with); + if (Config.JINGLE_MESSAGE_INIT_STRICT_OFFLINE_CHECK + && contact.getPresences().isEmpty()) { + intent.putExtra( + RtpSessionActivity.EXTRA_LAST_REPORTED_STATE, + RtpEndUserState.CONTACT_OFFLINE.toString()); + callIntegration = + Connection.createFailedConnection( + new DisconnectCause(DisconnectCause.ERROR, "contact is offline")); + } else { + final var proposal = + service.getJingleConnectionManager() + .proposeJingleRtpSession(account, with, media); + intent.putExtra( + RtpSessionActivity.EXTRA_LAST_REPORTED_STATE, + RtpEndUserState.FINDING_DEVICE.toString()); + callIntegration = proposal.getCallIntegration(); + } if (Media.audioOnly(media)) { intent.putExtra( RtpSessionActivity.EXTRA_LAST_ACTION, @@ -115,7 +126,6 @@ public class CallIntegrationConnectionService extends ConnectionService { RtpSessionActivity.EXTRA_LAST_ACTION, RtpSessionActivity.ACTION_MAKE_VIDEO_CALL); } - callIntegration = proposal.getCallIntegration(); } else { final JingleRtpConnection jingleRtpConnection = service.getJingleConnectionManager().initializeRtpSession(account, with, media); diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index c40a49a7239640746fb452cea60d3e1209c7a795..ee07700234c16aafdd29fb47ba719706e5c64d9f 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -95,6 +95,7 @@ public class RtpSessionActivity extends XmppActivity RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.SECURITY_ERROR, RtpEndUserState.DECLINED_OR_BUSY, + RtpEndUserState.CONTACT_OFFLINE, RtpEndUserState.CONNECTIVITY_ERROR, RtpEndUserState.CONNECTIVITY_LOST_ERROR, RtpEndUserState.RETRACTED); @@ -881,6 +882,7 @@ public class RtpSessionActivity extends XmppActivity case FINDING_DEVICE -> setTitle(R.string.rtp_state_finding_device); case RINGING -> setTitle(R.string.rtp_state_ringing); case DECLINED_OR_BUSY -> setTitle(R.string.rtp_state_declined_or_busy); + case CONTACT_OFFLINE -> setTitle(R.string.rtp_state_contact_offline); case CONNECTIVITY_ERROR -> setTitle(R.string.rtp_state_connectivity_error); case CONNECTIVITY_LOST_ERROR -> setTitle(R.string.rtp_state_connectivity_lost_error); case RETRACTED -> setTitle(R.string.rtp_state_retracted); @@ -974,7 +976,8 @@ public class RtpSessionActivity extends XmppActivity this.binding.acceptCall.setOnClickListener((v -> acceptContentAdd(contentAddition))); this.binding.acceptCall.setImageResource(R.drawable.ic_baseline_check_24); this.binding.acceptCall.setVisibility(View.VISIBLE); - } else if (state == RtpEndUserState.DECLINED_OR_BUSY) { + } else if (asList(RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.CONTACT_OFFLINE) + .contains(state)) { this.binding.rejectCall.setContentDescription(getString(R.string.exit)); this.binding.rejectCall.setOnClickListener(this::exit); this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java index 885820460a60897eb0853549d8631852cf07ab64..fff82031a9fe4b5473f879befe92ed16be262140 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpEndUserState.java @@ -1,20 +1,23 @@ package eu.siacs.conversations.xmpp.jingle; public enum RtpEndUserState { - INCOMING_CALL, //received a 'propose' message - CONNECTING, //session-initiate or session-accepted but no webrtc peer connection yet - CONNECTED, //session-accepted and webrtc peer connection is connected - RECONNECTING, //session-accepted and webrtc peer connection was connected once but is currently disconnected or failed - INCOMING_CONTENT_ADD, //session-accepted with a pending, incoming content-add - FINDING_DEVICE, //'propose' has been sent out; no 184 ack yet - RINGING, //'propose' has been sent out and it has been 184 acked - ACCEPTING_CALL, //'proceed' message has been sent; but no session-initiate has been received - ENDING_CALL, //libwebrt says 'closed' but session-terminate has not gone through - ENDED, //close UI - DECLINED_OR_BUSY, //other party declined; no retry button - CONNECTIVITY_ERROR, //network error; retry button - CONNECTIVITY_LOST_ERROR, //network error but for call duration > 0 - RETRACTED, //user pressed home or power button during 'ringing' - shows retry button - APPLICATION_ERROR, //something rather bad happened; libwebrtc failed or we got in IQ-error - SECURITY_ERROR //problem with DTLS (missing) or verification + INCOMING_CALL, // received a 'propose' message + CONNECTING, // session-initiate or session-accepted but no webrtc peer connection yet + CONNECTED, // session-accepted and webrtc peer connection is connected + RECONNECTING, // session-accepted and webrtc peer connection was connected once but is currently + // disconnected or failed + INCOMING_CONTENT_ADD, // session-accepted with a pending, incoming content-add + FINDING_DEVICE, // 'propose' has been sent out; no 184 ack yet + RINGING, // 'propose' has been sent out and it has been 184 acked + ACCEPTING_CALL, // 'proceed' message has been sent; but no session-initiate has been received + ENDING_CALL, // libwebrt says 'closed' but session-terminate has not gone through + ENDED, // close UI + DECLINED_OR_BUSY, // other party declined; no retry button + CONTACT_OFFLINE, // when `JINGLE_MESSAGE_INIT_STRICT_OFFLINE_CHECK` is true this shows up when + // the contact is offline, generally similar to BUSY + CONNECTIVITY_ERROR, // network error; retry button + CONNECTIVITY_LOST_ERROR, // network error but for call duration > 0 + RETRACTED, // user pressed home or power button during 'ringing' - shows retry button + APPLICATION_ERROR, // something rather bad happened; libwebrtc failed or we got in IQ-error + SECURITY_ERROR // problem with DTLS (missing) or verification } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 996977eab39ed51affba9e5db9f0f830491d64ea..f47731d069c82f2d1742854b776c9c72d8cfb79e 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -929,6 +929,7 @@ Discovering devices Ringing Busy + Contact is not available Could not connect call Connection lost Retracted call From bcc0c32af3277a5628264fd96efca64d389c7080 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 18 Jan 2024 13:16:14 +0100 Subject: [PATCH 018/363] fix crash when using direct jingle init on offline contacts --- .../eu/siacs/conversations/ui/util/PresenceSelector.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java b/src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java index 6edcf33492eb49f799cb9633bb3de31499e78a50..d9cda665af2c46f015eeab9df481153264ed06ae 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java +++ b/src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java @@ -32,6 +32,7 @@ package eu.siacs.conversations.ui.util; import android.app.Activity; import android.content.Context; import android.util.Pair; +import android.widget.Toast; import androidx.appcompat.app.AlertDialog; @@ -60,7 +61,9 @@ public class PresenceSelector { public static void selectFullJidForDirectRtpConnection(final Activity activity, final Contact contact, final RtpCapability.Capability required, final OnFullJidSelected onFullJidSelected) { final String[] resources = RtpCapability.filterPresences(contact, required); - if (resources.length == 1) { + if (resources.length == 0) { + Toast.makeText(activity,R.string.rtp_state_contact_offline,Toast.LENGTH_LONG).show(); + } else if (resources.length == 1) { onFullJidSelected.onFullJidSelected(contact.getJid().withResource(resources[0])); } else { showPresenceSelectionDialog(activity, contact, resources, onFullJidSelected); From b7da7f3367fc2b2e53f9eb5bf8b23f7710addef1 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 18 Jan 2024 19:54:55 +0100 Subject: [PATCH 019/363] add generator for JMI finish message --- .../generator/MessageGenerator.java | 16 +++++++++++++++- .../conversations/parser/MessageParser.java | 4 +++- .../CallIntegrationConnectionService.java | 12 ++++++++++++ .../xmpp/jingle/JingleConnectionManager.java | 10 ++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java index 4e99ab086ead562e072db52679810750f4de55a0..18322453e99c464e36bfd1f9e647475ca1283584 100644 --- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java @@ -21,6 +21,7 @@ import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.jingle.Media; +import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; public class MessageGenerator extends AbstractGenerator { @@ -228,6 +229,20 @@ public class MessageGenerator extends AbstractGenerator { return packet; } + public MessagePacket sessionFinish( + final Jid with, final String sessionId, final Reason reason) { + final MessagePacket packet = new MessagePacket(); + packet.setType(MessagePacket.TYPE_CHAT); + packet.setTo(with); + packet.setId(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX + sessionId); + final Element finish = packet.addChild("finish", Namespace.JINGLE_MESSAGE); + finish.setAttribute("id", sessionId); + final Element reasonElement = finish.addChild("reason", Namespace.JINGLE); + reasonElement.addChild(reason.toString()); + packet.addChild("store", "urn:xmpp:hints"); + return packet; + } + public MessagePacket sessionProposal(final JingleConnectionManager.RtpSessionProposal proposal) { final MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_CHAT); //we want to carbon copy those @@ -238,7 +253,6 @@ public class MessageGenerator extends AbstractGenerator { for (final Media media : proposal.media) { propose.addChild("description", Namespace.JINGLE_APPS_RTP).setAttribute("media", media.toString()); } - packet.addChild("request", "urn:xmpp:receipts"); packet.addChild("store", "urn:xmpp:hints"); return packet; diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index bf3cba1782ab015155c40b45282a753218b036d9..d20e4dd46282186585697e770c9d52507c53d639 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -53,7 +53,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss", Locale.ENGLISH); private static final List JINGLE_MESSAGE_ELEMENT_NAMES = - Arrays.asList("accept", "propose", "proceed", "reject", "retract", "ringing"); + Arrays.asList("accept", "propose", "proceed", "reject", "retract", "ringing", "finish"); public MessageParser(XmppConnectionService service) { super(service); @@ -913,6 +913,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece Log.d(Config.LOGTAG, "unable to find original rtp session message for received propose"); } + } else if ("finish".equals(action)) { + Log.d(Config.LOGTAG,"received JMI 'finish' during MAM catch-up. Can be used to update success/failure and duration"); } } else { //MAM reloads (non catchups diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index de39abb518aec58b24317003d01b2af276b2e61f..013a3964a72e06b4712b8f74d36864d3d2d82589 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -30,16 +30,19 @@ import com.google.common.util.concurrent.SettableFuture; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.ui.RtpSessionActivity; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection; import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.jingle.Media; import eu.siacs.conversations.xmpp.jingle.RtpEndUserState; +import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; import java.util.Collection; import java.util.Collections; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -108,6 +111,9 @@ public class CallIntegrationConnectionService extends ConnectionService { callIntegration = Connection.createFailedConnection( new DisconnectCause(DisconnectCause.ERROR, "contact is offline")); + // we can use a JMI 'finish' message to notify the contact of a call we never + // actually attempted + // sendJingleFinishMessage(service, contact, Reason.CONNECTIVITY_ERROR); } else { final var proposal = service.getJingleConnectionManager() @@ -137,6 +143,12 @@ public class CallIntegrationConnectionService extends ConnectionService { return callIntegration; } + private static void sendJingleFinishMessage( + final XmppConnectionService service, final Contact contact, final Reason reason) { + service.getJingleConnectionManager() + .sendJingleMessageFinish(contact, UUID.randomUUID().toString(), reason); + } + @Override public Connection onCreateOutgoingConnection( final PhoneAccountHandle phoneAccountHandle, final ConnectionRequest request) { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index e90a35a0c7fc01d9cd47ba28b10c086d9af98563..c07a3b60facda7cc5801eb671f495295906f0c65 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -746,6 +746,16 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } + public void sendJingleMessageFinish( + final Contact contact, final String sessionId, final Reason reason) { + final var account = contact.getAccount(); + final MessagePacket messagePacket = + mXmppConnectionService + .getMessageGenerator() + .sessionFinish(contact.getJid(), sessionId, reason); + mXmppConnectionService.sendMessagePacket(account, messagePacket); + } + public boolean hasMatchingProposal(final Account account, final Jid with) { synchronized (this.rtpSessionProposals) { for (Map.Entry entry : From ef5508e5b1ea3c50adefe3264de574ab86f47a54 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 19 Jan 2024 10:49:48 +0100 Subject: [PATCH 020/363] trigger incoming call integration only for rtp connections --- .../conversations/xmpp/jingle/JingleConnectionManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index c07a3b60facda7cc5801eb671f495295906f0c65..d9389c50606c772c1f083a11f11905a49bfae668 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -135,7 +135,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { } connections.put(id, connection); - CallIntegrationConnectionService.addNewIncomingCall(getXmppConnectionService(), id); + if (connection instanceof JingleRtpConnection) { + CallIntegrationConnectionService.addNewIncomingCall(getXmppConnectionService(), id); + } mXmppConnectionService.updateConversationUi(); connection.deliverPacket(packet); From 32da5853d7570749a602c97093593da64f4906d8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 19 Jan 2024 16:01:07 +0100 Subject: [PATCH 021/363] track offline message queue --- .../services/XmppConnectionService.java | 5 +++++ .../conversations/xmpp/XmppConnection.java | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 41c467b155f5522f49f0f17d5e52b7fe064b40b6..8e79ef1b1e47e847616931888844a24945414ea4 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -371,14 +371,19 @@ public class XmppConnectionService extends Service { } final boolean flexible = account.getXmppConnection().getFeatures().flexibleOfflineMessageRetrieval(); final boolean catchup = getMessageArchiveService().inCatchup(account); + final boolean trackOfflineMessageRetrieval; if (flexible && catchup && account.getXmppConnection().isMamPreferenceAlways()) { + trackOfflineMessageRetrieval = false; sendIqPacket(account, mIqGenerator.purgeOfflineMessages(), (acc, packet) -> { if (packet.getType() == IqPacket.TYPE.RESULT) { Log.d(Config.LOGTAG, acc.getJid().asBareJid() + ": successfully purged offline messages"); } }); + } else { + trackOfflineMessageRetrieval = true; } sendPresence(account); + account.getXmppConnection().trackOfflineMessageRetrieval(trackOfflineMessageRetrieval); if (mPushManagementService.available(account)) { mPushManagementService.registerPushTokenOnServer(account); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 9141e1c70e3fb1006ecb4b896b2c5c47eb22080f..0500517dcdfdf1ecfa9e493781d43206be4908ca 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -165,6 +165,7 @@ public class XmppConnection implements Runnable { private boolean inSmacksSession = false; private boolean quickStartInProgress = false; private boolean isBound = false; + private boolean offlineMessagesRetrieved = false; private Element streamFeatures; private Element boundStreamFeatures; private StreamId streamId = null; @@ -2200,6 +2201,7 @@ public class XmppConnection implements Runnable { } private void finalizeBind() { + this.offlineMessagesRetrieved = false; if (bindListener != null) { bindListener.onBind(account); } @@ -2727,6 +2729,24 @@ public class XmppConnection implements Runnable { return mXmppConnectionService.getIqGenerator(); } + public void trackOfflineMessageRetrieval(boolean trackOfflineMessageRetrieval) { + if (trackOfflineMessageRetrieval) { + final IqPacket iqPing = new IqPacket(IqPacket.TYPE.GET); + iqPing.addChild("ping", Namespace.PING); + this.sendIqPacket( + iqPing, + (a, response) -> { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": received ping response after sending initial presence"); + XmppConnection.this.offlineMessagesRetrieved = true; + }); + } else { + this.offlineMessagesRetrieved = true; + } + } + private class MyKeyManager implements X509KeyManager { @Override public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { From 5158fc4530d9de249ed5eeb7e2f89fc1bd29a45d Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 19 Jan 2024 18:01:29 +0100 Subject: [PATCH 022/363] do not process JMI from offline queue as live messages --- .../conversations/parser/MessageParser.java | 43 ++++++++++++------- .../conversations/xmpp/XmppConnection.java | 6 ++- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index d20e4dd46282186585697e770c9d52507c53d639..cb0620fa10f6b21b8339bd1f9858b5dcf75ec4ea 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -378,6 +378,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece final Element result = MessageArchiveService.Version.findResult(original); final String queryId = result == null ? null : result.getAttribute("queryid"); final MessageArchiveService.Query query = queryId == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(queryId); + final boolean offlineMessagesRetrieved = account.getXmppConnection().isOfflineMessagesRetrieved(); if (query != null && query.validFrom(original.getFrom())) { final Pair f = original.getForwardedMessagePacket("result", query.version.namespace); if (f == null) { @@ -852,7 +853,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece if (sessionId == null) { break; } - if (query == null) { + if (query == null && offlineMessagesRetrieved) { if (serverMsgId == null) { serverMsgId = extractStanzaId(account, packet); } @@ -873,24 +874,25 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece && contact.showInContactList()) { processMessageReceipts(account, packet, remoteMsgId, null); } - } else if (query.isCatchup()) { + } else if ((query != null && query.isCatchup()) || !offlineMessagesRetrieved) { if ("propose".equals(action)) { final Element description = child.findChild("description"); - final String namespace = description == null ? null : description.getNamespace(); + final String namespace = + description == null ? null : description.getNamespace(); if (Namespace.JINGLE_APPS_RTP.equals(namespace)) { - final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), false, false); - final Message preExistingMessage = c.findRtpSession(sessionId, status); + final Conversation c = + mXmppConnectionService.findOrCreateConversation( + account, counterpart.asBareJid(), false, false); + final Message preExistingMessage = + c.findRtpSession(sessionId, status); if (preExistingMessage != null) { preExistingMessage.setServerMsgId(serverMsgId); mXmppConnectionService.updateMessage(preExistingMessage); break; } - final Message message = new Message( - c, - status, - Message.TYPE_RTP_SESSION, - sessionId - ); + final Message message = + new Message( + c, status, Message.TYPE_RTP_SESSION, sessionId); message.setServerMsgId(serverMsgId); message.setTime(timestamp); message.setBody(new RtpSessionStatus(false, 0).toString()); @@ -898,9 +900,14 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece mXmppConnectionService.databaseBackend.createMessage(message); } } else if ("proceed".equals(action)) { - //status needs to be flipped to find the original propose - final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), false, false); - final int s = packet.fromAccount(account) ? Message.STATUS_RECEIVED : Message.STATUS_SEND; + // status needs to be flipped to find the original propose + final Conversation c = + mXmppConnectionService.findOrCreateConversation( + account, counterpart.asBareJid(), false, false); + final int s = + packet.fromAccount(account) + ? Message.STATUS_RECEIVED + : Message.STATUS_SEND; final Message message = c.findRtpSession(sessionId, s); if (message != null) { message.setBody(new RtpSessionStatus(true, 0).toString()); @@ -910,11 +917,15 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece message.setTime(timestamp); mXmppConnectionService.updateMessage(message, true); } else { - Log.d(Config.LOGTAG, "unable to find original rtp session message for received propose"); + Log.d( + Config.LOGTAG, + "unable to find original rtp session message for received propose"); } } else if ("finish".equals(action)) { - Log.d(Config.LOGTAG,"received JMI 'finish' during MAM catch-up. Can be used to update success/failure and duration"); + Log.d( + Config.LOGTAG, + "received JMI 'finish' during MAM catch-up. Can be used to update success/failure and duration"); } } else { //MAM reloads (non catchups diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 0500517dcdfdf1ecfa9e493781d43206be4908ca..7011c4d54ea89bd532074990dcbe8c9f2e05915f 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -2739,7 +2739,7 @@ public class XmppConnection implements Runnable { Log.d( Config.LOGTAG, account.getJid().asBareJid() - + ": received ping response after sending initial presence"); + + ": got ping response after sending initial presence"); XmppConnection.this.offlineMessagesRetrieved = true; }); } else { @@ -2747,6 +2747,10 @@ public class XmppConnection implements Runnable { } } + public boolean isOfflineMessagesRetrieved() { + return this.offlineMessagesRetrieved; + } + private class MyKeyManager implements X509KeyManager { @Override public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { From bff1ac5ebc0db303385166b83ae5181ca29a0d20 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 20 Jan 2024 11:41:37 +0100 Subject: [PATCH 023/363] do not nofiy UI if UI triggered retract --- .../xmpp/jingle/JingleConnectionManager.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index d9389c50606c772c1f083a11f11905a49bfae668..4356a0686880009083c1ae977afa5925d2c8b3f9 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -659,12 +659,17 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } if (matchingProposal != null) { - retractSessionProposal(matchingProposal); + retractSessionProposal(matchingProposal, false); } } } private void retractSessionProposal(final RtpSessionProposal rtpSessionProposal) { + retractSessionProposal(rtpSessionProposal, true); + } + + private void retractSessionProposal( + final RtpSessionProposal rtpSessionProposal, final boolean refresh) { final Account account = rtpSessionProposal.account; Log.d( Config.LOGTAG, @@ -673,11 +678,13 @@ public class JingleConnectionManager extends AbstractConnectionManager { + rtpSessionProposal.with); this.rtpSessionProposals.remove(rtpSessionProposal); rtpSessionProposal.callIntegration.retracted(); - mXmppConnectionService.notifyJingleRtpConnectionUpdate( - account, - rtpSessionProposal.with, - rtpSessionProposal.sessionId, - RtpEndUserState.RETRACTED); + if (refresh) { + mXmppConnectionService.notifyJingleRtpConnectionUpdate( + account, + rtpSessionProposal.with, + rtpSessionProposal.sessionId, + RtpEndUserState.RETRACTED); + } final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionRetract(rtpSessionProposal); writeLogMissedOutgoing( From bfe2aff7a158e767aad1a567f070a799690660b9 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 21 Jan 2024 10:07:35 +0100 Subject: [PATCH 024/363] show speaker selection during 'ringing' --- .../conversations/ui/RtpSessionActivity.java | 89 +++++++++++++++---- .../xmpp/jingle/AbstractJingleConnection.java | 8 +- .../xmpp/jingle/JingleConnectionManager.java | 32 +++++-- .../xmpp/jingle/JingleRtpConnection.java | 19 +++- .../xmpp/jingle/OngoingRtpSession.java | 9 ++ 5 files changed, 127 insertions(+), 30 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index ee07700234c16aafdd29fb47ba719706e5c64d9f..429f2cfdb6cbd1a074e1466ccd523ec2e4e10d61 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -63,6 +63,7 @@ import eu.siacs.conversations.xmpp.jingle.ContentAddition; import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.jingle.Media; +import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession; import eu.siacs.conversations.xmpp.jingle.RtpCapability; import eu.siacs.conversations.xmpp.jingle.RtpEndUserState; @@ -117,6 +118,13 @@ public class RtpSessionActivity extends XmppActivity RtpEndUserState.ACCEPTING_CALL, RtpEndUserState.CONNECTING, RtpEndUserState.RECONNECTING); + private static final List STATES_SHOWING_SPEAKER_CONFIGURATION = + new ImmutableList.Builder() + .add(RtpEndUserState.FINDING_DEVICE) + .add(RtpEndUserState.RINGING) + .add(RtpEndUserState.CONNECTING) + .addAll(STATES_CONSIDERED_CONNECTED) + .build(); private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session"; private static final int REQUEST_ACCEPT_CALL = 0x1111; private static final int REQUEST_ACCEPT_CONTENT = 0x1112; @@ -139,8 +147,13 @@ public class RtpSessionActivity extends XmppActivity public static Set actionToMedia(final String action) { if (ACTION_MAKE_VIDEO_CALL.equals(action)) { return ImmutableSet.of(Media.AUDIO, Media.VIDEO); - } else { + } else if (ACTION_MAKE_VOICE_CALL.equals(action)) { return ImmutableSet.of(Media.AUDIO); + } else { + Log.w( + Config.LOGTAG, + "actionToMedia can not get media set from unknown action " + action); + return Collections.emptySet(); } } @@ -274,14 +287,15 @@ public class RtpSessionActivity extends XmppActivity private void retractSessionProposal() { final Intent intent = getIntent(); final String action = intent.getAction(); + final String lastAction = intent.getStringExtra(EXTRA_LAST_ACTION); final Account account = extractAccount(intent); final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH)); final String state = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE); if (!Intent.ACTION_VIEW.equals(action) || state == null || !END_CARD.contains(RtpEndUserState.valueOf(state))) { - resetIntent( - account, with, RtpEndUserState.RETRACTED, actionToMedia(intent.getAction())); + final Set media = actionToMedia(lastAction == null ? action : lastAction); + resetIntent(account, with, RtpEndUserState.RETRACTED, media); } xmppConnectionService .getJingleConnectionManager() @@ -1049,6 +1063,14 @@ public class RtpSessionActivity extends XmppActivity } else { this.binding.inCallActionLeft.setVisibility(View.GONE); } + } else if (STATES_SHOWING_SPEAKER_CONFIGURATION.contains(state) + && !isPictureInPicture() + && Media.audioOnly(media)) { + final CallIntegration callIntegration = requireCallIntegration(); + updateInCallButtonConfigurationSpeaker( + callIntegration.getSelectedAudioDevice(), + callIntegration.getAudioDevices().size()); + this.binding.inCallActionFarRight.setVisibility(View.GONE); } else { this.binding.inCallActionLeft.setVisibility(View.GONE); this.binding.inCallActionRight.setVisibility(View.GONE); @@ -1297,17 +1319,13 @@ public class RtpSessionActivity extends XmppActivity } } - private void switchToEarpiece(View view) { - requireRtpConnection() - .getCallIntegration() - .setAudioDevice(CallIntegration.AudioDevice.EARPIECE); + private void switchToEarpiece(final View view) { + requireCallIntegration().setAudioDevice(CallIntegration.AudioDevice.EARPIECE); acquireProximityWakeLock(); } - private void switchToSpeaker(View view) { - requireRtpConnection() - .getCallIntegration() - .setAudioDevice(CallIntegration.AudioDevice.SPEAKER_PHONE); + private void switchToSpeaker(final View view) { + requireCallIntegration().setAudioDevice(CallIntegration.AudioDevice.SPEAKER_PHONE); releaseProximityWakeLock(); } @@ -1359,6 +1377,33 @@ public class RtpSessionActivity extends XmppActivity return connection; } + private CallIntegration requireCallIntegration() { + return requireOngoingRtpSession().getCallIntegration(); + } + + private OngoingRtpSession requireOngoingRtpSession() { + final JingleRtpConnection connection = + this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null; + if (connection != null) { + return connection; + } + final Intent currentIntent = getIntent(); + final String withExtra = + currentIntent == null ? null : currentIntent.getStringExtra(EXTRA_WITH); + final var account = extractAccount(currentIntent); + if (withExtra == null) { + throw new IllegalStateException("Current intent has no EXTRA_WITH"); + } + final var matching = + xmppConnectionService + .getJingleConnectionManager() + .matchingProposal(account, Jid.of(withExtra)); + if (matching.isPresent()) { + return matching.get(); + } + throw new IllegalStateException("No matching session proposal"); + } + @Override public void onJingleRtpConnectionUpdate( Account account, Jid with, final String sessionId, RtpEndUserState state) { @@ -1425,16 +1470,23 @@ public class RtpSessionActivity extends XmppActivity + ", available:" + availableAudioDevices); try { - final RtpEndUserState endUserState = requireRtpConnection().getEndUserState(); - final Set media = getMedia(); + final OngoingRtpSession ongoingRtpSession = requireOngoingRtpSession(); + final RtpEndUserState endUserState; + if (ongoingRtpSession instanceof JingleRtpConnection jingleRtpConnection) { + endUserState = jingleRtpConnection.getEndUserState(); + } else { + // for session proposals all end user states are functionally the same + endUserState = RtpEndUserState.RINGING; + } + final Set media = ongoingRtpSession.getMedia(); if (END_CARD.contains(endUserState)) { Log.d( Config.LOGTAG, "onAudioDeviceChanged() nothing to do because end card has been reached"); } else { - if (Media.audioOnly(media) && endUserState == RtpEndUserState.CONNECTED) { - final CallIntegration callIntegration = - requireRtpConnection().getCallIntegration(); + if (Media.audioOnly(media) + && STATES_SHOWING_SPEAKER_CONFIGURATION.contains(endUserState)) { + final CallIntegration callIntegration = requireCallIntegration(); updateInCallButtonConfigurationSpeaker( callIntegration.getSelectedAudioDevice(), callIntegration.getAudioDevices().size()); @@ -1457,16 +1509,17 @@ public class RtpSessionActivity extends XmppActivity if (withExtra == null) { return; } + final Set media = actionToMedia(currentIntent.getStringExtra(EXTRA_LAST_ACTION)); if (Jid.ofEscaped(withExtra).asBareJid().equals(with)) { runOnUiThread( () -> { updateVerifiedShield(false); updateStateDisplay(state); - updateButtonConfiguration(state); + updateButtonConfiguration(state, media, null); updateIncomingCallScreen(state); invalidateOptionsMenu(); }); - resetIntent(account, with, state, actionToMedia(currentIntent.getAction())); + resetIntent(account, with, state, media); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java index efc32f5ff2eacf7e2ae921a9f5d5ac093d951d8b..6aeb348c11b126d35ab803809594a385dac027c5 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java @@ -219,8 +219,7 @@ public abstract class AbstractJingleConnection { if (isTerminated()) { this.jingleConnectionManager.finishConnectionOrThrow(this); } else { - throw new AssertionError( - String.format("Unable to call finish from %s", this.state)); + throw new AssertionError(String.format("Unable to call finish from %s", this.state)); } } @@ -348,7 +347,7 @@ public abstract class AbstractJingleConnection { return features != null && features.contains(feature); } - public static class Id implements OngoingRtpSession { + public static class Id { public final Account account; public final Jid with; public final String sessionId; @@ -400,17 +399,14 @@ public abstract class AbstractJingleConnection { return Objects.hashCode(account.getUuid(), with, sessionId); } - @Override public Account getAccount() { return account; } - @Override public Jid getWith() { return with; } - @Override public String getSessionId() { return sessionId; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 4356a0686880009083c1ae977afa5925d2c8b3f9..9dee8904df3697fc8439bff4985f15ec52ff181e 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -601,11 +601,11 @@ public class JingleConnectionManager extends AbstractConnectionManager { public Optional getOngoingRtpConnection(final Contact contact) { for (final Map.Entry entry : this.connections.entrySet()) { - if (entry.getValue() instanceof JingleRtpConnection) { + if (entry.getValue() instanceof JingleRtpConnection jingleRtpConnection) { final AbstractJingleConnection.Id id = entry.getKey(); if (id.account == contact.getAccount() && id.with.asBareJid().equals(contact.getJid().asBareJid())) { - return Optional.of(id); + return Optional.of(jingleRtpConnection); } } } @@ -765,9 +765,22 @@ public class JingleConnectionManager extends AbstractConnectionManager { mXmppConnectionService.sendMessagePacket(account, messagePacket); } + public Optional matchingProposal(final Account account, final Jid with) { + synchronized (this.rtpSessionProposals) { + for (final Map.Entry entry : + this.rtpSessionProposals.entrySet()) { + final RtpSessionProposal proposal = entry.getKey(); + if (proposal.account == account && with.asBareJid().equals(proposal.with)) { + return Optional.of(proposal); + } + } + } + return Optional.absent(); + } + public boolean hasMatchingProposal(final Account account, final Jid with) { synchronized (this.rtpSessionProposals) { - for (Map.Entry entry : + for (final Map.Entry entry : this.rtpSessionProposals.entrySet()) { final var state = entry.getValue(); final RtpSessionProposal proposal = entry.getKey(); @@ -1102,9 +1115,15 @@ public class JingleConnectionManager extends AbstractConnectionManager { return sessionId; } + @Override public CallIntegration getCallIntegration() { return this.callIntegration; } + + @Override + public Set getMedia() { + return this.media; + } } public class ProposalStateCallback implements CallIntegration.Callback { @@ -1126,8 +1145,11 @@ public class JingleConnectionManager extends AbstractConnectionManager { @Override public void onAudioDeviceChanged( - CallIntegration.AudioDevice selectedAudioDevice, - Set availableAudioDevices) {} + final CallIntegration.AudioDevice selectedAudioDevice, + final Set availableAudioDevices) { + mXmppConnectionService.notifyJingleRtpConnectionUpdate( + selectedAudioDevice, availableAudioDevices); + } @Override public void onCallIntegrationReject() {} diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index edb58656585a6dff33bae0523fc79e5cbd4b18d1..df48ee8d7630982c20da679026a7ef573a944e8b 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -30,6 +30,7 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.CryptoFailedException; import eu.siacs.conversations.crypto.axolotl.FingerprintStatus; +import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.Message; @@ -68,7 +69,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class JingleRtpConnection extends AbstractJingleConnection - implements WebRTCWrapper.EventCallback, CallIntegration.Callback { + implements WebRTCWrapper.EventCallback, CallIntegration.Callback, OngoingRtpSession { public static final List STATES_SHOWING_ONGOING_CALL = Arrays.asList( @@ -2645,6 +2646,7 @@ public class JingleRtpConnection extends AbstractJingleConnection return this.sessionDuration.elapsed(TimeUnit.MILLISECONDS); } + @Override public CallIntegration getCallIntegration() { return this.callIntegration; } @@ -2870,6 +2872,21 @@ public class JingleRtpConnection extends AbstractJingleConnection return remoteHasFeature(Namespace.SDP_OFFER_ANSWER); } + @Override + public Account getAccount() { + return id.account; + } + + @Override + public Jid getWith() { + return id.with; + } + + @Override + public String getSessionId() { + return id.sessionId; + } + private interface OnIceServersDiscovered { void onIceServersDiscovered(List iceServers); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java index ebd2d8850fcfdf8a5c8bfa2df152e0e105caa883..358411bcb08e19015449bfacab54f597ab3e0719 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java @@ -1,10 +1,19 @@ package eu.siacs.conversations.xmpp.jingle; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.services.CallIntegration; import eu.siacs.conversations.xmpp.Jid; +import java.util.Set; + public interface OngoingRtpSession { Account getAccount(); + Jid getWith(); + String getSessionId(); + + CallIntegration getCallIntegration(); + + Set getMedia(); } From a78747eaa275083cbd52159bc7bc2b0e55cb4bee Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 21 Jan 2024 11:08:43 +0100 Subject: [PATCH 025/363] react to onSilence() and stop ringtone --- .../eu/siacs/conversations/services/CallIntegration.java | 7 +++++++ .../conversations/xmpp/jingle/JingleConnectionManager.java | 5 +++++ .../conversations/xmpp/jingle/JingleRtpConnection.java | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index f07ebe28da991a58493c59a4bdddbe521219b507..fa262768bbfd6878cb6ab90a64201e6e70f44059 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -300,6 +300,11 @@ public class CallIntegration extends Connection { return this.appRTCAudioManager; } + @Override + public void onSilence() { + this.callback.onCallIntegrationSilence(); + } + @Override public void onStateChanged(final int state) { Log.d(Config.LOGTAG, "onStateChanged(" + state + ")"); @@ -473,5 +478,7 @@ public class CallIntegration extends Connection { void onCallIntegrationReject(); void onCallIntegrationAnswer(); + + void onCallIntegrationSilence(); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 9dee8904df3697fc8439bff4985f15ec52ff181e..8541f6a44f74b5e74a8143c835ee5995411d586f 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -1156,5 +1156,10 @@ public class JingleConnectionManager extends AbstractConnectionManager { @Override public void onCallIntegrationAnswer() {} + + @Override + public void onCallIntegrationSilence() { + + } } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index df48ee8d7630982c20da679026a7ef573a944e8b..74eb4abd04a9353a0a7383c602eea0bb0841cdee 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -2718,6 +2718,11 @@ public class JingleRtpConnection extends AbstractJingleConnection xmppConnectionService.startActivity(intent); } + @Override + public void onCallIntegrationSilence() { + xmppConnectionService.getNotificationService().stopSoundAndVibration(); + } + @Override public void onAudioDeviceChanged( final CallIntegration.AudioDevice selectedAudioDevice, From 21b8bf424a7488807358e7c0b56dd8c6de39faed Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 21 Jan 2024 13:53:41 +0100 Subject: [PATCH 026/363] skip automatic audio device selection when BT is available --- .../siacs/conversations/services/CallIntegration.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index fa262768bbfd6878cb6ab90a64201e6e70f44059..378dd62776693a12052fd9c6140ea41f66ae8dbf 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -159,14 +159,15 @@ public class CallIntegration extends Connection { public void setAudioDeviceWhenAvailable(final AudioDevice audioDevice) { final var available = getAudioDevices(); - if (available.contains(audioDevice)) { + if (available.contains(audioDevice) && !available.contains(AudioDevice.BLUETOOTH)) { this.setAudioDevice(audioDevice); } else { Log.d( Config.LOGTAG, "application requested to switch to " + audioDevice - + " but device was not available"); + + " but we won't because available devices are " + + available); } } @@ -407,13 +408,14 @@ public class CallIntegration extends Connection { final Set availableAudioDevices) { if (this.initialAudioDevice != null && this.initialAudioDeviceConfigured.compareAndSet(false, true)) { - if (availableAudioDevices.contains(this.initialAudioDevice)) { + if (availableAudioDevices.contains(this.initialAudioDevice) + && !availableAudioDevices.contains(AudioDevice.BLUETOOTH)) { setAudioDevice(this.initialAudioDevice); Log.d(Config.LOGTAG, "configured initial audio device"); } else { Log.d( Config.LOGTAG, - "initial audio device not available. available devices: " + "not setting initial audio device. available devices: " + availableAudioDevices); } } From 18dea352b05622978422605b6408765e71ac78c2 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 21 Jan 2024 14:48:07 +0100 Subject: [PATCH 027/363] send jmi finish alongside session terminate --- .../conversations/generator/MessageGenerator.java | 1 - .../conversations/xmpp/jingle/JingleRtpConnection.java | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java index 18322453e99c464e36bfd1f9e647475ca1283584..e217f7f1d41af5f3e46c4e5583abf787f60cf35e 100644 --- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java @@ -234,7 +234,6 @@ public class MessageGenerator extends AbstractGenerator { final MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_CHAT); packet.setTo(with); - packet.setId(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX + sessionId); final Element finish = packet.addChild("finish", Namespace.JINGLE_MESSAGE); finish.setAttribute("id", sessionId); final Element reasonElement = finish.addChild("reason", Namespace.JINGLE); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 74eb4abd04a9353a0a7383c602eea0bb0841cdee..e22d6574e80cff823f98ee9dab12a88018a69adc 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -1986,6 +1986,7 @@ public class JingleRtpConnection extends AbstractJingleConnection protected void sendSessionTerminate(final Reason reason, final String text) { sendSessionTerminate(reason, text, this::writeLogMessage); + sendJingleMessageFinish(reason); } private void sendTransportInfo( @@ -2358,6 +2359,15 @@ public class JingleRtpConnection extends AbstractJingleConnection xmppConnectionService.sendMessagePacket(id.account, messagePacket); } + private void sendJingleMessageFinish(final Reason reason) { + final var account = id.getAccount(); + final MessagePacket messagePacket = + xmppConnectionService + .getMessageGenerator() + .sessionFinish(id.with, id.sessionId, reason); + xmppConnectionService.sendMessagePacket(account, messagePacket); + } + private boolean isOmemoEnabled() { final Conversational conversational = message.getConversation(); if (conversational instanceof Conversation) { From d2d76322b9204224db54903c05b2dd192d121661 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 21 Jan 2024 18:09:28 +0100 Subject: [PATCH 028/363] show speaker configuration during ACCEPTING --- src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 429f2cfdb6cbd1a074e1466ccd523ec2e4e10d61..08351b7d584a5f1ec4a601ea06056c6e342e7e80 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -122,6 +122,7 @@ public class RtpSessionActivity extends XmppActivity new ImmutableList.Builder() .add(RtpEndUserState.FINDING_DEVICE) .add(RtpEndUserState.RINGING) + .add(RtpEndUserState.ACCEPTING_CALL) .add(RtpEndUserState.CONNECTING) .addAll(STATES_CONSIDERED_CONNECTED) .build(); From a04dc6e4ad6512912129604213ad7b077a816cd7 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 7 Feb 2024 15:02:06 +0100 Subject: [PATCH 029/363] show warning when call integration accounts exceed 10 --- .../CallIntegrationConnectionService.java | 32 +++++++++++++++++-- src/main/res/values/strings.xml | 1 + 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index 013a3964a72e06b4712b8f74d36864d3d2d82589..0455d4d6e24fcbb55fb1db4469c17c4d05a3685b 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -211,6 +211,25 @@ public class CallIntegrationConnectionService extends ConnectionService { } public static void registerPhoneAccount(final Context context, final Account account) { + try { + registerPhoneAccountOrThrow(context, account); + } catch (final IllegalArgumentException e) { + Toast.makeText(context, R.string.call_integration_not_available, Toast.LENGTH_LONG) + .show(); + } + } + + public static void registerPhoneAccountOrThrow(final Context context, final Account account) { + final var handle = getHandle(context, account); + final var telecomManager = context.getSystemService(TelecomManager.class); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (telecomManager.getOwnSelfManagedPhoneAccounts().contains(handle)) { + Log.d( + Config.LOGTAG, + "a phone account for " + account.getJid().asBareJid() + " already exists"); + return; + } + } final var builder = PhoneAccount.builder(getHandle(context, account), account.getJid().asBareJid()); builder.setSupportedUriSchemes(Collections.singletonList("xmpp")); @@ -220,14 +239,21 @@ public class CallIntegrationConnectionService extends ConnectionService { | PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING); } final var phoneAccount = builder.build(); - - context.getSystemService(TelecomManager.class).registerPhoneAccount(phoneAccount); + telecomManager.registerPhoneAccount(phoneAccount); } public static void registerPhoneAccounts( final Context context, final Collection accounts) { for (final Account account : accounts) { - registerPhoneAccount(context, account); + try { + registerPhoneAccountOrThrow(context, account); + } catch (final IllegalArgumentException e) { + Log.w( + Config.LOGTAG, + "could not register phone account for " + account.getJid().asBareJid(), + e); + return; + } } } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index f47731d069c82f2d1742854b776c9c72d8cfb79e..4f67b8a95a3167c1a72351e4cdc84573cb5db8b2 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -1027,4 +1027,5 @@ Report spam and block spammer Privacy policy Contact list integration is not available + Call integration not available! From e416a6c4eb4c0c77fe612af78ade88e761d20ed0 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 9 Feb 2024 15:27:33 +0100 Subject: [PATCH 030/363] maintain phone accounts only for enabled accounts --- .../CallIntegrationConnectionService.java | 91 +++++++++++++++---- .../services/XmppConnectionService.java | 13 +-- 2 files changed, 77 insertions(+), 27 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index 0455d4d6e24fcbb55fb1db4469c17c4d05a3685b..01602dc70e0d7b251988b565650efd1e458b0d82 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -22,6 +22,7 @@ import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListenableFuture; @@ -44,11 +45,16 @@ import java.util.Collections; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class CallIntegrationConnectionService extends ConnectionService { + private static final ExecutorService ACCOUNT_REGISTRATION_EXECUTOR = + Executors.newSingleThreadExecutor(); + private ListenableFuture serviceFuture; @Override @@ -210,16 +216,36 @@ public class CallIntegrationConnectionService extends ConnectionService { return jingleRtpConnection.getCallIntegration(); } - public static void registerPhoneAccount(final Context context, final Account account) { + public static void togglePhoneAccountAsync(final Context context, final Account account) { + ACCOUNT_REGISTRATION_EXECUTOR.execute(() -> togglePhoneAccount(context, account)); + } + + private static void togglePhoneAccount(final Context context, final Account account) { + if (account.isEnabled()) { + registerPhoneAccount(context, account); + } else { + unregisterPhoneAccount(context, account); + } + } + + private static void registerPhoneAccount(final Context context, final Account account) { try { registerPhoneAccountOrThrow(context, account); } catch (final IllegalArgumentException e) { - Toast.makeText(context, R.string.call_integration_not_available, Toast.LENGTH_LONG) - .show(); + Log.w( + Config.LOGTAG, + "could not register phone account for " + account.getJid().asBareJid(), + e); + ContextCompat.getMainExecutor(context) + .execute(() -> showCallIntegrationNotAvailable(context)); } } - public static void registerPhoneAccountOrThrow(final Context context, final Account account) { + private static void showCallIntegrationNotAvailable(final Context context) { + Toast.makeText(context, R.string.call_integration_not_available, Toast.LENGTH_LONG).show(); + } + + private static void registerPhoneAccountOrThrow(final Context context, final Account account) { final var handle = getHandle(context, account); final var telecomManager = context.getSystemService(TelecomManager.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -242,24 +268,39 @@ public class CallIntegrationConnectionService extends ConnectionService { telecomManager.registerPhoneAccount(phoneAccount); } - public static void registerPhoneAccounts( + public static void togglePhoneAccountsAsync( + final Context context, final Collection accounts) { + ACCOUNT_REGISTRATION_EXECUTOR.execute(() -> togglePhoneAccounts(context, accounts)); + } + + private static void togglePhoneAccounts( final Context context, final Collection accounts) { for (final Account account : accounts) { - try { - registerPhoneAccountOrThrow(context, account); - } catch (final IllegalArgumentException e) { - Log.w( - Config.LOGTAG, - "could not register phone account for " + account.getJid().asBareJid(), - e); - return; + if (account.isEnabled()) { + try { + registerPhoneAccountOrThrow(context, account); + } catch (final IllegalArgumentException e) { + Log.w( + Config.LOGTAG, + "could not register phone account for " + account.getJid().asBareJid(), + e); + } + } else { + unregisterPhoneAccount(context, account); } } } public static void unregisterPhoneAccount(final Context context, final Account account) { - context.getSystemService(TelecomManager.class) - .unregisterPhoneAccount(getHandle(context, account)); + final var handle = getHandle(context, account); + final var telecomManager = context.getSystemService(TelecomManager.class); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (telecomManager.getOwnSelfManagedPhoneAccounts().contains(handle)) { + telecomManager.unregisterPhoneAccount(handle); + } + } else { + telecomManager.unregisterPhoneAccount(handle); + } } public static PhoneAccountHandle getHandle(final Context context, final Account account) { @@ -288,8 +329,13 @@ public class CallIntegrationConnectionService extends ConnectionService { .show(); return; } - service.getSystemService(TelecomManager.class) - .placeCall(CallIntegration.address(with), extras); + try { + service.getSystemService(TelecomManager.class) + .placeCall(CallIntegration.address(with), extras); + } catch (final SecurityException e) { + Toast.makeText(service, R.string.call_integration_not_available, Toast.LENGTH_LONG) + .show(); + } } else { final var connection = createOutgoingRtpConnection(service, account, with, media); if (connection != null) { @@ -319,8 +365,15 @@ public class CallIntegrationConnectionService extends ConnectionService { final var extras = new Bundle(); extras.putString("sid", id.sessionId); bundle.putBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras); - context.getSystemService(TelecomManager.class) - .addNewIncomingCall(phoneAccountHandle, bundle); + try { + context.getSystemService(TelecomManager.class) + .addNewIncomingCall(phoneAccountHandle, bundle); + } catch (final SecurityException e) { + Log.e( + Config.LOGTAG, + id.account.getJid().asBareJid() + ": call integration not available", + e); + } } public static class ServiceConnectionService { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 8e79ef1b1e47e847616931888844a24945414ea4..849c7daa858b867d41125fc8fe7882ec672ea63b 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -40,8 +40,6 @@ import android.os.SystemClock; import android.preference.PreferenceManager; import android.provider.ContactsContract; import android.security.KeyChain; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; @@ -1284,15 +1282,13 @@ public class XmppConnectionService extends Service { toggleSetProfilePictureActivity(hasEnabledAccounts); reconfigurePushDistributor(); - CallIntegrationConnectionService.registerPhoneAccounts(this, this.accounts); + CallIntegrationConnectionService.togglePhoneAccountsAsync(this, this.accounts); restoreFromDatabase(); if (QuickConversationsService.isContactListIntegration(this) - && (Build.VERSION.SDK_INT < Build.VERSION_CODES.M - || ContextCompat.checkSelfPermission( - this, Manifest.permission.READ_CONTACTS) - == PackageManager.PERMISSION_GRANTED)) { + && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) + == PackageManager.PERMISSION_GRANTED) { startContactObserver(); } FILE_OBSERVER_EXECUTOR.execute(fileBackend::deleteHistoricAvatarPath); @@ -2465,7 +2461,7 @@ public class XmppConnectionService extends Service { public void createAccount(final Account account) { account.initAccountServices(this); databaseBackend.createAccount(account); - CallIntegrationConnectionService.registerPhoneAccount(this, account); + CallIntegrationConnectionService.togglePhoneAccountAsync(this, account); this.accounts.add(account); this.reconnectAccountInBackground(account); updateAccountUi(); @@ -2589,6 +2585,7 @@ public class XmppConnectionService extends Service { toggleForegroundService(); syncEnabledAccountSetting(); mChannelDiscoveryService.cleanCache(); + CallIntegrationConnectionService.togglePhoneAccountAsync(this, account); return true; } else { return false; From ca1d8b4d1b3f4e77f56fe450f3759d40e9f9091a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 17 Feb 2024 09:05:40 +0100 Subject: [PATCH 031/363] fix race condition accessing rtpSender --- .../eu/siacs/conversations/xmpp/jingle/TrackWrapper.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/TrackWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/TrackWrapper.java index e62aa18fdb71bdba822fe5425034c74d49690572..16e89ca3b16c802ea25b4f97b505c3d277a8785e 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/TrackWrapper.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/TrackWrapper.java @@ -65,8 +65,14 @@ class TrackWrapper { public static RtpTransceiver getTransceiver( @Nonnull final PeerConnection peerConnection, final TrackWrapper trackWrapper) { final RtpSender rtpSender = trackWrapper.rtpSender; + final String rtpSenderId; + try { + rtpSenderId = rtpSender.id(); + } catch (final IllegalStateException e) { + return null; + } for (final RtpTransceiver transceiver : peerConnection.getTransceivers()) { - if (transceiver.getSender().id().equals(rtpSender.id())) { + if (transceiver.getSender().id().equals(rtpSenderId)) { return transceiver; } } From 7eaad9842cf025f54e096ed56d9d9f44e8e2a6ea Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 18 Feb 2024 08:50:19 +0100 Subject: [PATCH 032/363] remove mic availability check --- .../services/AppRTCAudioManager.java | 37 +-------------- .../conversations/ui/RtpSessionActivity.java | 45 ++----------------- 2 files changed, 4 insertions(+), 78 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java index 894b2ace64286b913f9a56e588cc9dbcdbd95ad2..b256d66557ed7240cd58aefd9fda2848e59749ae 100644 --- a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java +++ b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java @@ -95,44 +95,9 @@ public class AppRTCAudioManager { AppRTCUtils.logDeviceInfo(Config.LOGTAG); } - public static boolean isMicrophoneAvailable() { - microphoneLatch = new CountDownLatch(1); - AudioRecord audioRecord = null; - boolean available = true; - try { - final int sampleRate = 44100; - final int channel = AudioFormat.CHANNEL_IN_MONO; - final int format = AudioFormat.ENCODING_PCM_16BIT; - final int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channel, format); - audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channel, format, bufferSize); - audioRecord.startRecording(); - final short[] buffer = new short[bufferSize]; - final int audioStatus = audioRecord.read(buffer, 0, bufferSize); - if (audioStatus == AudioRecord.ERROR_INVALID_OPERATION || audioStatus == AudioRecord.STATE_UNINITIALIZED) - available = false; - } catch (Exception e) { - available = false; - } finally { - release(audioRecord); - - } - microphoneLatch.countDown(); - return available; - } - - private static void release(final AudioRecord audioRecord) { - if (audioRecord == null) { - return; - } - try { - audioRecord.release(); - } catch (Exception e) { - //ignore - } - } @SuppressWarnings("deprecation") - public void start(AudioManagerEvents audioManagerEvents) { + public void start(final AudioManagerEvents audioManagerEvents) { Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".start()"); ThreadUtils.checkIsOnMainThread(); if (amState == AudioManagerState.RUNNING) { diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 08351b7d584a5f1ec4a601ea06056c6e342e7e80..793ef16f90f847dfe64a549676e17d4d1f450593 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -6,7 +6,6 @@ import static java.util.Arrays.asList; import android.Manifest; import android.annotation.SuppressLint; -import android.app.Activity; import android.app.PictureInPictureParams; import android.content.ActivityNotFoundException; import android.content.Context; @@ -18,7 +17,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; -import android.os.SystemClock; import android.util.Log; import android.util.Rational; import android.view.KeyEvent; @@ -48,7 +46,6 @@ import eu.siacs.conversations.databinding.ActivityRtpSessionBinding; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; -import eu.siacs.conversations.services.AppRTCAudioManager; import eu.siacs.conversations.services.CallIntegration; import eu.siacs.conversations.services.CallIntegrationConnectionService; import eu.siacs.conversations.services.XmppConnectionService; @@ -364,7 +361,7 @@ public class RtpSessionActivity extends XmppActivity final List permissions = permissions(getMedia()); if (PermissionUtils.hasPermission(this, permissions, REQUEST_ACCEPT_CALL)) { putScreenInCallMode(); - checkRecorderAndAcceptCall(); + acceptCall(); } } @@ -381,8 +378,7 @@ public class RtpSessionActivity extends XmppActivity return permissions.build(); } - private void checkRecorderAndAcceptCall() { - checkMicrophoneAvailabilityAsync(); + private void acceptCall() { try { requireRtpConnection().acceptCall(); } catch (final IllegalStateException e) { @@ -390,40 +386,6 @@ public class RtpSessionActivity extends XmppActivity } } - private void checkMicrophoneAvailabilityAsync() { - new Thread(new MicrophoneAvailabilityCheck(this)).start(); - } - - private static class MicrophoneAvailabilityCheck implements Runnable { - - private final WeakReference activityReference; - - private MicrophoneAvailabilityCheck(final Activity activity) { - this.activityReference = new WeakReference<>(activity); - } - - @Override - public void run() { - final long start = SystemClock.elapsedRealtime(); - final boolean isMicrophoneAvailable = AppRTCAudioManager.isMicrophoneAvailable(); - final long stop = SystemClock.elapsedRealtime(); - Log.d(Config.LOGTAG, "checking microphone availability took " + (stop - start) + "ms"); - if (isMicrophoneAvailable) { - return; - } - final Activity activity = activityReference.get(); - if (activity == null) { - return; - } - activity.runOnUiThread( - () -> - Toast.makeText( - activity, - R.string.microphone_unavailable, - Toast.LENGTH_LONG) - .show()); - } - } private void putScreenInCallMode() { putScreenInCallMode(requireRtpConnection().getMedia()); @@ -597,7 +559,6 @@ public class RtpSessionActivity extends XmppActivity private void proposeJingleRtpSession( final Account account, final Jid with, final Set media) { - checkMicrophoneAvailabilityAsync(); if (with.isBareJid()) { xmppConnectionService .getJingleConnectionManager() @@ -617,7 +578,7 @@ public class RtpSessionActivity extends XmppActivity PermissionUtils.removeBluetoothConnect(permissions, grantResults); if (PermissionUtils.allGranted(permissionResult.grantResults)) { if (requestCode == REQUEST_ACCEPT_CALL) { - checkRecorderAndAcceptCall(); + acceptCall(); } else if (requestCode == REQUEST_ACCEPT_CONTENT) { acceptContentAdd(); } else if (requestCode == REQUEST_ADD_CONTENT) { From 3ae561d74aea3f1ef225ca07451818f845009627 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 18 Feb 2024 08:50:35 +0100 Subject: [PATCH 033/363] bump dependencies --- build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index e3fb55a76b02645758345a297c1964a6a4f2206f..36ed74d55c8f6ef2cef4dda1839482172c620331 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.2.0-rc03' + classpath 'com.android.tools.build:gradle:8.2.2' } } @@ -35,13 +35,13 @@ dependencies { implementation 'androidx.viewpager:viewpager:1.0.0' - playstoreImplementation('com.google.firebase:firebase-messaging:23.4.0') { + playstoreImplementation('com.google.firebase:firebase-messaging:23.4.1') { exclude group: 'com.google.firebase', module: 'firebase-core' exclude group: 'com.google.firebase', module: 'firebase-analytics' exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' } conversationsPlaystoreImplementation("com.android.installreferrer:installreferrer:2.2") - quicksyPlaystoreImplementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1' + quicksyPlaystoreImplementation 'com.google.android.gms:play-services-auth-api-phone:18.0.2' implementation 'com.github.open-keychain.open-keychain:openpgp-api:v5.7.1' implementation("com.github.CanHub:Android-Image-Cropper:2.0.0") implementation 'androidx.appcompat:appcompat:1.6.1' @@ -81,7 +81,7 @@ dependencies { implementation "com.squareup.okhttp3:okhttp:4.12.0" implementation 'com.google.guava:guava:32.1.3-android' - quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.13.17' + quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.13.28' implementation 'im.conversations.webrtc:webrtc-android:119.0.0' } From f1abfbdf35f6ea6aa2a70631843d7c6b6d33cbbf Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 23 Feb 2024 09:43:44 +0100 Subject: [PATCH 034/363] work around dead system exception when querying active network --- .../services/XmppConnectionService.java | 12 +++++------- .../conversations/utils/Compatibility.java | 18 +++++++++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 849c7daa858b867d41125fc8fe7882ec672ea63b..f120c4c5480bd20b02e636b259b0a3b5ca436d79 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1054,15 +1054,13 @@ public class XmppConnectionService extends Service { } public boolean isDataSaverDisabled() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - final ConnectivityManager connectivityManager = - (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); - return !connectivityManager.isActiveNetworkMetered() - || Compatibility.getRestrictBackgroundStatus(connectivityManager) - == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; - } else { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { return true; } + final ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class); + return !Compatibility.isActiveNetworkMetered(connectivityManager) + || Compatibility.getRestrictBackgroundStatus(connectivityManager) + == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; } private void directReply(final Conversation conversation, final String body, final String lastMessageUuid, final boolean dismissAfterReply) { diff --git a/src/main/java/eu/siacs/conversations/utils/Compatibility.java b/src/main/java/eu/siacs/conversations/utils/Compatibility.java index 23c935a2b5a190ea2cc9cca372b7ccaa6dd82016..6d3ba6224121f0d7e19361f853a6fd6bdf0d280f 100644 --- a/src/main/java/eu/siacs/conversations/utils/Compatibility.java +++ b/src/main/java/eu/siacs/conversations/utils/Compatibility.java @@ -43,11 +43,8 @@ public class Compatibility { Collections.singletonList("message_notification_settings"); public static boolean hasStoragePermission(final Context context) { - return Build.VERSION.SDK_INT < Build.VERSION_CODES.M - || Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU - || ContextCompat.checkSelfPermission( - context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) - == PackageManager.PERMISSION_GRANTED; + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU || ContextCompat.checkSelfPermission( + context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; } public static boolean s() { @@ -181,6 +178,17 @@ public class Compatibility { } } + @RequiresApi(api = Build.VERSION_CODES.N) + public static boolean isActiveNetworkMetered( + @NonNull final ConnectivityManager connectivityManager) { + try { + return connectivityManager.isActiveNetworkMetered(); + } catch (final RuntimeException e) { + // when in doubt better assume it's metered + return true; + } + } + public static Bundle pgpStartIntentSenderOptions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { return ActivityOptions.makeBasic() From 94f3b1fb577daf127db4f22daf9dd21df1aabe30 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 23 Feb 2024 09:52:43 +0100 Subject: [PATCH 035/363] version bump to 2.14.0-beta + changelog --- CHANGELOG.md | 4 ++++ build.gradle | 4 ++-- fastlane/metadata/android/en-US/changelogs/4209504.txt | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/4209504.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index e20272a2c0d9d548294b726f20f9f07e35338faf..ccd607ce79c091921398b9b83a190c04173ff5cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### Version 2.14.0 + +* Improve integration of A/V calls into the operating system + ### Version 2.13.4 * Fix minor regressions introduced with 2.13.1 diff --git a/build.gradle b/build.gradle index 36ed74d55c8f6ef2cef4dda1839482172c620331..96f35d13d526fc50c2f96c057ef47385f93175b9 100644 --- a/build.gradle +++ b/build.gradle @@ -97,8 +97,8 @@ android { defaultConfig { minSdkVersion 23 targetSdkVersion 34 - versionCode 42094 - versionName "2.13.4" + versionCode 42095 + versionName "2.14.0-beta" archivesBaseName += "-$versionName" applicationId "eu.siacs.conversations" resValue "string", "applicationId", applicationId diff --git a/fastlane/metadata/android/en-US/changelogs/4209504.txt b/fastlane/metadata/android/en-US/changelogs/4209504.txt new file mode 100644 index 0000000000000000000000000000000000000000..94c8f6058d1f4f72690a3ddb102a817a2a79c8d7 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/4209504.txt @@ -0,0 +1 @@ +* Improve integration of A/V calls into the operating system From 34b60bba396faf3c58e86f2322f453b124710704 Mon Sep 17 00:00:00 2001 From: ghose Date: Wed, 21 Feb 2024 06:35:53 +0000 Subject: [PATCH 036/363] Translated using Weblate (Galician) Currently translated at 100.0% (982 of 982 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/ --- src/main/res/values-gl/strings.xml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index fac37134b5e5b5b1c78f7e11e18a47ea2d80cc00..5e5bc7c070bebebe14c4756a2712a0c889fad5ab 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -517,11 +517,10 @@ Texto compartido con %s Permitir que %1$s acceda ao almacenaxe externo Permitir que %1$s acceda á cámara - Sincronice con todos os contactos - %1$s quere ter permiso para acceder á túa libreta de enderezos para comparala coa lista de contactos XMPP. -\nDeste xeito poderá mostrar o nome completo e avatares dos teus contactos. + Integración coa lista de contactos + %1$s procesa a túa lista de contactos de xeito local, no teu dispositivo, para mostrar os nomes e fotos de perfil dos contactos con conta XMPP. \n -\n%1$s só utilizará de xeito local a túa lista de contactos, sen subila a ningún servidor. +\nNingún dato da túa lista de contactos sae do teu dispositivo! Notificar todas as mensaxes Notificar só cando é mencionada Notificacións desactivadas @@ -1031,5 +1030,5 @@ Benvida a Quicksy! Quicksy solicita permiso para usar os teus datos Política de privacidade - Non está dispoñible a integración coa libreta de enderezos + Non está dispoñible a integración coa lista de contactos \ No newline at end of file From 6e7892415a660bf1ce278c6b964fdc88516ca83e Mon Sep 17 00:00:00 2001 From: Mako Date: Wed, 21 Feb 2024 12:55:33 +0000 Subject: [PATCH 037/363] Translated using Weblate (Japanese) Currently translated at 100.0% (982 of 982 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/ --- src/main/res/values-ja/strings.xml | 107 ++++++++++++++++------------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index 80bb88581873fae86ce267d6a68bfffac6757a31..d25240881f90a974134638b86fae727cc095a557 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -7,7 +7,7 @@ 会話を閉じる 連絡先の詳細 グループチャットの詳細 - チャンネルの詳細 + 談話室の詳細 アカウントを追加 名前を編集 アドレス帳に追加 @@ -35,11 +35,11 @@ 送信中… メッセージを復号しています。しばらくお待ちください… OpenPGPで暗号化されたメッセージ - ニックネームは既に使用されています - このニックネームは使えません + ニックネームはすでに使用されています + ニックネームが正しくありません 管理者 所有者 - 調停者 + 司会者 参加者 訪問者 連絡先リストから%sを削除しますか? この連絡先との会話は削除されません。 @@ -160,10 +160,10 @@ サーバーが見つかりません 接続なし 登録に失敗しました - ユーザー名は既に使用されています + ユーザー名はすでに使用されています 登録が完了しました サーバーは登録をサポートしていません - 登録トークンが無効です + 登録トークンが正しくありません TLS ネゴシエーションに失敗しました 検証不可能なドメイン ポリシー違反 @@ -216,12 +216,12 @@ 暗号化されたメッセージです。復号するには OpenKeychain をインストールしてください。 新規の OpenPGP で暗号化されたメッセージが見つかりました OpenPGP 鍵 ID - OMEMO フィンガープリント - v\\OMEMO フィンガープリント - OMEMO フィンガープリント (メッセージ起源) - v\\OMEMO フィンガープリント (メッセージ起源) + OMEMO 指紋 + v\\OMEMO 指紋 + OMEMO 指紋 (メッセージ起源) + v\\OMEMO 指紋 (メッセージ起源) 他のデバイス - OMEMO フィンガープリントを信頼 + OMEMO 指紋を信頼 暗号鍵の取得中… 完了 復号 @@ -233,7 +233,7 @@ 連絡先のブロックを解除 作成 選択 - 連絡先は既に存在します + 連絡先はすでに存在しています 参加 channel@conference.example.com/nick channel@conference.example.com @@ -249,7 +249,7 @@ トピック グループチャットに参加しています… 退出 - 連絡先があなたを連絡先名簿に追加しました + 連絡先があなたを連絡先リストに追加しました 戻りを追加 %s はここまで読みました %s はここまで読みました @@ -267,7 +267,7 @@ %s へ 非公開メッセージを %s へ送信 接続 - このアカウントは既に存在します + このアカウントはすでに存在しています 次へ セッションが確立 スキップ @@ -292,7 +292,7 @@ 消音時間の間、通知は無音になります その他 ブックマーク同期 - OMEMO フィンガープリントをクリップボードにコピーしました + OMEMO 指紋をクリップボードにコピーしました このグループチャットへの参加はブロックされています このグループチャットはメンバー制です リソース制限 @@ -316,8 +316,8 @@ XMPP アドレスをクリップボードにコピーしました エラーメッセージをクリップボードにコピーしました ウェブアドレス - 二次元バーコードをスキャン - 二次元バーコードを表示 + QR コードをスキャン + QR コードを表示 ブロック一覧を表示 アカウントの詳細 確認 @@ -354,7 +354,7 @@ グループチャットのサーバーが見つかりませんでした グループチャットを作成できません アカウントのアバター - クリップボードに OMEMO フィンガープリントをコピー + クリップボードに OMEMO 指紋をコピー OMEMO 鍵を再生成 デバイスを消去 OMEMO の告知から他のすべてのデバイスを消去してもよろしいですか? お使いのデバイスが次回接続したとき、それらのデバイスは自分自身を再告知しますが、その間に送信されたメッセージを受信できない場合があります。 @@ -396,7 +396,7 @@ 公開談話室の環境設定 非公開、メンバーのみ XMPPアドレスを誰でも見れるようにする - 談話室の調停をする + 談話室を司会ありにする あなたは参加していません グループチャットの設定が変更されました! グループチャットの設定を変更できませんでした @@ -509,11 +509,10 @@ %s でテキスト共有 %1$s に外部ストレージへのアクセス権を付与してください %1$s にカメラへのアクセス権を付与 - 連絡先と同期 - %1$s はあなたのアドレス帳にアクセスして、あなたのXMPP 連絡先名簿と照合する権限を求めています。 -\nこれにより、連絡先のフルネームとアバターが表示されます。 + 連絡先リストの統合 + %1$s は、デバイス上で、連絡先リストをローカルに処理し、XMPP 上の一致する連絡先の名前とプロフィール写真を表示します。 \n -\n%1$s は、あなたのサーバーに何かをアップロードすることなく、あなたのアドレス帳を読み込んで照合するだけです。 +\n連絡先リストのデータがデバイスから流出することはありません。 すべてのメッセージで通知 メンションされたときにのみ通知 通知は無効 @@ -531,13 +530,14 @@ このフィールドは必須項目です メッセージを修正 修正したメッセージを送信 - あなたは信頼を確認するために、この人の指紋を安全に検証しました。“完了”を選択すると、 %s がこのグループチャットの一員であることを確認したことになります。 + すでにこの人の指紋を信頼しています。“完了”を選択すると、 %s がこのグループチャットの一員であることを確認したことになります。 このアカウントを無効化しました セキュリティエラー: 不正なファイルアクセス! URI を共有するアプリが見つかりません …で URI を共有 同意して続行 - conversations.im 上にアカウントを作成する設定の指南です。¹\nconversations.im をプロバイダーとして選択した場合、あなたの完全な XMPP アドレスを他のプロバイダーのユーザーに示すことで、その人と連絡をとることができます。 + conversations.im 上にアカウントを作成する設定の指南です。 +\nconversations.im をプロバイダーとして選択した場合、あなたの完全な XMPP アドレスを他のプロバイダーのユーザーに示すことで、その人と連絡をとることができます。 あなたの完全なXMPPアドレスは: %s アカウントを作成 自分のプロバイダーを使用 @@ -589,7 +589,7 @@ アカウントを更新できません この XMPP アドレスをスパムとして報告する。 OMEMO ID を削除 - OMEMO 鍵を再生成します。すべての連絡先を再度確認する必要があります。使用するのは最後の手段のみとしてください。 + OMEMO 鍵を再生成します。すべての連絡先を再度検証する必要があります。使用するのは最後の手段のみとしてください。 選択した鍵を削除 アバターを公開するには接続する必要があります。 エラーメッセージを表示 @@ -599,20 +599,20 @@ お使いのデバイスは、%1$s のデータセーバーを無効にできません。 一時ファイルを作成できません このデバイスは検証済です - フィンガープリントをコピー - 所有するすべての OMEMO 鍵を検証完了 - バーコードに、この会話のフィンガープリントが含まれていません。 - フィンガープリントを検証しました + 指紋をコピー + 所有するすべての OMEMO 鍵を検証しました + バーコードに、この会話の指紋が含まれていません。 + 指紋を検証しました カメラを使用して連絡先のバーコードをスキャンします 鍵が取得されるのをお待ちください バーコードで共有 XMPP URI で共有 HTTP リンクで共有 - 認証前で鍵を使用 - 認証されていない連絡先からの新規デバイスを信頼するが、認証されている連絡先からの新規デバイスについては手動での確認を求める。 - 認証せずOMEMO 鍵を信用しています。このままでは盗聴される危険性があります。 + 検証前に無条件に信頼 + 検証されていない連絡先からの新しいデバイスは信頼しますが、検証済みの連絡先については新しいデバイスを手動で確認するよう求めます。 + 無条件に信頼されている OMEMO 鍵です。他の誰かである可能性や、誰かに盗聴される危険性があります。 信頼されていない - 不正な二次元バーコード + QR コードが正しくありません キャッシュフォルダを消去します (カメラアプリで使用) キャッシュを消去 プライベートストレージを消去 @@ -625,7 +625,8 @@ 非アクティブを表示 非アクティブを非表示 信頼できないデバイス - このデバイスの検証を削除してよろしいですか?\nこのデバイスとそのデバイスからのメッセージは、“信頼できない”とマークされます。 + このデバイスの検証を削除してよろしいですか? +\nこのデバイスとそのデバイスからのメッセージは、“信頼されていない”とマークされます。 %d秒 @@ -644,7 +645,7 @@ %dか月 - 自動でメッセージを削除 + メッセージの自動削除 設定された期間よりも古いメッセージを、このデバイスから自動的に削除します。 メッセージの暗号化中 ローカル保存期間のためにメッセージを取得しません。 @@ -694,7 +695,7 @@ %1$s は %2$s に暗号化メッセージを送れません。連絡先が利用しているサーバーが古すぎるか、クライアントが OMEMO を扱えません。 デバイスの一覧を取得できません 暗号化の鍵を取得できません - ヒント: お互いが連絡先名簿に加えれば解決するでしょう。 + ヒント: お互いが連絡先リストに加えればこの問題は解決するでしょう。 この会話で OMEMO の暗号化を無効化してもよろしいですか?\nこれにより、サーバー管理者がメッセージを読むことが可能になりますが、時代遅れのクライアントを使っている人と連絡をとるには、この方法しかないかもしれません。 今すぐ無効化 下書き: @@ -771,14 +772,14 @@ 中 (360p) 高 (720p) 中止しました - あなたは既にメッセージを作成中です。 + あなたはすでにメッセージを作成中です。 実装されてない機能 - 不正な国コード + 国コードが正しくありません 国を選択 電話番号 電話番号を検証 - Quicksy から電話番号を確認するための SMS メッセージ(キャリア料金がかかる場合があります)が送信されます。国番号と電話番号を入力してください: -
%s

これでよろしいでしょうか、それとも番号を編集しますか?]]>
+ Quicksy から電話番号を検証するための SMS メッセージ(キャリア料金がかかる場合があります)が送信されます。国番号と電話番号を入力してください: + 電話番号の検証を行います

%s

これでよろしいでしょうか、それとも番号を編集しますか?
%s は有効な電話番号ではありません。 電話番号を入力してください。 国を検索 @@ -805,7 +806,7 @@ 安全な接続を確立できませんでした。 サーバーが見つかりません。 要求の処理中に、何か問題が発生しました。 - 無効なユーザーの入力 + ユーザーの入力が正しくありません 一時的に入手不可能です。後でもう一度お試しください。 ネットワーク接続なし。 %s でもう一度お試しください。 @@ -846,9 +847,9 @@ XMPP アドレスをご記入ください これは XMPP アドレスです。名前をご記入ください。 公開談話室を作成中… - この談話室は既に存在します + この談話室はすでに存在しています 存在している談話室に参加しています - 談話室の環境設定を保存できません + 談話室の環境設定を保存できませんでした 誰にでもトピックの編集を許可 誰にでも他の人の招待を許可 誰でもトピックを編集できます。 @@ -864,7 +865,7 @@ 参加者を検索 ファイルが大きすぎます 添付 - 談話室を発見 + 談話室を探索 談話室を検索 プライバシー侵害の可能性あり! 既にアカウントを持っています @@ -908,7 +909,7 @@ 接続切断 撤回された通話 アプリの失敗 - 検証に問題 + 検証の問題 通話を切る 継続中の通話 継続中のビデオ通話 @@ -951,7 +952,7 @@ 音声メールを録音 音声を再生 音声を一時停止 - 連絡先を追加、作成またはグループチャットに参加、またはチャンネルを発見する + 連絡先を追加、作成またはグループチャットに参加、または談話室を探索する %1$d人の参加者を表示 @@ -986,7 +987,7 @@ ログアウトしました 発信通話 · %s オーディオブック - 談話室の発見は<a href=https://search.jabber.network>search.jabber.network</a>というサービスを利用します.<br><br>利用するとIPアドレスと検索語はそのサービスに送信されます。詳細についてはそのサービスの<a href=https://search.jabber.network/privacy>個人情報保護方針</a>を参照してください。 + 談話室の探索は<a href=https://search.jabber.network>search.jabber.network</a>というサービスを利用します.<br><br>利用するとIPアドレスと検索語はそのサービスに送信されます。詳細についてはそのサービスの<a href=https://search.jabber.network/privacy>個人情報保護方針</a>を参照してください。 自分で保存したバックアップしか復元しないでください! XMPP経由でPushメッセージを端末に転送するユーザー指定のPushサーバー。 ログイン @@ -1002,4 +1003,14 @@ Pushメッセージを受信する際に経由するアカウント。 サーバーからアカウントを削除できませんでした 概要 + トラックを追加しますか ? + 他のホストで再接続 + 連絡先リストの統合は利用できません + 個人情報保護方針 + スパムを報告し、スパマーをブロック + MUC に出入りするときに「自動参加」フラグを立て、他のクライアントによる変更に反応します。 + Quicksy はあなたのデータを使用することについて同意を求めています + Quicksy へようこそ ! + 連絡先は未検証のデバイスを使用しています。 QR コードをスキャンして検証を実行し、アクティブな MITM 攻撃を阻止してください。 + 未検証のデバイスを使用しています。他のデバイスで QR コードをスキャンして検証を実行し、アクティブな MITM 攻撃を阻止してください。 \ No newline at end of file From aad750f2c87e8047256997e6ce63de30aa81e3e5 Mon Sep 17 00:00:00 2001 From: Besnik_b Date: Tue, 20 Feb 2024 21:51:16 +0000 Subject: [PATCH 038/363] Translated using Weblate (Albanian) Currently translated at 98.5% (968 of 982 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/ --- src/main/res/values-sq-rAL/strings.xml | 62 ++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/src/main/res/values-sq-rAL/strings.xml b/src/main/res/values-sq-rAL/strings.xml index c86c426b67f73b47c3aa40c84d3c6a8c4611ccab..968d845e913373dab2a910f12a324f4287ac17b9 100644 --- a/src/main/res/values-sq-rAL/strings.xml +++ b/src/main/res/values-sq-rAL/strings.xml @@ -254,8 +254,8 @@ Adresa XMPP u kopjua në clipboard Mesazhi i gabimit u kopjua në të papastër adresë web - Skano Kod me vija 2D - Shfaq Kod me vija 2D + Skano Kod QR + Shfaq Kod QR Shfaqe listë bllokimesh Hollësi llogarie Ripohojeni @@ -403,7 +403,7 @@ Portë Ky s’është numër i vlefshëm porte Ky s’është një strehëemër i vlefshëm - Njëkohësoje me kontaktet + Integrim liste kontaktesh Njofto për krejt mesazhet Njoftomë vetëm kur përmendem Njoftime të çaktivizuara @@ -480,7 +480,7 @@ Ndajeni me të tjerë si lidhje HTTP Besim i Verbër Para Verifikimi Jo i besuar - Kod 2D me vija i pavlefshëm + Kod QR i pavlefshëm Spastro fshehtinën Spastro depozitë private Vazhdoni @@ -830,7 +830,7 @@ Pajisja juaj përdor optimizime shumë të thella baterie për %1$s, çka mund të shpjerë në vonesa njoftimesh, ose madje edhe humbje mesazhesh. \n \nTani do t’ju kërkohet t’i çaktivizoni ato. - I keni vlerësuar në mënyrë të parrezik shenjat e gishtave të këtij personi, për të ripohuar besimin. Duke përzgjedhur “U bë”, thjesht po ripohoni se %s është pjesë e kësaj fjalosjeje në grup. + I keni besuar tashmë shenjat e gishtave të këtij personi. Duke përzgjedhur “U bë”, thjesht po ripohoni se %s është pjesë e kësaj fjalosjeje në grup. Ju ndan një hap nga verifikimi i kyçeve OMEMO të llogarisë tuaj. Kjo është e sigurt vetëm nëse e ndoqët këtë lidhje prej një burimi të besuar, ku vetëm ju do të mund ta kishit publikuar këtë lidhje. Jeni i sigurt se doni të hiqet verifikimi i kësaj pajisjeje\? \nKësaj pajisjeje dhe mesazheve prej saj do t’u vihet shenjë si “Jo i besuar”. @@ -869,7 +869,7 @@ Aplikacioni që përdorët për të dhënë këtë kartelë, nuk jep leje të mjaftueshme. Jeni i sigurt se doni të hiqet kyçi juaj publik OpenPGP nga njoftimi juaj për prani\? \nKontaktet tuaj s’do të jenë më në gjendje t’ju dërgojnë mesazhe të fshehtëzuar me OpenPGP. - Fshirja e llogarisë tuaj fshin krejt historikun e bisedave tuaja + Jeni i sigurt se doni të fshihet llogaria juaj? Fshirja e saj shkakton fshirjen e krejt historikut të bisedave tuaja Mesazh i fshehtëzuar. Që ta shfshehtëzoni, ju lutemi, instaloni OpenKeychain. Shenja gishtash OMEMO (origjinë mesazhi) Shenja gishtash v\\OMEMO (origjinë mesazhi) @@ -925,10 +925,9 @@ Figurat iu dhanë %s Teksti iu dha %s Akordoji %1$s hyrje te depozitë e jashtme - %1$s dëshiron leje të përdorë librin tuaj të adresave, për përkim me listën tuaj të kontakteve XMPP. -\nKjo do të sjellë shfaqjen e emrave të plotë dhe avatarëve të kontakteve tuaj. + %1$s e përpunon lokalisht listën tuaj të kontakteve, në pajisjen tuaj, për t’ju shfaqur emrat dhe foto profili për kontakte me përkim në XMPP. \n -\n%1$s vetëm sa do të lexojë librin tuaj të adresave dhe bëjë lokalisht përkimin, pa ngarkuar gjë në shërbyesin tuaj. +\nNga pajisja juaj s’del kurrë ndonjë e dhënë liste kontaktesh! Ndihmëz: Përdorni “Zgjidhni kartelë”, në vend se “Zgjidhni foto”, për të dërguar figura të pangjeshura, pavarësisht nga ky rregullim. Pajisja juaj përdor optimizime shumë të thella baterie për %1$s, çka mund të shpjerë në vonesa njoftimesh, ose madje edhe humbje mesazhesh. \nRekomandohet të çaktivizohen ato. @@ -993,4 +992,49 @@ %1$d thirrje të humbur prej %2$d kontakti %1$d thirrje të humbur prej %2$d kontaktesh + Mos u rrekni të riktheni kopjeruajtje që s’i keni krijuar ju vetë! + Të shtohen pjesë shtesë? + XEP-0280: Message Carbons + Rilidhu te një tjetër strehë + Ju është kufizuar shpejtësia + jabber.network + kërkoni në fjalosje grupesh + Fjalosje në grup + Ruaje si fjalosjeje grupi + Po përdorni pajisje të paverifikuara. Që të kryhet verifikimi dhe të pengohen sulme MITM, skanoni Kodin QR në pajisjet tuaja të tjera. + Listë bllokimesh + I dalë + XEP-0352: Client State Indication + XEP-0191: Blocking Command + XEP-0237: Roster Versioning + XEP-0215: External Service Discovery + XEP-0357: Push + Njoftime për mungesë kyçi publik + Publikoje + Citim + Ç’jepet + Audiolibër + Pasje + Shërbyes A- ose adresë Onion + Quicksy kërkon pranimin tuaj që të përdorë të dhëna tuajat + Keni dalë nga kjo llogari + Mirë se vini te Quicksy! + Caktoni gjendjen tuaj, kur përpunoni mesazhin për të. + Kyçe OMEMO të besuar verbërisht, që do të thotë mund të jetë dikush tjetër, ose dikush ka arritur të përgjojë. + S’ka aplikacion tregu të instaluar. + Po rrekeni të importoni një format të vjetruar kartelash kopjeruajtje + Thirrje ardhëse (%s) · %s + Thirrje për (%s) · %s + Thirrje për · %s + Hidhe poshtë + Hiqe llogarinë nga shërbyesi + S’u fshi dot llogari nga shërbyesi + Fshihe njoftimin + Dilni + Hyni + Raportoni mesazh të padëshiruar + Raportoni mesazh të padëshiruar dhe bllokoni dërguesin + Rregulla privatësie + Integrimi i listës së kontakteve s’është i mundshmë + Kontakti juaj përdor pajisje të paverifikuara. Që të kryhet verifikimi dhe të pengohen sulme MITM, skanoni Kodin e tij QR. \ No newline at end of file From b8a56c4d618be7be59a88c1263af72ef2e888168 Mon Sep 17 00:00:00 2001 From: Outbreak2096 Date: Wed, 21 Feb 2024 11:02:05 +0000 Subject: [PATCH 039/363] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (982 of 982 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/ --- src/main/res/values-zh-rCN/strings.xml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 216c355d84f4ca2f983ff5a1524140885a9a7479..98d14d5332415db880bcbcb81faf1e5f859ac64f 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -529,11 +529,10 @@ 文本已分享给 %s 授予 %1$s 访问外部存储的权限 授予 %1$s 访问相机的权限 - 与联系人同步 - %1$s 想要访问通讯录权限来将它与 XMPP 联系人列表相匹配。 -\n将会显示联系人的全名和头像。 + 联系人列表集成 + %1$s 在您的设备上本地处理您的联系人列表,以向您显示 XMPP 上匹配联系人的名称和个人资料图片。 \n -\n%1$s 将只会读取通讯录并在本地匹配,不会上传任何内容到服务器。 +\n任何联系人列表数据都不会离开您的设备! 通知所有消息 仅在提及时通知 通知已禁用 @@ -1037,5 +1036,5 @@ 欢迎使用 Quicksy! Quicksy 请求您同意使用您的数据 隐私政策 - 通讯录集成不可用 + 联系人列表集成不可用 \ No newline at end of file From 6c19c46742283d2b6f567a69f656c6d04fb7f4ad Mon Sep 17 00:00:00 2001 From: Outbreak2096 Date: Wed, 21 Feb 2024 11:03:38 +0000 Subject: [PATCH 040/363] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (57 of 57 strings) Translation: Conversations/App Store Metadata (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/ --- fastlane/metadata/android/zh-CN/changelogs/4209404.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 fastlane/metadata/android/zh-CN/changelogs/4209404.txt diff --git a/fastlane/metadata/android/zh-CN/changelogs/4209404.txt b/fastlane/metadata/android/zh-CN/changelogs/4209404.txt new file mode 100644 index 0000000000000000000000000000000000000000..e89631ebe6f2a1c872b1748483b7bc1967ce8f02 --- /dev/null +++ b/fastlane/metadata/android/zh-CN/changelogs/4209404.txt @@ -0,0 +1 @@ +* 修复 2.13.1 中出现的小问题 From 676acce1e299b7a83c23c5877ddebf84b9fdc5fa Mon Sep 17 00:00:00 2001 From: Besnik_b Date: Tue, 20 Feb 2024 21:08:27 +0000 Subject: [PATCH 041/363] Translated using Weblate (Albanian) Currently translated at 100.0% (2 of 2 strings) Translation: Conversations/App Store Metadata (Quicksy) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/sq/ --- .../metadata/android/sq/full_description.txt | 14 ++++++++++++++ .../metadata/android/sq/short_description.txt | 1 + 2 files changed, 15 insertions(+) create mode 100644 src/quicksy/fastlane/metadata/android/sq/full_description.txt create mode 100644 src/quicksy/fastlane/metadata/android/sq/short_description.txt diff --git a/src/quicksy/fastlane/metadata/android/sq/full_description.txt b/src/quicksy/fastlane/metadata/android/sq/full_description.txt new file mode 100644 index 0000000000000000000000000000000000000000..5624b1bd1c917082d4dae641b8e7c1d1b9741459 --- /dev/null +++ b/src/quicksy/fastlane/metadata/android/sq/full_description.txt @@ -0,0 +1,14 @@ +Quicksy është një program me fillesë klientin popullor Jabber/XMPP, me zbulim të automatizuar kontaktesh. + +Regjistroheni me numrin tuaj të telefonit dhe Quicksy—baszuar në numrat e telefonave në librin tuaj të adresave—do t’ju sugjerojë automatikisht kontakte të mundshëm. + +Nën kapak, Quicksy është një klient i plotë Jabber, që ju lejon të komunikoni me cilindo përdorues në cilindo shërbyes federimi publik. Në mënyrë të ngjashme, me përdoruesit në Quicksy mund të lidhesh që nga jashtë thjesht duke shtuar +phonenumber@quicksy.im te lista juaj e kontakteve. + +Hiq njëkohësimin e kontakteve, ndërfaqja e përdoruesit është lënë qëllimisht sa më afër Conversations që të jetë e mundur. Kjo u lejon përdoruesve që të migrojnë, nëse duan, nga Quicksy në Conversations pa u dashur të rimësojnë se si funksionon aplikacioni. + +Kontaktet e sugjeruara përbëhen nga përdorues të tjerë të Quicksy-it dhe përdorues Jabber-i/XMPP-je të zakonshëm që kanë dhënë ID-në e tyre Jabber te Lista Quicksy (https://quicksy.im/#get-listed). + +SHËNIM: Që të jepni (https://quicksy.im/enter/) ID-në tuaj Jabber te Lista +Quicksy, lypset një tarifë regjistrimi e aplikueshme një herë vetëm. + +Për më tepër hollësi, lexoni Rregulla Privatësie (https://quicksy.im/#privacy). diff --git a/src/quicksy/fastlane/metadata/android/sq/short_description.txt b/src/quicksy/fastlane/metadata/android/sq/short_description.txt new file mode 100644 index 0000000000000000000000000000000000000000..05a7f29696560d2291cd58fcfa1136804554fde3 --- /dev/null +++ b/src/quicksy/fastlane/metadata/android/sq/short_description.txt @@ -0,0 +1 @@ +Jabber/XMPP me Hyrje të Lehtë dhe Zbulim të Lehtë From f3390a54bc56b6970145769a5bc8b2a67be9b9f5 Mon Sep 17 00:00:00 2001 From: Application-Maker Date: Thu, 22 Feb 2024 07:55:20 +0000 Subject: [PATCH 042/363] Translated using Weblate (Russian) Currently translated at 100.0% (982 of 982 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/ --- src/main/res/values-ru/strings.xml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index eb43f114248c60fd38eedd7d0a8a9437fe2ac751..fe5e5bc2dcac7f16a9ed206842169afd91c505c9 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -130,7 +130,7 @@ Время, на которое уведомления будут отключены, когда вы пользуетесь аккаунтом на другом устройстве. Дополнительно Не отправлять отчёты об ошибках - Отправляя отчёты об ошибках, вы помогаете разработке Quicksy + Отправляя отчёты об ошибках, вы помогаете разработке Отчёты о получении Позволяет вашим контактам видеть, когда вы получили и прочитали их сообщения Запретить скриншоты @@ -513,11 +513,10 @@ Текст отправлен %s Предоставить %1$s разрешение на использование внешнего накопителя Предоставить %1$s разрешение на использование камеры - Синхронизировать с контактами - %1$s нужно разрешение на доступ к контактам, чтобы соотнести их с вашими XMPP-контактами. -\nЭто позволит отобразить полные имена и аватары контактов. + Интеграция списка контактов + %1$s обрабатывает ваш список контактов локально, на вашем устройстве, чтобы показать вам имена и изображения профиля для сопоставления контактов по XMPP. \n -\n%1$s сделает это локально, без отправки чего-либо на ваш сервер. +\nНикакие данные списка контактов никогда не покидают ваше устройство! Все сообщения Уведомлять только при упоминании Уведомления выключены @@ -993,7 +992,7 @@ Несовместимый клиент Групповые беседы - медиафайл + файл мультимедиа Продолжить Сервер уведомлений Распределитель UnifiedPush @@ -1028,7 +1027,7 @@ Выбираемый пользователем сервер для перенаправления уведомлений на Ваше устройство. Обзор каналов использует сторонний сервис <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Эта функция передаст Ваш IP-адрес и ваш поисковый запрос этому сервису. Ознакомьтесь с их <a href=https://search.jabber.network/privacy>Политикой приватности</a> для получения подробностей. Нет (неактивно) - Вы подтверждаете ваши собственные OMEMO-ключи. Это безопасно только если вы перешли по ссылке из доверенного источника, где только вы могли разместить эту ссылку. + Вы собираетесь проверить ключи OMEMO своей учетной записи. Это безопасно только в том случае, если вы перешли по этой ссылке из надежного источника, где только вы могли опубликовать эту ссылку. Не пытайтесь восстановить резервные копии, которые не были созданы вами! %1$d пропущенный вызов от %2$s @@ -1050,8 +1049,12 @@ Скрыть уведомление Ваш контакт использует неподтверждённые устройства. Отсканируйте его QR-код для проверки и предотвращения атаки посредника. Выйти - Деавторизован + Вышел из системы Вы используете неподтверждённые устройства. Отсканируйте QR-код на подтверждённом устройстве для проверки и предотвращения атаки посредника. Пожаловаться на спам и заблокировать Пожаловаться на спам + Политика конфиденциальности + Интеграция списка контактов недоступна + Quicksy запрашивает ваше согласие на использование ваших данных + Добро пожаловать в Quicksy! \ No newline at end of file From d38c8b2e9d38590ab8b56f1b599430754824708c Mon Sep 17 00:00:00 2001 From: licaon-kter Date: Thu, 22 Feb 2024 11:31:15 +0000 Subject: [PATCH 043/363] Translated using Weblate (Romanian) Currently translated at 100.0% (982 of 982 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/ --- src/main/res/values-ro-rRO/strings.xml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index 8d06a15bcf5f23e1b3a1382f1b6c6714b1b67fc5..ae91b368a4c566485445212a2b9b1c14224996aa 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -520,11 +520,10 @@ Text partajat cu %s Permiteți %1$s acces la stocarea externă Permiteți %1$s acces la camera foto - Sincronizează cu contactele - %1$s dorește permisiunea de a vă accesa contactele pentru a putea potrivi lista de contacte XMPP cu cea din dispozitiv. -\nAșa v-a afișa numele lor complete și avatarele. + Integrarea listei de contacte + %1$s procesează lista de contacte la nivel local, pe dispozitiv, pentru a vă arăta numele și pozele de profil ale contactelor pe care le aveți și pe XMPP. \n -\n%1$s va citi și potrivi local fără a fi încărcate pe serverul dumneavoastră. +\nDatele din lista de contacte nu părăsesc niciodată dispozitivul dumneavoastră! Notifică la toate mesajele Notifică doar atunci când cineva vă menționează numele Notificări dezactivate @@ -1046,6 +1045,6 @@ Raportează spam Bine ați venit la Quicksy! Quicksy vă solicită consimțământul pentru a utiliza datele dumneavoastră - Integrarea agendei nu este disponibilă + Integrarea listei de contacte nu este disponibilă Politica de confidențialitate \ No newline at end of file From b381f125de1711745d1217c856d82c9180e46d33 Mon Sep 17 00:00:00 2001 From: ghose Date: Thu, 22 Feb 2024 05:01:10 +0000 Subject: [PATCH 044/363] Translated using Weblate (Galician) Currently translated at 50.8% (29 of 57 strings) Translation: Conversations/App Store Metadata (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/ --- fastlane/metadata/android/gl-ES/changelogs/4209404.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 fastlane/metadata/android/gl-ES/changelogs/4209404.txt diff --git a/fastlane/metadata/android/gl-ES/changelogs/4209404.txt b/fastlane/metadata/android/gl-ES/changelogs/4209404.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc1d02e69718482c2855c14722fe64f064b0b00a --- /dev/null +++ b/fastlane/metadata/android/gl-ES/changelogs/4209404.txt @@ -0,0 +1 @@ +* Arranxo de regresións menores introducidas en 2.13.1 From 93867694096ab5d4f66740a6cffab11ac13efd7e Mon Sep 17 00:00:00 2001 From: acioustick Date: Thu, 22 Feb 2024 16:03:27 +0000 Subject: [PATCH 045/363] Translated using Weblate (Japanese) Currently translated at 50.0% (1 of 2 strings) Translation: Conversations/App Store Metadata (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/ja/ --- .../fastlane/metadata/android/ja-JP/short_description.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/conversations/fastlane/metadata/android/ja-JP/short_description.txt diff --git a/src/conversations/fastlane/metadata/android/ja-JP/short_description.txt b/src/conversations/fastlane/metadata/android/ja-JP/short_description.txt new file mode 100644 index 0000000000000000000000000000000000000000..ade292722c0c54d2a8808802df9b0ddf550e6fc8 --- /dev/null +++ b/src/conversations/fastlane/metadata/android/ja-JP/short_description.txt @@ -0,0 +1 @@ +携帯端末で簡単に操作できるXMPP暗号化インスタント・メッセンジャー From bd2b9b414e1f4ca83fbc7fe55aa3527d09018226 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 23 Feb 2024 17:58:33 +0100 Subject: [PATCH 046/363] do not enforce main thread for getting audio devices fixes #206 --- .../services/AppRTCAudioManager.java | 339 ++++++++---------- 1 file changed, 155 insertions(+), 184 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java index b256d66557ed7240cd58aefd9fda2848e59749ae..2cd3ac3468601bb9e9474ef1c3b7c5616bf784dc 100644 --- a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java +++ b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java @@ -15,42 +15,31 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.media.AudioDeviceInfo; -import android.media.AudioFormat; import android.media.AudioManager; -import android.media.AudioRecord; -import android.media.MediaRecorder; -import android.os.Build; import android.util.Log; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; +import com.google.common.collect.ImmutableSet; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.utils.AppRTCUtils; + import org.webrtc.ThreadUtils; -import java.util.Collections; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.CountDownLatch; -import eu.siacs.conversations.Config; -import eu.siacs.conversations.utils.AppRTCUtils; -import eu.siacs.conversations.xmpp.jingle.Media; - -/** - * AppRTCAudioManager manages all audio related parts of the AppRTC demo. - */ +/** AppRTCAudioManager manages all audio related parts of the AppRTC demo. */ public class AppRTCAudioManager { - private static CountDownLatch microphoneLatch; - private final Context apprtcContext; // Contains speakerphone setting: auto, true or false // Handles all tasks related to Bluetooth headset devices. private final AppRTCBluetoothManager bluetoothManager; - @Nullable - private final AudioManager audioManager; - @Nullable - private AudioManagerEvents audioManagerEvents; + @Nullable private final AudioManager audioManager; + @Nullable private AudioManagerEvents audioManagerEvents; private AudioManagerState amState; private boolean savedIsSpeakerPhoneOn; private boolean savedIsMicrophoneMute; @@ -76,8 +65,7 @@ public class AppRTCAudioManager { // Broadcast receiver for wired headset intent broadcasts. private final BroadcastReceiver wiredHeadsetReceiver; // Callback method for changes in audio focus. - @Nullable - private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener; + @Nullable private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener; public AppRTCAudioManager(final Context context) { apprtcContext = context; @@ -95,7 +83,6 @@ public class AppRTCAudioManager { AppRTCUtils.logDeviceInfo(Config.LOGTAG); } - @SuppressWarnings("deprecation") public void start(final AudioManagerEvents audioManagerEvents) { Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".start()"); @@ -104,7 +91,6 @@ public class AppRTCAudioManager { Log.e(Config.LOGTAG, "AudioManager is already active"); return; } - awaitMicrophoneLatch(); this.audioManagerEvents = audioManagerEvents; amState = AudioManagerState.RUNNING; // Store current audio state so we can restore it when stop() is called. @@ -112,48 +98,45 @@ public class AppRTCAudioManager { savedIsMicrophoneMute = audioManager.isMicrophoneMute(); hasWiredHeadset = hasWiredHeadset(); // Create an AudioManager.OnAudioFocusChangeListener instance. - audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { - // Called on the listener to notify if the audio focus for this listener has been changed. - // The |focusChange| value indicates whether the focus was gained, whether the focus was lost, - // and whether that loss is transient, or whether the new focus holder will hold it for an - // unknown amount of time. - // TODO(henrika): possibly extend support of handling audio-focus changes. Only contains - // logging for now. - @Override - public void onAudioFocusChange(int focusChange) { - final String typeOfChange; - switch (focusChange) { - case AudioManager.AUDIOFOCUS_GAIN: - typeOfChange = "AUDIOFOCUS_GAIN"; - break; - case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: - typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT"; - break; - case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: - typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE"; - break; - case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: - typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"; - break; - case AudioManager.AUDIOFOCUS_LOSS: - typeOfChange = "AUDIOFOCUS_LOSS"; - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: - typeOfChange = "AUDIOFOCUS_LOSS_TRANSIENT"; - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: - typeOfChange = "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"; - break; - default: - typeOfChange = "AUDIOFOCUS_INVALID"; - break; - } - Log.d(Config.LOGTAG, "onAudioFocusChange: " + typeOfChange); - } - }; + audioFocusChangeListener = + new AudioManager.OnAudioFocusChangeListener() { + // Called on the listener to notify if the audio focus for this listener has + // been changed. + // The |focusChange| value indicates whether the focus was gained, whether the + // focus was lost, + // and whether that loss is transient, or whether the new focus holder will hold + // it for an + // unknown amount of time. + // TODO(henrika): possibly extend support of handling audio-focus changes. Only + // contains + // logging for now. + @Override + public void onAudioFocusChange(final int focusChange) { + final String typeOfChange = + switch (focusChange) { + case AudioManager.AUDIOFOCUS_GAIN -> "AUDIOFOCUS_GAIN"; + case AudioManager + .AUDIOFOCUS_GAIN_TRANSIENT -> "AUDIOFOCUS_GAIN_TRANSIENT"; + case AudioManager + .AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE -> "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE"; + case AudioManager + .AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK -> "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"; + case AudioManager.AUDIOFOCUS_LOSS -> "AUDIOFOCUS_LOSS"; + case AudioManager + .AUDIOFOCUS_LOSS_TRANSIENT -> "AUDIOFOCUS_LOSS_TRANSIENT"; + case AudioManager + .AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"; + default -> "AUDIOFOCUS_INVALID"; + }; + Log.d(Config.LOGTAG, "onAudioFocusChange: " + typeOfChange); + } + }; // Request audio playout focus (without ducking) and install listener for changes in focus. - int result = audioManager.requestAudioFocus(audioFocusChangeListener, - AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + int result = + audioManager.requestAudioFocus( + audioFocusChangeListener, + AudioManager.STREAM_VOICE_CALL, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { Log.d(Config.LOGTAG, "Audio focus request granted for VOICE_CALL streams"); } else { @@ -182,21 +165,9 @@ public class AppRTCAudioManager { Log.d(Config.LOGTAG, "AudioManager started"); } - private void awaitMicrophoneLatch() { - final CountDownLatch latch = microphoneLatch; - if (latch == null) { - return; - } - try { - latch.await(); - } catch (InterruptedException e) { - //ignore - } - } - @SuppressWarnings("deprecation") public void stop() { - Log.d(Config.LOGTAG,"appRtpAudioManager.stop()"); + Log.d(Config.LOGTAG, "appRtpAudioManager.stop()"); Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".stop()"); ThreadUtils.checkIsOnMainThread(); if (amState != AudioManagerState.RUNNING) { @@ -214,60 +185,44 @@ public class AppRTCAudioManager { audioManager.abandonAudioFocus(audioFocusChangeListener); audioFocusChangeListener = null; audioManagerEvents = null; - Log.d(Config.LOGTAG,"appRtpAudioManager.stopped()"); + Log.d(Config.LOGTAG, "appRtpAudioManager.stopped()"); } - /** - * Changes selection of the currently active audio device. - */ - private void setAudioDeviceInternal(CallIntegration.AudioDevice device) { + /** Changes selection of the currently active audio device. */ + private void setAudioDeviceInternal(final CallIntegration.AudioDevice device) { Log.d(Config.LOGTAG, "setAudioDeviceInternal(device=" + device + ")"); AppRTCUtils.assertIsTrue(audioDevices.contains(device)); switch (device) { - case SPEAKER_PHONE: - setSpeakerphoneOn(true); - break; - case EARPIECE: - case WIRED_HEADSET: - case BLUETOOTH: - setSpeakerphoneOn(false); - break; - default: - Log.e(Config.LOGTAG, "Invalid audio device selection"); - break; + case SPEAKER_PHONE -> setSpeakerphoneOn(true); + case EARPIECE, WIRED_HEADSET, BLUETOOTH -> setSpeakerphoneOn(false); + default -> Log.e(Config.LOGTAG, "Invalid audio device selection"); } selectedAudioDevice = device; } /** - * Changes default audio device. - * TODO(henrika): add usage of this method in the AppRTCMobile client. + * Changes default audio device. TODO(henrika): add usage of this method in the AppRTCMobile + * client. */ - public void setDefaultAudioDevice(CallIntegration.AudioDevice defaultDevice) { + public void setDefaultAudioDevice(final CallIntegration.AudioDevice defaultDevice) { ThreadUtils.checkIsOnMainThread(); switch (defaultDevice) { - case SPEAKER_PHONE: - defaultAudioDevice = defaultDevice; - break; - case EARPIECE: + case SPEAKER_PHONE -> defaultAudioDevice = defaultDevice; + case EARPIECE -> { if (hasEarpiece()) { defaultAudioDevice = defaultDevice; } else { defaultAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE; } - break; - default: - Log.e(Config.LOGTAG, "Invalid default audio device selection"); - break; + } + default -> Log.e(Config.LOGTAG, "Invalid default audio device selection"); } Log.d(Config.LOGTAG, "setDefaultAudioDevice(device=" + defaultAudioDevice + ")"); updateAudioDeviceState(); } - /** - * Changes selection of the currently active audio device. - */ - public void selectAudioDevice(CallIntegration.AudioDevice device) { + /** Changes selection of the currently active audio device. */ + public void selectAudioDevice(final CallIntegration.AudioDevice device) { ThreadUtils.checkIsOnMainThread(); if (!audioDevices.contains(device)) { Log.e(Config.LOGTAG, "Can not select " + device + " from available " + audioDevices); @@ -276,38 +231,27 @@ public class AppRTCAudioManager { updateAudioDeviceState(); } - /** - * Returns current set of available/selectable audio devices. - */ + /** Returns current set of available/selectable audio devices. */ public Set getAudioDevices() { - ThreadUtils.checkIsOnMainThread(); - return Collections.unmodifiableSet(new HashSet<>(audioDevices)); + return ImmutableSet.copyOf(audioDevices); } - /** - * Returns the currently selected audio device. - */ + /** Returns the currently selected audio device. */ public CallIntegration.AudioDevice getSelectedAudioDevice() { return selectedAudioDevice; } - /** - * Helper method for receiver registration. - */ + /** Helper method for receiver registration. */ private void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { apprtcContext.registerReceiver(receiver, filter); } - /** - * Helper method for unregistration of an existing receiver. - */ + /** Helper method for unregistration of an existing receiver. */ private void unregisterReceiver(BroadcastReceiver receiver) { apprtcContext.unregisterReceiver(receiver); } - /** - * Sets the speaker phone mode. - */ + /** Sets the speaker phone mode. */ private void setSpeakerphoneOn(boolean on) { boolean wasOn = audioManager.isSpeakerphoneOn(); if (wasOn == on) { @@ -316,9 +260,7 @@ public class AppRTCAudioManager { audioManager.setSpeakerphoneOn(on); } - /** - * Sets the microphone mute state. - */ + /** Sets the microphone mute state. */ private void setMicrophoneMute(boolean on) { boolean wasMuted = audioManager.isMicrophoneMute(); if (wasMuted == on) { @@ -327,53 +269,57 @@ public class AppRTCAudioManager { audioManager.setMicrophoneMute(on); } - /** - * Gets the current earpiece state. - */ + /** Gets the current earpiece state. */ private boolean hasEarpiece() { return apprtcContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); } /** - * Checks whether a wired headset is connected or not. - * This is not a valid indication that audio playback is actually over - * the wired headset as audio routing depends on other conditions. We - * only use it as an early indicator (during initialization) of an attached - * wired headset. + * Checks whether a wired headset is connected or not. This is not a valid indication that audio + * playback is actually over the wired headset as audio routing depends on other conditions. We + * only use it as an early indicator (during initialization) of an attached wired headset. */ @Deprecated private boolean hasWiredHeadset() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return audioManager.isWiredHeadsetOn(); - } else { - final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL); - for (AudioDeviceInfo device : devices) { - final int type = device.getType(); - if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET) { - Log.d(Config.LOGTAG, "hasWiredHeadset: found wired headset"); - return true; - } else if (type == AudioDeviceInfo.TYPE_USB_DEVICE) { - Log.d(Config.LOGTAG, "hasWiredHeadset: found USB audio device"); - return true; - } + final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL); + for (AudioDeviceInfo device : devices) { + final int type = device.getType(); + if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET) { + Log.d(Config.LOGTAG, "hasWiredHeadset: found wired headset"); + return true; + } else if (type == AudioDeviceInfo.TYPE_USB_DEVICE) { + Log.d(Config.LOGTAG, "hasWiredHeadset: found USB audio device"); + return true; } - return false; } + return false; } /** - * Updates list of possible audio devices and make new device selection. - * TODO(henrika): add unit test to verify all state transitions. + * Updates list of possible audio devices and make new device selection. TODO(henrika): add unit + * test to verify all state transitions. */ public void updateAudioDeviceState() { ThreadUtils.checkIsOnMainThread(); - Log.d(Config.LOGTAG, "--- updateAudioDeviceState: " - + "wired headset=" + hasWiredHeadset + ", " - + "BT state=" + bluetoothManager.getState()); - Log.d(Config.LOGTAG, "Device status: " - + "available=" + audioDevices + ", " - + "selected=" + selectedAudioDevice + ", " - + "user selected=" + userSelectedAudioDevice); + Log.d( + Config.LOGTAG, + "--- updateAudioDeviceState: " + + "wired headset=" + + hasWiredHeadset + + ", " + + "BT state=" + + bluetoothManager.getState()); + Log.d( + Config.LOGTAG, + "Device status: " + + "available=" + + audioDevices + + ", " + + "selected=" + + selectedAudioDevice + + ", " + + "user selected=" + + userSelectedAudioDevice); // Check if any Bluetooth headset is connected. The internal BT state will // change accordingly. // TODO(henrika): perhaps wrap required state into BT manager. @@ -410,12 +356,14 @@ public class AppRTCAudioManager { // If BT is not available, it can't be the user selection. userSelectedAudioDevice = CallIntegration.AudioDevice.NONE; } - if (hasWiredHeadset && userSelectedAudioDevice == CallIntegration.AudioDevice.SPEAKER_PHONE) { + if (hasWiredHeadset + && userSelectedAudioDevice == CallIntegration.AudioDevice.SPEAKER_PHONE) { // If user selected speaker phone, but then plugged wired headset then make // wired headset as user selected device. userSelectedAudioDevice = CallIntegration.AudioDevice.WIRED_HEADSET; } - if (!hasWiredHeadset && userSelectedAudioDevice == CallIntegration.AudioDevice.WIRED_HEADSET) { + if (!hasWiredHeadset + && userSelectedAudioDevice == CallIntegration.AudioDevice.WIRED_HEADSET) { // If user selected wired headset, but then unplugged wired headset then make // speaker phone as user selected device. userSelectedAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE; @@ -425,20 +373,30 @@ public class AppRTCAudioManager { boolean needBluetoothAudioStart = bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE && (userSelectedAudioDevice == CallIntegration.AudioDevice.NONE - || userSelectedAudioDevice == CallIntegration.AudioDevice.BLUETOOTH); + || userSelectedAudioDevice + == CallIntegration.AudioDevice.BLUETOOTH); // Need to stop Bluetooth audio if user selected different device and // Bluetooth SCO connection is established or in the process. boolean needBluetoothAudioStop = (bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED - || bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTING) + || bluetoothManager.getState() + == AppRTCBluetoothManager.State.SCO_CONNECTING) && (userSelectedAudioDevice != CallIntegration.AudioDevice.NONE - && userSelectedAudioDevice != CallIntegration.AudioDevice.BLUETOOTH); + && userSelectedAudioDevice + != CallIntegration.AudioDevice.BLUETOOTH); if (bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE || bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTING || bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED) { - Log.d(Config.LOGTAG, "Need BT audio: start=" + needBluetoothAudioStart + ", " - + "stop=" + needBluetoothAudioStop + ", " - + "BT state=" + bluetoothManager.getState()); + Log.d( + Config.LOGTAG, + "Need BT audio: start=" + + needBluetoothAudioStart + + ", " + + "stop=" + + needBluetoothAudioStop + + ", " + + "BT state=" + + bluetoothManager.getState()); } // Start or stop Bluetooth SCO connection given states set earlier. if (needBluetoothAudioStop) { @@ -467,7 +425,8 @@ public class AppRTCAudioManager { } else { // No wired headset and no Bluetooth, hence the audio-device list can contain speaker // phone (on a tablet), or speaker phone and earpiece (on mobile phone). - // |defaultAudioDevice| contains either AudioDevice.SPEAKER_PHONE or AudioDevice.EARPIECE + // |defaultAudioDevice| contains either AudioDevice.SPEAKER_PHONE or + // AudioDevice.EARPIECE // depending on the user's selection. newAudioDevice = defaultAudioDevice; } @@ -475,9 +434,14 @@ public class AppRTCAudioManager { if (newAudioDevice != selectedAudioDevice || audioDeviceSetUpdated) { // Do the required device switch. setAudioDeviceInternal(newAudioDevice); - Log.d(Config.LOGTAG, "New device status: " - + "available=" + audioDevices + ", " - + "selected=" + newAudioDevice); + Log.d( + Config.LOGTAG, + "New device status: " + + "available=" + + audioDevices + + ", " + + "selected=" + + newAudioDevice); if (audioManagerEvents != null) { // Notify a listening client that audio device has been changed. audioManagerEvents.onAudioDeviceChanged(selectedAudioDevice, audioDevices); @@ -490,22 +454,19 @@ public class AppRTCAudioManager { ContextCompat.getMainExecutor(apprtcContext).execute(runnable); } - /** - * AudioManager state. - */ + /** AudioManager state. */ public enum AudioManagerState { UNINITIALIZED, PREINITIALIZED, RUNNING, } - /** - * Selected audio device change event. - */ + /** Selected audio device change event. */ public interface AudioManagerEvents { // Callback fired once audio device is changed or list of available audio devices changed. void onAudioDeviceChanged( - CallIntegration.AudioDevice selectedAudioDevice, Set availableAudioDevices); + CallIntegration.AudioDevice selectedAudioDevice, + Set availableAudioDevices); } /* Receiver which handles changes in wired headset availability. */ @@ -520,13 +481,23 @@ public class AppRTCAudioManager { int state = intent.getIntExtra("state", STATE_UNPLUGGED); int microphone = intent.getIntExtra("microphone", HAS_NO_MIC); String name = intent.getStringExtra("name"); - Log.d(Config.LOGTAG, "WiredHeadsetReceiver.onReceive" + AppRTCUtils.getThreadInfo() + ": " - + "a=" + intent.getAction() + ", s=" - + (state == STATE_UNPLUGGED ? "unplugged" : "plugged") + ", m=" - + (microphone == HAS_MIC ? "mic" : "no mic") + ", n=" + name + ", sb=" - + isInitialStickyBroadcast()); + Log.d( + Config.LOGTAG, + "WiredHeadsetReceiver.onReceive" + + AppRTCUtils.getThreadInfo() + + ": " + + "a=" + + intent.getAction() + + ", s=" + + (state == STATE_UNPLUGGED ? "unplugged" : "plugged") + + ", m=" + + (microphone == HAS_MIC ? "mic" : "no mic") + + ", n=" + + name + + ", sb=" + + isInitialStickyBroadcast()); hasWiredHeadset = (state == STATE_PLUGGED); updateAudioDeviceState(); } } -} \ No newline at end of file +} From a72214bab33978e4d3ae4d91058f427cf4cbc835 Mon Sep 17 00:00:00 2001 From: nautilusx Date: Fri, 23 Feb 2024 13:04:04 +0000 Subject: [PATCH 047/363] Translated using Weblate (German) Currently translated at 100.0% (985 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/ --- src/main/res/values-de/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 77c02e0d21e739eeae9e9a6dcf182d2625d8e5a3..ebedabda4587e308b7350ce6ca70a9edfa213a5d 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -1028,4 +1028,7 @@ Datenschutzbestimmungen Quicksy bittet dich um deine Zustimmung zur Verwendung deiner Daten Kontaktlistenintegration ist nicht verfügbar + Anrufintegration nicht verfügbar! + Keine Berechtigung für Telefonanrufe + Kontakt ist nicht verfügbar \ No newline at end of file From 339204212e0d28cfcf10b4f0f7d2b49e947bf517 Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Fri, 23 Feb 2024 11:42:36 +0000 Subject: [PATCH 048/363] Translated using Weblate (Spanish) Currently translated at 100.0% (985 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/ --- src/main/res/values-es/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index b7b87ee79f3c15ee0fc1d47d642e3feba4255335..16e440bb6bfd06676fdcd5361d5aa66641ac4c73 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -1042,4 +1042,7 @@ Quicksy pide tu consentimiento para utilizar tus datos Política de privacidad La lista de contactos no está disponible + Sin permiso para llamar por teléfono + Contacto no disponible + ¡Sin integración de llamadas! \ No newline at end of file From fa655811bc96871eb1fab8aafa144c9a1ab94ae7 Mon Sep 17 00:00:00 2001 From: Besnik_b Date: Fri, 23 Feb 2024 10:14:59 +0000 Subject: [PATCH 049/363] Translated using Weblate (Albanian) Currently translated at 98.7% (973 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/ --- src/main/res/values-sq-rAL/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/res/values-sq-rAL/strings.xml b/src/main/res/values-sq-rAL/strings.xml index 968d845e913373dab2a910f12a324f4287ac17b9..ae11c901b20c6cd802cebeaf6d790c949da4a8d7 100644 --- a/src/main/res/values-sq-rAL/strings.xml +++ b/src/main/res/values-sq-rAL/strings.xml @@ -1037,4 +1037,9 @@ Rregulla privatësie Integrimi i listës së kontakteve s’është i mundshmë Kontakti juaj përdor pajisje të paverifikuara. Që të kryhet verifikimi dhe të pengohen sulme MITM, skanoni Kodin e tij QR. + Kontakti s’është gati + S’ka integrim thirrjesh! + Pa leje për bërje thirrjesh + U kalua në version të mëparshëm SASL-i + Pikasje kanalesh përdor një shërbim palë të tretë të quajtur <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Përdorim i kësaj veçorie do t’i transmetojë atij shërbimi adresën tuaj IP dhe terma kërkimesh. Për më tepër hollësi, shihni <a href=https://search.jabber.network/privacy>Rregullat e tyre mbi Privatësinë</a>. \ No newline at end of file From c7e153def9e940edeac46d60a360d0898d86c247 Mon Sep 17 00:00:00 2001 From: Outbreak2096 Date: Fri, 23 Feb 2024 11:38:55 +0000 Subject: [PATCH 050/363] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (985 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/ --- src/main/res/values-zh-rCN/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 98d14d5332415db880bcbcb81faf1e5f859ac64f..fe4787e6784db692f0045bc7642a8a8bcb3379dd 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -1037,4 +1037,7 @@ Quicksy 请求您同意使用您的数据 隐私政策 联系人列表集成不可用 + 联系人不可用 + 没有拨打电话的权限 + 呼叫集成不可用! \ No newline at end of file From 7ee1b9ea134e5fcdc5dbc26cb2837cd009a94c6c Mon Sep 17 00:00:00 2001 From: nautilusx Date: Fri, 23 Feb 2024 13:02:45 +0000 Subject: [PATCH 051/363] Translated using Weblate (German) Currently translated at 100.0% (58 of 58 strings) Translation: Conversations/App Store Metadata (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/de/ --- fastlane/metadata/android/de-DE/changelogs/4209504.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 fastlane/metadata/android/de-DE/changelogs/4209504.txt diff --git a/fastlane/metadata/android/de-DE/changelogs/4209504.txt b/fastlane/metadata/android/de-DE/changelogs/4209504.txt new file mode 100644 index 0000000000000000000000000000000000000000..92e5e62ad98693024b011de36a47be65b85584dc --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/4209504.txt @@ -0,0 +1 @@ +* Verbesserung der Integration von A/V-Aufrufen in das Betriebssystem From 4454da3a48cbd31808f417f527b64f1bf346b3a4 Mon Sep 17 00:00:00 2001 From: Besnik_b Date: Fri, 23 Feb 2024 10:18:45 +0000 Subject: [PATCH 052/363] Translated using Weblate (Albanian) Currently translated at 100.0% (58 of 58 strings) Translation: Conversations/App Store Metadata (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/sq/ --- fastlane/metadata/android/sq/changelogs/42037.txt | 10 +++++----- fastlane/metadata/android/sq/changelogs/42050.txt | 1 + fastlane/metadata/android/sq/changelogs/42059.txt | 2 ++ fastlane/metadata/android/sq/changelogs/42060.txt | 1 + fastlane/metadata/android/sq/changelogs/42061.txt | 1 + fastlane/metadata/android/sq/changelogs/42062.txt | 1 + fastlane/metadata/android/sq/changelogs/42065.txt | 1 + fastlane/metadata/android/sq/changelogs/42068.txt | 2 ++ fastlane/metadata/android/sq/changelogs/42072.txt | 3 +++ fastlane/metadata/android/sq/changelogs/4207704.txt | 3 +++ fastlane/metadata/android/sq/changelogs/4208104.txt | 4 ++++ fastlane/metadata/android/sq/changelogs/4208804.txt | 3 +++ fastlane/metadata/android/sq/changelogs/4209004.txt | 2 ++ fastlane/metadata/android/sq/changelogs/4209204.txt | 2 ++ fastlane/metadata/android/sq/changelogs/4209404.txt | 1 + fastlane/metadata/android/sq/changelogs/4209504.txt | 1 + 16 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 fastlane/metadata/android/sq/changelogs/42050.txt create mode 100644 fastlane/metadata/android/sq/changelogs/42059.txt create mode 100644 fastlane/metadata/android/sq/changelogs/42060.txt create mode 100644 fastlane/metadata/android/sq/changelogs/42061.txt create mode 100644 fastlane/metadata/android/sq/changelogs/42062.txt create mode 100644 fastlane/metadata/android/sq/changelogs/42065.txt create mode 100644 fastlane/metadata/android/sq/changelogs/42068.txt create mode 100644 fastlane/metadata/android/sq/changelogs/42072.txt create mode 100644 fastlane/metadata/android/sq/changelogs/4207704.txt create mode 100644 fastlane/metadata/android/sq/changelogs/4208104.txt create mode 100644 fastlane/metadata/android/sq/changelogs/4208804.txt create mode 100644 fastlane/metadata/android/sq/changelogs/4209004.txt create mode 100644 fastlane/metadata/android/sq/changelogs/4209204.txt create mode 100644 fastlane/metadata/android/sq/changelogs/4209404.txt create mode 100644 fastlane/metadata/android/sq/changelogs/4209504.txt diff --git a/fastlane/metadata/android/sq/changelogs/42037.txt b/fastlane/metadata/android/sq/changelogs/42037.txt index 35e334aad83fcb77dac95650d719060ac9f34833..dab12a7a6fc2c85124594a3c35a3e7354ac7a4a6 100644 --- a/fastlane/metadata/android/sq/changelogs/42037.txt +++ b/fastlane/metadata/android/sq/changelogs/42037.txt @@ -1,8 +1,8 @@ Version 2.10.9 -* Kërko leje Bluetooth, kur bëhen thirrje A/V (Mund ta hidhni tej këtë, nëse s’përdorni kufje Bluetooth me mikrofon) -* Ndreqje të mete, kur thirret dikush në Movim -* Ndreqje shfaqjeje avatari gabim për fjalosje në grup +* Kërko leje Bluetooth, kur bëhen thirrje A/V (Mund ta hidhni tej, nëse s’përdorni kufje Bluetooth me mikrofon) +* Ndreqje të mete, për thirrje në Movim +* Ndreqje shfaqjeje avatari të gabuar për fjalosje në grup * Pyet përherë për lënie jashtë optimizimesh për baterinë * Vendosje flamurke “vetëm vendore” për njoftime “x llogari të lidhura” -* Ndreqje ndërveprimi me shtojcën Google Maps Share Location Plugin -* Heqje poshtëshënimi lidhur me tarifa shërbyesi +* Ndreqje ndërveprimi me shtojcën Google Maps Share Location +* Heqje poshtëshënimi lidhur me tarifë shërbyesi diff --git a/fastlane/metadata/android/sq/changelogs/42050.txt b/fastlane/metadata/android/sq/changelogs/42050.txt new file mode 100644 index 0000000000000000000000000000000000000000..30e4121e3419676d14f72b58ed4068b70c618378 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/42050.txt @@ -0,0 +1 @@ +* Rritje rrezeje cepi për foto profilesh diff --git a/fastlane/metadata/android/sq/changelogs/42059.txt b/fastlane/metadata/android/sq/changelogs/42059.txt new file mode 100644 index 0000000000000000000000000000000000000000..2896fb077f0516270ffb31e3f8dbd8d674c7789e --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/42059.txt @@ -0,0 +1,2 @@ +* Kalim i versionit për Target SDK sërish në 33 +* Ndreqje problemesh për shërbyes që mbulojnë SASL2 me/pa Administrim brendazi Rrjedhash diff --git a/fastlane/metadata/android/sq/changelogs/42060.txt b/fastlane/metadata/android/sq/changelogs/42060.txt new file mode 100644 index 0000000000000000000000000000000000000000..dcd9dfe3651f497aa71d61d9a560fde88a266866 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/42060.txt @@ -0,0 +1 @@ +* Ndreqje e interpretimit gabimisht të 'q' si një shkronjë cirilike diff --git a/fastlane/metadata/android/sq/changelogs/42061.txt b/fastlane/metadata/android/sq/changelogs/42061.txt new file mode 100644 index 0000000000000000000000000000000000000000..bb3622e75ea02a2c99255652c6216c40eb6b9892 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/42061.txt @@ -0,0 +1 @@ +* Heqje nga versioni në Google Play e veçorisë së pikasjes së kanaleve diff --git a/fastlane/metadata/android/sq/changelogs/42062.txt b/fastlane/metadata/android/sq/changelogs/42062.txt new file mode 100644 index 0000000000000000000000000000000000000000..c9b0cac5e307e370d69b7a62214488bae1732aad --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/42062.txt @@ -0,0 +1 @@ +* Çaktivizim i hapjes së kartelave kopjeruajtje (ceb) nga përgjegjës kartelash diff --git a/fastlane/metadata/android/sq/changelogs/42065.txt b/fastlane/metadata/android/sq/changelogs/42065.txt new file mode 100644 index 0000000000000000000000000000000000000000..a6a7fa9fcdecdefa1ec76a669f4997b09d05c010 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/42065.txt @@ -0,0 +1 @@ +* Sjellje për herë të parë e formati të ri kartelash kopjeruajtje diff --git a/fastlane/metadata/android/sq/changelogs/42068.txt b/fastlane/metadata/android/sq/changelogs/42068.txt new file mode 100644 index 0000000000000000000000000000000000000000..89ba9f52d423c7c7b942053aa730041e9a939d73 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/42068.txt @@ -0,0 +1,2 @@ +* mbulim rregullimesh njoftimi bazuar në bisedë specifike +* përdorim opus-i për mesazhe zanorë në Android 10 diff --git a/fastlane/metadata/android/sq/changelogs/42072.txt b/fastlane/metadata/android/sq/changelogs/42072.txt new file mode 100644 index 0000000000000000000000000000000000000000..e836fb46c86557fb4d9b3bec6deb59aba745c14e --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/42072.txt @@ -0,0 +1,3 @@ +* Kalim versioni për varësi libwebrtc në M117 dhe ndryshim edhe për libvpx +* Rikthim te AAC për mesazhe zanorë +* Mbulim rregullimesh gjuhe sipas aplikacioni diff --git a/fastlane/metadata/android/sq/changelogs/4207704.txt b/fastlane/metadata/android/sq/changelogs/4207704.txt new file mode 100644 index 0000000000000000000000000000000000000000..9b77d55620334af1b75a5021cd32ca571988f742 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/4207704.txt @@ -0,0 +1,3 @@ +* Mbulim për DNS Private (DNS përmes TLS) +* Mbulim për ikonë nisësi të përshtatur nga temë +* Ndreqje problemi të rrallë me leje, kur ndahen kartela në Android 11+ diff --git a/fastlane/metadata/android/sq/changelogs/4208104.txt b/fastlane/metadata/android/sq/changelogs/4208104.txt new file mode 100644 index 0000000000000000000000000000000000000000..6905c9f74b70f0651f75b58bc48de878cc6b80d0 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/4208104.txt @@ -0,0 +1,4 @@ +* Përdorim më i lehtë i “Shfaq kod QR” +* Mbulim për Faqerojtës PEP të vetëm sistemit +* Shtim mbulimi për Ofertë SDP / Model Përgjigjesh (Përdorur nga kanale SIP) +* Ngritje e versioni të synuar API në Android 14 diff --git a/fastlane/metadata/android/sq/changelogs/4208804.txt b/fastlane/metadata/android/sq/changelogs/4208804.txt new file mode 100644 index 0000000000000000000000000000000000000000..2a49661a08620fa46318009704dc13546a43ac7d --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/4208804.txt @@ -0,0 +1,3 @@ +* Mbulim shpërnguljesh kartelash P2P përmes kanalesh WebRTC të dhënash +* Ndreqje problemesh shkalle ndërveprimi me Bind 2.0 në ejabberd +* Paketim dëshmish Let’s Encrypt rrënje për Android <= 7 diff --git a/fastlane/metadata/android/sq/changelogs/4209004.txt b/fastlane/metadata/android/sq/changelogs/4209004.txt new file mode 100644 index 0000000000000000000000000000000000000000..1241fb7bba0de9c5e9cde1f57dc3ac2ed73e2d44 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/4209004.txt @@ -0,0 +1,2 @@ +* ndreqje të metash të vockla +* ndryshime të vockla te hapat e mirëseardhjes në Quicksy diff --git a/fastlane/metadata/android/sq/changelogs/4209204.txt b/fastlane/metadata/android/sq/changelogs/4209204.txt new file mode 100644 index 0000000000000000000000000000000000000000..08c5f1322760f42d765e78e8031c14c6923791ea --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/4209204.txt @@ -0,0 +1,2 @@ +* Furnizim hyrjeje më të lehtë te “Rregulla Privatësie” në versionin Play Store (Quicksy dhe Conversations) +* Heqje integrimi libri adresash te versioni në Play Store i Conversations diff --git a/fastlane/metadata/android/sq/changelogs/4209404.txt b/fastlane/metadata/android/sq/changelogs/4209404.txt new file mode 100644 index 0000000000000000000000000000000000000000..bd417916b0442b974959e8e3e7d6d891e7d18520 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/4209404.txt @@ -0,0 +1 @@ +* Ndreqje prapakthimesh të vockla të sjella me 2.13.1 diff --git a/fastlane/metadata/android/sq/changelogs/4209504.txt b/fastlane/metadata/android/sq/changelogs/4209504.txt new file mode 100644 index 0000000000000000000000000000000000000000..d9ae9cd78eb0a773fd10bfc7fee87c06c738ba89 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/4209504.txt @@ -0,0 +1 @@ +* Përmirësim integrimi thirrjesh A/V me sistemin operativ From 5f2666835a706e8402c89d3b304241cca64486ca Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Fri, 23 Feb 2024 11:43:44 +0000 Subject: [PATCH 053/363] Translated using Weblate (Spanish) Currently translated at 100.0% (58 of 58 strings) Translation: Conversations/App Store Metadata (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/es/ --- fastlane/metadata/android/es-ES/changelogs/4209504.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 fastlane/metadata/android/es-ES/changelogs/4209504.txt diff --git a/fastlane/metadata/android/es-ES/changelogs/4209504.txt b/fastlane/metadata/android/es-ES/changelogs/4209504.txt new file mode 100644 index 0000000000000000000000000000000000000000..4900383ba9ac69d9366249b293225c3c74240635 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/4209504.txt @@ -0,0 +1 @@ +* Mejorar la integración de las llamadas A/V en el sistema operativo From 1c3ad05353d23c378c3615937f527f70e136ac11 Mon Sep 17 00:00:00 2001 From: Outbreak2096 Date: Fri, 23 Feb 2024 11:40:39 +0000 Subject: [PATCH 054/363] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (58 of 58 strings) Translation: Conversations/App Store Metadata (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/zh_Hans/ --- fastlane/metadata/android/zh-CN/changelogs/4209504.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 fastlane/metadata/android/zh-CN/changelogs/4209504.txt diff --git a/fastlane/metadata/android/zh-CN/changelogs/4209504.txt b/fastlane/metadata/android/zh-CN/changelogs/4209504.txt new file mode 100644 index 0000000000000000000000000000000000000000..784da44bb34cc0839288fa3a96445f162d385457 --- /dev/null +++ b/fastlane/metadata/android/zh-CN/changelogs/4209504.txt @@ -0,0 +1 @@ +* 改进音频/视频通话与操作系统的集成 From faf042d29e8bb55ac50bcb1e26b48adead7151da Mon Sep 17 00:00:00 2001 From: licaon-kter Date: Fri, 23 Feb 2024 15:09:23 +0000 Subject: [PATCH 055/363] Translated using Weblate (Romanian) Currently translated at 100.0% (985 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/ --- src/main/res/values-ro-rRO/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index ae91b368a4c566485445212a2b9b1c14224996aa..48a2f61a0097ba14cb8b9c630502159090814ab8 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -1047,4 +1047,7 @@ Quicksy vă solicită consimțământul pentru a utiliza datele dumneavoastră Integrarea listei de contacte nu este disponibilă Politica de confidențialitate + Nu este permisă efectuarea de apeluri + Integrarea apelurilor nu este disponibilă! + Contactul nu este disponibil \ No newline at end of file From 733abc4b45e2d10b141203a957658f7e5d3a5344 Mon Sep 17 00:00:00 2001 From: ghose Date: Sat, 24 Feb 2024 06:00:29 +0000 Subject: [PATCH 056/363] Translated using Weblate (Galician) Currently translated at 100.0% (985 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/ --- src/main/res/values-gl/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index 5e5bc7c070bebebe14c4756a2712a0c889fad5ab..e517ecd313a9fa50b44968e3a557a0c172e3ef97 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -1031,4 +1031,7 @@ Quicksy solicita permiso para usar os teus datos Política de privacidade Non está dispoñible a integración coa lista de contactos + Sen permiso para chamar por teléfono + Non está dispoñible o servizo de chamadas! + O contacto non está dispoñible \ No newline at end of file From cebe688a149c25b5c5f7e02e075402bd2fb17043 Mon Sep 17 00:00:00 2001 From: SomeTr Date: Fri, 23 Feb 2024 21:18:28 +0000 Subject: [PATCH 057/363] Translated using Weblate (Ukrainian) Currently translated at 100.0% (985 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/ --- src/main/res/values-uk/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml index 13019dcae1590a9e0ef1baabca9c4a69a6b6b6b2..23186977eec4a0da137de796f0d7efec11d4db78 100644 --- a/src/main/res/values-uk/strings.xml +++ b/src/main/res/values-uk/strings.xml @@ -1079,4 +1079,7 @@ Quicksy просить згоду на використання Ваших даних Політика конфіденційності Інтеграція зі списком контактів недоступна + Немає дозволу на телефонні дзвінки + Контакт недоступний + Інтеграція викликів недоступна! \ No newline at end of file From e5c73927894fa245dd73b0a4a5dc072c92c35184 Mon Sep 17 00:00:00 2001 From: ghose Date: Sat, 24 Feb 2024 06:02:18 +0000 Subject: [PATCH 058/363] Translated using Weblate (Galician) Currently translated at 51.7% (30 of 58 strings) Translation: Conversations/App Store Metadata (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/gl/ --- fastlane/metadata/android/gl-ES/changelogs/4209504.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 fastlane/metadata/android/gl-ES/changelogs/4209504.txt diff --git a/fastlane/metadata/android/gl-ES/changelogs/4209504.txt b/fastlane/metadata/android/gl-ES/changelogs/4209504.txt new file mode 100644 index 0000000000000000000000000000000000000000..33f2083755c70aba5791e160b4b911392c443c95 --- /dev/null +++ b/fastlane/metadata/android/gl-ES/changelogs/4209504.txt @@ -0,0 +1 @@ +* Melloras na integración das chamadas de A/V co sistema operativo From bab9553750c6e3466244b17ad144cf450d18f6fc Mon Sep 17 00:00:00 2001 From: SomeTr Date: Fri, 23 Feb 2024 21:15:29 +0000 Subject: [PATCH 059/363] Translated using Weblate (Ukrainian) Currently translated at 100.0% (58 of 58 strings) Translation: Conversations/App Store Metadata (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/uk/ --- fastlane/metadata/android/uk/changelogs/4209504.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 fastlane/metadata/android/uk/changelogs/4209504.txt diff --git a/fastlane/metadata/android/uk/changelogs/4209504.txt b/fastlane/metadata/android/uk/changelogs/4209504.txt new file mode 100644 index 0000000000000000000000000000000000000000..102af6b1b251067bc56b26a529f2cd2c225ad9e0 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/4209504.txt @@ -0,0 +1 @@ +* Покращено інтеграцію аудіо- та відеовикликів в операційну систему From 6f8d8b9330f86392e6645eda7041cf077c4bc371 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 28 Feb 2024 12:01:18 +0100 Subject: [PATCH 060/363] log reason for SM resume failure --- .../eu/siacs/conversations/utils/XmlHelper.java | 13 +++++++++++-- .../eu/siacs/conversations/xmpp/XmppConnection.java | 7 ++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/utils/XmlHelper.java b/src/main/java/eu/siacs/conversations/utils/XmlHelper.java index 7287297e32c7a8a042312de31225605449c47017..54880be8fc7689563d18282f9741968d6c714725 100644 --- a/src/main/java/eu/siacs/conversations/utils/XmlHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/XmlHelper.java @@ -1,13 +1,15 @@ package eu.siacs.conversations.utils; import com.google.common.base.Joiner; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import eu.siacs.conversations.xml.Element; + +import java.util.Collection; import java.util.Collections; import java.util.List; -import eu.siacs.conversations.xml.Element; - public class XmlHelper { public static String encodeEntities(String content) { content = content.replace("&", "&"); @@ -28,4 +30,11 @@ public class XmlHelper { child -> child != null ? child.getName() : null); return Joiner.on(", ").join(features); } + + public static String print(final Collection children) { + if (children == null) { + return null; + } + return Joiner.on("").join(Iterables.transform(children, Element::toString)); + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 7011c4d54ea89bd532074990dcbe8c9f2e05915f..e5ddbe9de325d36571be1b3ae5ed869ea42b0ec6 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -1142,7 +1142,12 @@ public class XmppConnection implements Runnable { mXmppConnectionService.updateConversationUi(); } } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": resumption failed"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": resumption failed (" + + XmlHelper.print(failed.getChildren()) + + ")"); } resetStreamId(); if (sendBindRequest) { From e1a2d81294efe636252201a2784e649ae1c5681e Mon Sep 17 00:00:00 2001 From: p42ity Date: Sat, 2 Mar 2024 12:39:22 +0100 Subject: [PATCH 061/363] Added Oukitel WP12 Pro and Volla Phone X to the list of AAC sensitive devices (truncated voice messages workaround) --- src/main/java/eu/siacs/conversations/ui/RecordingActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java b/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java index d3b3a96b28f81b0872dce8239807e2c6fa46eed3..1179de143d3632e22f5f54498e2705d78ce4a3df 100644 --- a/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java @@ -106,6 +106,8 @@ public class RecordingActivity extends Activity implements View.OnClickListener .add("ONEPLUS A6010") // OnePlus 6T https://codeberg.org/monocles/monocles_chat/issues/133 .add("ONEPLUS A6013") // OnePlus 6T https://codeberg.org/monocles/monocles_chat/issues/133 .add("Pixel 4a") // Pixel 4a https://github.com/iNPUTmice/Conversations/issues/4223 + .add("WP12 Pro") // Oukitel WP 12 Pro https://github.com/iNPUTmice/Conversations/issues/4223 + .add("Volla Phone X") // Volla Phone X https://github.com/iNPUTmice/Conversations/issues/4223 .build(); private boolean startRecording() { From 2ac4efa2596498dc05d9cc7e69b3cb28d62d456a Mon Sep 17 00:00:00 2001 From: inference Date: Thu, 29 Feb 2024 19:34:50 +0000 Subject: [PATCH 062/363] Improve onboarding text string --- src/conversations/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/conversations/res/values/strings.xml b/src/conversations/res/values/strings.xml index fffee31d613060c6c6dd9bff2e5d540028994478..3e8a0526d0ae8eb55e745d8b609ea7fad003a56b 100644 --- a/src/conversations/res/values/strings.xml +++ b/src/conversations/res/values/strings.xml @@ -4,7 +4,7 @@ Use conversations.im Create new account Do you already have an XMPP account? This might be the case if you are already using a different XMPP client or have used Conversations before. If not you can create a new XMPP account right now.\nHint: Some email providers also provide XMPP accounts. - XMPP is a provider independent instant messaging network. You can use this client with what ever XMPP server you choose.\nHowever for your convenience we made it easy to create an account on conversations.im; a provider specially suited for the use with Conversations. + XMPP is a provider-independent instant messaging network. You can use this client with whichever XMPP server you choose.\nHowever, for your convenience we made it easy to create an account on conversations.im; a provider specifically suited for use with Conversations. You have been invited to %1$s. We will guide you through the process of creating an account.\nWhen picking %1$s as a provider you will be able to communicate with users of other providers by giving them your full XMPP address. You have been invited to %1$s. A username has already been picked for you. We will guide you through the process of creating an account.\nYou will be able to communicate with users of other providers by giving them your full XMPP address. Your server invitation @@ -13,4 +13,4 @@ If your contact is nearby, they can also scan the code below to accept your invitation. Join %1$s and chat with me: %2$s Share invite with… - \ No newline at end of file + From 86b733e159831cc26f0b8fd561eff011305a0edf Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 1 Mar 2024 14:39:54 +0100 Subject: [PATCH 063/363] prevent receiving (as share with target) file URIs as Element (Matrix client) demonstrated again file URIs are unnecessarily dangerous. On Android 7+ there is no good reason to process them anymore --- .../persistance/FileBackend.java | 38 +++++++++++-------- .../ui/ConversationFragment.java | 4 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index d8050646a7f6a6c49bf0bd1213326d97a644456c..eea7486ab40e369b01d70aa29fb6b1b16d662522 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -370,29 +370,35 @@ public class FileBackend { } } - public static boolean weOwnFile(final Uri uri) { - if (uri == null || !ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { - return false; - } else { - return weOwnFileLollipop(uri); + public static boolean dangerousFile(final Uri uri) { + if (uri == null || Strings.isNullOrEmpty(uri.getScheme())) { + return true; + } + if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + // On Android 7 (and apps that target 7) it is now longer possible to share files + // with a file scheme. By now you should probably not be running apps that target + // anything less than 7 any more + return true; + } else { + return isFileOwnedByProcess(uri); + } } + return false; } - private static boolean weOwnFileLollipop(final Uri uri) { + private static boolean isFileOwnedByProcess(final Uri uri) { final String path = uri.getPath(); if (path == null) { - return false; + return true; } - try { - File file = new File(path); - FileDescriptor fd = - ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY) - .getFileDescriptor(); - StructStat st = Os.fstat(fd); + try (final var pfd = + ParcelFileDescriptor.open(new File(path), ParcelFileDescriptor.MODE_READ_ONLY)) { + final FileDescriptor fd = pfd.getFileDescriptor(); + final StructStat st = Os.fstat(fd); return st.st_uid == android.os.Process.myUid(); - } catch (FileNotFoundException e) { - return false; - } catch (Exception e) { + } catch (final Exception e) { + // when in doubt. better safe than sorry return true; } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index da281617e95e3442d6d019502116c29ec95f4c49..c97aea645f6cd353199c98fc527ffc45958e610e 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -2619,10 +2619,10 @@ public class ConversationFragment extends XmppFragment final Iterator iterator = uris.iterator(); while (iterator.hasNext()) { final Uri uri = iterator.next(); - if (FileBackend.weOwnFile(uri)) { + if (FileBackend.dangerousFile(uri)) { iterator.remove(); Toast.makeText( - getActivity(), + requireActivity(), R.string.security_violation_not_attaching_file, Toast.LENGTH_SHORT) .show(); From 00f52226d8fa1243534fc07bb179f83471b8d8b4 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 3 Mar 2024 08:02:31 +0100 Subject: [PATCH 064/363] execute all account state managments on ping thread --- .../conversations/services/XmppConnectionService.java | 10 ++++++++-- .../eu/siacs/conversations/utils/WakeLockHelper.java | 8 ++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index f120c4c5480bd20b02e636b259b0a3b5ca436d79..887ffe93708f78eb8b83d6cf0eb72ed1e7ab2920 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -83,6 +83,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -879,7 +880,12 @@ public class XmppConnectionService extends Service { } return START_NOT_STICKY; } - manageAccountConnectionStates(action, intent == null ? null : intent.getExtras()); + final var extras = intent == null ? null : intent.getExtras(); + try { + internalPingExecutor.execute(() -> manageAccountConnectionStates(action, extras)); + } catch (final RejectedExecutionException e) { + Log.e(Config.LOGTAG, "can not schedule connection states manager"); + } if (SystemClock.elapsedRealtime() - mLastExpiryRun.get() >= Config.EXPIRY_INTERVAL) { expireOldMessages(); } @@ -959,7 +965,7 @@ public class XmppConnectionService extends Service { } } - private boolean processAccountState(Account account, boolean interactive, boolean isUiAction, boolean isAccountPushed, HashSet pingCandidates) { + private boolean processAccountState(final Account account, final boolean interactive, final boolean isUiAction, final boolean isAccountPushed, final HashSet pingCandidates) { if (!account.getStatus().isAttemptReconnect()) { return false; } diff --git a/src/main/java/eu/siacs/conversations/utils/WakeLockHelper.java b/src/main/java/eu/siacs/conversations/utils/WakeLockHelper.java index ce1631b03116ec0f9db696208239714914ae9dc2..9fb38ef86eede1658e50a74f074d29cb26676f51 100644 --- a/src/main/java/eu/siacs/conversations/utils/WakeLockHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/WakeLockHelper.java @@ -36,15 +36,15 @@ import eu.siacs.conversations.Config; public class WakeLockHelper { - public static void acquire(PowerManager.WakeLock wakeLock) { + public static void acquire(final PowerManager.WakeLock wakeLock) { try { wakeLock.acquire(2000); - } catch (RuntimeException e) { + } catch (final RuntimeException e) { Log.d(Config.LOGTAG, "unable to acquire wake lock", e); } } - public static void release(PowerManager.WakeLock wakeLock) { + public static void release(final PowerManager.WakeLock wakeLock) { if (wakeLock == null) { return; } @@ -52,7 +52,7 @@ public class WakeLockHelper { if (wakeLock.isHeld()) { wakeLock.release(); } - } catch (RuntimeException e) { + } catch (final RuntimeException e) { Log.d(Config.LOGTAG, "unable to release wake lock", e); } } From b9a19dc6c7b20abb006c35d34d1673829d8df25a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 3 Mar 2024 20:14:27 +0100 Subject: [PATCH 065/363] do not terminate jingle ft session twice (after iq timeout) --- .../xmpp/jingle/JingleFileTransferConnection.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java index 632a8f034b0e44f729a920a4c6f2e024abc215e0..845f91ecc7bdae1dd011006f86672ab11a906e95 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java @@ -843,6 +843,8 @@ public class JingleFileTransferConnection extends AbstractJingleConnection if (transport == null) { return; } + // TODO consider setting transport callback to null. requires transport to handle null callback + //transport.setTransportCallback(null); transport.terminate(); this.transport = null; } @@ -980,7 +982,10 @@ public class JingleFileTransferConnection extends AbstractJingleConnection public void onTransportSetupFailed() { final var transport = this.transport; if (transport == null) { - // this really is not supposed to happen + // this can happen on IQ timeouts + if (isTerminated()) { + return; + } sendSessionTerminate(Reason.FAILED_APPLICATION, null); return; } From 29978a0f2a127223260db1b37420bed9b5e97e9a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 4 Mar 2024 09:49:03 +0100 Subject: [PATCH 066/363] null check SurfaceTextureHelper --- .../eu/siacs/conversations/xmpp/jingle/VideoSourceWrapper.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/VideoSourceWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/VideoSourceWrapper.java index c1ff24521f83a78b68361fa68fbb2618aaf2c9ab..89552fc1814b1d9215ce6727268923d64182d67a 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/VideoSourceWrapper.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/VideoSourceWrapper.java @@ -57,6 +57,9 @@ class VideoSourceWrapper { final EglBase.Context eglBaseContext) { final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("webrtc", eglBaseContext); + if (surfaceTextureHelper == null) { + throw new IllegalStateException("Could not create SurfaceTextureHelper"); + } this.videoSource = peerConnectionFactory.createVideoSource(false); this.cameraVideoCapturer.initialize( surfaceTextureHelper, context, this.videoSource.getCapturerObserver()); From 6ea8a258025fdbe2b917d5e1a55925640ed1f300 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 4 Mar 2024 09:50:30 +0100 Subject: [PATCH 067/363] call client app in welcome screen --- src/conversations/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values/strings.xml b/src/conversations/res/values/strings.xml index 3e8a0526d0ae8eb55e745d8b609ea7fad003a56b..df76b0739a7eb0ee713cb89b67e696d05e7e1748 100644 --- a/src/conversations/res/values/strings.xml +++ b/src/conversations/res/values/strings.xml @@ -4,7 +4,7 @@ Use conversations.im Create new account Do you already have an XMPP account? This might be the case if you are already using a different XMPP client or have used Conversations before. If not you can create a new XMPP account right now.\nHint: Some email providers also provide XMPP accounts. - XMPP is a provider-independent instant messaging network. You can use this client with whichever XMPP server you choose.\nHowever, for your convenience we made it easy to create an account on conversations.im; a provider specifically suited for use with Conversations. + XMPP is a provider-independent instant messaging network. You can use this app with whichever XMPP server you choose.\nHowever, for your convenience we made it easy to create an account on conversations.im; a provider specifically suited for use with Conversations. You have been invited to %1$s. We will guide you through the process of creating an account.\nWhen picking %1$s as a provider you will be able to communicate with users of other providers by giving them your full XMPP address. You have been invited to %1$s. A username has already been picked for you. We will guide you through the process of creating an account.\nYou will be able to communicate with users of other providers by giving them your full XMPP address. Your server invitation From ff96570e4f00bf178efdeca2165721b3e0e42df8 Mon Sep 17 00:00:00 2001 From: Besnik_b Date: Sat, 24 Feb 2024 21:13:17 +0000 Subject: [PATCH 068/363] Translated using Weblate (Albanian) Currently translated at 100.0% (2 of 2 strings) Translation: Conversations/App Store Metadata (Quicksy) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/sq/ --- src/quicksy/fastlane/metadata/android/sq/short_description.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quicksy/fastlane/metadata/android/sq/short_description.txt b/src/quicksy/fastlane/metadata/android/sq/short_description.txt index 05a7f29696560d2291cd58fcfa1136804554fde3..5848ee953fbb214ab78af55223dff1d183fb232d 100644 --- a/src/quicksy/fastlane/metadata/android/sq/short_description.txt +++ b/src/quicksy/fastlane/metadata/android/sq/short_description.txt @@ -1 +1 @@ -Jabber/XMPP me Hyrje të Lehtë dhe Zbulim të Lehtë +Jabber/XMPP me Hyrje të Lehtë dhe Zbulim të Kollajtë From 6414894ffee242e29b3dee8a4981516531db7399 Mon Sep 17 00:00:00 2001 From: MasoudAbkenar Date: Sun, 3 Mar 2024 07:30:05 +0000 Subject: [PATCH 069/363] Translated using Weblate (Persian) Currently translated at 100.0% (985 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fa/ --- src/main/res/values-fa-rIR/strings.xml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/res/values-fa-rIR/strings.xml b/src/main/res/values-fa-rIR/strings.xml index 0de5326b67907663ace139003aeabb8f19a9c1d3..9eae38a8ba17be8d1ebafba4fcf6c2743fdfee0a 100644 --- a/src/main/res/values-fa-rIR/strings.xml +++ b/src/main/res/values-fa-rIR/strings.xml @@ -844,10 +844,9 @@ لطفاً شمارهٔ تلفن خود را وارد کنید. نوشته با %s هم‌رسانی شد لطفاً %s دیگر دوباره تلاش کنید - %1$s نیاز به دسترسی به فهرست مخاطبانتان دارد تا آن را با فهرست مخاطبان XMPP شما مقایسه کند. -\nبا این کار نام کامل و تصویر نمایهٔ مخاطبانتان نشان داده خواهد شد. + %1$s فهرست مخاطبان شما را به طور محلی، فقط روی همین دستگاه، بررسی می‌کند تا نام و تصویر نمایهٔ مخاطبان مشترک در XMPP را نشان دهد. \n -\n%1$s فهرست مخاطبان شما را فقط به طور آفلاین مقایسه می‌کند، بی‌آن‌که چیزی را به سرور بفرستد. +\nهیچ اطلاعاتی دربارهٔ فهرست مخاطبان شما به جایی فرستاده نمی‌شود! اعتمادنشده ‪xmpp.example.com‬ لرزش معادل بی‌صدا @@ -896,7 +895,7 @@ ثبت نام شکست خورد: رمز زیادی ضعیف است تلاش‌های بیش از حد این برنامه به شما پیامکی می‌فرستد (ممکن است برایتان هزینه داشته باشد) تا شمارهٔ تلفن شما را تأیید کند. کد کشور و شمارهٔ تلفن خود را وارد کنید: - همگام‌سازی با مخاطبان + یکپارچه‌سازی فهرست مخاطبان خطای اتصال تأییدیهٔ امنیتی قابل خواندن نبود پیش‌نویس: @@ -1048,4 +1047,11 @@ نام میزبان کپی نشانی وب به‌روزرسانی حساب ممکن نبود + مخاطب در دسترس نیست + به Quicksy خوش آمدید! + سیاست محرمانگی + یکپارچه‌سازی فهرست مخاطبان در دسترس نیست + این برنامه برای به‌کاربردن داده‌های شما نیازمند موافقت شماست + یکپارچه‌سازی تماس تلفنی در دسترس نیست! + اجازهٔ تماس تلفنی وجود ندارد \ No newline at end of file From df8ba6c4bc348d28ce30d72ff5f55ca74eaad23e Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:48 +0000 Subject: [PATCH 070/363] Translated using Weblate (Bulgarian) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/bg/ --- src/conversations/res/values-bg/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-bg/strings.xml b/src/conversations/res/values-bg/strings.xml index 92667523d4cabe3b3e5b50627ce3b43791aed81a..21f9c8d9af5f8b95c6732ca5c394c797b3750689 100644 --- a/src/conversations/res/values-bg/strings.xml +++ b/src/conversations/res/values-bg/strings.xml @@ -5,7 +5,8 @@ Създаване не нов профил Имате ли вече XMPP профил? Може да имате, ако вече използвате друг клиент на XMPP или сте използвали Conversations и преди. Ако не, можете да създадете нов XMPP профил сега.\nСъвет: някои доставчици на е-поща също предоставят XMPP профили.   - XMPP е мрежа за общуване чрез мигновени съобщения, която не е обвързана с конкретен доставчик. Можете да използвате клиента с всеки сървър, който работи с XMPP.\nЗа Ваше удобство, обаче, ние предоставяме лесен начин да си създадете профил в conversations.im — сървър, пригоден да работи най-добре с Conversations. + XMPP е мрежа за общуване чрез мигновени съобщения, която не е обвързана с конкретен доставчик. Можете да използвате клиента с всеки сървър, който работи с XMPP. +\nЗа Ваше удобство, обаче, ние предоставяме лесен начин да си създадете профил в conversations.im — сървър, пригоден да работи най-добре с Conversations. Получихте покана за %1$s. Ще Ви преведем през процеса на създаване на профил.\nИзбирайки %1$s за доставчик, Вие ще можете да общувате и с потребители на други доставчици, като им предоставите своя пълен XMPP адрес. Получихте покана за %1$s. Вече Ви избрахме потребителско име. Ще Ви преведем през процеса на създаване на профил.\nЩе можете да общувате и с потребители на други доставчици, като им предоставите своя пълен XMPP адрес. Вашата покана за сървъра From c3df579aeb1e06fd5c7ce11512f33dca6e227a07 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:48 +0000 Subject: [PATCH 071/363] Translated using Weblate (Danish) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/da/ --- src/conversations/res/values-da-rDK/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-da-rDK/strings.xml b/src/conversations/res/values-da-rDK/strings.xml index f79a920780a8587b9665e6f22dc324fbeda0048c..28f460eafa9aaa3029a3513d235415678580a712 100644 --- a/src/conversations/res/values-da-rDK/strings.xml +++ b/src/conversations/res/values-da-rDK/strings.xml @@ -4,7 +4,8 @@ Brug conversations.im Opret ny konto Har du allerede en XMPP-konto? Dette kan være tilfældet, hvis du allerede bruger en anden XMPP-klient eller har brugt Conversations før. Hvis ikke, kan du lige nu oprette en ny XMPP-konto.\nTip: Nogle e-mail-udbydere leverer også XMPP-konti. - XMPP er et udbyderuafhængigt onlinemeddelelsesnetværk. Du kan bruge denne klient med hvilken XMPP-server du end vælger.\nMen for din nemhedsskyld har vi gjort vi det let at oprette en konto på conversations.im; en udbyder, der er specielt velegnet til brug med Conversations. + XMPP er et udbyderuafhængigt onlinemeddelelsesnetværk. Du kan bruge denne klient med hvilken XMPP-server du end vælger. +\nMen for din nemhedsskyld har vi gjort vi det let at oprette en konto på conversations.im; en udbyder, der er specielt velegnet til brug med Conversations. Du er blevet inviteret til %1$s. Vi guider dig gennem processen med at oprette en konto.\nNår du vælger %1$s som udbyder, kan du kommunikere med brugere fra andre udbydere ved at give dem din fulde XMPP-adresse. Du er blevet inviteret til %1$s. Der er allerede valgt et brugernavn til dig. Vi guider dig gennem processen med at oprette en konto.\nDu vil være i stand til at kommunikere med brugere fra andre udbydere ved at give dem din fulde XMPP-adresse. Din server invitation From 489e5cf41fc1a754664b55b76dfd9e5d49ec038e Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:48 +0000 Subject: [PATCH 072/363] Translated using Weblate (German) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/de/ --- src/conversations/res/values-de/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-de/strings.xml b/src/conversations/res/values-de/strings.xml index 2fd0319a9fae00d6af57bb74db6b85e114a47be6..5881988ee2e81ed163542f42c800f3bd213328d2 100644 --- a/src/conversations/res/values-de/strings.xml +++ b/src/conversations/res/values-de/strings.xml @@ -4,7 +4,8 @@ Benutze conversations.im Neues Konto erstellen Hast du bereits ein XMPP-Konto? Dies kann der Fall sein, wenn du bereits einen anderen XMPP-Client verwendest oder bereits Conversations verwendet hast. Wenn nicht, kannst du jetzt ein neues XMPP-Konto erstellen.\nTipp: Einige E-Mail-Anbieter bieten auch XMPP-Konten an. - XMPP ist ein anbieterunabhängiges Instant Messaging Netzwerk. Du kannst diesen Client mit jedem beliebigen XMPP-Server nutzen.\nUm es dir leicht zu machen, haben wir die Möglichkeit geschaffen, ein Konto auf conversations.im anzulegen; ein Anbieter, der speziell für die Verwendung mit Conversations geeignet ist. + XMPP ist ein anbieterunabhängiges Instant Messaging Netzwerk. Du kannst diesen Client mit jedem beliebigen XMPP-Server nutzen. +\nUm es dir leicht zu machen, haben wir die Möglichkeit geschaffen, ein Konto auf conversations.im anzulegen; ein Anbieter, der speziell für die Verwendung mit Conversations geeignet ist. Du wurdest zu %1$s eingeladen. Wir führen dich durch den Prozess der Kontoerstellung.\nWenn du %1$s als Provider wählst, kannst du mit Nutzern anderer Anbieter kommunizieren, indem du ihnen deine vollständige XMPP-Adresse gibst. Du wurdest zu %1$seingeladen. Ein Benutzername ist bereits für dich ausgewählt worden. Wir führen dich durch den Prozess der Kontoerstellung.\nDu kannst mit Nutzern anderer Anbieter kommunizieren, indem du ihnen deine vollständige XMPP-Adresse gibst. Deine Einladung für den Server From e58fdaddf155f9fa1c1f2c7c2414efe760c62a82 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:48 +0000 Subject: [PATCH 073/363] Translated using Weblate (Greek) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/el/ --- src/conversations/res/values-el/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-el/strings.xml b/src/conversations/res/values-el/strings.xml index c64e3d68e4b22698877f9f8e4ce3bf49d18997c3..7dbc1905632e263236c7518276e5cc14aebf475d 100644 --- a/src/conversations/res/values-el/strings.xml +++ b/src/conversations/res/values-el/strings.xml @@ -4,7 +4,8 @@ Χρήση του conversations.im Δημιουργία νέου λογαριασμού Έχετε ήδη λογαριασμό XMPP; Αυτό μπορεί να συμβαίνει αν ήδη χρησιμοποιείτε ένα άλλο πρόγραμμα XMPP ή έχετε χρησιμοποιήσει το Conversations παλιότερα. Αν όχι, μπορείτε να δημιουργήσετε ένα νέο λογαριασμό XMPP τώρα.\nΧρήσιμη πληροφορία: Κάποιοι πάροχοι e-mail παρέχουν επίσης και λογαριασμούς XMPP. - Το XMPP είναι ένα δίκτυο άμεσης ανταλλαγής μηνυμάτων ανεξάρτητο παρόχου. Μπορείτε να χρησιμοποιήσετε αυτό το πρόγραμμα με όποιον διακομιστή XMPP επιθυμείτε.\nΓια διευκόλυνση πάντως μπορείτε να δημιουργήσετε έναν λογαριασμό στο conversations.im, έναν πάροχο ειδικά σχεδιασμένο για χρήση με το Conversations. + Το XMPP είναι ένα δίκτυο άμεσης ανταλλαγής μηνυμάτων ανεξάρτητο παρόχου. Μπορείτε να χρησιμοποιήσετε αυτό το πρόγραμμα με όποιον διακομιστή XMPP επιθυμείτε. +\nΓια διευκόλυνση πάντως μπορείτε να δημιουργήσετε έναν λογαριασμό στο conversations.im, έναν πάροχο ειδικά σχεδιασμένο για χρήση με το Conversations. Έχετε προσκληθεί στο %1$s. Θα σας καθοδηγήσουμε στη διαδικασία δημιουργίας λογαριασμού.\nΕπιλέγοντας τον %1$s ως πάροχο θα μπορείτε να επικοινωνείτε με χρήστες άλλων παρόχων δίνοντάς τους την πλήρη διεύθυνση XMPP σας. Έχετε προσκληθεί στο %1$s. Ένα όνομα χρήστη έχει ήδη επιλεγεί για εσάς. Θα σας καθοδηγήσουμε στη διαδικασία δημιουργίας λογαριασμού.\nΘα μπορείτε να επικοινωνείτε με χρήστες άλλων παρόχων δίνοντάς τους την πλήρη διεύθυνση XMPP σας. Η πρόσκλησή σας στον διακομιστή From 9ed182c6d619a0572085416e3eb6e361c8f2061f Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:49 +0000 Subject: [PATCH 074/363] Translated using Weblate (Basque) Currently translated at 30.7% (4 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/eu/ --- src/conversations/res/values-eu/strings.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/conversations/res/values-eu/strings.xml b/src/conversations/res/values-eu/strings.xml index bf9555311eff9c0f5d57583804a9aa466e2c4cd6..a8efd240d6d0efd7f0632f5f3247af89eda7f8bd 100644 --- a/src/conversations/res/values-eu/strings.xml +++ b/src/conversations/res/values-eu/strings.xml @@ -4,5 +4,6 @@ Erabili conversations.im Kontu berria sortu XMPP kontu bat badaukazu dagoeneko? Horrela izan daiteke beste XMPP aplikazio bat erabiltzen baduzu edo Conversations lehenago erabili baduzu. Bestela XMPP kontu berri bat sortu dezakezu oraintxe bertan.\nIradokizuna: email hornitzaile batzuek XMPP kontuak hornitzen dituzte ere. - XMPP hornitzailez independientea den bat-bateko mezularitza sare bat da. Aplikazio hau nahi duzun XMPP zerbitzariarekin erabili dezakezu.\nHala ere zure erosotasunerako conversations.im-en, Conversationsekin bereziki erabiltzeko egokia den hornitzaile batean, kontu bat sortzea erraz egin dugu. - \ No newline at end of file + XMPP hornitzailez independientea den bat-bateko mezularitza sare bat da. Aplikazio hau nahi duzun XMPP zerbitzariarekin erabili dezakezu. +\nHala ere zure erosotasunerako conversations.im-en, Conversationsekin bereziki erabiltzeko egokia den hornitzaile batean, kontu bat sortzea erraz egin dugu. + \ No newline at end of file From 5ed98b25a3d194e318d6e1d8880c506b93e0b3f1 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:49 +0000 Subject: [PATCH 075/363] Translated using Weblate (Finnish) Currently translated at 76.9% (10 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/fi/ --- src/conversations/res/values-fi/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-fi/strings.xml b/src/conversations/res/values-fi/strings.xml index 17c75a2977d6777910f2834fe9f874800b1462c2..c416b255544f83e01f5bc9d99e693b3dcab29283 100644 --- a/src/conversations/res/values-fi/strings.xml +++ b/src/conversations/res/values-fi/strings.xml @@ -4,7 +4,8 @@ Käytä conversations.im:ää Luo uusi tili Onko sinulla jo XMPP-tunnus? Jos käytät jo toista XMPP-sovellusta tai olet käyttänyt Conversationsia aiemmin, niin voi olla. Jos ei, voit tehdä uuden XMPP-tilin saman tien.\nVinkki: Jotkin sähköpostipalvelut tarjoavat myös XMPP-tilin. - XMPP on tietystä palveluntarjoasta riippumaton pikaviestiverkosto. Voit käyttää tätä asiakasohjelmaa minkä tahansa haluamasi XMPP-palvelimen kanssa.\nHelppouden nimissä olemme kuitenkin helpottaneet tilin luomista conversations.im:iin. + XMPP on tietystä palveluntarjoasta riippumaton pikaviestiverkosto. Voit käyttää tätä asiakasohjelmaa minkä tahansa haluamasi XMPP-palvelimen kanssa. +\nHelppouden nimissä olemme kuitenkin helpottaneet tilin luomista conversations.im:iin. Sinut on kutsuttu %1$s:iin. Opastamme sinua tilin luomisen kanssa.\nValitessasi palvelimen %1$s palveluntarjoajaksesi voit jutella muiden palveluntajoajien käyttäjien kanssa kertomalla heille koko XMPP-osoitteesi. Sinut on kutsuttu palvelimelle %1$s. Käyttäjänimesi on valittu valmiiksi puolestasi. Opastamme sinua tilin luomisen kanssa.\nVoit jutella muiden palveluntarjoajien käyttäjien kanssa kertomalle heille koko XMPP-osoitteesi. Kutsusi palvelimelle From cf134927dd330df9322e327457ff9843d388a569 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:49 +0000 Subject: [PATCH 076/363] Translated using Weblate (French) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/fr/ --- src/conversations/res/values-fr/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-fr/strings.xml b/src/conversations/res/values-fr/strings.xml index f0c96726fa5b35739f4e84ad1e0a044fc93c748a..752d700412ea3da8e26e8f9278e0616a8f9f9c4d 100644 --- a/src/conversations/res/values-fr/strings.xml +++ b/src/conversations/res/values-fr/strings.xml @@ -4,7 +4,8 @@ Utiliser conversations.im Créer un nouveau compte Avez-vous déjà un compte XMPP ? Cela peut être le cas si vous utilisez déjà un autre client XMPP ou si vous avez déjà utilisé Conversations auparavant. Sinon, vous pouvez créer un nouveau compte XMPP dès maintenant.\nRemarque : Certains fournisseurs de messagerie proposent également des comptes XMPP. - XMPP est un réseau de messagerie instantanée indépendant du fournisseur. Vous pouvez utiliser ce client avec n’importe quel serveur XMPP de votre choix.\nToutefois, pour votre commodité, nous avons facilité la création d’un compte sur conversations.im ; un fournisseur spécialement conçu pour Conversations. + XMPP est un réseau de messagerie instantanée indépendant du fournisseur. Vous pouvez utiliser ce client avec n’importe quel serveur XMPP de votre choix. +\nToutefois, pour votre commodité, nous avons facilité la création d’un compte sur conversations.im ; un fournisseur spécialement conçu pour Conversations. Vous avez été invité à %1$s. Nous allons vous guider à travers le processus de création d’un compte.\nEn choisissant %1$s comme fournisseur, vous pourrez communiquer avec les utilisateurs des autres fournisseurs en leur donnant votre adresse XMPP complète. Vous avez été invité à %1$s. Un nom d’utilisateur a déjà été choisi pour vous. Nous allons vous guider à travers le processus de création d’un compte.\nVous pourrez communiquer avec les utilisateurs des autres fournisseurs en leur donnant votre adresse XMPP complète. Votre invitation au serveur From 5d31d14ed7a367c60564f6c93ca8a1b24ef6070a Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:49 +0000 Subject: [PATCH 077/363] Translated using Weblate (Galician) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/gl/ --- src/conversations/res/values-gl/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-gl/strings.xml b/src/conversations/res/values-gl/strings.xml index 2becd8bea7516c0611416003cf190f2e544094a6..5d9f097dbd9634ecc98e306b5dfda8711a03e3fd 100644 --- a/src/conversations/res/values-gl/strings.xml +++ b/src/conversations/res/values-gl/strings.xml @@ -4,7 +4,8 @@ Utilizar conversations.im Crear nova conta Xa posúes unha conta XMPP? Este pode ser o caso se xa estás a utilizar outro cliente XMPP ou utilizaches Conversations previamente. Se non é así podes crear unha nova conta agora mesmo.\nTruco: Algúns provedores de correo tamén proporcionan contas XMPP. - XMPP é unha rede de mensaxería independente do provedor. Podes utilizar este cliente con calquera provedor XMPP da túa elección.\nMais para a tua conveniencia fixemos que fose doado crear unha conta en conversations.im; un provedor especialmente axeitado para utilizar con Conversations. + XMPP é unha rede de mensaxería independente do provedor. Podes utilizar este cliente con calquera provedor XMPP da túa elección. +\nMais para a tua conveniencia fixemos que fose doado crear unha conta en conversations.im; un provedor especialmente axeitado para utilizar con Conversations. Convidáronte a %1$s. Guiarémoste no proceso para crear unha conta.\nAo elexir %1$s como provedor poderás comunicarte con usuarias doutros provedores cando lles deas o teu enderezo XMPP completo. Convidáronte a %1$s. Xa eleximos un nome de usuaria para ti. Guiarémoste no proceso de crear unha conta.\nPoderás comunicarte con usuarias doutros provedores cando lles digas o teu enderezo XMPP completo. O convite do teu servidor From e77eacc668771f68815a73dac241e54cfcc24f33 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:49 +0000 Subject: [PATCH 078/363] Translated using Weblate (Croatian) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/hr/ --- src/conversations/res/values-hr/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-hr/strings.xml b/src/conversations/res/values-hr/strings.xml index 093c6d0b93515d0dbf590ba949a1a6e63b9edfb2..21b0823bef837e1cc0124d66ac3e1b28a460edf6 100644 --- a/src/conversations/res/values-hr/strings.xml +++ b/src/conversations/res/values-hr/strings.xml @@ -4,7 +4,8 @@ Koristite conversations.im Napravi novi račun Već imate XMPP račun? To može biti slučaj ako već koristite drugi XMPP klijent ili ste prije koristili Razgovore. Ako niste, možete odmah stvoriti novi XMPP račun.\nSavjet: Neki pružatelji usluga e-pošte također nude XMPP račune. - XMPP je mreža za razmjenu izravnih poruka neovisna o pružatelju usluga. Možete koristiti ovaj klijent s bilo kojim XMPP poslužiteljem koji odaberete.\nMeđutim, radi vaše udobnosti olakšali smo kreiranje računa na conversations.im; pružatelj usluga posebno prilagođen za korištenje s Conversations. + XMPP je mreža za razmjenu izravnih poruka neovisna o pružatelju usluga. Možete koristiti ovaj klijent s bilo kojim XMPP poslužiteljem koji odaberete. +\nMeđutim, radi vaše udobnosti olakšali smo kreiranje računa na conversations.im; pružatelj usluga posebno prilagođen za korištenje s Conversations. Pozvani ste na %1$s. Vodit ćemo vas kroz postupak kreiranja računa.\nPrilikom odabira %1$s pružatelja moći ćete komunicirati s korisnicima drugih pružatelja dajući im svoju punu XMPP adresu. Pozvani ste na %1$s. Korisničko ime je već odabrano za vas. Vodit ćemo vas kroz postupak kreiranja računa.\nMoći ćete komunicirati s korisnicima drugih pružatelja tako da im date svoju punu XMPP adresu. Vaša pozivnica za poslužitelj From aa28472a218f46b7d8793c55510381c70cf8ebdb Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:49 +0000 Subject: [PATCH 079/363] Translated using Weblate (Hungarian) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/hu/ --- src/conversations/res/values-hu/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-hu/strings.xml b/src/conversations/res/values-hu/strings.xml index f4c180889977eb0c2f8f0c16003dec0cad86004e..27280900778c3a8930b030f46b7c7bcb0cdd298d 100644 --- a/src/conversations/res/values-hu/strings.xml +++ b/src/conversations/res/values-hu/strings.xml @@ -4,7 +4,8 @@ A conversations.im használata Új fiók létrehozása Már rendelkezik XMPP-fiókkal? Ez az eset állhat fenn, ha már egy másik XMPP-klienst használ, vagy ha már korábban használta a Conversations alkalmazást. Ha nem, akkor most létrehozhat egy új XMPP-fiókot.\nTipp: egyes e-mail szolgáltatók is biztosítanak XMPP-fiókokat. - Az XMPP egy szolgáltatófüggetlen, azonnali üzenetküldő hálózat. Ezt a kliensprogramot bármely XMPP-kiszolgálóhoz használhatja.\nAzonban a kényelem érdekében megkönnyítettük a conversations.im szolgáltatón való fióklétrehozást, ami kifejezetten a Conversations alkalmazással történő használatra lett tervezve. + Az XMPP egy szolgáltatófüggetlen, azonnali üzenetküldő hálózat. Ezt a kliensprogramot bármely XMPP-kiszolgálóhoz használhatja. +\nAzonban a kényelem érdekében megkönnyítettük a conversations.im szolgáltatón való fióklétrehozást, ami kifejezetten a Conversations alkalmazással történő használatra lett tervezve. Meghívást kapott a(z) %1$s kiszolgálóra. Végig fogjuk vezetni egy fiók létrehozásának folyamatán.\nHa a(z) %1$s kiszolgálót választja szolgáltatóként, akkor képes lesz más szolgáltatók felhasználóival is kommunikálni, ha megadja nekik a teljes XMPP-címét. Meghívást kapott a(z) %1$s kiszolgálóra. Már kiválasztottak Önnek egy felhasználónevet. Végig fogjuk vezetni egy fiók létrehozásának folyamatán.\nKépes lesz más szolgáltatók felhasználóival is kommunikálni, ha megadja nekik a teljes XMPP-címét. Az Ön kiszolgálómeghívása From 14ac2a7be02d7a4f5a1d9279bc5fce8561e3921f Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:49 +0000 Subject: [PATCH 080/363] Translated using Weblate (Japanese) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ja/ --- src/conversations/res/values-ja/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-ja/strings.xml b/src/conversations/res/values-ja/strings.xml index 2d240bedce8c03ae96f93ee02b43d44dfe2857c4..287ee093baf7be2ba746877cc3ae47d25d98469a 100644 --- a/src/conversations/res/values-ja/strings.xml +++ b/src/conversations/res/values-ja/strings.xml @@ -4,7 +4,8 @@ conversations.im を利用する 新規アカウントを作成 XMPP アカウントをお持ちですか?既にほかの XMPP クライアントを利用しているか、 Conversations を利用したことがある場合はこちら。初めての方は、今すぐ新規 XMPP アカウントを作成できます。\nヒント: e メールのプロバイダーが XMPP アカウントも提供している場合があります。 - XMPP は、プロバイダーに依存しないインスタントメッセージのプロトコルです。 XMPP サーバーならどこでも、このクライアントを使用することができます。\nよろしければ、 Conversations に最適化されたプロバイダー conversations.im で簡単にアカウントを作成することもできます。 + XMPP は、プロバイダーに依存しないインスタントメッセージのプロトコルです。 XMPP サーバーならどこでも、このクライアントを使用することができます。 +\nよろしければ、 Conversations に最適化されたプロバイダー conversations.im で簡単にアカウントを作成することもできます。 %1$s へ招待されました。アカウント作成手順をご案内します。 \n%1$s をプロバイダーに選択してほかのプロバイダーのユーザーと会話するには、 XMPP のフルアドレスを相手にお知らせください。 %1$s へ招待されました。ユーザー名は既に選択されています。アカウント作成手順をご案内します。 \nほかのプロバイダーのユーザーと会話するには、 XMPP のフルアドレスを相手にお知らせください。 サーバーの招待 From bad24ffaccab519cfea4c916600f63ae83ef8f7e Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:49 +0000 Subject: [PATCH 081/363] Translated using Weblate (Dutch) Currently translated at 84.6% (11 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/nl/ --- src/conversations/res/values-nl/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-nl/strings.xml b/src/conversations/res/values-nl/strings.xml index bc7dbc2fab5aad37288b62e1e974e9e576de3d2b..366506e84a887e0d2e19e780e593b1c49b29f02d 100644 --- a/src/conversations/res/values-nl/strings.xml +++ b/src/conversations/res/values-nl/strings.xml @@ -4,7 +4,8 @@ Conversations.im gebruiken Nieuwe account registreren Heb je al een XMPP-account? Als je al een andere XMPP-cliënt gebruikt, of Conversations vroeger al eens hebt gebruikt, is dit waarschijnlijk het geval. Zo niet, kan je nu een nieuwe XMPP-account aanmaken.\nTip: sommige e-mailproviders bieden ook XMPP-accounts aan. - XMPP is een provider-onafhankelijk berichtennetwerk. Je kan deze cliënt gebruiken met eender welke XMPP-server.\nOm het je gemakkelijker te maken kun je simpelweg een account aanmaken op conversations.im; een provider speciaal geschikt voor Conversations. + XMPP is een provider-onafhankelijk berichtennetwerk. Je kan deze cliënt gebruiken met eender welke XMPP-server. +\nOm het je gemakkelijker te maken kun je simpelweg een account aanmaken op conversations.im; een provider speciaal geschikt voor Conversations. Je ontving een uitnodiging voor %1$s. We zullen je helpen een account aan te maken.\nWanneer je %1$s als je provider kiest kan je met gebruikers van andere providers communiceren door hen je volledige XMPP-adres te geven. Je ontving een uitnodiging voor %1$s. Er werd reeds een gebruikersnaam voor jou gekozen. We zullen je helpen een account aan te maken.\nJe zal met gebruikers van andere providers communiceren door hen je volledige XMPP-adres te geven. Je server uitnodiging From 282126862f7c9b2d2326c747768e73ddd6da5cff Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:49 +0000 Subject: [PATCH 082/363] Translated using Weblate (Polish) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/pl/ --- src/conversations/res/values-pl/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-pl/strings.xml b/src/conversations/res/values-pl/strings.xml index f3771aed2fe0f81499a51111cf16007fc668eab6..585fe01b39fbc7ba43d5ff6820510603bdbeb7dc 100644 --- a/src/conversations/res/values-pl/strings.xml +++ b/src/conversations/res/values-pl/strings.xml @@ -4,7 +4,8 @@ Użyj conversations.im Utwórz nowe konto Czy masz już konto XMPP? Tak może być jeśli używasz już innego klienta XMPP lub używałeś już Conversations. Jeśli nie możesz stworzyć nowe konto XMPP teraz.\nPodpowiedź: Niektórzy dostawcy poczty oferują również konta XMPP. - XMPP to niezależna od dostawcy sieć komunikacji błyskawicznej. Możesz użyć tego klienta z dowolnym serwerem XMPP.\nDla twojej wygody jednak ułatwiliśmy stworzenie konta na conversations.im; dostawcy specjalnie dostosowanego do pracy z Conversations. + XMPP to niezależna od dostawcy sieć komunikacji błyskawicznej. Możesz użyć tego klienta z dowolnym serwerem XMPP. +\nDla twojej wygody jednak ułatwiliśmy stworzenie konta na conversations.im; dostawcy specjalnie dostosowanego do pracy z Conversations. Zostałeś zaproszony do %1$s. Poprowadzimy ciebie przez proces tworzenia konta.\nWybierając %1$s jako dostawcę będziesz mógł komunikować się z innymi użytkownikami podając swój pełny adres XMPP. Zostałeś zaproszony do %1$s. Nazwa użytkownika została już dla ciebie wybrana. Poprowadzimy ciebie przez proces tworzenia konta.\nBęziesz mógł komunikować się z innymi użytkownikami podając swój adres XMPP. Zaproszenie twojego serwera From c5d396706c10100a8f9ac0fb89f633b0dc2f3f83 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:49 +0000 Subject: [PATCH 083/363] Translated using Weblate (Portuguese (Brazil)) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/pt_BR/ --- src/conversations/res/values-pt-rBR/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-pt-rBR/strings.xml b/src/conversations/res/values-pt-rBR/strings.xml index 0a4b54191b7b37cef7beccf475d071045e26107c..210d814e165b9718dcae9ce84b0c44d59efaa73a 100644 --- a/src/conversations/res/values-pt-rBR/strings.xml +++ b/src/conversations/res/values-pt-rBR/strings.xml @@ -4,7 +4,8 @@ Usar o conversations.im Criar uma nova conta Você já possui uma conta XMPP? Esse pode ser o seu caso caso já esteja usando um outro cliente XMPP ou tenha usado o Conversations antes. Caso contrário, você pode criar uma nova conta XMPP agora.\nDica: alguns provedores de e-mail também fornecem contas XMPP. - O XMPP é uma rede de mensageria instantânea independente de provedor. Você pode usar esse cliente com qualquer servidor XMPP que você escolher.\nEntretanto, para sua conveniência, nós simplificamos o processo de criação de uma conta em conversations.im, um provedor especialmente configurado para se usar com o Conversations. + O XMPP é uma rede de mensageria instantânea independente de provedor. Você pode usar esse cliente com qualquer servidor XMPP que você escolher. +\nEntretanto, para sua conveniência, nós simplificamos o processo de criação de uma conta em conversations.im, um provedor especialmente configurado para se usar com o Conversations. Você foi convidado para %1$s. Nós iremos guiá-lo ao longo do processo de criação de uma conta.\nAo escolher %1$s como um provedor você conseguirá se comunicar com usuários de outros provedores dando a eles seu endereço XMPP completo. Você foi convidado para %1$s. Um nome de usuário já foi escolhido para você. Nós iremos guiá-lo ao longo do processo de criação de uma conta.\nVocê conseguirá se comunicar com usuários de outros provedores dando a eles seu endereço XMPP completo. Seu convite do servidor From b896504bb2b0bd1649ae6c62ae64fdee255e578c Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:50 +0000 Subject: [PATCH 084/363] Translated using Weblate (Romanian) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ro/ --- src/conversations/res/values-ro-rRO/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-ro-rRO/strings.xml b/src/conversations/res/values-ro-rRO/strings.xml index baefb00c66e7eaa40140d0cc320cc7dbe893f4bc..1c49b14186f6ecf1368dbc38b70a99115cbbebde 100644 --- a/src/conversations/res/values-ro-rRO/strings.xml +++ b/src/conversations/res/values-ro-rRO/strings.xml @@ -4,7 +4,8 @@ Folosește conversations.im Creează un cont nou Aveți deja un cont XMPP? S-ar putea să fie așa dacă deja utilizați un alt client XMPP sau dacă ați folosit Conversations în trecut. Dacă nu, puteți crea un cont nou XMPP chiar acum.\nIdee: Unii furnizori de e-mail oferă de asemenea și conturi XMPP. - XMPP este o rețea de mesagerie instant ce nu depinde de un anumit furnizor. Aveți posibilitatea să utilizați acest client cu orice server XMPP doriți.\nTotuși, pentru confortul dumneavoastră, am facilitat crearea unui cont pe conversations.im; un furnizor potrivit pentru utilizarea cu aplicația Conversations. + XMPP este o rețea de mesagerie instant ce nu depinde de un anumit furnizor. Aveți posibilitatea să utilizați acest client cu orice server XMPP doriți. +\nTotuși, pentru confortul dumneavoastră, am facilitat crearea unui cont pe conversations.im; un furnizor potrivit pentru utilizarea cu aplicația Conversations. Ați fost invitați la %1$s. Vă vom ghida prin procesul de creare al unui cont.\nCând alegeți %1$s ca furnizor veți putea comunica cu utilizatorii altor furnizori oferindu-le adresa dumneavoastră completă XMPP. Ați fost invitați la %1$s. Un nume de utilizator a fost deja ales pentru dumneavoastră. Vă vom ghida prin procesul de creare al unui cont.\nVeți putea comunica cu utilizatorii altor furnizori oferindu-le adresa dumneavoastră completă XMPP. Invitația serverului dumneavoastră From 1c0334f796079aaa2e5207de066f98f03e192aec Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:50 +0000 Subject: [PATCH 085/363] Translated using Weblate (Russian) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ru/ --- src/conversations/res/values-ru/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-ru/strings.xml b/src/conversations/res/values-ru/strings.xml index 20b99a7b306327037393c73f51860fbdbd9d18e4..2cf7426c8f5a8cf84cdf2615739b39081d76cc43 100644 --- a/src/conversations/res/values-ru/strings.xml +++ b/src/conversations/res/values-ru/strings.xml @@ -5,7 +5,8 @@ Создать новый аккаунт У вас есть аккаунт XMPP\? Если вы использовали Conversations или другой XMPP-клиент в прошлом, то скорее всего, он у вас есть. Если у вас нет аккаунта, вы можете создать его прямо сейчас. \nПодсказка: Некоторые провайдеры электронной почты также регистрируют аккаунты XMPP. - XMPP - это независимая сеть обмена сообщениями. Conversations позволяет вам подключиться к любому XMPP-серверу на ваш выбор.\nЕсли у вас нет сервера, предлагаем вам зарегистрировать аккаунт на conversations.im, сервере, специально предназначенном для работы с Conversations. + XMPP - это независимая сеть обмена сообщениями. Conversations позволяет вам подключиться к любому XMPP-серверу на ваш выбор. +\nЕсли у вас нет сервера, предлагаем вам зарегистрировать аккаунт на conversations.im, сервере, специально предназначенном для работы с Conversations. Вас пригласили на %1$s. Мы проведём вас через процесс создания аккаунта. \nАккаунт на %1$s позволит вам общаться с пользователями и на этом, и на других серверах, используя ваш полный XMPP-адрес. Вас пригласили на %1$s. Вам уже назначили имя пользователя. Мы проведём вас через процесс создания аккаунта. From 3450a0d5231aa39e4297cacab0c6a2212977d0e3 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:50 +0000 Subject: [PATCH 086/363] Translated using Weblate (Slovak) Currently translated at 76.9% (10 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/sk/ --- src/conversations/res/values-sk/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-sk/strings.xml b/src/conversations/res/values-sk/strings.xml index e280344c4b7f92ee7cdb8314e3bd389ad47e1a16..58d638e37f8001b1a431146a59572164b6e85f16 100644 --- a/src/conversations/res/values-sk/strings.xml +++ b/src/conversations/res/values-sk/strings.xml @@ -4,7 +4,8 @@ Použiť conversations.im Vytvoriť nové konto Máte už svoje XMPP konto? Môže to tak byť v prípade, že už používate iného klienta XMPP alebo ste predtým používali Conversations. Ak nie, môžete si vytvoriť nové XMPP konto práve teraz.\nHint: Niektorí poskytovatelia emailu zároveň poskytujú aj XMPP kontá. - XMPP je sieť pre okamžité správy nezávislá od poskytovateľa. Tohto klienta môžete používať s akýmkoľvek XMPP serverom, ktorý si vyberiete..\nAvšak pre vaše pohodlie sme zjednodušili vytvorenie konta na conversations.im; poskytovateľ špeciálne vhodný na používanie s Conversations. + XMPP je sieť pre okamžité správy nezávislá od poskytovateľa. Tohto klienta môžete používať s akýmkoľvek XMPP serverom, ktorý si vyberiete.. +\nAvšak pre vaše pohodlie sme zjednodušili vytvorenie konta na conversations.im; poskytovateľ špeciálne vhodný na používanie s Conversations. Boli ste pozvaný do %1$s. Prevedieme vás procesom vytvorenia konta..\nPo výbere %1$s ako poskytovateľa, budete môcť komunikovať s užívateľmi iných poskytovateľov tak, že im dáte vašu úplnú XMPP adresu. Boli ste pozvaný do %1$s . Užívateľské meno vám už bolo vopred vybrané. Prevedieme vás procesom vytvorenia konta..\nBudete môcť komunikovať s užívateľmi iných poskytovateľov tak, že im dáte vašu úplnú XMPP adresu. Ťuknite na tlačidlo zdieľať na odoslanie pozvánky do %1$s vášmu kontaktu. From 8b52e0ac5cd77c489e0981d747eb8c16803b1dfd Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:50 +0000 Subject: [PATCH 087/363] Translated using Weblate (Serbian) Currently translated at 38.4% (5 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/sr/ --- src/conversations/res/values-sr/strings.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/conversations/res/values-sr/strings.xml b/src/conversations/res/values-sr/strings.xml index e668ed7e65f00c1e97dbfd074506e5311c1fc4a8..3bbef8725fd0c659f4d3d9056e2d2e46f5d9acfb 100644 --- a/src/conversations/res/values-sr/strings.xml +++ b/src/conversations/res/values-sr/strings.xml @@ -4,6 +4,7 @@ Користи conversations.im Направи нови налог Да ли већ имате ИксМПП налог? Извесно је да га имате ако користите неки ИксМПП клијент или сте раније користили Конверзацију. Ако немате, сада можете направити нови ИксМПП налог.\nСавет: неки поштански провајдери такође омогућавају и ИксМПП налоге. - ИксМПП је мрежа брзих порука, независна од провајдера. Овај клијент можете користити уз било који сервер по вашем избору.\nДа бисмо вам олакшали, омогућили смо креирање налога на conversations.im; провајдеру специјално прилаг.ођеном за коришћење уз Конверзацију + ИксМПП је мрежа брзих порука, независна од провајдера. Овај клијент можете користити уз било који сервер по вашем избору. +\nДа бисмо вам олакшали, омогућили смо креирање налога на conversations.im; провајдеру специјално прилаг.ођеном за коришћење уз Конверзацију Ваша серверска позивница - \ No newline at end of file + \ No newline at end of file From 6328773c6f8cc0a04d5fccc1a92fb6ddc3f23a4a Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:50 +0000 Subject: [PATCH 088/363] Translated using Weblate (Silesian) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/szl/ --- src/conversations/res/values-szl/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-szl/strings.xml b/src/conversations/res/values-szl/strings.xml index 6e0134d065afd48b7dbf893d2383ebb4c2c8d576..bc26248a585893f3707b81d5ce34b93925d4e086 100644 --- a/src/conversations/res/values-szl/strings.xml +++ b/src/conversations/res/values-szl/strings.xml @@ -4,7 +4,8 @@ Użyj conversations.im Stwōrz nowe kōnto Mosz już kōnto XMPP? Tak może być, jeźli już używosz inkszego klijynta XMPP aboś używoł abo używała wcześnij Conversations. Jak niy, to możesz stworzić teroz nowe kōnto XMPP.\nDorada: Niykerzi liferańcio emaili dowajōm tyż kōnta XMPP. - XMPP to je nec wartkich wiadōmości niyzależny ôd liferanta. Możesz używać tego klijynta ze serwerym XMPP, jaki sie wybieresz.\nAle dlo twojij wygody ułacniyli my tworzynie kōnt na conversations.im; liferańcie ekstra dopasowanym do używanio ze Conversations. + XMPP to je nec wartkich wiadōmości niyzależny ôd liferanta. Możesz używać tego klijynta ze serwerym XMPP, jaki sie wybieresz. +\nAle dlo twojij wygody ułacniyli my tworzynie kōnt na conversations.im; liferańcie ekstra dopasowanym do używanio ze Conversations. Mosz zaproszynie na %1$s. Pokludzymy cie bez proces tworzynio kōnta.\nPo wybraniu %1$s za liferanta, poradzisz kōmunikować sie ze używoczami ôd inkszych liferantōw bez danie im swojij połnyj adresy XMPP. Mosz zaproszynie na %1$s. Miano ôd używocza już je do ciebie wybrane. Pokludzymy cie bez proces tworzynio kōnta.\nBydzie szło kōmunikować sie ze używoczami ôd inkszych liferantōw bez danie im swojij połnyj adresy XMPP. Twoje zaproszynie na serwer From 973431755f443ff433d72783f9bea4774b680817 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Sun, 3 Mar 2024 19:26:50 +0000 Subject: [PATCH 089/363] Translated using Weblate (Turkish) Currently translated at 92.3% (12 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/tr/ --- src/conversations/res/values-tr-rTR/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversations/res/values-tr-rTR/strings.xml b/src/conversations/res/values-tr-rTR/strings.xml index 415bc89e09f418b583b849173f751d800313374e..db1b0ac999b4b6ce09ee55526dd15203d390ff99 100644 --- a/src/conversations/res/values-tr-rTR/strings.xml +++ b/src/conversations/res/values-tr-rTR/strings.xml @@ -4,7 +4,8 @@ conversations.im kullan Yeni hesap oluştur Zaten bir XMPP hesabınız var mı? Bunun sebebi, zaten başka bir XMPP istemcisi kullanıyor oluşunuz veya Conversations\'ı önceden kullanmış olmanız olabilir. Eğer durum bu değilse şimdi yeni bir XMPP hesabı oluşturabilirsiniz.\nİpucu: Bağzı e-posta sağlayıcıları da XMPP hesapları kullanabilir. - XMPP; anlık yazışmalar için bağımsız bir sağlayıcıdır. Bu istemciyi istediğiniz herhangi bir XMPP sunucusu ile birlikte kullanabilirsiniz.\nAncak kullanım rahatlığı adına sizin için conversations.im; Conversations için özellikle tasarlanmış bir sağlayıcıda hesap açmanızı kolaylaştırdık. + XMPP; anlık yazışmalar için bağımsız bir sağlayıcıdır. Bu istemciyi istediğiniz herhangi bir XMPP sunucusu ile birlikte kullanabilirsiniz. +\nAncak kullanım rahatlığı adına sizin için conversations.im; Conversations için özellikle tasarlanmış bir sağlayıcıda hesap açmanızı kolaylaştırdık. %1$s sağlayıcısına davet edildiniz. Sizi hesap oluşturulması konusunda yönlendireceğiz.\n%1$s bir sağlayıcı olark seçildiğinde, başka sağlayıcılar kullanan kullanıcılarla, onlara tam XMPP adresinizi vererek iletişim kurabileceksiniz. %1$s sağlayıcısına davet edildiniz. Sizin için zaten bir kullanıcı adı seçildi. Sizi hesap oluşturulması konusunda yönlendireceğiz.\nBaşka sağlayıcılar kullanan kullanıcılarla, onlara tam XMPP adresinizi vererek iletişim kurabileceksiniz. Sunucu davetiyeniz From f80e8918e0951eb48b92375c9fd206d783a45366 Mon Sep 17 00:00:00 2001 From: ghose Date: Mon, 4 Mar 2024 05:09:53 +0000 Subject: [PATCH 090/363] Translated using Weblate (Galician) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/gl/ --- src/conversations/res/values-gl/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values-gl/strings.xml b/src/conversations/res/values-gl/strings.xml index 5d9f097dbd9634ecc98e306b5dfda8711a03e3fd..0c49a79312d0c82d85d0a48d77a9baa8695ddd9f 100644 --- a/src/conversations/res/values-gl/strings.xml +++ b/src/conversations/res/values-gl/strings.xml @@ -4,7 +4,7 @@ Utilizar conversations.im Crear nova conta Xa posúes unha conta XMPP? Este pode ser o caso se xa estás a utilizar outro cliente XMPP ou utilizaches Conversations previamente. Se non é así podes crear unha nova conta agora mesmo.\nTruco: Algúns provedores de correo tamén proporcionan contas XMPP. - XMPP é unha rede de mensaxería independente do provedor. Podes utilizar este cliente con calquera provedor XMPP da túa elección. + XMPP é unha rede de mensaxería independente-do-provedor. Podes utilizar este cliente con calquera provedor XMPP da túa elección. \nMais para a tua conveniencia fixemos que fose doado crear unha conta en conversations.im; un provedor especialmente axeitado para utilizar con Conversations. Convidáronte a %1$s. Guiarémoste no proceso para crear unha conta.\nAo elexir %1$s como provedor poderás comunicarte con usuarias doutros provedores cando lles deas o teu enderezo XMPP completo. Convidáronte a %1$s. Xa eleximos un nome de usuaria para ti. Guiarémoste no proceso de crear unha conta.\nPoderás comunicarte con usuarias doutros provedores cando lles digas o teu enderezo XMPP completo. From a3538f975381f7ef004b1bdbf494f5cb8efa6b63 Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Mon, 4 Mar 2024 13:49:57 +0000 Subject: [PATCH 091/363] Translated using Weblate (Spanish) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/es/ --- src/conversations/res/values-es/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/conversations/res/values-es/strings.xml b/src/conversations/res/values-es/strings.xml index 80958fadc576be0d65adfaab8391262c6cf36bdc..53d97d6e9d88620d16dc8aee63431f33c73bb5d9 100644 --- a/src/conversations/res/values-es/strings.xml +++ b/src/conversations/res/values-es/strings.xml @@ -4,8 +4,8 @@ Usa conversations.im Crear nueva cuenta ¿Ya tienes una cuenta XMPP? Este puede ser el caso si ya estás usando un cliente XMPP diferente o has usado Conversations anteriormente. Si no es así, puedes crear una nueva cuenta XMPP ahora mismo.\nConsejo: Algunos proveedores de email también ofrecen una cuenta XMPP. - XMPP es una red de mensajería instantánea independiente del proveedor. Puedes usar este cliente con cualquier servidor XMPP que elijas. -\nSin embargo, para tu conveniencia, hacemos de forma sencilla la creación de una cuenta en conversations.im; un proveedor especializado para el uso con Conversations. + XMPP es una red de mensajería instantánea independiente del proveedor. Puedes utilizar esta aplicación con cualquier servidor XMPP que elijas. +\nSin embargo, para tu comodidad, te facilitamos la creación de una cuenta en conversations.im, un proveedor específicamente adaptado para su uso con Conversations. Has sido invitado a %1$s. Te guiaremos durante el proceso de creación de la cuenta.\nCuando selecciones %1$s como proveedor podrás comunicarte con usuarios de otros servidores proporcionándoles tu dirección XMPP completa. Has sido invitado a %1$s. Un nombre de usuario ya ha sido escogido para ti. Te guiaremos durante el proceso de creación de la cuenta.\nPodrás comunicarte con otros usuarios de otros servidores proporcionándoles tu dirección XMPP completa. Tu invitación al servidor From 4587b7e818fc8dc33fafcdffab8354c599a911ef Mon Sep 17 00:00:00 2001 From: licaon-kter Date: Mon, 4 Mar 2024 09:27:57 +0000 Subject: [PATCH 092/363] Translated using Weblate (Romanian) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ro/ --- src/conversations/res/values-ro-rRO/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values-ro-rRO/strings.xml b/src/conversations/res/values-ro-rRO/strings.xml index 1c49b14186f6ecf1368dbc38b70a99115cbbebde..90b8763a9286a926594b8fc3d22af532842916e8 100644 --- a/src/conversations/res/values-ro-rRO/strings.xml +++ b/src/conversations/res/values-ro-rRO/strings.xml @@ -4,7 +4,7 @@ Folosește conversations.im Creează un cont nou Aveți deja un cont XMPP? S-ar putea să fie așa dacă deja utilizați un alt client XMPP sau dacă ați folosit Conversations în trecut. Dacă nu, puteți crea un cont nou XMPP chiar acum.\nIdee: Unii furnizori de e-mail oferă de asemenea și conturi XMPP. - XMPP este o rețea de mesagerie instant ce nu depinde de un anumit furnizor. Aveți posibilitatea să utilizați acest client cu orice server XMPP doriți. + XMPP este o rețea de mesagerie instant ce nu depinde de un anumit furnizor. Aveți posibilitatea să utilizați această aplicație cu orice server XMPP doriți. \nTotuși, pentru confortul dumneavoastră, am facilitat crearea unui cont pe conversations.im; un furnizor potrivit pentru utilizarea cu aplicația Conversations. Ați fost invitați la %1$s. Vă vom ghida prin procesul de creare al unui cont.\nCând alegeți %1$s ca furnizor veți putea comunica cu utilizatorii altor furnizori oferindu-le adresa dumneavoastră completă XMPP. Ați fost invitați la %1$s. Un nume de utilizator a fost deja ales pentru dumneavoastră. Vă vom ghida prin procesul de creare al unui cont.\nVeți putea comunica cu utilizatorii altor furnizori oferindu-le adresa dumneavoastră completă XMPP. From b4fd767429b1cfab5a77d725844d98822b77dc55 Mon Sep 17 00:00:00 2001 From: Besnik_b Date: Mon, 4 Mar 2024 09:24:23 +0000 Subject: [PATCH 093/363] Translated using Weblate (Albanian) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/sq/ --- src/conversations/res/values-sq/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values-sq/strings.xml b/src/conversations/res/values-sq/strings.xml index 1e3f34b5b9fed8100052f995230cc926f45d7a63..b87d55c82d128516d55a039a1cf3ba74bc3e990f 100644 --- a/src/conversations/res/values-sq/strings.xml +++ b/src/conversations/res/values-sq/strings.xml @@ -1,6 +1,6 @@ - XMPP është një rrjet shkëmbimi mesazhesh të atypëratyshëm i pavarur nga shërbimet. Këtë klient mund ta përdorni me cilindo shërbyes XMPP që zgjidhni. + XMPP është një rrjet shkëmbimi mesazhesh të atypëratyshëm i pavarur nga shërbimet. Këtë aplikacion mund ta përdorni me cilindo shërbyes XMPP që zgjidhni. \nMegjithatë, për lehtësi, e kemi bërë të kollajshme të krijohet një llogari te conversations.im, një shërbim posaçërisht i përshtatshëm për përdorim me Conversations. Jeni ftuar te %1$s. Do t’ju udhëheqim përmes procesit të krijimit të një llogarie. \nKur zgjidhet %1$s si shërbim, do të jeni në gjendje të komunikoni me përdorues nga shërbime të tjera duke u dhënë adresën tuaj të plotë XMPP. From 5993328cde849ba5de3e2350239214f33862458b Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Mon, 4 Mar 2024 13:51:33 +0000 Subject: [PATCH 094/363] Translated using Weblate (Spanish) Currently translated at 100.0% (2 of 2 strings) Translation: Conversations/App Store Metadata (Quicksy) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-quicksy/es/ --- .../metadata/android/es-ES/full_description.txt | 14 ++++++++++++++ .../metadata/android/es-ES/short_description.txt | 1 + 2 files changed, 15 insertions(+) create mode 100644 src/quicksy/fastlane/metadata/android/es-ES/full_description.txt create mode 100644 src/quicksy/fastlane/metadata/android/es-ES/short_description.txt diff --git a/src/quicksy/fastlane/metadata/android/es-ES/full_description.txt b/src/quicksy/fastlane/metadata/android/es-ES/full_description.txt new file mode 100644 index 0000000000000000000000000000000000000000..7b75633e869fb601e192fa6e5d717c56347ce56e --- /dev/null +++ b/src/quicksy/fastlane/metadata/android/es-ES/full_description.txt @@ -0,0 +1,14 @@ +Quicksy es un programa que se ejecuta en el popular cliente Jabber/XMPP, con descubrimiento de contactos automatizado. + +Regístrese con su número de teléfono y Quicksy, según los números de teléfono de su agenda, sugerirá automáticamente contactos potenciales. + +En esencia, Quicksy es un cliente Jabber completo que le permite comunicarse con cualquier usuario en cualquier servidor público. De manera similar, se puede contactar a los usuarios de Quicksy desde el extranjero simplemente agregando +phonenumber@quicksy.im a su lista de contactos. + +Elimine la sincronización de contactos; la interfaz de usuario se deja intencionalmente lo más cerca posible de Conversations. Esto permite a los usuarios migrar, si lo desean, de Quicksy a Conversations sin tener que volver a aprender cómo funciona la aplicación. + +Los contactos sugeridos consisten en otros usuarios de Quicksy y usuarios habituales de Jabber/XMPP que han proporcionado su ID de Jabber a la Lista de Quicksy (https://quicksy.im/#get-listed). + +NOTA: Para proporcionar (https://quicksy.im/enter/) su ID de Jabber a la Lista +Quicksy requiere una tarifa de registro aplicable única. + +Para más detalles, lea la Política de Privacidad (https://quicksy.im/#privacy). diff --git a/src/quicksy/fastlane/metadata/android/es-ES/short_description.txt b/src/quicksy/fastlane/metadata/android/es-ES/short_description.txt new file mode 100644 index 0000000000000000000000000000000000000000..e988bbf912ec17fa1d7d57b59c39b05c3f1e92b4 --- /dev/null +++ b/src/quicksy/fastlane/metadata/android/es-ES/short_description.txt @@ -0,0 +1 @@ +Jabber/XMPP fácil de ingresar y fácil de descubrir From 08ec15e9e8af8ca66489dc719cfcaeba8c0a9365 Mon Sep 17 00:00:00 2001 From: nautilusx Date: Mon, 4 Mar 2024 21:42:46 +0000 Subject: [PATCH 095/363] Translated using Weblate (German) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/de/ --- src/conversations/res/values-de/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values-de/strings.xml b/src/conversations/res/values-de/strings.xml index 5881988ee2e81ed163542f42c800f3bd213328d2..f3a547897e361bb4c8a704e2a3c2fbf80022c9ec 100644 --- a/src/conversations/res/values-de/strings.xml +++ b/src/conversations/res/values-de/strings.xml @@ -4,7 +4,7 @@ Benutze conversations.im Neues Konto erstellen Hast du bereits ein XMPP-Konto? Dies kann der Fall sein, wenn du bereits einen anderen XMPP-Client verwendest oder bereits Conversations verwendet hast. Wenn nicht, kannst du jetzt ein neues XMPP-Konto erstellen.\nTipp: Einige E-Mail-Anbieter bieten auch XMPP-Konten an. - XMPP ist ein anbieterunabhängiges Instant Messaging Netzwerk. Du kannst diesen Client mit jedem beliebigen XMPP-Server nutzen. + XMPP ist ein anbieterunabhängiges Instant Messaging Netzwerk. Du kannst diese App mit jedem beliebigen XMPP-Server nutzen. \nUm es dir leicht zu machen, haben wir die Möglichkeit geschaffen, ein Konto auf conversations.im anzulegen; ein Anbieter, der speziell für die Verwendung mit Conversations geeignet ist. Du wurdest zu %1$s eingeladen. Wir führen dich durch den Prozess der Kontoerstellung.\nWenn du %1$s als Provider wählst, kannst du mit Nutzern anderer Anbieter kommunizieren, indem du ihnen deine vollständige XMPP-Adresse gibst. Du wurdest zu %1$seingeladen. Ein Benutzername ist bereits für dich ausgewählt worden. Wir führen dich durch den Prozess der Kontoerstellung.\nDu kannst mit Nutzern anderer Anbieter kommunizieren, indem du ihnen deine vollständige XMPP-Adresse gibst. From 6ecc481eca762c60287c6dbeb460965509545ec8 Mon Sep 17 00:00:00 2001 From: ghose Date: Tue, 5 Mar 2024 05:14:42 +0000 Subject: [PATCH 096/363] Translated using Weblate (Galician) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/gl/ --- src/conversations/res/values-gl/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/conversations/res/values-gl/strings.xml b/src/conversations/res/values-gl/strings.xml index 0c49a79312d0c82d85d0a48d77a9baa8695ddd9f..c36e20c552fd27ddb87d09ed09f0f1b79daf2dc6 100644 --- a/src/conversations/res/values-gl/strings.xml +++ b/src/conversations/res/values-gl/strings.xml @@ -4,8 +4,8 @@ Utilizar conversations.im Crear nova conta Xa posúes unha conta XMPP? Este pode ser o caso se xa estás a utilizar outro cliente XMPP ou utilizaches Conversations previamente. Se non é así podes crear unha nova conta agora mesmo.\nTruco: Algúns provedores de correo tamén proporcionan contas XMPP. - XMPP é unha rede de mensaxería independente-do-provedor. Podes utilizar este cliente con calquera provedor XMPP da túa elección. -\nMais para a tua conveniencia fixemos que fose doado crear unha conta en conversations.im; un provedor especialmente axeitado para utilizar con Conversations. + XMPP é unha rede de mensaxería independente-do-provedor. Podes utilizar esta app con calquera provedor XMPP da túa elección. +\nEmporiso, pola tua comenencia, fixemos que fose doado crear unha conta en conversations.im; un provedor moi axeitado para utilizar con Conversations. Convidáronte a %1$s. Guiarémoste no proceso para crear unha conta.\nAo elexir %1$s como provedor poderás comunicarte con usuarias doutros provedores cando lles deas o teu enderezo XMPP completo. Convidáronte a %1$s. Xa eleximos un nome de usuaria para ti. Guiarémoste no proceso de crear unha conta.\nPoderás comunicarte con usuarias doutros provedores cando lles digas o teu enderezo XMPP completo. O convite do teu servidor From 20e1f542779b1c9027449e4e9875814e2f0fa05a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 5 Mar 2024 13:08:43 +0100 Subject: [PATCH 097/363] play ringback sound on android 6/7 --- .../services/AppRTCAudioManager.java | 28 +++++++++++++++++++ .../services/CallIntegration.java | 10 +++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java index 2cd3ac3468601bb9e9474ef1c3b7c5616bf784dc..d9eaeee4871a948e25bfce7c1e7b4edb05b31f62 100644 --- a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java +++ b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java @@ -16,6 +16,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.media.AudioDeviceInfo; import android.media.AudioManager; +import android.media.ToneGenerator; import android.util.Log; import androidx.annotation.Nullable; @@ -25,11 +26,14 @@ import com.google.common.collect.ImmutableSet; import eu.siacs.conversations.Config; import eu.siacs.conversations.utils.AppRTCUtils; +import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; import org.webrtc.ThreadUtils; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; /** AppRTCAudioManager manages all audio related parts of the AppRTC demo. */ public class AppRTCAudioManager { @@ -66,6 +70,7 @@ public class AppRTCAudioManager { private final BroadcastReceiver wiredHeadsetReceiver; // Callback method for changes in audio focus. @Nullable private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener; + private ScheduledFuture ringBackFuture; public AppRTCAudioManager(final Context context) { apprtcContext = context; @@ -454,6 +459,29 @@ public class AppRTCAudioManager { ContextCompat.getMainExecutor(apprtcContext).execute(runnable); } + public void startRingBack() { + this.ringBackFuture = + JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate( + () -> { + final var toneGenerator = + new ToneGenerator( + AudioManager.STREAM_MUSIC, + CallIntegration.DEFAULT_VOLUME); + toneGenerator.startTone(ToneGenerator.TONE_CDMA_DIAL_TONE_LITE, 750); + }, + 0, + 3, + TimeUnit.SECONDS); + } + + public void stopRingBack() { + final var future = this.ringBackFuture; + if (future == null || future.isDone()) { + return; + } + future.cancel(true); + } + /** AudioManager state. */ public enum AudioManagerState { UNINITIALIZED, diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index 378dd62776693a12052fd9c6140ea41f66ae8dbf..0074d7079091ce2e750c3627e50b6502c7bee993 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean; public class CallIntegration extends Connection { - private static final int DEFAULT_VOLUME = 80; + public static final int DEFAULT_VOLUME = 80; private final Context context; @@ -309,7 +309,13 @@ public class CallIntegration extends Connection { @Override public void onStateChanged(final int state) { Log.d(Config.LOGTAG, "onStateChanged(" + state + ")"); - // TODO devices before selfManaged() will likely have to play their own ringback sound + if (notSelfManaged()) { + if (state == STATE_DIALING) { + requireAppRtcAudioManager().startRingBack(); + } else { + requireAppRtcAudioManager().stopRingBack(); + } + } if (state == STATE_ACTIVE) { playConnectedSound(); } else if (state == STATE_DISCONNECTED) { From 71763902f82743cbe9fa37d38551826531376fb1 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 7 Mar 2024 11:38:21 +0100 Subject: [PATCH 098/363] do not use call integration on devices w/o telephony support --- .../services/CallIntegration.java | 32 ++++++++++++++----- .../CallIntegrationConnectionService.java | 4 +-- .../xmpp/jingle/JingleRtpConnection.java | 2 +- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index 0074d7079091ce2e750c3627e50b6502c7bee993..7963680961f8dad017fc4848514feaf85b412918 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.services; import android.content.Context; +import android.content.pm.PackageManager; import android.media.AudioManager; import android.media.ToneGenerator; import android.net.Uri; @@ -50,7 +51,12 @@ public class CallIntegration extends Connection { public CallIntegration(final Context context) { this.context = context.getApplicationContext(); if (selfManaged()) { - setConnectionProperties(Connection.PROPERTY_SELF_MANAGED); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + setConnectionProperties(Connection.PROPERTY_SELF_MANAGED); + } else { + throw new AssertionError( + "Trying to set connection properties on unsupported version"); + } this.appRTCAudioManager = null; } else { this.appRTCAudioManager = new AppRTCAudioManager(context); @@ -151,7 +157,11 @@ public class CallIntegration extends Connection { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { setAudioDeviceUpsideDownCake(audioDevice); } else if (selfManaged()) { - setAudioDeviceOreo(audioDevice); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + setAudioDeviceOreo(audioDevice); + } else { + throw new AssertionError("Trying to set audio devices on unsupported version"); + } } else { setAudioDeviceFallback(audioDevice); } @@ -309,7 +319,7 @@ public class CallIntegration extends Connection { @Override public void onStateChanged(final int state) { Log.d(Config.LOGTAG, "onStateChanged(" + state + ")"); - if (notSelfManaged()) { + if (notSelfManaged(context)) { if (state == STATE_DIALING) { requireAppRtcAudioManager().startRingBack(); } else { @@ -432,18 +442,24 @@ public class CallIntegration extends Connection { callback.onAudioDeviceChanged(selectedAudioDevice, availableAudioDevices); } - public static boolean selfManaged() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + private boolean selfManaged() { + return selfManaged(context); } - public static boolean notSelfManaged() { - return Build.VERSION.SDK_INT < Build.VERSION_CODES.O; + public static boolean selfManaged(final Context context) { + final var packageManager = context.getPackageManager(); + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); + } + + public static boolean notSelfManaged(final Context context) { + return !selfManaged(context); } public void setInitialAudioDevice(final AudioDevice audioDevice) { Log.d(Config.LOGTAG, "setInitialAudioDevice(" + audioDevice + ")"); this.initialAudioDevice = audioDevice; - if (CallIntegration.selfManaged()) { + if (selfManaged()) { // once the 'CallIntegration' gets added to the system we receive calls to update audio // state return; diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index 01602dc70e0d7b251988b565650efd1e458b0d82..ce4f0eb85ea1d8734a86540ad939b553bc4df87e 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -314,7 +314,7 @@ public class CallIntegrationConnectionService extends ConnectionService { final Account account, final Jid with, final Set media) { - if (CallIntegration.selfManaged()) { + if (CallIntegration.selfManaged(service)) { final var extras = new Bundle(); extras.putParcelable( TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, getHandle(service, account)); @@ -349,7 +349,7 @@ public class CallIntegrationConnectionService extends ConnectionService { public static void addNewIncomingCall( final Context context, final AbstractJingleConnection.Id id) { - if (CallIntegration.notSelfManaged()) { + if (CallIntegration.notSelfManaged(context)) { Log.d( Config.LOGTAG, "not adding incoming call to TelecomManager on Android " diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index e22d6574e80cff823f98ee9dab12a88018a69adc..bd069fa8b6d59d7f0188367b05731cf7aaecfd00 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -1692,7 +1692,7 @@ public class JingleRtpConnection extends AbstractJingleConnection ringingTimeoutFuture = jingleConnectionManager.schedule( this::ringingTimeout, BUSY_TIME_OUT, TimeUnit.SECONDS); - if (CallIntegration.selfManaged()) { + if (CallIntegration.selfManaged(xmppConnectionService)) { return; } xmppConnectionService.getNotificationService().startRinging(id, getMedia()); From 55fd7157c810cd93e3c8698419eb7b0b5c27ae1f Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 7 Mar 2024 11:39:16 +0100 Subject: [PATCH 099/363] version bump to 2.14.0-beta.2 --- build.gradle | 4 ++-- .../android/de-DE/changelogs/{4209504.txt => 4209804.txt} | 0 .../android/en-US/changelogs/{4209504.txt => 4209804.txt} | 0 .../android/es-ES/changelogs/{4209504.txt => 4209804.txt} | 0 .../android/gl-ES/changelogs/{4209504.txt => 4209804.txt} | 0 .../android/sq/changelogs/{4209504.txt => 4209804.txt} | 0 .../android/uk/changelogs/{4209504.txt => 4209804.txt} | 0 .../android/zh-CN/changelogs/{4209504.txt => 4209804.txt} | 0 8 files changed, 2 insertions(+), 2 deletions(-) rename fastlane/metadata/android/de-DE/changelogs/{4209504.txt => 4209804.txt} (100%) rename fastlane/metadata/android/en-US/changelogs/{4209504.txt => 4209804.txt} (100%) rename fastlane/metadata/android/es-ES/changelogs/{4209504.txt => 4209804.txt} (100%) rename fastlane/metadata/android/gl-ES/changelogs/{4209504.txt => 4209804.txt} (100%) rename fastlane/metadata/android/sq/changelogs/{4209504.txt => 4209804.txt} (100%) rename fastlane/metadata/android/uk/changelogs/{4209504.txt => 4209804.txt} (100%) rename fastlane/metadata/android/zh-CN/changelogs/{4209504.txt => 4209804.txt} (100%) diff --git a/build.gradle b/build.gradle index 96f35d13d526fc50c2f96c057ef47385f93175b9..f04284345c92bd3cba45f63a47c47e43a946ada1 100644 --- a/build.gradle +++ b/build.gradle @@ -97,8 +97,8 @@ android { defaultConfig { minSdkVersion 23 targetSdkVersion 34 - versionCode 42095 - versionName "2.14.0-beta" + versionCode 42098 + versionName "2.14.0-beta.2" archivesBaseName += "-$versionName" applicationId "eu.siacs.conversations" resValue "string", "applicationId", applicationId diff --git a/fastlane/metadata/android/de-DE/changelogs/4209504.txt b/fastlane/metadata/android/de-DE/changelogs/4209804.txt similarity index 100% rename from fastlane/metadata/android/de-DE/changelogs/4209504.txt rename to fastlane/metadata/android/de-DE/changelogs/4209804.txt diff --git a/fastlane/metadata/android/en-US/changelogs/4209504.txt b/fastlane/metadata/android/en-US/changelogs/4209804.txt similarity index 100% rename from fastlane/metadata/android/en-US/changelogs/4209504.txt rename to fastlane/metadata/android/en-US/changelogs/4209804.txt diff --git a/fastlane/metadata/android/es-ES/changelogs/4209504.txt b/fastlane/metadata/android/es-ES/changelogs/4209804.txt similarity index 100% rename from fastlane/metadata/android/es-ES/changelogs/4209504.txt rename to fastlane/metadata/android/es-ES/changelogs/4209804.txt diff --git a/fastlane/metadata/android/gl-ES/changelogs/4209504.txt b/fastlane/metadata/android/gl-ES/changelogs/4209804.txt similarity index 100% rename from fastlane/metadata/android/gl-ES/changelogs/4209504.txt rename to fastlane/metadata/android/gl-ES/changelogs/4209804.txt diff --git a/fastlane/metadata/android/sq/changelogs/4209504.txt b/fastlane/metadata/android/sq/changelogs/4209804.txt similarity index 100% rename from fastlane/metadata/android/sq/changelogs/4209504.txt rename to fastlane/metadata/android/sq/changelogs/4209804.txt diff --git a/fastlane/metadata/android/uk/changelogs/4209504.txt b/fastlane/metadata/android/uk/changelogs/4209804.txt similarity index 100% rename from fastlane/metadata/android/uk/changelogs/4209504.txt rename to fastlane/metadata/android/uk/changelogs/4209804.txt diff --git a/fastlane/metadata/android/zh-CN/changelogs/4209504.txt b/fastlane/metadata/android/zh-CN/changelogs/4209804.txt similarity index 100% rename from fastlane/metadata/android/zh-CN/changelogs/4209504.txt rename to fastlane/metadata/android/zh-CN/changelogs/4209804.txt From 5cca842e6621818f81bf9ff4009fa7ed72d7cb6f Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 7 Mar 2024 13:37:40 +0100 Subject: [PATCH 100/363] fix return to call --- .../eu/siacs/conversations/ui/ConversationFragment.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index c97aea645f6cd353199c98fc527ffc45958e610e..763bc37600639c2830c0a6a2b25bf7dd2a38d071 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1560,17 +1560,18 @@ public class ConversationFragment extends XmppFragment RtpSessionActivity.EXTRA_ACCOUNT, id.getAccount().getJid().asBareJid().toEscapedString()); intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.getWith().toEscapedString()); - if (id instanceof AbstractJingleConnection.Id) { + if (id instanceof AbstractJingleConnection) { intent.setAction(Intent.ACTION_VIEW); intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.getSessionId()); - } else if (id instanceof JingleConnectionManager.RtpSessionProposal) { - if (((JingleConnectionManager.RtpSessionProposal) id).media.contains(Media.VIDEO)) { + startActivity(intent); + } else if (id instanceof JingleConnectionManager.RtpSessionProposal proposal) { + if (proposal.media.contains(Media.VIDEO)) { intent.setAction(RtpSessionActivity.ACTION_MAKE_VIDEO_CALL); } else { intent.setAction(RtpSessionActivity.ACTION_MAKE_VOICE_CALL); } + startActivity(intent); } - startActivity(intent); } } From 781741b43af2f617585a868cc9fe3109362b480f Mon Sep 17 00:00:00 2001 From: Hund Date: Fri, 8 Mar 2024 10:03:21 +0000 Subject: [PATCH 101/363] Translated using Weblate (Swedish) Currently translated at 98.9% (975 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/ --- src/main/res/values-sv/strings.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 5bdd6aac1899be71688fb479990c3eee9cc89a98..84b259d184f6b9f95215e41905186330e46ab912 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -329,8 +329,8 @@ Kopierade XMPP-adress till urklipp Kopierade felmeddelandet till urklipp webbadress - Scanna 2D-streckkod - Visa 2D-streckkod + Scanna QR-kod + Visa QR-kod Visa blockeringslista Kontodetaljer Bekräfta @@ -524,7 +524,7 @@ Text delades med %s Bevilja %1$s åtkomst till extern lagring Bevilja %1$s åtkomst till kameran - Synkronisera med kontakter + Kontaktlistintegration %1$s vill ha behörighet att komma åt din adressbok för att matcha den med din XMPP-kontaktlista. \nDetta kommer att visa dina kontakters fullständiga namn och visningsbilder. \n @@ -1045,4 +1045,5 @@ Funktionen Channel Discovery, använder en tredjepartstjänst som heter <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Om du använder den här funktionen, överförs din IP-adress och din söktermer till den tjänsten. Se deras <a href=https://search.jabber.network/privacy>sekretesspolicy</a> för mer information. Försök inte att återställa säkerhetskopior som du inte har skapat själv! Det gick inte att ta bort kontot från servern + Quicksy ber om ditt samtycke för att använda dina uppgifter \ No newline at end of file From a3cf788e0d0a0114c1ce6842f2c6edab98f8534e Mon Sep 17 00:00:00 2001 From: Outbreak2096 Date: Thu, 7 Mar 2024 09:50:59 +0000 Subject: [PATCH 102/363] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/zh_Hans/ --- src/conversations/res/values-zh-rCN/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values-zh-rCN/strings.xml b/src/conversations/res/values-zh-rCN/strings.xml index 34cf3573435def88de5e2dd17a9286321702f7f5..b16b819f7b288f0229938a714499e72571b1a6ac 100644 --- a/src/conversations/res/values-zh-rCN/strings.xml +++ b/src/conversations/res/values-zh-rCN/strings.xml @@ -5,7 +5,7 @@ 创建新账号 您已经有 XMPP 账号了吗?如果您之前使用过 Conversations 或其他 XMPP 客户端,那么您已经有账号了。如果没有,您可以立即创建一个。 \n提示:一些电子邮件服务也提供 XMPP 账号。 - XMPP 是独立于提供者的即时通讯网络。您选择的任何 XMPP 服务器都可以使用此客户端。 + XMPP 是独立于提供者的即时通讯网络。您选择的任何 XMPP 服务器都可以使用此应用。 \n不过,您可以轻松地在 conversations.im 上创建账号;特别适合与 Conversations 使用的提供者。 您已受邀加入 %1$s。我们将指导您创建账号。 \n当选择 %1$s 作为提供者时,向其他 XMPP 用户提供您的完整地址,就能和对方交流。 From 88030c8b8b024280a5884b9888e14d6ba18cb627 Mon Sep 17 00:00:00 2001 From: petitpois Date: Fri, 8 Mar 2024 21:58:04 +0000 Subject: [PATCH 103/363] Translated using Weblate (French) Currently translated at 99.3% (979 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fr/ --- src/main/res/values-fr/strings.xml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index fcd0cc8112233a9408c7650fdde09a680f7b0c62..9dea126015e588e036386ec8282e5c181ffde3d8 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -319,8 +319,8 @@ Adresse XMPP copiée dans le presse-papiers Message d\'erreur copié dans le presse-papier adresse internet - Scanner le code-barres 2D - Montrer le code-barres 2D + Scanner le QR code + Montrer le QR code Afficher la liste des contacts bloqués Détails du compte Confirmer @@ -534,7 +534,7 @@ Ce champ est requis Corriger le message Envoyer le message corrigé - Vous avez déjà validé l\'empreinte de cette personne pour accorder votre confiance. En sélectionnant « Terminé », vous confirmez simplement que %s fait partie de ce groupe. + Vous avez déjà fait confiance à l\'empreinte de cette personne pour accorder votre confiance. En sélectionnant « Terminé », vous confirmez simplement que %s fait partie de ce groupe. Vous avez désactivé ce compte Erreur de sécurité : accès invalide au fichier ! Aucune application disponible pour partager l\'URI @@ -615,7 +615,7 @@ Faire automatiquement confiance aux nouveaux appareils des contacts qui n\'ont pas été vérifiés auparavant mais demander une confirmation manuelle à chaque fois qu\'un contact vérifié auparavant utilise un nouvel appareil. Les clés OMEMO ont fait l\'objet d\'une confiance aveugle, cela signifie qu\'il pourrait s\'agir de quelqu\'un d\'autre ou que quelqu\'un aurait pu intercepter l\'échange. Non approuvée - Code-barres 2D invalide + QR code invalide Vide le dossier de cache (utilisé par l\'appplication caméra) Vider le cache Vider le stockage privé @@ -699,7 +699,7 @@ Désirez-vous quand-même vous connecter ? Détails du certificat : Une fois - La lecture d\'un QR Code nécessite l\'accès à l\'appareil photo + La lecture d\'un QR code nécessite l\'accès à l\'appareil photo Faire défiler l\'écran jusqu\'en bas Faire défiler l\'écran jusqu\'en bas après avoir envoyé un message Modifier le message de l\'état @@ -1036,4 +1036,17 @@ Livre audio Distributeur UnifiedPush Ne tentez pas de restaurer des sauvegardes que vous n\'avez pas créées vous-même ! + Signaler un spam + Politique de confidentialité + Quicksy vous demande votre consentement pour utiliser vos données + Signaler un spam et bloquer son auteur + Déconnecté + S\'identifier + Vous vous êtes déconnecté⸱e de ce compte + Bienvenue sur Quicksy ! + Reconnexion sur un autre hôte + Pas d\'autorisation pour passer un appel téléphonique + Contact non disponible + Cacher la notification + Se déconnecter \ No newline at end of file From 6bb99d9da601478c3bcb14482b759013d9d46d31 Mon Sep 17 00:00:00 2001 From: petitpois Date: Fri, 8 Mar 2024 21:34:40 +0000 Subject: [PATCH 104/363] Translated using Weblate (French) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/fr/ --- src/conversations/res/values-fr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values-fr/strings.xml b/src/conversations/res/values-fr/strings.xml index 752d700412ea3da8e26e8f9278e0616a8f9f9c4d..ba7122979650f86a7e3281a23475ab7d97be7625 100644 --- a/src/conversations/res/values-fr/strings.xml +++ b/src/conversations/res/values-fr/strings.xml @@ -4,7 +4,7 @@ Utiliser conversations.im Créer un nouveau compte Avez-vous déjà un compte XMPP ? Cela peut être le cas si vous utilisez déjà un autre client XMPP ou si vous avez déjà utilisé Conversations auparavant. Sinon, vous pouvez créer un nouveau compte XMPP dès maintenant.\nRemarque : Certains fournisseurs de messagerie proposent également des comptes XMPP. - XMPP est un réseau de messagerie instantanée indépendant du fournisseur. Vous pouvez utiliser ce client avec n’importe quel serveur XMPP de votre choix. + XMPP est un réseau de messagerie instantanée indépendant du fournisseur. Vous pouvez utiliser cette application avec n’importe quel serveur XMPP de votre choix. \nToutefois, pour votre commodité, nous avons facilité la création d’un compte sur conversations.im ; un fournisseur spécialement conçu pour Conversations. Vous avez été invité à %1$s. Nous allons vous guider à travers le processus de création d’un compte.\nEn choisissant %1$s comme fournisseur, vous pourrez communiquer avec les utilisateurs des autres fournisseurs en leur donnant votre adresse XMPP complète. Vous avez été invité à %1$s. Un nom d’utilisateur a déjà été choisi pour vous. Nous allons vous guider à travers le processus de création d’un compte.\nVous pourrez communiquer avec les utilisateurs des autres fournisseurs en leur donnant votre adresse XMPP complète. From 4264764d41c857c26e2c14487e3538aba3d5034d Mon Sep 17 00:00:00 2001 From: petitpois Date: Fri, 8 Mar 2024 22:18:39 +0000 Subject: [PATCH 105/363] Translated using Weblate (French) Currently translated at 50.0% (1 of 2 strings) Translation: Conversations/App Store Metadata (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/fr/ --- .../fastlane/metadata/android/fr-FR/short_description.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/conversations/fastlane/metadata/android/fr-FR/short_description.txt diff --git a/src/conversations/fastlane/metadata/android/fr-FR/short_description.txt b/src/conversations/fastlane/metadata/android/fr-FR/short_description.txt new file mode 100644 index 0000000000000000000000000000000000000000..b4ae66d63e11132d66b0742a0475b1c578f0b277 --- /dev/null +++ b/src/conversations/fastlane/metadata/android/fr-FR/short_description.txt @@ -0,0 +1 @@ +Messagerie instantanée XMPP chiffrée, facile à utiliser avec votre appareil mobile From ea78617ee1f06a25d1b3bcdbbec2ffddf3c20e1d Mon Sep 17 00:00:00 2001 From: Eryk Michalak Date: Sat, 9 Mar 2024 13:27:59 +0000 Subject: [PATCH 106/363] Translated using Weblate (Polish) Currently translated at 99.6% (982 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/ --- src/main/res/values-pl/strings.xml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index c567574f3a350ed11d307e53952aef9320adb3ed..11e31ccdb295365c86e396d5ee6e553ad8ebba34 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -324,7 +324,7 @@ Skopiowano adres XMPP do schowka Skopiowano komunikat błędu do schowka adres URL - Zeskanuj kod + Zeskanuj kod QR Pokaż kod QR Wyświetl listę banów Szczegóły konta @@ -523,11 +523,10 @@ Tekst udostępniony %s Pozwól %1$s na dostęp do zewnętrznego magazynu Pozwól %1$s na dostępu do aparatu - Synchronizuj z kontaktami - %1$s potrzebuje dostępu do twojej książki adresowej aby dopasować ją z twoją listą kontaktów XMPP. -\nDzięki temu wyświetlone zostaną pełne nazwy i awatary kontaktów. + Integracja listy kontaktów + %1$s przetwarza twoje kontakty lokalnie, na twoim urządzeniu, by pokazać nazwę i awatar profilu zgodny z kontaktami z XMPP. \n -\n%1$s użyje książki adresowej wyłącznie do lokalnego dopasowania bez wysyłania czegokolwiek na serwer. +\nŻaden kontakt nie opuści twojego urządzenia! Powiadom o wszystkich wiadomościach Powiadamiaj tylko w przypadku wzmianki o mnie Powiadomienia wyłączone @@ -629,7 +628,7 @@ Automatycznie ufaj wszystkim nowym urządzeniom kontaktów, którzy nie zostali zweryfikowani wcześniej i poproś o ręczne potwierdzenie za każdym razem, kiedy zweryfikowany kontakt dodaje nowe urządzenie. Ślepo zaufane klucze OMEMO, to jest mogą należeć do kogoś innego lub ktoś może się podszywać. Niezaufane - Nieprawidłowy kod kreskowy 2D + Nieprawidłowy kod QR Wyczyść cache (używane przez aparat) Wyczyść cache Wyczyść prywatny magazyn @@ -1052,10 +1051,14 @@ Wylogowano się z tego konta Zaloguj się Ukryj powiadomienie - Twój kontakt korzysta z niezweryfikowanych urządzeń. Zeskanuj ich kod kreskowy 2D, aby przeprowadzić weryfikację i uniemożliwić aktywne ataki MITM. + Twój kontakt korzysta z niezweryfikowanych urządzeń. Zeskanuj ich kod QR, aby przeprowadzić weryfikację i uniemożliwić aktywne ataki MITM. Zgłoś spam i zablokuj nadawcę Wyloguj się Wylogowano - Używasz z niezweryfikowanych urządzeń. Zeskanuj kod kreskowy 2D na innych urządzeniach, aby przeprowadzić weryfikację i uniemożliwić aktywne ataki MITM. + Korzystasz z niezweryfikowanych urządzeń. Zeskanuj kod QR na innych urządzeniach, aby przeprowadzić weryfikację i uniemożliwić aktywne ataki MITM. Zgłoś spam + Polityka prywatności + Witamy w Quicksy! + Kontakt nie jest dostępny + Integracja listy kontaktów nie jest dostępna \ No newline at end of file From bc3ccfb1be87e217fac13329c7a7d5df59924060 Mon Sep 17 00:00:00 2001 From: Eryk Michalak Date: Sat, 9 Mar 2024 13:30:31 +0000 Subject: [PATCH 107/363] Translated using Weblate (Polish) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/pl/ --- src/conversations/res/values-pl/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values-pl/strings.xml b/src/conversations/res/values-pl/strings.xml index 585fe01b39fbc7ba43d5ff6820510603bdbeb7dc..62a7f4104d8d40af6ae0f80791b99917ffaaa9d0 100644 --- a/src/conversations/res/values-pl/strings.xml +++ b/src/conversations/res/values-pl/strings.xml @@ -4,7 +4,7 @@ Użyj conversations.im Utwórz nowe konto Czy masz już konto XMPP? Tak może być jeśli używasz już innego klienta XMPP lub używałeś już Conversations. Jeśli nie możesz stworzyć nowe konto XMPP teraz.\nPodpowiedź: Niektórzy dostawcy poczty oferują również konta XMPP. - XMPP to niezależna od dostawcy sieć komunikacji błyskawicznej. Możesz użyć tego klienta z dowolnym serwerem XMPP. + XMPP to niezależna od dostawcy sieć komunikacji błyskawicznej. Możesz użyć tej aplikacji z dowolnym serwerem XMPP. \nDla twojej wygody jednak ułatwiliśmy stworzenie konta na conversations.im; dostawcy specjalnie dostosowanego do pracy z Conversations. Zostałeś zaproszony do %1$s. Poprowadzimy ciebie przez proces tworzenia konta.\nWybierając %1$s jako dostawcę będziesz mógł komunikować się z innymi użytkownikami podając swój pełny adres XMPP. Zostałeś zaproszony do %1$s. Nazwa użytkownika została już dla ciebie wybrana. Poprowadzimy ciebie przez proces tworzenia konta.\nBęziesz mógł komunikować się z innymi użytkownikami podając swój adres XMPP. From e5cffa11befeef6b0c5515d749b3e7290423f48a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 10 Mar 2024 17:38:56 +0100 Subject: [PATCH 108/363] fix stanza counting error after inline SM enable --- .../java/eu/siacs/conversations/xmpp/XmppConnection.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 9141e1c70e3fb1006ecb4b896b2c5c47eb22080f..88f740d5b73439cae4acb87b2a43b18baa405641 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -935,7 +935,8 @@ public class XmppConnection implements Runnable { private void resetOutboundStanzaQueue() { synchronized (this.mStanzaQueue) { - final List intermediateStanzas = new ArrayList<>(); + final ImmutableList.Builder intermediateStanzasBuilder = + new ImmutableList.Builder<>(); if (Config.EXTENDED_SM_LOGGING) { Log.d( Config.LOGTAG, @@ -946,12 +947,13 @@ public class XmppConnection implements Runnable { for (int i = this.stanzasSentBeforeAuthentication + 1; i <= this.stanzasSent; ++i) { final AbstractAcknowledgeableStanza stanza = this.mStanzaQueue.get(i); if (stanza != null) { - intermediateStanzas.add(stanza); + intermediateStanzasBuilder.add(stanza); } } this.mStanzaQueue.clear(); + final var intermediateStanzas = intermediateStanzasBuilder.build(); for (int i = 0; i < intermediateStanzas.size(); ++i) { - this.mStanzaQueue.put(i, intermediateStanzas.get(i)); + this.mStanzaQueue.append(i + 1, intermediateStanzas.get(i)); } this.stanzasSent = intermediateStanzas.size(); if (Config.EXTENDED_SM_LOGGING) { From 9ad5b68d572f1dd6789dc1a02d9d7ab27a34f8ae Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 10 Mar 2024 17:40:41 +0100 Subject: [PATCH 109/363] do not attempt unique/exporter channel binding on non conscrypt sockets --- .../crypto/sasl/ChannelBinding.java | 29 ++++------ .../crypto/sasl/ChannelBindingMechanism.java | 17 ++++-- .../siacs/conversations/utils/SSLSockets.java | 56 +++++++++---------- 3 files changed, 53 insertions(+), 49 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBinding.java b/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBinding.java index 2eb5e39fb2999e47a6c97c9691838c195a26c43d..3cbaaeeb9ab08b29b0f264ac06ce99240a282446 100644 --- a/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBinding.java +++ b/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBinding.java @@ -10,15 +10,15 @@ import com.google.common.collect.BiMap; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableBiMap; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - import eu.siacs.conversations.Config; import eu.siacs.conversations.utils.SSLSockets; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Namespace; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + public enum ChannelBinding { NONE, TLS_EXPORTER, @@ -104,22 +104,17 @@ public enum ChannelBinding { } private static String shortName(final ChannelBinding channelBinding) { - switch (channelBinding) { - case TLS_UNIQUE: - return "UNIQ"; - case TLS_EXPORTER: - return "EXPR"; - case TLS_SERVER_END_POINT: - return "ENDP"; - case NONE: - return "NONE"; - default: - throw new AssertionError("Missing short name for " + channelBinding); - } + return switch (channelBinding) { + case TLS_UNIQUE -> "UNIQ"; + case TLS_EXPORTER -> "EXPR"; + case TLS_SERVER_END_POINT -> "ENDP"; + case NONE -> "NONE"; + default -> throw new AssertionError("Missing short name for " + channelBinding); + }; } public static int priority(final ChannelBinding channelBinding) { - if (Arrays.asList(TLS_EXPORTER,TLS_UNIQUE).contains(channelBinding)) { + if (Arrays.asList(TLS_EXPORTER, TLS_UNIQUE).contains(channelBinding)) { return 2; } else if (channelBinding == ChannelBinding.TLS_SERVER_END_POINT) { return 1; diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBindingMechanism.java b/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBindingMechanism.java index 7343eb86e82b5a065882d6bc49552b4b20222a67..6c763a38220713fecc23dddee5b5096b9aef95b1 100644 --- a/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBindingMechanism.java +++ b/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBindingMechanism.java @@ -20,12 +20,18 @@ public interface ChannelBindingMechanism { ChannelBinding getChannelBinding(); - static byte[] getChannelBindingData(final SSLSocket sslSocket, final ChannelBinding channelBinding) + static byte[] getChannelBindingData( + final SSLSocket sslSocket, final ChannelBinding channelBinding) throws SaslMechanism.AuthenticationException { if (sslSocket == null) { - throw new SaslMechanism.AuthenticationException("Channel binding attempt on non secure socket"); + throw new SaslMechanism.AuthenticationException( + "Channel binding attempt on non secure socket"); } if (channelBinding == ChannelBinding.TLS_EXPORTER) { + if (!Conscrypt.isConscrypt(sslSocket)) { + throw new SaslMechanism.AuthenticationException( + "Channel binding attempt on non supporting socket"); + } final byte[] keyingMaterial; try { keyingMaterial = @@ -39,6 +45,10 @@ public interface ChannelBindingMechanism { } return keyingMaterial; } else if (channelBinding == ChannelBinding.TLS_UNIQUE) { + if (!Conscrypt.isConscrypt(sslSocket)) { + throw new SaslMechanism.AuthenticationException( + "Channel binding attempt on non supporting socket"); + } final byte[] unique = Conscrypt.getTlsUnique(sslSocket); if (unique == null) { throw new SaslMechanism.AuthenticationException( @@ -99,8 +109,7 @@ public interface ChannelBindingMechanism { } static int getPriority(final SaslMechanism mechanism) { - if (mechanism instanceof ChannelBindingMechanism) { - final ChannelBindingMechanism channelBindingMechanism = (ChannelBindingMechanism) mechanism; + if (mechanism instanceof ChannelBindingMechanism channelBindingMechanism) { return ChannelBinding.priority(channelBindingMechanism.getChannelBinding()); } else { return 0; diff --git a/src/main/java/eu/siacs/conversations/utils/SSLSockets.java b/src/main/java/eu/siacs/conversations/utils/SSLSockets.java index ae853bea8a4914ff03bab3a71554659b32119c5e..4a8680e20edbbd5726577572b158cfbecd705815 100644 --- a/src/main/java/eu/siacs/conversations/utils/SSLSockets.java +++ b/src/main/java/eu/siacs/conversations/utils/SSLSockets.java @@ -7,6 +7,9 @@ import androidx.annotation.RequiresApi; import com.google.common.base.Strings; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.entities.Account; + import org.conscrypt.Conscrypt; import java.lang.reflect.Method; @@ -24,22 +27,19 @@ import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; -import eu.siacs.conversations.Config; -import eu.siacs.conversations.entities.Account; - public class SSLSockets { public static void setSecurity(final SSLSocket sslSocket) { final String[] supportProtocols; - final Collection supportedProtocols = new LinkedList<>( - Arrays.asList(sslSocket.getSupportedProtocols())); + final Collection supportedProtocols = + new LinkedList<>(Arrays.asList(sslSocket.getSupportedProtocols())); supportedProtocols.remove("SSLv3"); supportProtocols = supportedProtocols.toArray(new String[0]); sslSocket.setEnabledProtocols(supportProtocols); - final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites( - sslSocket.getSupportedCipherSuites()); + final String[] cipherSuites = + CryptoHelper.getOrderedCipherSuites(sslSocket.getSupportedCipherSuites()); if (cipherSuites.length > 0) { sslSocket.setEnabledCipherSuites(cipherSuites); } @@ -70,7 +70,8 @@ public class SSLSockets { socket.setSSLParameters(parameters); } - private static void setApplicationProtocolReflection(final SSLSocket socket, final String protocol) { + private static void setApplicationProtocolReflection( + final SSLSocket socket, final String protocol) { try { final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class); // the concatenation of 8-bit, length prefixed protocol names, just one in our case... @@ -78,16 +79,17 @@ public class SSLSockets { final byte[] protocolUTF8Bytes = protocol.getBytes(StandardCharsets.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}); + System.arraycopy( + protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length); + method.invoke(socket, new Object[] {lengthPrefixedProtocols}); } catch (Throwable e) { - Log.e(Config.LOGTAG,"unable to set ALPN on socket",e); + Log.e(Config.LOGTAG, "unable to set ALPN on socket", e); } } public static void setApplicationProtocol(final SSLSocket socket, final String protocol) { if (Conscrypt.isConscrypt(socket)) { - Conscrypt.setApplicationProtocols(socket, new String[]{protocol}); + Conscrypt.setApplicationProtocols(socket, new String[] {protocol}); } else { setApplicationProtocolReflection(socket, protocol); } @@ -113,9 +115,12 @@ public class SSLSockets { } public static Version version(final Socket socket) { - if (socket instanceof SSLSocket) { - final SSLSocket sslSocket = (SSLSocket) socket; - return Version.of(sslSocket.getSession().getProtocol()); + if (socket instanceof SSLSocket sslSocket) { + if (Conscrypt.isConscrypt(sslSocket)) { + return Version.of(sslSocket.getSession().getProtocol()); + } else { + return Version.TLS_UNSUPPORTED_VERSION; + } } else { return Version.NONE; } @@ -126,22 +131,17 @@ public class SSLSockets { TLS_1_1, TLS_1_2, TLS_1_3, - UNKNOWN, + TLS_UNSUPPORTED_VERSION, NONE; private static Version of(final String protocol) { - switch (Strings.nullToEmpty(protocol)) { - case "TLSv1": - return TLS_1_0; - case "TLSv1.1": - return TLS_1_1; - case "TLSv1.2": - return TLS_1_2; - case "TLSv1.3": - return TLS_1_3; - default: - return UNKNOWN; - } + return switch (Strings.nullToEmpty(protocol)) { + case "TLSv1" -> TLS_1_0; + case "TLSv1.1" -> TLS_1_1; + case "TLSv1.2" -> TLS_1_2; + case "TLSv1.3" -> TLS_1_3; + default -> TLS_UNSUPPORTED_VERSION; + }; } } } From 1bf1411e11965d0c5f6c96b53118c99db3661cf3 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 10 Mar 2024 18:58:08 +0100 Subject: [PATCH 110/363] add FAST to doap file --- conversations.doap | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/conversations.doap b/conversations.doap index 4d492f9b3faad10115bd84261a67a0e759511997..486baed404255f278a705e34867d1b2e44b63a63 100644 --- a/conversations.doap +++ b/conversations.doap @@ -474,12 +474,19 @@ 0.1.0 + + + + complete + 0.1.0 + + - 2.9.13 - 2021-05-03 - + 2.13.4 + 2024-02-20 + From 20935f2271197b0b19fb180fe685f83623b85655 Mon Sep 17 00:00:00 2001 From: Eryk Michalak Date: Sun, 10 Mar 2024 14:51:44 +0000 Subject: [PATCH 111/363] Translated using Weblate (Polish) Currently translated at 100.0% (985 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/ --- src/main/res/values-pl/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 11e31ccdb295365c86e396d5ee6e553ad8ebba34..a8e690e7643c945515dea6ba34cac225b6578d36 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -1061,4 +1061,7 @@ Witamy w Quicksy! Kontakt nie jest dostępny Integracja listy kontaktów nie jest dostępna + Quicksy pyta o pozwolenie na użycie twoich danych + Brak pozwolenia na wypisanie numeru telefonu + Integracja połączeń nie jest dostępna! \ No newline at end of file From d242aaf92050cdb6cc4c82c1c9dd0de73710430d Mon Sep 17 00:00:00 2001 From: v1s7 Date: Sun, 10 Mar 2024 07:44:31 +0000 Subject: [PATCH 112/363] Translated using Weblate (Russian) Currently translated at 100.0% (985 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/ --- src/main/res/values-ru/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index fe5e5bc2dcac7f16a9ed206842169afd91c505c9..4c18fb1df00fcfc460052f45b835d2eba0c76a8d 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -1057,4 +1057,7 @@ Интеграция списка контактов недоступна Quicksy запрашивает ваше согласие на использование ваших данных Добро пожаловать в Quicksy! + Контакт недоступен + Интеграция вызовов недоступна! + Нет разрешения на телефонный звонок \ No newline at end of file From f4d8601d010ec6540f18307fc37336ebecbebeaf Mon Sep 17 00:00:00 2001 From: Hund Date: Sun, 10 Mar 2024 19:56:03 +0000 Subject: [PATCH 113/363] Translated using Weblate (Swedish) Currently translated at 99.5% (981 of 985 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sv/ --- src/main/res/values-sv/strings.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 84b259d184f6b9f95215e41905186330e46ab912..30f202d1c8ff84e67c945cf0d1a23e932a095173 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -1026,7 +1026,7 @@ Logga in Dölj notis Återanslut på annan värd - Din kontakt använder overifierade enheter. Skanna deras 2D-streckkod för att utföra en verifiering och för att förhindra aktiva MITM-attacker. + Din kontakt använder overifierade enheter. Skanna deras QR-kod för att utföra en verifiering och för att förhindra aktiva MITM-attacker. Ta bort kontot från servern Rapportera spam och blockera spammaren Inkommande samtal (%s) · %s @@ -1037,7 +1037,7 @@ Neka Utgående samtal (%s) · %s Utloggad - Du använder overifierade enheter. Skanna 2D-streckkoden på dina andra enheter, för att utföra en verifiering och för att förhindra aktiva MITM-attacker. + Du använder overifierade enheter. Skanna QR-kod på dina andra enheter, för att utföra en verifiering och för att förhindra aktiva MITM-attacker. Utgående samtal · %s Spara som gruppchatt Ljudbok @@ -1046,4 +1046,8 @@ Försök inte att återställa säkerhetskopior som du inte har skapat själv! Det gick inte att ta bort kontot från servern Quicksy ber om ditt samtycke för att använda dina uppgifter + Integritetspolicy + Integrering av kontaktlista är inte tillgängligt + Samtalsintegration inte tillgänglig! + Kontakten är inte tillgänglig \ No newline at end of file From 0f50f711765affd3bac5cbcf08c49167ce00f8cb Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 12 Mar 2024 09:22:26 +0100 Subject: [PATCH 114/363] prevent deletion of bookmark w/o also closing conversation --- .../ui/ConferenceDetailsActivity.java | 30 ++++--------------- .../ui/StartConversationActivity.java | 26 ++++++++++++---- src/main/res/menu/muc_details.xml | 5 ---- src/main/res/values-ar/strings.xml | 1 - src/main/res/values-bg/strings.xml | 1 - src/main/res/values-ca/strings.xml | 1 - src/main/res/values-cs/strings.xml | 2 -- src/main/res/values-da-rDK/strings.xml | 2 -- src/main/res/values-de/strings.xml | 2 -- src/main/res/values-el/strings.xml | 1 - src/main/res/values-es/strings.xml | 2 -- src/main/res/values-fa-rIR/strings.xml | 2 -- src/main/res/values-fi/strings.xml | 2 -- src/main/res/values-fr/strings.xml | 2 -- src/main/res/values-gl/strings.xml | 2 -- src/main/res/values-hr/strings.xml | 1 - src/main/res/values-hu/strings.xml | 1 - src/main/res/values-id/strings.xml | 1 - src/main/res/values-it/strings.xml | 2 -- src/main/res/values-ja/strings.xml | 2 -- src/main/res/values-nl/strings.xml | 1 - src/main/res/values-pl/strings.xml | 2 -- src/main/res/values-pt-rBR/strings.xml | 1 - src/main/res/values-ro-rRO/strings.xml | 2 -- src/main/res/values-ru/strings.xml | 2 -- src/main/res/values-sk/strings.xml | 1 - src/main/res/values-sq-rAL/strings.xml | 2 -- src/main/res/values-sr/strings.xml | 1 - src/main/res/values-sv/strings.xml | 2 -- src/main/res/values-szl/strings.xml | 1 - src/main/res/values-tr-rTR/strings.xml | 1 - src/main/res/values-uk/strings.xml | 2 -- src/main/res/values-vi/strings.xml | 2 -- src/main/res/values-zh-rCN/strings.xml | 2 -- src/main/res/values-zh-rTW/strings.xml | 2 -- src/main/res/values/strings.xml | 4 ++- 36 files changed, 29 insertions(+), 87 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index f5fb9c7f5dd536b6252a6e23ad660e4cce48418d..47debf9c1c24be70d2aa4e4cc93f350bbf6a8fe5 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -243,9 +243,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers case R.id.action_save_as_bookmark: saveAsBookmark(); break; - case R.id.action_delete_bookmark: - deleteBookmark(); - break; case R.id.action_destroy_room: destroyRoom(); break; @@ -345,28 +342,21 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers } @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 menuItemDestroyRoom = menu.findItem(R.id.action_destroy_room); + public boolean onPrepareOptionsMenu(final Menu menu) { + final MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark); + final MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode); + final MenuItem menuItemDestroyRoom = menu.findItem(R.id.action_destroy_room); menuItemAdvancedMode.setChecked(mAdvancedMode); if (mConversation == null) { return true; } - if (mConversation.getBookmark() != null) { - menuItemSaveBookmark.setVisible(false); - menuItemDeleteBookmark.setVisible(true); - } else { - menuItemDeleteBookmark.setVisible(false); - menuItemSaveBookmark.setVisible(true); - } + menuItemSaveBookmark.setVisible(mConversation.getBookmark() == null); menuItemDestroyRoom.setVisible(mConversation.getMucOptions().getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER)); return true; } @Override - public boolean onCreateOptionsMenu(Menu menu) { + public boolean onCreateOptionsMenu(final Menu menu) { final boolean groupChat = mConversation != null && mConversation.isPrivateAndNonAnonymous(); getMenuInflater().inflate(R.menu.muc_details, menu); final MenuItem share = menu.findItem(R.id.action_share); @@ -392,14 +382,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers xmppConnectionService.saveConversationAsBookmark(mConversation, mConversation.getMucOptions().getName()); } - protected void deleteBookmark() { - final Account account = mConversation.getAccount(); - final Bookmark bookmark = mConversation.getBookmark(); - bookmark.setConversation(null); - xmppConnectionService.deleteBookmark(account, bookmark); - updateView(); - } - protected void destroyRoom() { final boolean groupChat = mConversation != null && mConversation.isPrivateAndNonAnonymous(); AlertDialog.Builder builder = new AlertDialog.Builder(this); diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index 8b94dd44039adf17a9e85c20e329581d81828f83..1e54f2205e3473e0c2322171b00602801de96a17 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -502,17 +502,25 @@ public class StartConversationActivity extends XmppActivity implements XmppConne } protected void deleteConference() { - int position = conference_context_id; + final int position = conference_context_id; final Bookmark bookmark = (Bookmark) conferences.get(position); - - AlertDialog.Builder builder = new AlertDialog.Builder(this); + final var conversation = bookmark.getConversation(); + final boolean hasConversation = conversation != null; + final AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setNegativeButton(R.string.cancel, null); builder.setTitle(R.string.delete_bookmark); - builder.setMessage(JidDialog.style(this, R.string.remove_bookmark_text, bookmark.getJid().toEscapedString())); - builder.setPositiveButton(R.string.delete, (dialog, which) -> { + if (hasConversation) { + builder.setMessage(JidDialog.style(this, R.string.remove_bookmark_and_close, bookmark.getJid().toEscapedString())); + } else { + builder.setMessage(JidDialog.style(this, R.string.remove_bookmark, bookmark.getJid().toEscapedString())); + } + builder.setPositiveButton(hasConversation ? R.string.delete_and_close : R.string.delete, (dialog, which) -> { bookmark.setConversation(null); final Account account = bookmark.getAccount(); xmppConnectionService.deleteBookmark(account, bookmark); + if (conversation != null) { + xmppConnectionService.archiveConversation(conversation); + } filter(mSearchEditText.getText().toString()); }); builder.create().show(); @@ -1245,7 +1253,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne } @Override - public void onCreateContextMenu(final ContextMenu menu, final View v, final ContextMenuInfo menuInfo) { + public void onCreateContextMenu(@NonNull final ContextMenu menu, @NonNull final View v, final ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); final StartConversationActivity activity = (StartConversationActivity) getActivity(); if (activity == null) { @@ -1258,6 +1266,12 @@ public class StartConversationActivity extends XmppActivity implements XmppConne final Bookmark bookmark = (Bookmark) activity.conferences.get(acmi.position); final Conversation conversation = bookmark.getConversation(); final MenuItem share = menu.findItem(R.id.context_share_uri); + final MenuItem delete = menu.findItem(R.id.context_delete_conference); + if (conversation != null) { + delete.setTitle(R.string.delete_and_close); + } else { + delete.setTitle(R.string.delete_bookmark); + } share.setVisible(conversation == null || !conversation.isPrivateAndNonAnonymous()); } else if (mResContextMenu == R.menu.contact_context) { activity.contact_context_id = acmi.position; diff --git a/src/main/res/menu/muc_details.xml b/src/main/res/menu/muc_details.xml index 16063b2b1598ec72d804b75dd7e6000004b8d393..4bd5fa4ee848cea00b77a88a1ce63da96eea2d99 100644 --- a/src/main/res/menu/muc_details.xml +++ b/src/main/res/menu/muc_details.xml @@ -26,11 +26,6 @@ android:orderInCategory="80" app:showAsAction="never" android:title="@string/save_as_bookmark"/> - الغاء حجب جميع جهات الإتصال من %s? جهة الاتصال محجوبه محجوب - هل تريد حذف %sمن قائمة المفضلة؟ المحادثات مع هذا المفضل لن تحذف. تسجيل حساب جديد في سيرفر تغيير كلمة المرور في سيرفر مشاركة مع diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index 910b10fc287b0233c4cd58e5c41efc712fc848ad..3778e236efc800c52589c83209dbcb56ea5d0d73 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -50,7 +50,6 @@ Деблокиране на всички контакти от %s? Контактът е блокиран Блокиран - Искате ли да премахнете отметката за %s? Разговорите, свързани с тази отметка, няма да бъдат премахнати. Регистриране на нов профил на сървъра Промяна на паролата в сървъра Споделяне с… diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index d57c9a0ccc91e311c1915e99c45e6188e886c67d..15859ec692e428ae1167ac6bc0b07b640e3f1203 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -50,7 +50,6 @@ Voleu desbloquejar tots el contactes de %s? Contacte bloquejat Bloquejats - Vols esborrar %s com a marcador? Les converses amb aquest marcador no seran eliminades. Registra un compte nou al servidor Canvia la contrasenya al servidor Comparteix amb… diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index 18e2941cd801e82289d770d7e74ea946187b49e5..62ec6018e12626535b7b3a9c536ad744bd93fb98 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -52,7 +52,6 @@ Odblokovat všechny kontakty z %s? Kontakt zablokován Zablokovaný - Přejete si odstranit %s ze záložek? Předešlé rozhovory pod záložkou nebudou odstraněny. Registrovat nový účet na serveru Změnit heslo na serveru Sdílet s… @@ -993,7 +992,6 @@ Zmeškané hovory Uložit jako skupinový chat Tento skupinový chat jste opustili z technických důvodů - Vyhledávání kanálů používá službu třetí strany jménem <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Používání této služby odešle vaši IP adresu a vyhledávaný termín této službě. Pro více informací konzultujte jejich <a href=https://search.jabber.network/privacy>Zásady ochrany osobních údajů</a>. Znovu spojuji hovor Slepě důvěřované klíče OMEMO, které mohou být někdo jiný nebo někým zasaženy. Příchozí hovor ·(%s) · %s diff --git a/src/main/res/values-da-rDK/strings.xml b/src/main/res/values-da-rDK/strings.xml index a7a534e1c6538fbcb993d6adee16540ef5bf45bc..29d0e054eff324d5f406bcdcc8a18d0ca795aad3 100644 --- a/src/main/res/values-da-rDK/strings.xml +++ b/src/main/res/values-da-rDK/strings.xml @@ -50,7 +50,6 @@ Frigiv alle kontakter fra %s? Kontakt blokeret Blokeret - Vil du gerne slette %s som et bogmærke? Samtaler med dette bogmærke vil ikke blive slettet. Register ny konto på server Ændr adgangskode på server Del med… @@ -1003,7 +1002,6 @@ UnifiedPush-distributør Udgående opkald · %s Udgående opkald (%s) · %s - Kanalopdagelse bruger en tredjepartstjeneste kaldet <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Brug af denne funktion vil overføre din IP-adresse og søgetermer til den service. Se deres <a href=https://search.jabber.network/privacy>privatlivspolitik</a> for at få flere oplysninger. Indkommende opkald (%s) · %s Fjern konto fra server En brugervalgt push-server til at videresende push-meddelelser via XMPP til din enhed. diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index ebedabda4587e308b7350ce6ca70a9edfa213a5d..e0edf2989624325c1cafd2451cb8a47580ab5091 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -50,7 +50,6 @@ Alle Kontakte von %s entsperren? Kontakt gesperrt Gesperrt - Möchtest du %s als Lesezeichen entfernen? Unterhaltungen mit diesem Lesezeichen werden dabei nicht entfernt. Neues Konto auf Server erstellen Passwort ändern Teilen mit… @@ -1010,7 +1009,6 @@ Gruppenchats Als Gruppenchat speichern Gruppenchats durchsuchen - Die Channelsuche verwendet einen Drittanbieterservice namens <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Wenn du diese Funktion verwendest, werden deine IP-Adresse und deine Suchbegriffe an diesen Dienst übertragen. Weitere Informationen findest du in der <a href=https://search.jabber.network/privacy>Datenschutzerklärung</a>. Versuche nicht, Backups wiederherzustellen, die du nicht selbst erstellt hast! Du versuchst, ein veraltetes Sicherungsdateiformat zu importieren Hörbuch diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index aebf7e03acf39b019fda57762d830bba89c4d303..771dbb28df7d6f48fc4cdf07ea93938560900bdc 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -50,7 +50,6 @@ Άρση αποκλεισμού όλων των επαφών από το %s; Η επαφή αποκλείστηκε Αποκλεισμένος - Θέλετε να αφαιρέσετε το %s από σελιδοδείκτη; Οι συζητήσεις που σχετίζονται με αυτόν τον σελιδοδείκτη δεν θα αφαιρεθούν. Εγγραφή νέου λογαριασμού στον διακομιστή Αλλαγή συνθηματικού στον διακομιστή Διαμοιρασμός με... diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 16e440bb6bfd06676fdcd5361d5aa66641ac4c73..cf96d604e7997697d70567c9835620db07eb74d4 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -51,7 +51,6 @@ ¿Desbloquear todos los contatos de %s? Contacto bloqueado Bloqueado - ¿Quieres eliminar %s de tus marcadores? Las conversaciones con este marcador no serán eliminadas. Registrar nueva cuenta en servidor Cambiar contraseña en servidor Compartir con… @@ -1023,7 +1022,6 @@ No se pudo eliminar la cuenta del servidor Chats en grupo Buscar un grupo de chats - La búsqueda de canales utiliza un servicio de terceros denominado <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Si utiliza esta función, tu dirección IP y la búsqueda de términos serán transferidos a este servicio. Para obtener más información, consulta la <a href=https://search.jabber.network/privacy>Política de privacidad</a>. Guardar como un chat en grupo ¡No intentes restaurar las copias de seguridad que no creaste tu mismo! Estás intentando importar un formato de copia de seguridad obsoleto diff --git a/src/main/res/values-fa-rIR/strings.xml b/src/main/res/values-fa-rIR/strings.xml index 9eae38a8ba17be8d1ebafba4fcf6c2743fdfee0a..a29c7ca72e8c1d44e61e29bf7eb94ab65e4082df 100644 --- a/src/main/res/values-fa-rIR/strings.xml +++ b/src/main/res/values-fa-rIR/strings.xml @@ -582,7 +582,6 @@ XEP-0198: Stream Management همهٔ گفتگوها قطع اعلان‌ها - آیا می‌خواهید نشانک‌گذاری %s را بردارید؟ گفتگوها با این نشانک پاک نخواهند شد. بازگشت به تماس جاری آخرین بار یک دقیقه پیش تغییر نسبت %s ممکن نبود @@ -983,7 +982,6 @@ شما این حساب را غیرفعال کرده‌اید اتصال مخاطب می‌خواهد وضعیت وصل‌بودن شما را بداند - کاوش کانال‌ها یک سرویس خارجی به نام <a href=https://search.jabber.network>search.jabber.network</a> را به‌کار می‌برد.<br><br>به‌کاربردن این ویژگی نشانی IP و کلمات مورد جستجوی شما را به این سرویس می‌فرستد. برای اطلاعات بیشتر <a href=https://search.jabber.network/privacy>سیاست محرمانگی</a> آن‌ها را ببینید. کدی که وارد کردید نادرست است. شماره تلفن خودکار diff --git a/src/main/res/values-fi/strings.xml b/src/main/res/values-fi/strings.xml index c5f7f417265dda8be3b59150466850b0d4b7beab..61bf523f5ac0c4cb3c5c7a36d99b8ab055c83dc3 100644 --- a/src/main/res/values-fi/strings.xml +++ b/src/main/res/values-fi/strings.xml @@ -49,7 +49,6 @@ Perutaanko kaikkien verkkotunnuksen %s käyttäjien esto? Yhteystieto estetty Estetty - Poistetaanko %s kirjanmerkeistä? Mitään keskustelujasi sen kanssa ei poisteta. Rekisteröi uusi tili palvelimella Vaihda salasanaa palvelimelle Aloita keskustelu @@ -916,7 +915,6 @@ Ryhmäkeskustelut Etsi ryhmäkeskusteluista Suoraan hakuun - Kanavien löytö käyttää kolmannen osapuolen palvelua nimeltä <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Tämän ominaisuuden käyttö lähettää IP-osoitteesi ja hakusanasi palvelulle. Lue lisää heidän <a href=https://search.jabber.network/privacy>yksityisyyskäytännöstään</a> (englanniksi). Hae viesteistä Olet kirjautunut ulos tältä tililtä Vastaamattomat puhelut diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 9dea126015e588e036386ec8282e5c181ffde3d8..720c72a82188094ce57583b47de26c141ff303f9 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -51,7 +51,6 @@ Débloquer tous les contacts de %s ? Contact bloqué Bloqué - Voulez-vous retirer %s des favoris ? La conversation associée à ce favori ne sera pas supprimée. Créer un nouveau compte sur le serveur Changer de mot de passe sur le serveur Partager avec… @@ -994,7 +993,6 @@ \n \n%1$s ne lira que votre carnet d\'adresses et le comparera localement sans rien télécharger sur votre serveur. ficher multimédia - La découverte des canaux utilise un service tiers appelé <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>L\'utilisation de cette fonction transmet votre adresse IP et votre recherche à ce service. Voir leur <a href=https://search.jabber.network/privacy>Politique de gestion de la vie privée</a> pour plus d\'informations. Rechercher des groupes Appel sortant (%s) · %s Votre système d\'exploitation empêche %1$s d\'accéder à l\'internet lorsqu\'il est en arrière-plan. Pour être notifié de nouveaux messages, vous devez permettre à %1$s d\'accéder à Internet lorsque l\'économiseur de données est activé. diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index e517ecd313a9fa50b44968e3a557a0c172e3ef97..ff51f3010c6ac7f86b3072352bf7797c41598760 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -50,7 +50,6 @@ Desbloquear todos os contactos desde 1%s? Contacto bloqueado Bloqueado - Desexas eliminar o marcador %s? As conversas deste marcador non se eliminarán. Rexistrar nova conta no servidor Cambiar o contrasinal no servidor Compartir con… @@ -1013,7 +1012,6 @@ Gardar como conversa en grupo Buscar conversas en grupo Conversas en grupo - O descubrimento de canles usa un servizo externo chamado <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Ao usar esta ferramenta transmitirás o teu enderezo IP e termos de busca a ese servizo. Le a súa <a href=https://search.jabber.network/privacy>Política de Privacidade</a> para saber máis. Non intentes restablecer unha copia de apoio que non tiveses creado ti! Estás intentando importar un ficheiro de apoio co formato antigo Audiolibro diff --git a/src/main/res/values-hr/strings.xml b/src/main/res/values-hr/strings.xml index 2ee8522796f74817086ef7a31c3f1edc2576916f..37dec2d45f1006e531ab4da212c03f264c4fa6c8 100644 --- a/src/main/res/values-hr/strings.xml +++ b/src/main/res/values-hr/strings.xml @@ -56,7 +56,6 @@ Deblokirati sve kontakte iz %s? Kontakt blokiran Blokiran - Želite li ukloniti %s kao oznaku? Razgovori s ovom knjižnom oznakom neće biti uklonjeni. Registrirajte novi račun na poslužitelju Promjena lozinke na poslužitelju Podijeli s… diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml index 87b1e000fe56233b64652ae914e047628e247714..72ebc8e403d2b4e46c10e0cbff86f73a3c12792d 100644 --- a/src/main/res/values-hu/strings.xml +++ b/src/main/res/values-hu/strings.xml @@ -50,7 +50,6 @@ %s összes partnerének tiltását feloldja? Partner tiltva Tiltva - Szeretné eltávolítani ezt a könyvjelzőkből: %s? Ezzel a könyvjelzővel megjelölt beszélgetései nem lesznek eltávolítva. Új fiók regisztrálása a kiszolgálón Jelszó megváltoztatása a kiszolgálón Megosztás ezzel… diff --git a/src/main/res/values-id/strings.xml b/src/main/res/values-id/strings.xml index 21022e927ddaeb2296fc1b204231f8da5a88d6b9..16ee09fbcabbb35adebe674f396072b7b8331361 100644 --- a/src/main/res/values-id/strings.xml +++ b/src/main/res/values-id/strings.xml @@ -49,7 +49,6 @@ Batalkan blokir semua kontak dari %s? Kontak terblokir Diblok - Apakah Anda ingin menghapus %s bookmark ini? Percakapan di bookmark ini tidak akan dihapus. Daftarkan akun baru di server Ganti password di server Bagikan dengan... diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index de49a1aff65beb86fee8f0043e46051a99c3a016..555c3cf89d984b87e6298952e34977bdb63bd9de 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -51,7 +51,6 @@ Sbloccare tutti i contatti da %s? Contatto bloccato Bloccato - Vuoi rimuovere %s dai segnalibri? Le conversazioni con questo segnalibro non verranno rimosse. Registra un nuovo profilo sul server Cambia la password sul server Condividi con… @@ -1025,7 +1024,6 @@ Chat di gruppo Salva come chat di gruppo Cerca chat di gruppo - La scoperta dei canali usa un servizio di terze parti chiamato <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>L\'uso di questa funzione invierà il tuo indirizzo IP e i termini di ricerca a quel servizio. Vedi la sua <a href=https://search.jabber.network/privacy>informativa sulla privacy</a> per maggiori informazioni. Non tentare di ripristinare dei backup che non hai creato te stesso! Stai tentando di importare un formato di file di backup obsoleto Audiolibro diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index d25240881f90a974134638b86fae727cc095a557..5e3456cf3da5ae38a04ce4ed6e4358550c92b89b 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -49,7 +49,6 @@ %sのすべての連絡先のブロックを解除しますか? 連絡先をブロックしました ブロックしました - %s のブックマークを削除しますか? このブックマークとの会話は削除されません。 サーバーに新規アカウントを登録 サーバーのパスワードを変更 …で共有 @@ -987,7 +986,6 @@ ログアウトしました 発信通話 · %s オーディオブック - 談話室の探索は<a href=https://search.jabber.network>search.jabber.network</a>というサービスを利用します.<br><br>利用するとIPアドレスと検索語はそのサービスに送信されます。詳細についてはそのサービスの<a href=https://search.jabber.network/privacy>個人情報保護方針</a>を参照してください。 自分で保存したバックアップしか復元しないでください! XMPP経由でPushメッセージを端末に転送するユーザー指定のPushサーバー。 ログイン diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 4ccdcdfdeead43efb0e78d7e343b6a10fb43e072..29822d81fd5ec814c1967dbbc8ee2d0976e09f81 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -50,7 +50,6 @@ Alle contacten van %s deblokkeren? Contact geblokkeerd Geblokkeerd - Wil je %s als bladwijzer verwijderen? De gesprekken met deze bladwijzer zullen niet worden verwijderd. Nieuwe account op server registreren Wachtwoord op server veranderen Delen met… diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index a8e690e7643c945515dea6ba34cac225b6578d36..062d3c8688f278e3fee4e43f73ddc862ef3a45e9 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -52,7 +52,6 @@ Odblokować wszystkie kontakty z %s? Kontakt zablokowany Zablokowane - Czy chcesz usunąć zakładkę %s? Rozmowy z tą zakładką nie zostaną usunięte. Zarejestruj nowe konto na serwerze Zmień hasło na serwerze Udostępnij… @@ -1041,7 +1040,6 @@ Usuń konto z serwera Nie można usunąć konta z serwera Wyszukaj czatów grupowych - Wykrywanie kanałów korzysta z usługi innego podmiotu o nazwie <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Użycie tej funkcji spowoduje przesłanie adresu IP i wyszukiwanych terminów do tej usługi. Zobacz ich <a href=https://search.jabber.network/privacy>Politykę prywatności</a>, aby uzyskać więcej informacji. Czaty grupowe Zapisz jako czat grupowy Nie próbuj przywracać kopii zapasowych, których nie utworzono samodzielnie! diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index 7b823803cf8277f1591c287e9d3de8323e1eaf89..f34bb847cb14a094c0d647ce0086ef7a0ea28956 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -51,7 +51,6 @@ Desbloquear todos os contatos de %s? Contato bloqueado Bloqueado - Você deseja remover %s dos favoritos? As conversas associadas a esse favorito não serão removidas. Registrar uma nova conta no servidor Alterar a senha no servidor Compartilhar com... diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index 48a2f61a0097ba14cb8b9c630502159090814ab8..7fe2705413c3ecd9ae08ad20e1be34f8c01d6569 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -51,7 +51,6 @@ Deblochează toate contactele de la %s? Contact blocat Blocat - Ați dori să ștergeți %s din semne de carte? Conversațiile asociate cu acest semn de carte nu vor fi șterse. Înregistrează un cont nou pe server Schimbă parola pe server Partajează cu… @@ -1027,7 +1026,6 @@ Nu s-a putut șterge contul de pe server Șterge contul de pe server Discuții de grup - Descoperirea de canale publice folosește un serviciu terț numit <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Folosind această funcție se va transmite adresa dumneavoastră IP și cuvintele căutate către acest serviciu. Pentru mai multe informații citiți <a href=https://search.jabber.network/privacy>Politica de confidențialitate</a> a serviciului. Salvare ca discuție de grup Caută discuții de grup Nu încercați să restaurați copii de rezervă pe care nu le-ați creat personal! diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 4c18fb1df00fcfc460052f45b835d2eba0c76a8d..4aec6465036f994fcfcd838e7d7b66ee9b737597 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -52,7 +52,6 @@ Разблокировать всех пользователей домена %s? Контакт заблокирован Заблокирован - Вы хотите удалить %s из избранного? Беседы, связанные с данной закладкой, будут сохранены. Создать новую учётную запись на сервере Изменить пароль на сервере Поделиться с… @@ -1025,7 +1024,6 @@ Звонки выключены, пока используется Tor Учётная запись для получения пуш-уведомлений. Выбираемый пользователем сервер для перенаправления уведомлений на Ваше устройство. - Обзор каналов использует сторонний сервис <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Эта функция передаст Ваш IP-адрес и ваш поисковый запрос этому сервису. Ознакомьтесь с их <a href=https://search.jabber.network/privacy>Политикой приватности</a> для получения подробностей. Нет (неактивно) Вы собираетесь проверить ключи OMEMO своей учетной записи. Это безопасно только в том случае, если вы перешли по этой ссылке из надежного источника, где только вы могли опубликовать эту ссылку. Не пытайтесь восстановить резервные копии, которые не были созданы вами! diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index 410beca2062d662472a0d0088d22cd6755b179e0..bfa4210aeced3a1f3749fab0bfb049b4c7ebcd4b 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -59,7 +59,6 @@ Odblokovať všetky kontakty od %s? Kontakt zablokovaný Zablokovaný - Chcete vymazať %sako záložku? Rozhovory s touto záložkou nebudú zmazané. Registrovať nový účet na serveri Zmeniť heslo na serveri Zdieľať s diff --git a/src/main/res/values-sq-rAL/strings.xml b/src/main/res/values-sq-rAL/strings.xml index ae11c901b20c6cd802cebeaf6d790c949da4a8d7..5de0a527bcf935651ee6dd3053c05ba6eed6a158 100644 --- a/src/main/res/values-sq-rAL/strings.xml +++ b/src/main/res/values-sq-rAL/strings.xml @@ -840,7 +840,6 @@ Do të donit të hiqet %s prej listës tuaj të kontakteve\? Bisedat me këtë kontakt s’do të hiqen. Do të donit t’i bllokohet %s dërgimi i mesazheve për ju\? Do të donit të zhbllokohet %s dhe të lejohet t’ju dërgojë mesazhe\? - Do të donit të hiqet %s si faqerojtës\? Bisedat me këtë faqerojtës s’do të hiqen. Përdorimi i llogarisë tuaj XMPP për të dërguar “stack traces” ndihmon zhvillimin e pandërprerë të %1$s. Doni të fshihen krejt mesazhet te kjo bisedë\? \n @@ -1041,5 +1040,4 @@ S’ka integrim thirrjesh! Pa leje për bërje thirrjesh U kalua në version të mëparshëm SASL-i - Pikasje kanalesh përdor një shërbim palë të tretë të quajtur <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Përdorim i kësaj veçorie do t’i transmetojë atij shërbimi adresën tuaj IP dhe terma kërkimesh. Për më tepër hollësi, shihni <a href=https://search.jabber.network/privacy>Rregullat e tyre mbi Privatësinë</a>. \ No newline at end of file diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index 925c98ffedc8b4b22091ce71723f42ab184d8e91..82ed6360ad593983d634c230086ebcfd6d2845aa 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -51,7 +51,6 @@ Одблокирати све контакте од %s? Контакт блокиран Блокиран - Да ли желите да уклоните %s са обележивача? Преписке са овим контактом неће бити уклоњене. Региструј нови налог на серверу Промени лозинку на серверу Подели помоћу… diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 30f202d1c8ff84e67c945cf0d1a23e932a095173..dc98cfe6a5c04b5a3e207b8512f30b7b574a800d 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -50,7 +50,6 @@ Avblockera alla kontakter från %s? Kontakt blockerad Blockerad - Vill du ta bort %s som ett bokmärke\? Konversationer med detta bokmärke kommer inte att tas bort. Registrera ett nytt konto på servern Byt lösenord på servern Dela med… @@ -1042,7 +1041,6 @@ Spara som gruppchatt Ljudbok Rapportera spam - Funktionen Channel Discovery, använder en tredjepartstjänst som heter <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Om du använder den här funktionen, överförs din IP-adress och din söktermer till den tjänsten. Se deras <a href=https://search.jabber.network/privacy>sekretesspolicy</a> för mer information. Försök inte att återställa säkerhetskopior som du inte har skapat själv! Det gick inte att ta bort kontot från servern Quicksy ber om ditt samtycke för att använda dina uppgifter diff --git a/src/main/res/values-szl/strings.xml b/src/main/res/values-szl/strings.xml index 266a41688e9578d5abb1b8310333fbb636625993..71f2abdd3fc2fbaf34af01dc7249cea8ade56ac9 100644 --- a/src/main/res/values-szl/strings.xml +++ b/src/main/res/values-szl/strings.xml @@ -51,7 +51,6 @@ Ôdblokować wszyjske kōntakty ze %s\? Kōntakt zablokowany Zablokowane - Chcesz wymazać zokłodka %s\? Godki z niōm niy bydōm wymazane. Zaregistruj nowe kōnto na serwerze Umiyń hasło na serwerze Udostympnij… diff --git a/src/main/res/values-tr-rTR/strings.xml b/src/main/res/values-tr-rTR/strings.xml index 511981d150a1efc85e7c2c8e1f924f80cca8a188..eec089f97d71022305a6ab025aeeb763d1feb965 100644 --- a/src/main/res/values-tr-rTR/strings.xml +++ b/src/main/res/values-tr-rTR/strings.xml @@ -50,7 +50,6 @@ %s üzerinden gelen kişilerdeki engellemeyi kaldırmak istiyor musunuz? Kişi engellendi Engellendi - %s kişisini yer imlerinden çıkarmak ister misiniz? Bu yer imi ile kayıtlı konuşmalar silinmeyecektir. Sunucuda yeni bir hesap oluştur Sunucudaki şifreni değiştir Paylaş… diff --git a/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml index 23186977eec4a0da137de796f0d7efec11d4db78..53061aed42234e9d051bed6743633f2213a0423a 100644 --- a/src/main/res/values-uk/strings.xml +++ b/src/main/res/values-uk/strings.xml @@ -45,7 +45,6 @@ Розблокувати всі контакти з %s\? Контакт заблоковано Заблоковано - Бажаєте вилучити %s із закладок\? Розмови, пов\'язані з цією закладкою, залишаться. Зареєструвати новий обліковий запис на сервері Змінити пароль Поділитися… @@ -975,7 +974,6 @@ Ця розмова Додати контакт, створити чи приєднатися до групи або знайти канали Обліковий запис XMPP - Пошук каналів використовує сторонній сервіс <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Використання цієї функції передає Вашу IP-адресу та пошукові запити цьому сервісу. Перегляньте їхню <a href=https://search.jabber.network/privacy>Політику конфіденційності</a>, щоб отримати більше інформації. Вихідний виклик · %s %1$s використовує <b>OpenKeychain</b> для шифрування/дешифрування повідомлень і керування публічними ключами.<br><br>OpenKeychain поширюється на умовах ліцензії GPLv3+ і доступний для завантаження на F-Droid та Google Play.<br><br><small>(Після встановлення необхідно перезапустити %1$s.)</small> Додатково diff --git a/src/main/res/values-vi/strings.xml b/src/main/res/values-vi/strings.xml index dad02f28de5985ea94491779a8c183755bb933bb..9b0112df980773502c6a51d03bb9f7ab471f274d 100644 --- a/src/main/res/values-vi/strings.xml +++ b/src/main/res/values-vi/strings.xml @@ -49,7 +49,6 @@ Bỏ chặn tất cả liên hệ từ %s? Đã chặn liên hệ Đã chặn - Bạn có muốn xoá dấu trang %s không? Các cuộc hội thoại với dấu trang này sẽ không bị xoá. Đăng ký tài khoản mới trên máy chủ Đổi mật k trên máy chủ Chia sẻ với… @@ -962,7 +961,6 @@ Tìm kiếm tin nhắn trong nhóm Tải về thất bại: Tệp tin không hợp lệ Cuộc gọi nhỡ - Khám phá kênh sử dụng một dịch vụ bên thứ ba gọi là <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>SỬ dụng chức năng này sẽ truyền địa chỉ IP của bạn và tìm kiếm điều khoản đến dịch vụ đó. Hãy đọc <a href=https://search.jabber.network/privacy>Chính sách quyền riêng tư</a> của họ để có thêm thông tin. Chuyển sang gọi video\? Đang kết nối lại cuộc gọi Cuộc gọi đi · %s diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index fe4787e6784db692f0045bc7642a8a8bcb3379dd..835ae5dfe686912943fc978cd60819edb009e1c8 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -49,7 +49,6 @@ 解除屏蔽来自 %s 的所有联系人? 联系人已屏蔽 已屏蔽 - 是否从书签中移除 %s?将不会移除与此书签相关的对话。 在服务器上注册新账号 在服务器上修改密码 分享至… @@ -1014,7 +1013,6 @@ 来电 (%s) · %s 去电 (%s) · %s 去电 · %s - 频道发现使用称为 <a href=https://search.jabber.network>search.jabber.network</a> 的第三方服务。<br><br>使用此功能会将您的 IP 地址和搜索词传输到此服务。请参阅其 <a href=https://search.jabber.network/privacy>隐私政策</a> 以获取更多信息。 无法从服务器删除账号 群聊 保存为群聊 diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index 67ff79f2b72d59ba6baf7cb5603aab7006c2edf2..5d955366df3423ddc3202b2c01826ca423fc62a1 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -49,7 +49,6 @@ 要解除封鎖來自 %s 的所有聯絡人嗎? 聯絡人已封鎖 已封鎖 - 要從書籤中移除 %s 嗎?與此書籤相關的會話將不會被移除。 在伺服器上註冊新帳戶 在伺服器上變更密碼 分享至… @@ -1017,7 +1016,6 @@ 從伺服器移除帳戶 無法從伺服器刪除帳戶 搜尋群組聊天 - 頻道探索使用一個名為 <a href=https://search.jabber.network>search.jabber.network</a> 的第三方服務,<br><br>使用此功能會將您的 IP 位址和搜尋詞彙傳送至此服務。更多資訊請參見他們的 <a href=https://search.jabber.network/privacy>隱私權政策</a>。 群組聊天 儲存為群組聊天 您已登出此帳戶 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 4f67b8a95a3167c1a72351e4cdc84573cb5db8b2..cee84bfd32b550c73c6f1c490e6c441475195ebd 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -51,7 +51,8 @@ Unblock all contacts from %s? Contact blocked Blocked - Would you like to remove %s as a bookmark? Conversations with this bookmark will not be removed. + Would you like to remove the bookmark for %s? + Would you like to remove the bookmark for %s and close the conversation? Register new account on server Change password on server Share with… @@ -1028,4 +1029,5 @@ Privacy policy Contact list integration is not available Call integration not available! + Delete & Close From a2c67a6e383226253dcc5749a6b8aaf10bd997a7 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 12 Mar 2024 19:01:39 +0100 Subject: [PATCH 115/363] ensure that jingle ft transport is terminated --- .../jingle/JingleFileTransferConnection.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java index 845f91ecc7bdae1dd011006f86672ab11a906e95..749a44e107bd1393c7acb9421befd6797849abd4 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java @@ -253,6 +253,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection id.account.getJid().asBareJid() + ": improperly formatted contents", Throwables.getRootCause(e)); respondOk(jinglePacket); + terminateTransport(); sendSessionTerminate(Reason.of(e), e.getMessage()); return; } @@ -534,6 +535,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection if (isTerminated()) { return; } + terminateTransport(); final Throwable rootCause = Throwables.getRootCause(throwable); Log.d(Config.LOGTAG, "unable to send session accept", rootCause); sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage()); @@ -603,6 +605,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection id.account.getJid().asBareJid() + ": improperly formatted contents", Throwables.getRootCause(e)); respondOk(jinglePacket); + terminateTransport(); sendSessionTerminate(Reason.of(e), e.getMessage()); return; } @@ -646,6 +649,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection id.account.getJid().asBareJid() + ": improperly formatted contents", Throwables.getRootCause(e)); respondOk(jinglePacket); + terminateTransport(); sendSessionTerminate(Reason.of(e), e.getMessage()); return; } @@ -708,6 +712,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection } else if (transportInfo instanceof SocksByteStreamsTransportInfo.CandidateUsed candidateUsed) { if (!socksBytestreamsTransport.setCandidateUsed(candidateUsed.cid)) { + terminateTransport(); sendSessionTerminate( Reason.FAILED_TRANSPORT, String.format( @@ -734,6 +739,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection id.account.getJid().asBareJid() + ": improperly formatted contents", Throwables.getRootCause(e)); respondOk(jinglePacket); + terminateTransport(); sendSessionTerminate(Reason.of(e), e.getMessage()); return; } @@ -843,8 +849,9 @@ public class JingleFileTransferConnection extends AbstractJingleConnection if (transport == null) { return; } - // TODO consider setting transport callback to null. requires transport to handle null callback - //transport.setTransportCallback(null); + // TODO consider setting transport callback to null. requires transport to handle null + // callback + // transport.setTransportCallback(null); transport.terminate(); this.transport = null; } @@ -875,8 +882,12 @@ public class JingleFileTransferConnection extends AbstractJingleConnection } @Override - public void onFailure(@NonNull Throwable throwable) { - onFileTransmissionFailed(throwable); + public void onFailure(@NonNull final Throwable throwable) { + // The state transition in here should be synchronized to not race with the + // state transition in receiveSessionTerminate + synchronized (JingleFileTransferConnection.this) { + onFileTransmissionFailed(throwable); + } } }, MoreExecutors.directExecutor()); From 028b3cff884897acd8873e4a7307f9ddd52ae129 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 12 Mar 2024 19:05:23 +0100 Subject: [PATCH 116/363] remove contacts integration again --- src/playstore/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/playstore/AndroidManifest.xml b/src/playstore/AndroidManifest.xml index 07ac0123486062b91707bb50a29a83b5980b7c49..2f339c96f09dfa0a2180294b99c331d2f4b59da8 100644 --- a/src/playstore/AndroidManifest.xml +++ b/src/playstore/AndroidManifest.xml @@ -3,7 +3,7 @@ - + From 2fa541f2dc5ef6cfb14b4ed6be361e18805b15b2 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 12 Mar 2024 19:05:47 +0100 Subject: [PATCH 117/363] version bump to 2.13.5 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b0e6227544aecc3f059584390b76aab6247a1619..5f4ab3fd28f7af6aad588f342cc2ca793fe44b8a 100644 --- a/build.gradle +++ b/build.gradle @@ -97,8 +97,8 @@ android { defaultConfig { minSdkVersion 21 targetSdkVersion 34 - versionCode 42094 - versionName "2.13.4" + versionCode 42099 + versionName "2.13.5" archivesBaseName += "-$versionName" applicationId "eu.siacs.conversations" resValue "string", "applicationId", applicationId From 8f35242c35a3d49a136b231d6344c6d764070847 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 12 Mar 2024 19:17:55 +0100 Subject: [PATCH 118/363] version bump to 2.14.0-beta.3 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 9594a472744de3fdbfae6b50c5175bb3a0a4e03c..8b3316364452afa34a7ed263eddb33e937b80835 100644 --- a/build.gradle +++ b/build.gradle @@ -97,8 +97,8 @@ android { defaultConfig { minSdkVersion 23 targetSdkVersion 34 - versionCode 42099 - versionName "2.14.0-beta.2" + versionCode 42100 + versionName "2.14.0-beta.3" archivesBaseName += "-$versionName" applicationId "eu.siacs.conversations" resValue "string", "applicationId", applicationId From 4e72ef12f9056c22c5d18253571b5a2a2a26f4a9 Mon Sep 17 00:00:00 2001 From: Dirk Date: Tue, 12 Mar 2024 10:25:34 +0000 Subject: [PATCH 119/363] Translated using Weblate (German) Currently translated at 100.0% (987 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/de/ --- src/main/res/values-de/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index e0edf2989624325c1cafd2451cb8a47580ab5091..06e9f8283b1d52f3b844636fc77b5954f8f993f4 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -1029,4 +1029,8 @@ Anrufintegration nicht verfügbar! Keine Berechtigung für Telefonanrufe Kontakt ist nicht verfügbar + Möchtest du das Lesezeichen für %s entfernen und die Unterhaltung schließen? + Löschen & Schließen + Willst du das Lesezeichen für %s entfernen? + Die Channelsuche verwendet einen Drittanbieterservice namens <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Wenn du diese Funktion verwendest, werden deine IP-Adresse und deine Suchbegriffe an diesen Dienst übertragen. Weitere Informationen findest du in der <a href=https://search.jabber.network/privacy>Datenschutzerklärung</a>. \ No newline at end of file From bab05a0dab2659705467365a03fd97ba60ed00f3 Mon Sep 17 00:00:00 2001 From: ghose Date: Wed, 13 Mar 2024 05:08:12 +0000 Subject: [PATCH 120/363] Translated using Weblate (Galician) Currently translated at 100.0% (987 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/gl/ --- src/main/res/values-gl/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index ff51f3010c6ac7f86b3072352bf7797c41598760..2cddf75b47df52fa07bcbbf570ecbc94b61a8f2f 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -1032,4 +1032,8 @@ Sen permiso para chamar por teléfono Non está dispoñible o servizo de chamadas! O contacto non está dispoñible + Queres eliminar o marcador para %s e pechar a conversa? + Queres eliminar o marcador para %s? + Eliminar e Pechar + O descubrimento de canles usa un servicio alleo chamado <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Ao usar esta ferramenta transmites o teu enderezo IP e os termos de busca a ese servizo. Le a súa <a href=https://search.jabber.network/privacy>Política de Privacidade</a> para saber máis. \ No newline at end of file From 7823ccb1a1c1f170338a2c4698e010b499caf826 Mon Sep 17 00:00:00 2001 From: Mako Date: Wed, 13 Mar 2024 03:52:47 +0000 Subject: [PATCH 121/363] Translated using Weblate (Japanese) Currently translated at 100.0% (987 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ja/ --- src/main/res/values-ja/strings.xml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index 5e3456cf3da5ae38a04ce4ed6e4358550c92b89b..a146ca4090dff8ec21e0d0e18dba8d04659e1082 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -198,7 +198,7 @@ XEP-0191: ブロッキング コマンド XEP-0237: 名簿バージョン管理 XEP-0198: ストリーム管理 - XEP-0215: 外部サービスの発見 + XEP-0215: 外部サービス探索 XEP-0163: PEP (アバター / OMEMO) XEP-0363: HTTP ファイルアップロード XEP-0357: プッシュ @@ -886,8 +886,8 @@ jabber.network ローカルサーバー - ほとんどのユーザーは、公開されている XMPP エコシステム全体からより良い提案を得るために、‘jabber.network’を選択するはずです。 - 談話室の発見方法 + ほとんどのユーザーは、公開 XMPP エコシステム全体からより適切な提案を得るために「jabber.network」を選択する必要があります。 + 談話室の探索方法 バックアップ アカウントを有効化してください 通話をする @@ -1011,4 +1011,11 @@ Quicksy へようこそ ! 連絡先は未検証のデバイスを使用しています。 QR コードをスキャンして検証を実行し、アクティブな MITM 攻撃を阻止してください。 未検証のデバイスを使用しています。他のデバイスで QR コードをスキャンして検証を実行し、アクティブな MITM 攻撃を阻止してください。 + 電話をかける権限がありません + %s のブックマークを削除して会話を閉じますか ? + %s のブックマークを削除しますか ? + 談話室の探索には <a href=https://search.jabber.network>search.jabber.network</a> というサードパーティのサービスを利用します。<br><br>この機能を使用すると、IP アドレスと検索語がこのサービスに送信されます。詳細は、<a href=https://search.jabber.network/privacy>プライバシーポリシー</a>をご覧ください。 + 通話の統合は利用できません。 + 連絡先は利用できません + 削除して閉じる \ No newline at end of file From 39340ad9563615ca4e7f0fcdb2e71e8217be31b3 Mon Sep 17 00:00:00 2001 From: Quini98 Date: Tue, 12 Mar 2024 15:50:59 +0000 Subject: [PATCH 122/363] Translated using Weblate (Dutch) Currently translated at 79.1% (781 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/nl/ --- src/main/res/values-nl/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 29822d81fd5ec814c1967dbbc8ee2d0976e09f81..d19629bc3a7e277c9f3b2f2bbfb5701febdb2da0 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -815,4 +815,5 @@ Kan video niet schakelen. Onversleuteld document Accountregistraties zijn niet ondersteund + Door crashrapportages te versturen help je de ontwikkeling \ No newline at end of file From 7190475325ad6fc67bb897296e9805e8356f0757 Mon Sep 17 00:00:00 2001 From: SomeTr Date: Tue, 12 Mar 2024 19:07:24 +0000 Subject: [PATCH 123/363] Translated using Weblate (Ukrainian) Currently translated at 100.0% (987 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/uk/ --- src/main/res/values-uk/strings.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml index 53061aed42234e9d051bed6743633f2213a0423a..10348d6ceb1c8751a7506d38aa3f33e002d5fdf1 100644 --- a/src/main/res/values-uk/strings.xml +++ b/src/main/res/values-uk/strings.xml @@ -231,7 +231,7 @@ channel@conference.example.com/nick channel@conference.example.com Зберегти до закладок - Вилучити закладку + Видалити закладку Вилучити груповий чат Закрити канал Дійсно вилучити цей груповий чат\? @@ -1080,4 +1080,8 @@ Немає дозволу на телефонні дзвінки Контакт недоступний Інтеграція викликів недоступна! + Видалити і закрити + Пошук каналів використовує сторонній сервіс <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Використання цієї функції передає Вашу IP-адресу та пошукові запити цьому сервісу. Перегляньте їхню <a href=https://search.jabber.network/privacy>Політику конфіденційності</a>, щоб отримати більше інформації. + Бажаєте видалити закладку %s і закрити розмову? + Бажаєте видалити закладку %s? \ No newline at end of file From 40b8005e8db11076c11c29c1640ea8a1151255b0 Mon Sep 17 00:00:00 2001 From: licaon-kter Date: Tue, 12 Mar 2024 10:54:33 +0000 Subject: [PATCH 124/363] Translated using Weblate (Romanian) Currently translated at 100.0% (987 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ro/ --- src/main/res/values-ro-rRO/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index 7fe2705413c3ecd9ae08ad20e1be34f8c01d6569..d0e8499125f1c6ace02c1e81d64607b922524cc2 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -1048,4 +1048,8 @@ Nu este permisă efectuarea de apeluri Integrarea apelurilor nu este disponibilă! Contactul nu este disponibil + Ați dori să ștergeți semnul de carte pentru %s și să închideți conversația? + Ați dori să ștergeți semnul de carte pentru %s? + Șterge și închide + Descoperirea de canale publice folosește un serviciu terț numit <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Folosind această funcție se va transmite adresa dumneavoastră IP și cuvintele căutate către acest serviciu. Pentru mai multe informații citiți <a href=https://search.jabber.network/privacy>Politica de confidențialitate</a> a serviciului. \ No newline at end of file From 5ff205315ef2a8e809c691338a25d2853cd4a5ae Mon Sep 17 00:00:00 2001 From: Besnik_b Date: Tue, 12 Mar 2024 09:40:57 +0000 Subject: [PATCH 125/363] Translated using Weblate (Albanian) Currently translated at 98.7% (975 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/sq/ --- src/main/res/values-sq-rAL/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/res/values-sq-rAL/strings.xml b/src/main/res/values-sq-rAL/strings.xml index 5de0a527bcf935651ee6dd3053c05ba6eed6a158..90915fbaeac60b129dad218dde81ed26140491e9 100644 --- a/src/main/res/values-sq-rAL/strings.xml +++ b/src/main/res/values-sq-rAL/strings.xml @@ -1040,4 +1040,8 @@ S’ka integrim thirrjesh! Pa leje për bërje thirrjesh U kalua në version të mëparshëm SASL-i + Pikasja e kanaleve përdor një shërbim palë të tretë të quajtur <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Përdorim i kësaj veçorie do t’i transmetojë atij shërbimi adresën tuaj IP dhe terma kërkimesh. Për më tepër hollësi, shihni <a href=https://search.jabber.network/privacy>Rregullat e tyre mbi Privatësinë</a>. + Doni të hiqet faqerojtësi për %s dhe të mbyllet biseda? + Fshije & Mbylle + Doni të hiqet faqerojtësi për %s? \ No newline at end of file From 3fd322ec9b1b9832c9181227c7f868f8e39bf1f1 Mon Sep 17 00:00:00 2001 From: Outbreak2096 Date: Tue, 12 Mar 2024 13:20:45 +0000 Subject: [PATCH 126/363] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (987 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/ --- src/main/res/values-zh-rCN/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 835ae5dfe686912943fc978cd60819edb009e1c8..92b89a77f49adbaa3c487fbf88a0e8427237a8fc 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -1038,4 +1038,7 @@ 联系人不可用 没有拨打电话的权限 呼叫集成不可用! + 是否移除 %s 的书签并关闭对话? + 是否移除 %s 的书签? + 删除 & 关闭 \ No newline at end of file From 80437d20cfa594fe1244fb1b9ee4399b38578a19 Mon Sep 17 00:00:00 2001 From: melanotosjaunty Date: Tue, 12 Mar 2024 12:33:17 +0000 Subject: [PATCH 127/363] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (987 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/zh_Hans/ --- src/main/res/values-zh-rCN/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 92b89a77f49adbaa3c487fbf88a0e8427237a8fc..62235d15f803e07e46181fa00e5b8ea7378aa538 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -1041,4 +1041,5 @@ 是否移除 %s 的书签并关闭对话? 是否移除 %s 的书签? 删除 & 关闭 + 频道发现使用称为 <a href=https://search.jabber.network>search.jabber.network</a> 的第三方服务。<br><br>使用此功能会将您的 IP 地址和搜索词传输到此服务。请参阅其 <a href=https://search.jabber.network/privacy>隐私政策</a> 以获取更多信息。 \ No newline at end of file From ea19e7d46c6b730da6ff794ebcea72a3ef55c492 Mon Sep 17 00:00:00 2001 From: Mako Date: Wed, 13 Mar 2024 06:05:05 +0000 Subject: [PATCH 128/363] Translated using Weblate (Japanese) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ja/ --- src/conversations/res/values-ja/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values-ja/strings.xml b/src/conversations/res/values-ja/strings.xml index 287ee093baf7be2ba746877cc3ae47d25d98469a..851af0d4a0be9fa670c4e72575d413694ba3323e 100644 --- a/src/conversations/res/values-ja/strings.xml +++ b/src/conversations/res/values-ja/strings.xml @@ -4,7 +4,7 @@ conversations.im を利用する 新規アカウントを作成 XMPP アカウントをお持ちですか?既にほかの XMPP クライアントを利用しているか、 Conversations を利用したことがある場合はこちら。初めての方は、今すぐ新規 XMPP アカウントを作成できます。\nヒント: e メールのプロバイダーが XMPP アカウントも提供している場合があります。 - XMPP は、プロバイダーに依存しないインスタントメッセージのプロトコルです。 XMPP サーバーならどこでも、このクライアントを使用することができます。 + XMPP は、プロバイダーに依存しないインスタントメッセージのプロトコルです。 XMPP サーバーならどこでも、このアプリを使用することができます。 \nよろしければ、 Conversations に最適化されたプロバイダー conversations.im で簡単にアカウントを作成することもできます。 %1$s へ招待されました。アカウント作成手順をご案内します。 \n%1$s をプロバイダーに選択してほかのプロバイダーのユーザーと会話するには、 XMPP のフルアドレスを相手にお知らせください。 %1$s へ招待されました。ユーザー名は既に選択されています。アカウント作成手順をご案内します。 \nほかのプロバイダーのユーザーと会話するには、 XMPP のフルアドレスを相手にお知らせください。 From baf4788220ecc0c52ea291fb9724a2f10cadbf4f Mon Sep 17 00:00:00 2001 From: Quini98 Date: Tue, 12 Mar 2024 15:46:41 +0000 Subject: [PATCH 129/363] Translated using Weblate (Dutch) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/nl/ --- src/conversations/res/values-nl/strings.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/conversations/res/values-nl/strings.xml b/src/conversations/res/values-nl/strings.xml index 366506e84a887e0d2e19e780e593b1c49b29f02d..30e90fe6d5218206d3343370b575184b8f7d1c7e 100644 --- a/src/conversations/res/values-nl/strings.xml +++ b/src/conversations/res/values-nl/strings.xml @@ -4,13 +4,14 @@ Conversations.im gebruiken Nieuwe account registreren Heb je al een XMPP-account? Als je al een andere XMPP-cliënt gebruikt, of Conversations vroeger al eens hebt gebruikt, is dit waarschijnlijk het geval. Zo niet, kan je nu een nieuwe XMPP-account aanmaken.\nTip: sommige e-mailproviders bieden ook XMPP-accounts aan. - XMPP is een provider-onafhankelijk berichtennetwerk. Je kan deze cliënt gebruiken met eender welke XMPP-server. + XMPP is een provider-onafhankelijk berichtennetwerk. Je kan deze applicatie gebruiken met eender welke XMPP-server. \nOm het je gemakkelijker te maken kun je simpelweg een account aanmaken op conversations.im; een provider speciaal geschikt voor Conversations. Je ontving een uitnodiging voor %1$s. We zullen je helpen een account aan te maken.\nWanneer je %1$s als je provider kiest kan je met gebruikers van andere providers communiceren door hen je volledige XMPP-adres te geven. Je ontving een uitnodiging voor %1$s. Er werd reeds een gebruikersnaam voor jou gekozen. We zullen je helpen een account aan te maken.\nJe zal met gebruikers van andere providers communiceren door hen je volledige XMPP-adres te geven. Je server uitnodiging - Tik op de delen knop om een uitnodiging te versturen naar %1$s + Tik op de delen knop om een uitnodiging te versturen naar %1$s. Als je contactpersoon in de buurt is, kan deze ook onderstaande code scannen om de uitnodiging te aanvaarden. - Deel de uitnodiging met ... + Deel de uitnodiging met… Vergezel %1$s en chat met mij: %2$s + Onjuist geformatteerde provisioningcode \ No newline at end of file From faf2088452e81a9dafec2a23c604650fb9e0f3c5 Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Wed, 13 Mar 2024 13:43:11 +0000 Subject: [PATCH 130/363] Translated using Weblate (Spanish) Currently translated at 100.0% (987 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/es/ --- src/main/res/values-es/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index cf96d604e7997697d70567c9835620db07eb74d4..542185f64a1e13d4751bd3ef656399da10fb97c7 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -1043,4 +1043,8 @@ Sin permiso para llamar por teléfono Contacto no disponible ¡Sin integración de llamadas! + El observador de canales utiliza un servicio de terceros llamado <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>El uso de esta función transmitirá a ese servicio su dirección IP. y términos de búsqueda. Para obtener más detalles, consulte su <a href=https://search.jabber.network/privacy>Política de privacidad</a>. + Borrar y cerrar + ¿Quieres eliminar el marcador de %s ? + ¿Quieres eliminar el marcador de %s y cerrar la conversación? \ No newline at end of file From 72d194d8ffd2f3f56ca1e6c175426e2560b83a43 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 16 Mar 2024 13:54:47 +0100 Subject: [PATCH 131/363] treat delayed destruction call integration as busy --- .../services/CallIntegration.java | 15 +++++-- .../CallIntegrationConnectionService.java | 6 ++- .../xmpp/jingle/JingleConnectionManager.java | 40 ++++++++++++++----- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index 7963680961f8dad017fc4848514feaf85b412918..1777e9fd05c9d869794cde22b5a188c2ad1e856e 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -43,6 +43,7 @@ public class CallIntegration extends Connection { private AudioDevice initialAudioDevice = null; private final AtomicBoolean initialAudioDeviceConfigured = new AtomicBoolean(false); private final AtomicBoolean delayedDestructionInitiated = new AtomicBoolean(false); + private final AtomicBoolean isDestroyed = new AtomicBoolean(false); private List availableEndpoints = Collections.emptyList(); @@ -363,7 +364,6 @@ public class CallIntegration extends Connection { final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, DEFAULT_VOLUME); toneGenerator.startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375); this.destroyWithDelay(new DisconnectCause(DisconnectCause.ERROR, null), 375); - this.destroyWith(new DisconnectCause(DisconnectCause.ERROR, null)); } public void retracted() { @@ -389,7 +389,7 @@ public class CallIntegration extends Connection { JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule( () -> { this.setDisconnected(disconnectCause); - this.destroy(); + this.destroyCallIntegration(); }, delay, TimeUnit.MILLISECONDS); @@ -404,7 +404,7 @@ public class CallIntegration extends Connection { return; } this.setDisconnected(disconnectCause); - this.destroy(); + this.destroyCallIntegration(); Log.d(Config.LOGTAG, "destroyed!"); } @@ -472,6 +472,15 @@ public class CallIntegration extends Connection { audioManager.getAudioDevices())); } + private void destroyCallIntegration() { + super.destroy(); + this.isDestroyed.set(true); + } + + public boolean isDestroyed() { + return this.isDestroyed.get(); + } + /** AudioDevice is the names of possible audio devices that we currently support. */ public enum AudioDevice { NONE, diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index ce4f0eb85ea1d8734a86540ad939b553bc4df87e..d47a28a22bf6a183f271f790577498e802b57e8d 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -347,14 +347,14 @@ public class CallIntegrationConnectionService extends ConnectionService { } } - public static void addNewIncomingCall( + public static boolean addNewIncomingCall( final Context context, final AbstractJingleConnection.Id id) { if (CallIntegration.notSelfManaged(context)) { Log.d( Config.LOGTAG, "not adding incoming call to TelecomManager on Android " + Build.VERSION.RELEASE); - return; + return true; } final var phoneAccountHandle = CallIntegrationConnectionService.getHandle(context, id.account); @@ -373,7 +373,9 @@ public class CallIntegrationConnectionService extends ConnectionService { Config.LOGTAG, id.account.getJid().asBareJid() + ": call integration not available", e); + return false; } + return true; } public static class ServiceConnectionService { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 8541f6a44f74b5e74a8143c835ee5995411d586f..0232478f5d6afa1acb982da4963f12af4848e0bb 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -109,13 +109,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { + sessionEnded + ", stranger=" + stranger); - mXmppConnectionService.sendIqPacket( - account, packet.generateResponse(IqPacket.TYPE.RESULT), null); - final JinglePacket sessionTermination = - new JinglePacket(JinglePacket.Action.SESSION_TERMINATE, id.sessionId); - sessionTermination.setTo(id.with); - sessionTermination.setReason(Reason.BUSY, null); - mXmppConnectionService.sendIqPacket(account, sessionTermination, null); + sendSessionTerminate(account, packet, id); if (busy || stranger) { writeLogMissedIncoming( account, @@ -136,7 +130,21 @@ public class JingleConnectionManager extends AbstractConnectionManager { connections.put(id, connection); if (connection instanceof JingleRtpConnection) { - CallIntegrationConnectionService.addNewIncomingCall(getXmppConnectionService(), id); + if (!CallIntegrationConnectionService.addNewIncomingCall( + mXmppConnectionService, id)) { + connections.remove(id); + Log.e( + Config.LOGTAG, + account.getJid().asBareJid() + ": could not add incoming call"); + sendSessionTerminate(account, packet, id); + writeLogMissedIncoming( + account, + id.with, + id.sessionId, + null, + System.currentTimeMillis(), + false); + } } mXmppConnectionService.updateConversationUi(); @@ -147,14 +155,24 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } + private void sendSessionTerminate(final Account account, final IqPacket request, final AbstractJingleConnection.Id id) { + mXmppConnectionService.sendIqPacket( + account, request.generateResponse(IqPacket.TYPE.RESULT), null); + final JinglePacket sessionTermination = + new JinglePacket(JinglePacket.Action.SESSION_TERMINATE, id.sessionId); + sessionTermination.setTo(id.with); + sessionTermination.setReason(Reason.BUSY, null); + mXmppConnectionService.sendIqPacket(account, sessionTermination, null); + } + private boolean isUsingClearNet(final Account account) { return !account.isOnion() && !mXmppConnectionService.useTorToConnect(); } public boolean isBusy() { - for (AbstractJingleConnection connection : this.connections.values()) { - if (connection instanceof JingleRtpConnection) { - if (connection.isTerminated()) { + for (final AbstractJingleConnection connection : this.connections.values()) { + if (connection instanceof JingleRtpConnection rtpConnection) { + if (connection.isTerminated() && rtpConnection.getCallIntegration().isDestroyed()) { continue; } return true; From 3d5d257707613aa4b35bc29ee22d614f4dc9b4c6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 16 Mar 2024 16:30:57 +0100 Subject: [PATCH 132/363] fix system feature detection for call integration --- .../conversations/services/CallIntegration.java | 11 +++++++++-- .../services/XmppConnectionService.java | 12 +++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index 1777e9fd05c9d869794cde22b5a188c2ad1e856e..67ea605f57328db94c7bda9ce510d820143fafe9 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -447,9 +447,16 @@ public class CallIntegration extends Connection { } public static boolean selfManaged(final Context context) { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && hasSystemFeature(context); + } + + public static boolean hasSystemFeature(final Context context) { final var packageManager = context.getPackageManager(); - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - && packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + return packageManager.hasSystemFeature(PackageManager.FEATURE_TELECOM); + } else { + return packageManager.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE); + } } public static boolean notSelfManaged(final Context context) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 887ffe93708f78eb8b83d6cf0eb72ed1e7ab2920..35b831eb4824fe3d5f7662cf38a00ce47a880bbb 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1286,7 +1286,9 @@ public class XmppConnectionService extends Service { toggleSetProfilePictureActivity(hasEnabledAccounts); reconfigurePushDistributor(); - CallIntegrationConnectionService.togglePhoneAccountsAsync(this, this.accounts); + if (CallIntegration.hasSystemFeature(this)) { + CallIntegrationConnectionService.togglePhoneAccountsAsync(this, this.accounts); + } restoreFromDatabase(); @@ -2465,7 +2467,9 @@ public class XmppConnectionService extends Service { public void createAccount(final Account account) { account.initAccountServices(this); databaseBackend.createAccount(account); - CallIntegrationConnectionService.togglePhoneAccountAsync(this, account); + if (CallIntegration.hasSystemFeature(this)) { + CallIntegrationConnectionService.togglePhoneAccountAsync(this, account); + } this.accounts.add(account); this.reconnectAccountInBackground(account); updateAccountUi(); @@ -2589,7 +2593,9 @@ public class XmppConnectionService extends Service { toggleForegroundService(); syncEnabledAccountSetting(); mChannelDiscoveryService.cleanCache(); - CallIntegrationConnectionService.togglePhoneAccountAsync(this, account); + if (CallIntegration.hasSystemFeature(this)) { + CallIntegrationConnectionService.togglePhoneAccountAsync(this, account); + } return true; } else { return false; From 5978494e60773e358143fb0509f1ea82819e80f5 Mon Sep 17 00:00:00 2001 From: fnetX Date: Thu, 14 Mar 2024 21:34:19 +0000 Subject: [PATCH 133/363] Translated using Weblate (French) Currently translated at 99.2% (980 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/fr/ --- src/main/res/values-fr/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 720c72a82188094ce57583b47de26c141ff303f9..9368579329f812c3981fbfa6363e5d21af2fb71b 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -513,7 +513,7 @@ Texte partagé avec %s Autoriser %1$s à accéder au stockage externe Autoriser %1$s à accéder à la caméra - Synchroniser avec contacts + Intégration de la liste de contacts Notifier pour tous les messages Notifier seulement en cas de mention Notifications désactivées @@ -1047,4 +1047,6 @@ Contact non disponible Cacher la notification Se déconnecter + L\'intégration de la liste de contacts n\'est pas disponible + Supprimer & Fermer \ No newline at end of file From bac62261f87b7fe408b29ae459a62edb7a04e801 Mon Sep 17 00:00:00 2001 From: locness3 Date: Sun, 17 Mar 2024 18:52:31 +0000 Subject: [PATCH 134/363] Translated using Weblate (French) Currently translated at 50.0% (1 of 2 strings) Translation: Conversations/App Store Metadata (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata-conversations/fr/ --- .../android/fr-FR/full_description.txt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/conversations/fastlane/metadata/android/fr-FR/full_description.txt diff --git a/src/conversations/fastlane/metadata/android/fr-FR/full_description.txt b/src/conversations/fastlane/metadata/android/fr-FR/full_description.txt new file mode 100644 index 0000000000000000000000000000000000000000..653bdec1053be7fd5ae5efa4a9f60085f43be614 --- /dev/null +++ b/src/conversations/fastlane/metadata/android/fr-FR/full_description.txt @@ -0,0 +1,38 @@ +Facile à utiliser, fiable, respectueux de votre batterie. Prend en charge les images, les conversations de groupe et le chiffrement de bout-en-bout. + +Principes de conception : + +* Être le plus joli et simple d'utilisation possible, sans compromis sur la sécurité ou la vie privée. +* S'appuyer sur des protocoles existants, bien établis +* Ne pas nécessiter de compte Google ou spécifiquement le Google Cloud Messaging (GCM) +* Nécessiter le moins de permissions possible + +Fonctionnalités : +* Chiffrement de bout-en-bout avec au choix, OMEMO ou OpenPGP +* Envoi et réception d'images +* Appels audio et vidéo chiffrés (DTLS-SRTP) +* Interface utilisateur intuitive qui suit les directives de conception d'Android +* Images / avatars pour vos contacts +* Synchronisation avec des clients de bureau +* Conférences (avec prise en charge des marque-pages) +* Intégration avec le carnet d'adresses +* Plusieurs comptes / boîte de réception unifiée +* Impact très faible sur l'autonomie de la batterie + +Conversations facilite la création de compte sur le serveur gratuit conversations.im. Cependant Conversations fonctionne également avec n'importe quel autre serveur XMPP. De nombreux serveurs XMPP sont gérés par des bénévoles et gratuits. + +Fonctionnalités de XMPP : + +Conversations fonctionne avec n'importe quel serveur XMPP. Cependant XMPP est un protocole extensible. Ces extensions sont aussi standardisées dans ce que l'on appelle les XEP. Conversations en prend en charge quelques-unes pour rendre l'expérience utilisateur meilleure dans l'ensemble. Il y a des chances que votre serveur XMPP actuel ne les prenne pas en charge. Ainsi, pour tirer le meilleur parti de Conversations, vous devriez envisager soit de passer à un serveur XMPP qui le fait, ou encore mieux, gérer votre propre serveur XMPP pour vous et vos amis. + +Ces XEP sont actuellement : + +* XEP-0065: SOCKS5 Bytestreams (ou mod_proxy65). Sera utilisé pour transférer des fichiers si les deux correspondants sont derrière un pare-feu (NAT). +* XEP-0163: Personal Eventing Protocol pour les avatars +* XEP-0191: Blocking Command vous permet de mettre des spammeurs sur liste noire ou bloquer des contacts sans les retirer de vos contacts. +* XEP-0198: Stream Management permet à XMPP de survivre à des petites pannes de réseau et aux changements de la connexion TCP sous-jacente. +* XEP-0280: Message Carbons qui synchronise automatiquement les messages que vous envoyez à votre client de bureau et vous permet ainsi de passer sans heurt de votre client mobile à votre client de bureau et inversement dans une conversation. +* XEP-0237: Roster Versioning principalement pour économiser de la bande passante sur les connexions mobiles de mauvaise qualité. +* XEP-0313: Message Archive Management synchronise l'historique des messages avec le serveur. Retrouvez des messages qui ont été envoyés pendant que Conversations était hors ligne. +* XEP-0352: Client State Indication fait savoir au serveur si Conversations est ou n'est pas en arrière-plan. Permet au serveur d'économiser de la bande passante en différant des paquets non importants. +* XEP-0363: HTTP File Upload vous permet de partager des fichiers dans les conférences et avec des contacts hors-ligne. Nécessite un composant supplémentaire sur votre serveur. From 3e333eb97285e71514e1870c02a9b2b03a1e10fa Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 19 Mar 2024 18:00:12 +0100 Subject: [PATCH 135/363] catch exception when checking phone lock state --- .../services/XmppConnectionService.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 35b831eb4824fe3d5f7662cf38a00ce47a880bbb..a28b2a4843828187456c1e45352db31a9bc74b45 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1138,23 +1138,23 @@ public class XmppConnectionService extends Service { } public boolean isScreenLocked() { - final KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); - final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + final KeyguardManager keyguardManager = getSystemService(KeyguardManager.class); + final PowerManager powerManager = getSystemService(PowerManager.class); final boolean locked = keyguardManager != null && keyguardManager.isKeyguardLocked(); - final boolean interactive = powerManager != null && powerManager.isInteractive(); + final boolean interactive; + try { + interactive = powerManager != null && powerManager.isInteractive(); + } catch (final Exception e) { + return false; + } return locked || !interactive; } private boolean isPhoneSilenced() { - final boolean notificationDnd; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - final NotificationManager notificationManager = getSystemService(NotificationManager.class); - final int filter = notificationManager == null ? NotificationManager.INTERRUPTION_FILTER_UNKNOWN : notificationManager.getCurrentInterruptionFilter(); - notificationDnd = filter >= NotificationManager.INTERRUPTION_FILTER_PRIORITY; - } else { - notificationDnd = false; - } - final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + 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()) { @@ -1162,7 +1162,7 @@ public class XmppConnectionService extends Service { } else { return notificationDnd || ringerMode == AudioManager.RINGER_MODE_SILENT; } - } catch (Throwable throwable) { + } catch (final Throwable throwable) { Log.d(Config.LOGTAG, "platform bug in isPhoneSilenced (" + throwable.getMessage() + ")"); return notificationDnd; } From 400cbd8eee5bf7822b2fda4580403fc98b18b0a3 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 20 Mar 2024 07:26:20 +0100 Subject: [PATCH 136/363] channel discovery screen code clean up --- .../ui/ChannelDiscoveryActivity.java | 66 +++++++++++-------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java index 5cf9417e9f1b130ffc654beeeeefa42374bad168..1b21cfb770bae930fe4a47ca42e4df83562180b7 100644 --- a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java @@ -18,6 +18,7 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.databinding.DataBindingUtil; import com.google.common.base.Strings; @@ -130,7 +131,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O } @Override - public boolean onMenuItemActionExpand(MenuItem item) { + public boolean onMenuItemActionExpand(@NonNull MenuItem item) { mSearchEditText.post(() -> { mSearchEditText.requestFocus(); final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); @@ -140,7 +141,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O } @Override - public boolean onMenuItemActionCollapse(MenuItem item) { + public boolean onMenuItemActionCollapse(@NonNull MenuItem item) { final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); mSearchEditText.setText(""); @@ -189,7 +190,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O } @Override - public void onSaveInstanceState(Bundle savedInstanceState) { + public void onSaveInstanceState(@NonNull Bundle savedInstanceState) { if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) { savedInstanceState.putString("search", mSearchEditText != null ? mSearchEditText.getText().toString() : null); } @@ -248,40 +249,47 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O } @Override - public boolean onContextItemSelected(MenuItem item) { + public boolean onContextItemSelected(@NonNull MenuItem item) { final Room room = adapter.getCurrent(); - if (room != null) { - switch (item.getItemId()) { - case R.id.share_with: - StartConversationActivity.shareAsChannel(this, room.address); - return true; - case R.id.open_join_dialog: - final Intent intent = new Intent(this, StartConversationActivity.class); - intent.setAction(Intent.ACTION_VIEW); - intent.putExtra("force_dialog", true); - intent.setData(Uri.parse(String.format("xmpp:%s?join", room.address))); - startActivity(intent); - return true; - } + if (room == null) { + return false; + } + final int itemId = item.getItemId(); + if (itemId == R.id.share_with) { + StartConversationActivity.shareAsChannel(this, room.address); + return true; + } else if (itemId == R.id.open_join_dialog) { + final Intent intent = new Intent(this, StartConversationActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra("force_dialog", true); + intent.setData(Uri.parse(String.format("xmpp:%s?join", room.address))); + startActivity(intent); + return true; + } else { + return false; } - return false; } - public void joinChannelSearchResult(String selectedAccount, Room result) { - final Jid jid = Config.DOMAIN_LOCK == null ? Jid.ofEscaped(selectedAccount) : Jid.ofLocalAndDomainEscaped(selectedAccount, Config.DOMAIN_LOCK); + public void joinChannelSearchResult(final String selectedAccount, final Room result) { + final Jid jid = + Config.DOMAIN_LOCK == null + ? Jid.ofEscaped(selectedAccount) + : Jid.ofLocalAndDomainEscaped(selectedAccount, Config.DOMAIN_LOCK); final boolean syncAutoJoin = getBooleanPreference("autojoin", R.bool.autojoin); final Account account = xmppConnectionService.findAccountByJid(jid); - final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true); - Bookmark bookmark = conversation.getBookmark(); - if (bookmark != null) { - if (!bookmark.autojoin() && syncAutoJoin) { - bookmark.setAutojoin(true); - xmppConnectionService.createBookmark(account, bookmark); - } - } else { - bookmark = new Bookmark(account, conversation.getJid().asBareJid()); + final Conversation conversation = + xmppConnectionService.findOrCreateConversation( + account, result.getRoom(), true, true, true); + final var existingBookmark = conversation.getBookmark(); + if (existingBookmark == null) { + final var bookmark = new Bookmark(account, conversation.getJid().asBareJid()); bookmark.setAutojoin(syncAutoJoin); xmppConnectionService.createBookmark(account, bookmark); + } else { + if (!existingBookmark.autojoin() && syncAutoJoin) { + existingBookmark.setAutojoin(true); + xmppConnectionService.createBookmark(account, existingBookmark); + } } switchToConversation(conversation); } From 6b00e0b462c1cb315cb586bbb6119ce587d7eb75 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 20 Mar 2024 08:37:32 +0100 Subject: [PATCH 137/363] version bump to 2.14.0 --- build.gradle | 4 ++-- .../android/de-DE/changelogs/{4209804.txt => 4210104.txt} | 0 .../android/en-US/changelogs/{4209804.txt => 4210104.txt} | 0 .../android/es-ES/changelogs/{4209804.txt => 4210104.txt} | 0 .../android/gl-ES/changelogs/{4209804.txt => 4210104.txt} | 0 .../android/sq/changelogs/{4209804.txt => 4210104.txt} | 0 .../android/uk/changelogs/{4209804.txt => 4210104.txt} | 0 .../android/zh-CN/changelogs/{4209804.txt => 4210104.txt} | 0 8 files changed, 2 insertions(+), 2 deletions(-) rename fastlane/metadata/android/de-DE/changelogs/{4209804.txt => 4210104.txt} (100%) rename fastlane/metadata/android/en-US/changelogs/{4209804.txt => 4210104.txt} (100%) rename fastlane/metadata/android/es-ES/changelogs/{4209804.txt => 4210104.txt} (100%) rename fastlane/metadata/android/gl-ES/changelogs/{4209804.txt => 4210104.txt} (100%) rename fastlane/metadata/android/sq/changelogs/{4209804.txt => 4210104.txt} (100%) rename fastlane/metadata/android/uk/changelogs/{4209804.txt => 4210104.txt} (100%) rename fastlane/metadata/android/zh-CN/changelogs/{4209804.txt => 4210104.txt} (100%) diff --git a/build.gradle b/build.gradle index 8b3316364452afa34a7ed263eddb33e937b80835..734bf43eb44e17f55f8d1813daa76df7491320f0 100644 --- a/build.gradle +++ b/build.gradle @@ -97,8 +97,8 @@ android { defaultConfig { minSdkVersion 23 targetSdkVersion 34 - versionCode 42100 - versionName "2.14.0-beta.3" + versionCode 42101 + versionName "2.14.0" archivesBaseName += "-$versionName" applicationId "eu.siacs.conversations" resValue "string", "applicationId", applicationId diff --git a/fastlane/metadata/android/de-DE/changelogs/4209804.txt b/fastlane/metadata/android/de-DE/changelogs/4210104.txt similarity index 100% rename from fastlane/metadata/android/de-DE/changelogs/4209804.txt rename to fastlane/metadata/android/de-DE/changelogs/4210104.txt diff --git a/fastlane/metadata/android/en-US/changelogs/4209804.txt b/fastlane/metadata/android/en-US/changelogs/4210104.txt similarity index 100% rename from fastlane/metadata/android/en-US/changelogs/4209804.txt rename to fastlane/metadata/android/en-US/changelogs/4210104.txt diff --git a/fastlane/metadata/android/es-ES/changelogs/4209804.txt b/fastlane/metadata/android/es-ES/changelogs/4210104.txt similarity index 100% rename from fastlane/metadata/android/es-ES/changelogs/4209804.txt rename to fastlane/metadata/android/es-ES/changelogs/4210104.txt diff --git a/fastlane/metadata/android/gl-ES/changelogs/4209804.txt b/fastlane/metadata/android/gl-ES/changelogs/4210104.txt similarity index 100% rename from fastlane/metadata/android/gl-ES/changelogs/4209804.txt rename to fastlane/metadata/android/gl-ES/changelogs/4210104.txt diff --git a/fastlane/metadata/android/sq/changelogs/4209804.txt b/fastlane/metadata/android/sq/changelogs/4210104.txt similarity index 100% rename from fastlane/metadata/android/sq/changelogs/4209804.txt rename to fastlane/metadata/android/sq/changelogs/4210104.txt diff --git a/fastlane/metadata/android/uk/changelogs/4209804.txt b/fastlane/metadata/android/uk/changelogs/4210104.txt similarity index 100% rename from fastlane/metadata/android/uk/changelogs/4209804.txt rename to fastlane/metadata/android/uk/changelogs/4210104.txt diff --git a/fastlane/metadata/android/zh-CN/changelogs/4209804.txt b/fastlane/metadata/android/zh-CN/changelogs/4210104.txt similarity index 100% rename from fastlane/metadata/android/zh-CN/changelogs/4209804.txt rename to fastlane/metadata/android/zh-CN/changelogs/4210104.txt From 6e560dc5dc93a0c4cea5a7ca7011575afe6e8d4b Mon Sep 17 00:00:00 2001 From: wiktor Date: Thu, 21 Mar 2024 07:58:06 +0000 Subject: [PATCH 138/363] Translated using Weblate (Polish) Currently translated at 100.0% (987 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/pl/ --- src/main/res/values-pl/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 062d3c8688f278e3fee4e43f73ddc862ef3a45e9..db8b08592dd6c4074463a00a585e15a17a1e6ab8 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -1062,4 +1062,8 @@ Quicksy pyta o pozwolenie na użycie twoich danych Brak pozwolenia na wypisanie numeru telefonu Integracja połączeń nie jest dostępna! + Odkrywanie kanałów używa usługi firmy trzeciej <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Używanie tej funkcji spowoduje transmisję twojego adresu IP oraz kryteriów wyszukiwania. Sprawdź ich <a href=https://search.jabber.network/privacy>politykę prywatności</a> aby uzyskać więcej informacji. + Czy chcesz usunąć zakładkę dla %s? + Usuń i zamknij + Czy chcesz usunąć zakładkę dla %s i zamknąć rozmowę? \ No newline at end of file From ac4b588d2312b1a3e244583a57a9089c2104de58 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@users.noreply.translate.codeberg.org> Date: Wed, 20 Mar 2024 15:54:08 +0000 Subject: [PATCH 139/363] Translated using Weblate (Russian) Currently translated at 100.0% (987 of 987 strings) Translation: Conversations/Android App (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-shared/ru/ --- src/main/res/values-ru/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 4aec6465036f994fcfcd838e7d7b66ee9b737597..fe2a4ba9ec2afc1a0b57744ab08a0aa0c8314f03 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -1058,4 +1058,8 @@ Контакт недоступен Интеграция вызовов недоступна! Нет разрешения на телефонный звонок + Вы хотите удалить закладку для «%s»? + Закрыть и удалить + Обзор каналов использует сторонний сервис <a href=https://search.jabber.network>search.jabber.network</a>.<br><br>Эта функция передаст Ваш IP-адрес и ваш поисковый запрос этому сервису. Ознакомьтесь с его <a href=https://search.jabber.network/privacy>Политикой конфиденциальности</a> для получения подробностей. + Вы хотите удалить закладку для %s и закрыть беседу? \ No newline at end of file From 5fa42afcfc722465e90029f167ca3e85dfac03bd Mon Sep 17 00:00:00 2001 From: aei Date: Wed, 20 Mar 2024 08:52:53 +0000 Subject: [PATCH 140/363] Translated using Weblate (Arabic) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ar/ --- src/conversations/res/values-ar/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values-ar/strings.xml b/src/conversations/res/values-ar/strings.xml index 6483bc9dffc73d16a53efca7d8d0a6faf0eb424a..b0d52f8f7b32282eca9f438205a981998f008824 100644 --- a/src/conversations/res/values-ar/strings.xml +++ b/src/conversations/res/values-ar/strings.xml @@ -5,7 +5,7 @@ أنشئ حسابًا جديدًا هل تملك حساب XMPP؟؟ قد يكون ذلك ممكنا لو كنت تستعمل خدمة XMPP أخرى أو إستعملت تطبيق Conversations سابقا. أو يمكنك صنع حساب XMPP جديد الآن. \nملاحظة: بعض خدمات البريد الإلكتروني تقدم حسابات XMPP. - XMPP هو مزود مستقل لشبكة المراسلة الفورية. يمكنك استخدام هذا العميل مع أي خادم XMPP تختاره. + XMPP هو مزود مستقل لشبكة المراسلة الفورية. يمكنك استخدام هذا التطبيق مع أي خادم XMPP تختاره. \nولكن من أجل راحتك ، فقد جعلنا من السهل إنشاء حساب على موقع chat. مزود مناسب بشكل خاص للاستخدام مع المحادثات. لقد تمت دعوتك إلى%1$s. سنوجهك خلال عملية إنشاء حساب. \nعند اختيار%1$s كموفر ، ستتمكن من التواصل مع مستخدمي مقدمي الخدمات الآخرين من خلال منحهم عنوان XMPP الكامل الخاص بك. From fe988765d57d4c0372e146d628a6532007a09860 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@users.noreply.translate.codeberg.org> Date: Wed, 20 Mar 2024 15:53:15 +0000 Subject: [PATCH 141/363] Translated using Weblate (Russian) Currently translated at 100.0% (13 of 13 strings) Translation: Conversations/Android App (Conversations) Translate-URL: https://translate.codeberg.org/projects/conversations/android-app-conversations/ru/ --- src/conversations/res/values-ru/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversations/res/values-ru/strings.xml b/src/conversations/res/values-ru/strings.xml index 2cf7426c8f5a8cf84cdf2615739b39081d76cc43..a62334be82bb18a69532ec396c31788539923397 100644 --- a/src/conversations/res/values-ru/strings.xml +++ b/src/conversations/res/values-ru/strings.xml @@ -5,7 +5,7 @@ Создать новый аккаунт У вас есть аккаунт XMPP\? Если вы использовали Conversations или другой XMPP-клиент в прошлом, то скорее всего, он у вас есть. Если у вас нет аккаунта, вы можете создать его прямо сейчас. \nПодсказка: Некоторые провайдеры электронной почты также регистрируют аккаунты XMPP. - XMPP - это независимая сеть обмена сообщениями. Conversations позволяет вам подключиться к любому XMPP-серверу на ваш выбор. + XMPP - это независимая сеть обмена сообщениями. Это приложение позволяет подключиться к любому XMPP-серверу на ваш выбор. \nЕсли у вас нет сервера, предлагаем вам зарегистрировать аккаунт на conversations.im, сервере, специально предназначенном для работы с Conversations. Вас пригласили на %1$s. Мы проведём вас через процесс создания аккаунта. \nАккаунт на %1$s позволит вам общаться с пользователями и на этом, и на других серверах, используя ваш полный XMPP-адрес. From 3e06eab0fe77a76552996231631874c57d8207cd Mon Sep 17 00:00:00 2001 From: 0ko <0ko@users.noreply.translate.codeberg.org> Date: Wed, 20 Mar 2024 16:01:09 +0000 Subject: [PATCH 142/363] Translated using Weblate (Russian) Currently translated at 6.8% (4 of 58 strings) Translation: Conversations/App Store Metadata (shared) Translate-URL: https://translate.codeberg.org/projects/conversations/app-store-metadata/ru/ --- fastlane/metadata/android/ru-RU/changelogs/4209004.txt | 2 ++ fastlane/metadata/android/ru-RU/changelogs/4209204.txt | 2 ++ fastlane/metadata/android/ru-RU/changelogs/4209404.txt | 1 + fastlane/metadata/android/ru-RU/changelogs/4209804.txt | 1 + 4 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/ru-RU/changelogs/4209004.txt create mode 100644 fastlane/metadata/android/ru-RU/changelogs/4209204.txt create mode 100644 fastlane/metadata/android/ru-RU/changelogs/4209404.txt create mode 100644 fastlane/metadata/android/ru-RU/changelogs/4209804.txt diff --git a/fastlane/metadata/android/ru-RU/changelogs/4209004.txt b/fastlane/metadata/android/ru-RU/changelogs/4209004.txt new file mode 100644 index 0000000000000000000000000000000000000000..1ceb3240ac8dc0e943c957f8c0bdc70d7bbd023a --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/4209004.txt @@ -0,0 +1,2 @@ +* незначительные исправления +* значительные изменения приветствия Quicksy diff --git a/fastlane/metadata/android/ru-RU/changelogs/4209204.txt b/fastlane/metadata/android/ru-RU/changelogs/4209204.txt new file mode 100644 index 0000000000000000000000000000000000000000..fba3c9be51d40603dc891efd2d192b5ccc7f0a2e --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/4209204.txt @@ -0,0 +1,2 @@ +* Упрощён доступ к Политике конфиденциальности в версии для Play Маркета (Quicksy и Conversations) +* Из версии Conversations для Play Маркета удалена интеграция с Контактами diff --git a/fastlane/metadata/android/ru-RU/changelogs/4209404.txt b/fastlane/metadata/android/ru-RU/changelogs/4209404.txt new file mode 100644 index 0000000000000000000000000000000000000000..475ea334921b8d4ed01bf17c54919ffe9e1e7b31 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/4209404.txt @@ -0,0 +1 @@ +* Исправлены незначительные ухудшения, появившиеся в версии 2.13.1 diff --git a/fastlane/metadata/android/ru-RU/changelogs/4209804.txt b/fastlane/metadata/android/ru-RU/changelogs/4209804.txt new file mode 100644 index 0000000000000000000000000000000000000000..3b2e3069bb4a03d99dc86f1087e2b43c15c8db76 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/4209804.txt @@ -0,0 +1 @@ +* Улучшена интеграция вызовов A/V с операционной системой From c0a43c1f99f3db73b8c4457d3f5fed69d2c30ff4 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 22 Mar 2024 09:39:59 +0100 Subject: [PATCH 143/363] fix version code of ru-RU changelog --- .../android/ru-RU/changelogs/{4209804.txt => 4210104.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename fastlane/metadata/android/ru-RU/changelogs/{4209804.txt => 4210104.txt} (100%) diff --git a/fastlane/metadata/android/ru-RU/changelogs/4209804.txt b/fastlane/metadata/android/ru-RU/changelogs/4210104.txt similarity index 100% rename from fastlane/metadata/android/ru-RU/changelogs/4209804.txt rename to fastlane/metadata/android/ru-RU/changelogs/4210104.txt From e7edc2ce82ef44c8960ba74a9281715775d08e01 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 22 Mar 2024 11:56:21 +0100 Subject: [PATCH 144/363] unnecessary version code bump to trigger new Google review --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 734bf43eb44e17f55f8d1813daa76df7491320f0..2d513477f24f05180c8fdc91d462895b9310b115 100644 --- a/build.gradle +++ b/build.gradle @@ -97,7 +97,7 @@ android { defaultConfig { minSdkVersion 23 targetSdkVersion 34 - versionCode 42101 + versionCode 42103 versionName "2.14.0" archivesBaseName += "-$versionName" applicationId "eu.siacs.conversations" From 21732237d4cfb0512155ae8bccdf3edf67a1d4a2 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 25 Mar 2024 08:50:44 +0100 Subject: [PATCH 145/363] add safeguards to ringtone playing twice --- .../conversations/services/NotificationService.java | 10 ++++++---- .../xmpp/jingle/JingleConnectionManager.java | 2 ++ .../conversations/xmpp/jingle/JingleRtpConnection.java | 9 +++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 3ad18286765f2c2e9059df2f31a3729b9431ef8f..fce5c3c493a38ee99598561b6795d30854ab44b2 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -502,11 +502,9 @@ public class NotificationService { public synchronized void startRinging( final AbstractJingleConnection.Id id, final Set media) { showIncomingCallNotification(id, media); - final NotificationManager notificationManager = - (NotificationManager) - mXmppConnectionService.getSystemService(Context.NOTIFICATION_SERVICE); + final NotificationManager notificationManager = mXmppConnectionService.getSystemService(NotificationManager.class); final int currentInterruptionFilter; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && notificationManager != null) { + if (notificationManager != null) { currentInterruptionFilter = notificationManager.getCurrentInterruptionFilter(); } else { currentInterruptionFilter = 1; // INTERRUPTION_FILTER_ALL @@ -525,6 +523,10 @@ public class NotificationService { if (currentVibrationFuture != null) { currentVibrationFuture.cancel(true); } + final var preexistingRingtone = this.currentlyPlayingRingtone; + if (preexistingRingtone != null) { + preexistingRingtone.stop(); + } final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mXmppConnectionService); final Resources resources = mXmppConnectionService.getResources(); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 0232478f5d6afa1acb982da4963f12af4848e0bb..e0eaaa08fdea00a36f43aecfafe61b9fda089dfa 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -504,6 +504,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { final RtpSessionProposal proposal = getRtpSessionProposal(account, from.asBareJid(), sessionId); synchronized (rtpSessionProposals) { + // TODO remove the remove()!= null check to ensure we always call busy() if (proposal != null && rtpSessionProposals.remove(proposal) != null) { proposal.callIntegration.busy(); writeLogMissedOutgoing( @@ -763,6 +764,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { final RtpSessionProposal proposal = RtpSessionProposal.of(account, with.asBareJid(), media, callIntegration); callIntegration.setCallback(new ProposalStateCallback(proposal)); + // TODO ensure that there is no previous proposal?! this.rtpSessionProposals.put(proposal, DeviceDiscoveryState.SEARCHING); mXmppConnectionService.notifyJingleRtpConnectionUpdate( account, proposal.with, proposal.sessionId, RtpEndUserState.FINDING_DEVICE); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index bd069fa8b6d59d7f0188367b05731cf7aaecfd00..c9a7c055c3021ad56554269a2b577f34c85f21e8 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -2691,6 +2691,15 @@ public class JingleRtpConnection extends AbstractJingleConnection @Override public void onCallIntegrationShowIncomingCallUi() { + if (isTerminated()) { + // there might be race conditions with the call integration service invoking this + // callback when the rtp session has already ended. It should be enough to just return + // instead of throwing an exception. however throwing an exception gives us a sense of + // if and how frequently this happens + throw new IllegalStateException( + "CallIntegration requested incoming call UI but session was already terminated"); + } + // TODO apparently this can be called too early as well? xmppConnectionService.getNotificationService().startRinging(id, getMedia()); } From 6b5fb6fee6d8105db246804fb504267c0290c72b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 25 Mar 2024 10:39:24 +0100 Subject: [PATCH 146/363] deliver session-initiate before integrating call otherwise there could potentially be race conditions with showIncomingCallUi being called before we have media information --- .../xmpp/jingle/JingleConnectionManager.java | 44 ++++++++----------- .../xmpp/jingle/JingleRtpConnection.java | 18 ++++++++ 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index e0eaaa08fdea00a36f43aecfafe61b9fda089dfa..9dc13e1746cba77e4cd14cc2fae157d357be4640 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -128,33 +128,31 @@ public class JingleConnectionManager extends AbstractConnectionManager { return; } connections.put(id, connection); - - if (connection instanceof JingleRtpConnection) { - if (!CallIntegrationConnectionService.addNewIncomingCall( - mXmppConnectionService, id)) { - connections.remove(id); - Log.e( - Config.LOGTAG, - account.getJid().asBareJid() + ": could not add incoming call"); - sendSessionTerminate(account, packet, id); - writeLogMissedIncoming( - account, - id.with, - id.sessionId, - null, - System.currentTimeMillis(), - false); - } - } - mXmppConnectionService.updateConversationUi(); connection.deliverPacket(packet); + if (connection instanceof JingleRtpConnection rtpConnection) { + addNewIncomingCall(rtpConnection); + } } else { Log.d(Config.LOGTAG, "unable to route jingle packet: " + packet); respondWithJingleError(account, packet, "unknown-session", "item-not-found", "cancel"); } } + private void addNewIncomingCall(final JingleRtpConnection rtpConnection) { + if (rtpConnection.isTerminated()) { + Log.d( + Config.LOGTAG, + "skip call integration because something must have gone during initiate"); + return; + } + if (CallIntegrationConnectionService.addNewIncomingCall( + mXmppConnectionService, rtpConnection.getId())) { + return; + } + rtpConnection.integrationFailure(); + } + private void sendSessionTerminate(final Account account, final IqPacket request, final AbstractJingleConnection.Id id) { mXmppConnectionService.sendIqPacket( account, request.generateResponse(IqPacket.TYPE.RESULT), null); @@ -398,9 +396,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { this.connections.put(id, rtpConnection); rtpConnection.setProposedMedia(ImmutableSet.copyOf(media)); rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp); - - CallIntegrationConnectionService.addNewIncomingCall( - getXmppConnectionService(), id); + addNewIncomingCall(rtpConnection); // TODO actually do the automatic accept?! } else { Log.d( @@ -450,9 +446,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { this.connections.put(id, rtpConnection); rtpConnection.setProposedMedia(ImmutableSet.copyOf(media)); rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp); - - CallIntegrationConnectionService.addNewIncomingCall( - getXmppConnectionService(), id); + addNewIncomingCall(rtpConnection); } } else { Log.d( diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index c9a7c055c3021ad56554269a2b577f34c85f21e8..ab965918568d28248bd83dc026278525a15b4a2b 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -2237,6 +2237,24 @@ public class JingleRtpConnection extends AbstractJingleConnection } } + public synchronized void integrationFailure() { + final var state = getState(); + if (state == State.PROPOSED) { + Log.e( + Config.LOGTAG, + id.account.getJid().asBareJid() + + ": failed call integration in state proposed"); + rejectCallFromProposed(); + } else if (state == State.SESSION_INITIALIZED) { + Log.e(Config.LOGTAG, id.account.getJid().asBareJid() + ": failed call integration"); + this.webRTCWrapper.close(); + sendSessionTerminate(Reason.FAILED_APPLICATION, "CallIntegration failed"); + } else { + throw new IllegalStateException( + String.format("Can not fail integration in state %s", state)); + } + } + public synchronized void endCall() { if (isTerminated()) { Log.w( From f18ec53233b52240520bb88d186a8cbe9b6daedc Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 25 Mar 2024 10:58:20 +0100 Subject: [PATCH 147/363] minor safeguard to ensure call integration ends --- .../conversations/xmpp/jingle/JingleConnectionManager.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 9dc13e1746cba77e4cd14cc2fae157d357be4640..b70463eb016cf03a9bdc5049fd197f6519ac2b72 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -498,8 +498,8 @@ public class JingleConnectionManager extends AbstractConnectionManager { final RtpSessionProposal proposal = getRtpSessionProposal(account, from.asBareJid(), sessionId); synchronized (rtpSessionProposals) { - // TODO remove the remove()!= null check to ensure we always call busy() - if (proposal != null && rtpSessionProposals.remove(proposal) != null) { + if (proposal != null) { + rtpSessionProposals.remove(proposal); proposal.callIntegration.busy(); writeLogMissedOutgoing( account, proposal.with, proposal.sessionId, serverMsgId, timestamp); @@ -758,7 +758,6 @@ public class JingleConnectionManager extends AbstractConnectionManager { final RtpSessionProposal proposal = RtpSessionProposal.of(account, with.asBareJid(), media, callIntegration); callIntegration.setCallback(new ProposalStateCallback(proposal)); - // TODO ensure that there is no previous proposal?! this.rtpSessionProposals.put(proposal, DeviceDiscoveryState.SEARCHING); mXmppConnectionService.notifyJingleRtpConnectionUpdate( account, proposal.with, proposal.sessionId, RtpEndUserState.FINDING_DEVICE); From 38e9533be41f9079a0781b28a09edd7240357d83 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 26 Mar 2024 10:27:46 +0100 Subject: [PATCH 148/363] bump gradle and AGP --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 2d513477f24f05180c8fdc91d462895b9310b115..d7d71d99cbc8714430fbdef43d422ea856e1debe 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.2.2' + classpath 'com.android.tools.build:gradle:8.3.1' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a2c87f5efeb288045e0acb28e5f8f085d6014400..239aaaa41f8fb2e73d6fe8b9d098afb87f6168b7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip From dd73b01ab1f128c6eccaaa7d18c96efab01927a6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 27 Mar 2024 10:30:14 +0100 Subject: [PATCH 149/363] rudimentary XEP-0490 implementation --- .../conversations/entities/Conversation.java | 22 +- .../generator/AbstractGenerator.java | 83 ++++---- .../conversations/generator/IqGenerator.java | 22 ++ .../conversations/parser/MessageParser.java | 15 +- .../services/XmppConnectionService.java | 200 ++++++++++++++++-- .../eu/siacs/conversations/xml/Namespace.java | 3 + .../conversations/xmpp/XmppConnection.java | 12 ++ .../xmpp/pep/PublishOptions.java | 1 - 8 files changed, 287 insertions(+), 71 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 8bb65cc0f57ab3f0992f80c8f3ce41d0baba12c8..587bfc49638058501de6b874388b1f0c85446882 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -8,6 +8,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.json.JSONArray; @@ -437,6 +438,17 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return null; } + public Message findReceivedWithRemoteId(final String id) { + synchronized (this.messages) { + for (final Message message : this.messages) { + if (message.getStatus() == Message.STATUS_RECEIVED && id.equals(message.getRemoteMsgId())) { + return message; + } + } + } + return null; + } + public Message findMessageWithServerMsgId(String id) { synchronized (this.messages) { for (Message message : this.messages) { @@ -576,20 +588,20 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } } - public List markRead(String upToUuid) { - final List unread = new ArrayList<>(); + public List markRead(final String upToUuid) { + final ImmutableList.Builder unread = new ImmutableList.Builder<>(); synchronized (this.messages) { - for (Message message : this.messages) { + for (final Message message : this.messages) { if (!message.isRead()) { message.markRead(); unread.add(message); } if (message.getUuid().equals(upToUuid)) { - return unread; + return unread.build(); } } } - return unread; + return unread.build(); } public Message getLatestMessage() { diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index 42fc3c00fbc920e6b58e38409c571e779d7b1226..3f63edbd7dca8800af22b613884efcf4a6f805ab 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -2,6 +2,15 @@ package eu.siacs.conversations.generator; import android.util.Base64; +import eu.siacs.conversations.BuildConfig; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; +import eu.siacs.conversations.crypto.axolotl.AxolotlService; +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.xml.Namespace; +import eu.siacs.conversations.xmpp.XmppConnection; + import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; @@ -12,54 +21,42 @@ import java.util.List; import java.util.Locale; import java.util.TimeZone; -import eu.siacs.conversations.BuildConfig; -import eu.siacs.conversations.Config; -import eu.siacs.conversations.R; -import eu.siacs.conversations.crypto.axolotl.AxolotlService; -import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.services.XmppConnectionService; -import eu.siacs.conversations.utils.PhoneHelper; -import eu.siacs.conversations.xml.Namespace; -import eu.siacs.conversations.xmpp.XmppConnection; -import eu.siacs.conversations.xmpp.jingle.stanzas.FileTransferDescription; - public abstract class AbstractGenerator { - 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); private final String[] FEATURES = { - Namespace.JINGLE, - Namespace.JINGLE_APPS_FILE_TRANSFER, - Namespace.JINGLE_TRANSPORTS_S5B, - Namespace.JINGLE_TRANSPORTS_IBB, - Namespace.JINGLE_ENCRYPTED_TRANSPORT, - Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO, - "http://jabber.org/protocol/muc", - "jabber:x:conference", - Namespace.OOB, - "http://jabber.org/protocol/caps", - "http://jabber.org/protocol/disco#info", - "urn:xmpp:avatar:metadata+notify", - Namespace.NICK + "+notify", - "urn:xmpp:ping", - "jabber:iq:version", - "http://jabber.org/protocol/chatstates" + Namespace.JINGLE, + Namespace.JINGLE_APPS_FILE_TRANSFER, + Namespace.JINGLE_TRANSPORTS_S5B, + Namespace.JINGLE_TRANSPORTS_IBB, + Namespace.JINGLE_ENCRYPTED_TRANSPORT, + Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO, + "http://jabber.org/protocol/muc", + "jabber:x:conference", + Namespace.OOB, + "http://jabber.org/protocol/caps", + "http://jabber.org/protocol/disco#info", + "urn:xmpp:avatar:metadata+notify", + Namespace.NICK + "+notify", + "urn:xmpp:ping", + "jabber:iq:version", + "http://jabber.org/protocol/chatstates", + Namespace.MDS_DISPLAYED + "+notify" }; 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" + "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 + "urn:xmpp:time" // XEP-0202: Entity Time leaks time zone }; private final String[] VOIP_NAMESPACES = { - Namespace.JINGLE_TRANSPORT_ICE_UDP, - Namespace.JINGLE_FEATURE_AUDIO, - Namespace.JINGLE_FEATURE_VIDEO, - Namespace.JINGLE_APPS_RTP, - Namespace.JINGLE_APPS_DTLS, - Namespace.JINGLE_MESSAGE + Namespace.JINGLE_TRANSPORT_ICE_UDP, + Namespace.JINGLE_FEATURE_AUDIO, + Namespace.JINGLE_FEATURE_VIDEO, + Namespace.JINGLE_APPS_RTP, + Namespace.JINGLE_APPS_DTLS, + Namespace.JINGLE_MESSAGE }; protected XmppConnectionService mXmppConnectionService; @@ -90,7 +87,11 @@ public abstract class AbstractGenerator { String getCapHash(final Account account) { StringBuilder s = new StringBuilder(); - s.append("client/").append(getIdentityType()).append("//").append(getIdentityName()).append('<'); + s.append("client/") + .append(getIdentityType()) + .append("//") + .append(getIdentityName()) + .append('<'); MessageDigest md; try { md = MessageDigest.getInstance("SHA-1"); diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index c9fa7f6a6cf515638e344cbaef78da6073ebcaf9..df87932e5565bc5b47bfa540ce519599b1eb639f 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -129,6 +129,10 @@ public class IqGenerator extends AbstractGenerator { return retrieve(Namespace.BOOKMARKS2, null); } + public IqPacket retrieveMds() { + return retrieve(Namespace.MDS_DISPLAYED, null); + } + public IqPacket publishNick(String nick) { final Element item = new Element("item"); item.setAttribute("id", "current"); @@ -264,6 +268,24 @@ public class IqGenerator extends AbstractGenerator { return conference; } + public Element mdsDisplayed(final String stanzaId, final Conversation conversation) { + final Jid by; + if (conversation.getMode() == Conversation.MODE_MULTI) { + by = conversation.getJid().asBareJid(); + } else { + by = conversation.getAccount().getJid().asBareJid(); + } + return mdsDisplayed(stanzaId, by); + } + + private Element mdsDisplayed(final String stanzaId, final Jid by) { + final Element displayed = new Element("displayed", Namespace.MDS_DISPLAYED); + final Element stanzaIdElement = displayed.addChild("stanza-id", Namespace.STANZA_IDS); + stanzaIdElement.setAttribute("id", stanzaId); + stanzaIdElement.setAttribute("by", by); + return displayed; + } + public IqPacket publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey, final Set preKeyRecords, final int deviceId, Bundle publishOptions) { final Element item = new Element("item"); diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index cb0620fa10f6b21b8339bd1f9858b5dcf75ec4ea..ece8a78625f1224761487b93261db24b023909c1 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -271,6 +271,9 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece mXmppConnectionService.updateConversationUi(); } } + } else if (Namespace.MDS_DISPLAYED.equals(node) && account.getJid().asBareJid().equals(from)) { + final Element item = items.findChild("item"); + mXmppConnectionService.processMdsItem(account, item); } else { Log.d(Config.LOGTAG, account.getJid().asBareJid() + " received pubsub notification for node=" + node); } @@ -985,12 +988,18 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } } } - Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0"); + final Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0"); if (displayed != null) { final String id = displayed.getAttribute("id"); final Jid sender = InvalidJid.getNullForInvalid(displayed.getAttributeAsJid("sender")); if (packet.fromAccount(account) && !selfAddressed) { - dismissNotification(account, counterpart, query, id); + final Conversation c = + mXmppConnectionService.find(account, counterpart.asBareJid()); + final Message message = + (c == null || id == null) ? null : c.findReceivedWithRemoteId(id); + if (message != null && (query == null || query.isCatchup())) { + mXmppConnectionService.markReadUpTo(c, message); + } if (query == null) { activateGracePeriod(account); } @@ -1012,7 +1021,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece final boolean trueJidMatchesAccount = account.getJid().asBareJid().equals(trueJid == null ? null : trueJid.asBareJid()); if (trueJidMatchesAccount || conversation.getMucOptions().isSelf(counterpart)) { if (!message.isRead() && (query == null || query.isCatchup())) { //checking if message is unread fixes race conditions with reflections - mXmppConnectionService.markRead(conversation); + mXmppConnectionService.markReadUpTo(conversation, message); } } else if (!counterpart.isBareJid() && trueJid != null) { final ReadByMarker readByMarker = ReadByMarker.from(counterpart, trueJid); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index a28b2a4843828187456c1e45352db31a9bc74b45..bd936369674c425260dfe6e2cba8531b4843f337 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -49,6 +49,7 @@ import android.util.Pair; import androidx.annotation.BoolRes; import androidx.annotation.IntegerRes; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.app.RemoteInput; import androidx.core.content.ContextCompat; import androidx.core.util.Consumer; @@ -56,6 +57,8 @@ import androidx.core.util.Consumer; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.base.Strings; +import com.google.common.collect.Collections2; +import com.google.common.collect.Iterables; import org.conscrypt.Conscrypt; import org.jxmpp.stringprep.libidn.LibIdnXmppStringprep; @@ -152,6 +155,7 @@ import eu.siacs.conversations.utils.XmppUri; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.LocalizedContent; import eu.siacs.conversations.xml.Namespace; +import eu.siacs.conversations.xmpp.InvalidJid; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.OnBindListener; import eu.siacs.conversations.xmpp.OnContactStatusChanged; @@ -368,6 +372,12 @@ public class XmppConnectionService extends Service { } else if (!account.getXmppConnection().getFeatures().bookmarksConversion()) { fetchBookmarks(account); } + + if (connection.getFeatures().mds()) { + fetchMessageDisplayedSynchronization(account); + } else { + Log.d(Config.LOGTAG,account.getJid()+": server has no support for mds"); + } final boolean flexible = account.getXmppConnection().getFeatures().flexibleOfflineMessageRetrieval(); final boolean catchup = getMessageArchiveService().inCatchup(account); final boolean trackOfflineMessageRetrieval; @@ -392,6 +402,7 @@ public class XmppConnectionService extends Service { unifiedPushBroker.renewUnifiedPushEndpointsOnBind(account); } }; + private final AtomicLong mLastExpiryRun = new AtomicLong(0); private final LruCache, ServiceDiscoveryResult> discoCache = new LruCache<>(20); private final OnStatusChanged statusListener = new OnStatusChanged() { @@ -1902,18 +1913,88 @@ public class XmppConnectionService extends Service { public void fetchBookmarks2(final Account account) { final IqPacket retrieve = mIqGenerator.retrieveBookmarks(); - sendIqPacket(account, retrieve, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(final Account account, final IqPacket response) { - if (response.getType() == IqPacket.TYPE.RESULT) { - final Element pubsub = response.findChild("pubsub", Namespace.PUBSUB); - final Map bookmarks = Bookmark.parseFromPubsub(pubsub, account); - processBookmarksInitial(account, bookmarks, true); - } + sendIqPacket(account, retrieve, (a, response) -> { + if (response.getType() == IqPacket.TYPE.RESULT) { + final Element pubsub = response.findChild("pubsub", Namespace.PUBSUB); + final Map bookmarks = Bookmark.parseFromPubsub(pubsub, a); + processBookmarksInitial(a, bookmarks, true); } }); } + private void fetchMessageDisplayedSynchronization(final Account account) { + Log.d(Config.LOGTAG, account.getJid() + ": retrieve mds"); + final var retrieve = mIqGenerator.retrieveMds(); + sendIqPacket( + account, + retrieve, + (a, response) -> { + if (response.getType() != IqPacket.TYPE.RESULT) { + return; + } + final var pubSub = response.findChild("pubsub", Namespace.PUBSUB); + final Element items = pubSub == null ? null : pubSub.findChild("items"); + if (items == null + || !Namespace.MDS_DISPLAYED.equals(items.getAttribute("node"))) { + return; + } + for (final Element child : items.getChildren()) { + if ("item".equals(child.getName())) { + processMdsItem(account, child); + } + } + }); + } + + public void processMdsItem(final Account account, final Element item) { + final Jid jid = + item == null ? null : InvalidJid.getNullForInvalid(item.getAttributeAsJid("id")); + if (jid == null) { + return; + } + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": processing mds item for " + jid); + final Element displayed = item.findChild("displayed", Namespace.MDS_DISPLAYED); + final Element stanzaId = + displayed == null ? null : displayed.findChild("stanza-id", Namespace.STANZA_IDS); + final String id = stanzaId == null ? null : stanzaId.getAttribute("id"); + final Conversation conversation = find(account, jid); + if (id != null && conversation != null) { + markReadUpToStanzaId(conversation, id); + } + } + + public void markReadUpToStanzaId(final Conversation conversation, final String stanzaId) { + final Message message = conversation.findMessageWithServerMsgId(stanzaId); + if (message == null) { // do we want to check if isRead? + return; + } + markReadUpTo(conversation, message); + } + + public void markReadUpTo(final Conversation conversation, final Message message) { + final boolean isDismissNotification = isDismissNotification(message); + final var uuid = message.getUuid(); + Log.d( + Config.LOGTAG, + conversation.getAccount().getJid().asBareJid() + + ": mark " + + conversation.getJid().asBareJid() + + " as read up to " + + uuid); + markRead(conversation, uuid, isDismissNotification); + } + + private static boolean isDismissNotification(final Message message) { + Message next = message.next(); + while (next != null) { + if (message.getStatus() == Message.STATUS_RECEIVED) { + return false; + } + next = next.next(); + } + return true; + } + public void processBookmarksInitial(Account account, Map bookmarks, final boolean pep) { final Set previousBookmarks = account.getBookmarkedJids(); final boolean synchronizeWithBookmarks = synchronizeWithBookmarks(); @@ -2050,7 +2131,7 @@ public class XmppConnectionService extends Service { } }); } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error publishing bookmarks (retry=" + retry + ") " + response); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error publishing "+node+" (retry=" + retry + ") " + response); } }); } @@ -4534,22 +4615,99 @@ public class XmppConnectionService extends Service { } } - public void sendReadMarker(final Conversation conversation, String upToUuid) { - final boolean isPrivateAndNonAnonymousMuc = conversation.getMode() == Conversation.MODE_MULTI && conversation.isPrivateAndNonAnonymous(); + public void sendReadMarker(final Conversation conversation, final String upToUuid) { + final boolean isPrivateAndNonAnonymousMuc = + conversation.getMode() == Conversation.MODE_MULTI + && conversation.isPrivateAndNonAnonymous(); final List readMessages = this.markRead(conversation, upToUuid, true); - if (readMessages.size() > 0) { - updateConversationUi(); + if (readMessages.isEmpty()) { + return; } - final Message markable = Conversation.getLatestMarkableMessage(readMessages, isPrivateAndNonAnonymousMuc); - if (confirmMessages() - && markable != null - && (markable.trusted() || isPrivateAndNonAnonymousMuc) - && markable.getRemoteMsgId() != null) { - Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": sending read marker to " + markable.getCounterpart().toString()); - final Account account = conversation.getAccount(); - final MessagePacket packet = mMessageGenerator.confirm(markable); + final var account = conversation.getAccount(); + final var connection = account.getXmppConnection(); + updateConversationUi(); + final var last = + Iterables.getLast( + Collections2.filter( + readMessages, + m -> + !m.isPrivateMessage() + && m.getStatus() == Message.STATUS_RECEIVED), + null); + if (last == null) { + return; + } + + final boolean sendDisplayedMarker = + confirmMessages() + && (last.trusted() || isPrivateAndNonAnonymousMuc) + && last.getRemoteMsgId() != null + && (last.markable || isPrivateAndNonAnonymousMuc); + final boolean serverAssist = + connection != null && connection.getFeatures().mdsServerAssist(); + + final String stanzaId = last.getServerMsgId(); + + if (sendDisplayedMarker && serverAssist) { + final var mdsDisplayed = mIqGenerator.mdsDisplayed(stanzaId, conversation); + final MessagePacket packet = mMessageGenerator.confirm(last); + packet.addChild(mdsDisplayed); + if (!last.isPrivateMessage()) { + packet.setTo(packet.getTo().asBareJid()); + } + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": server assisted "+packet); this.sendMessagePacket(account, packet); + } else { + publishMds(last); + // read markers will be sent after MDS to flush the CSI stanza queue + if (sendDisplayedMarker) { + Log.d( + Config.LOGTAG, + conversation.getAccount().getJid().asBareJid() + + ": sending displayed marker to " + + last.getCounterpart().toString()); + final MessagePacket packet = mMessageGenerator.confirm(last); + this.sendMessagePacket(account, packet); + } + } + } + + private void publishMds(@Nullable final Message message) { + final String stanzaId = message == null ? null : message.getServerMsgId(); + if (Strings.isNullOrEmpty(stanzaId)) { + return; + } + final Conversation conversation; + final var conversational = message.getConversation(); + if (conversational instanceof Conversation c) { + conversation = c; + } else { + return; + } + final var account = conversation.getAccount(); + final var connection = account.getXmppConnection(); + if (connection == null || !connection.getFeatures().mds()) { + return; } + final Jid itemId; + if (message.isPrivateMessage()) { + itemId = message.getCounterpart(); + } else { + itemId = conversation.getJid().asBareJid(); + } + Log.d(Config.LOGTAG,"publishing mds for "+itemId+"/"+stanzaId); + publishMds(account, itemId, stanzaId, conversation); + } + + private void publishMds( + final Account account, final Jid itemId, final String stanzaId, final Conversation conversation) { + final var item = mIqGenerator.mdsDisplayed(stanzaId, conversation); + pushNodeAndEnforcePublishOptions( + account, + Namespace.MDS_DISPLAYED, + item, + itemId.toEscapedString(), + PublishOptions.persistentWhitelistAccessMaxItems()); } public MemorizingTrustManager getMemorizingTrustManager() { diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java index 85714c765a9069fe12e5995257a8ba5c00b9500a..2e5eb1c5a69bbaa288d334bb6321fe76c8cfe934 100644 --- a/src/main/java/eu/siacs/conversations/xml/Namespace.java +++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java @@ -24,6 +24,7 @@ public final class Namespace { public static final String TLS = "urn:ietf:params:xml:ns:xmpp-tls"; public static final String PUBSUB = "http://jabber.org/protocol/pubsub"; public static final String PUBSUB_PUBLISH_OPTIONS = PUBSUB + "#publish-options"; + public static final String PUBSUB_CONFIG_NODE_MAX = PUBSUB + "#config-node-max"; public static final String PUBSUB_ERROR = PUBSUB + "#errors"; public static final String PUBSUB_OWNER = PUBSUB + "#owner"; public static final String NICK = "http://jabber.org/protocol/nick"; @@ -76,4 +77,6 @@ public final class Namespace { public static final String REPORTING_REASON_SPAM = "urn:xmpp:reporting:spam"; public static final String SDP_OFFER_ANSWER = "urn:ietf:rfc:3264"; public static final String HASHES = "urn:xmpp:hashes:2"; + public static final String MDS_DISPLAYED = "urn:xmpp:mds:displayed:0"; + public static final String MDS_SERVER_ASSIST = "urn:xmpp:mds:server-assist:0"; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index cba12e19e65e9d6bd4ca51d3ba8871e05c19c1f1..b29129ee2c657dfc32aa1314393ba5b4f0b7d01d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -2968,6 +2968,10 @@ public class XmppConnection implements Runnable { return hasDiscoFeature(account.getJid().asBareJid(), Namespace.PUBSUB_PUBLISH_OPTIONS); } + public boolean pepConfigNodeMax() { + return hasDiscoFeature(account.getJid().asBareJid(), Namespace.PUBSUB_CONFIG_NODE_MAX); + } + public boolean pepOmemoWhitelisted() { return hasDiscoFeature( account.getJid().asBareJid(), AxolotlService.PEP_OMEMO_WHITELISTED); @@ -3068,5 +3072,13 @@ public class XmppConnection implements Runnable { public boolean externalServiceDiscovery() { return hasDiscoFeature(account.getDomain(), Namespace.EXTERNAL_SERVICE_DISCOVERY); } + + public boolean mds() { + return pepPublishOptions() && pepConfigNodeMax(); + } + + public boolean mdsServerAssist() { + return hasDiscoFeature(account.getJid().asBareJid(), Namespace.MDS_DISPLAYED); + } } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/pep/PublishOptions.java b/src/main/java/eu/siacs/conversations/xmpp/pep/PublishOptions.java index 24b429fd73ee8e95d572cf227526b54453f66508..ef1da85615c5c6c2db62590981dee5e312b52617 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/pep/PublishOptions.java +++ b/src/main/java/eu/siacs/conversations/xmpp/pep/PublishOptions.java @@ -31,7 +31,6 @@ public class PublishOptions { options.putString("pubsub#access_model", "whitelist"); options.putString("pubsub#send_last_published_item", "never"); options.putString("pubsub#max_items", "max"); - options.putString("pubsub#notify_delete", "true"); options.putString("pubsub#notify_retract", "true"); //one could also set notify=true on the retract From 6b37b6377bd77dd57364122f884b7bf04dac4234 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 27 Mar 2024 14:11:20 +0100 Subject: [PATCH 150/363] remember mds display state until after mam catchup --- .../eu/siacs/conversations/entities/Conversation.java | 9 +++++++++ .../conversations/services/MessageArchiveService.java | 10 +++++++++- .../conversations/services/XmppConnectionService.java | 9 ++++----- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 587bfc49638058501de6b874388b1f0c85446882..223a48efa238dfdacb02b0139e20bab2a88e17c5 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -85,6 +85,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl private ChatState mOutgoingChatState = Config.DEFAULT_CHAT_STATE; private ChatState mIncomingChatState = Config.DEFAULT_CHAT_STATE; private String mFirstMamReference = null; + private String displayState = null; public Conversation(final String name, final Account account, final Jid contactJid, final int mode) { @@ -1121,6 +1122,14 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return getName().toString(); } + public void setDisplayState(final String stanzaId) { + this.displayState = stanzaId; + } + + public String getDisplayState() { + return this.displayState; + } + public interface OnMessageFound { void onMessageFound(final Message message); } diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java index e74af3773b157b8d8e60408f8c10be54e3832884..afa7f79ef32b3f7be67da6e77e40405d1527a22b 100644 --- a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java +++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java @@ -282,10 +282,18 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { if (conversation != null) { conversation.sort(); conversation.setHasMessagesLeftOnServer(!done); + final var displayState = conversation.getDisplayState(); + if (displayState != null) { + mXmppConnectionService.markReadUpToStanzaId(conversation, displayState); + } } else { - for (Conversation tmp : this.mXmppConnectionService.getConversations()) { + for (final Conversation tmp : this.mXmppConnectionService.getConversations()) { if (tmp.getAccount() == query.getAccount()) { tmp.sort(); + final var displayState = tmp.getDisplayState(); + if (displayState != null) { + mXmppConnectionService.markReadUpToStanzaId(tmp, displayState); + } } } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index bd936369674c425260dfe6e2cba8531b4843f337..a581926b899379c968ad6a274ef478ce4c206794 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1952,13 +1952,13 @@ public class XmppConnectionService extends Service { if (jid == null) { return; } - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": processing mds item for " + jid); final Element displayed = item.findChild("displayed", Namespace.MDS_DISPLAYED); final Element stanzaId = displayed == null ? null : displayed.findChild("stanza-id", Namespace.STANZA_IDS); final String id = stanzaId == null ? null : stanzaId.getAttribute("id"); final Conversation conversation = find(account, jid); if (id != null && conversation != null) { + conversation.setDisplayState(id); markReadUpToStanzaId(conversation, id); } } @@ -3381,7 +3381,7 @@ public class XmppConnectionService extends Service { new Thread(() -> onMediaLoaded.onMediaLoaded(fileBackend.convertToAttachments(databaseBackend.getRelativeFilePaths(account, jid, limit)))).start(); } - public void persistSelfNick(MucOptions.User self) { + public void persistSelfNick(final MucOptions.User self) { final Conversation conversation = self.getConversation(); final boolean tookProposedNickFromBookmark = conversation.getMucOptions().isTookProposedNickFromBookmark(); Jid full = self.getFullJid(); @@ -3393,11 +3393,10 @@ public class XmppConnectionService extends Service { final Bookmark bookmark = conversation.getBookmark(); final String bookmarkedNick = bookmark == null ? null : bookmark.getNick(); - if (bookmark != null && (tookProposedNickFromBookmark || TextUtils.isEmpty(bookmarkedNick)) && !full.getResource().equals(bookmarkedNick)) { + if (bookmark != null && (tookProposedNickFromBookmark || Strings.isNullOrEmpty(bookmarkedNick)) && !full.getResource().equals(bookmarkedNick)) { final Account account = conversation.getAccount(); final String defaultNick = MucOptions.defaultNick(account); - if (TextUtils.isEmpty(bookmarkedNick) && full.getResource().equals(defaultNick)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": do not overwrite empty bookmark nick with default nick for " + conversation.getJid().asBareJid()); + if (Strings.isNullOrEmpty(bookmarkedNick) && full.getResource().equals(defaultNick)) { return; } Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": persist nick '" + full.getResource() + "' into bookmark for " + conversation.getJid().asBareJid()); From fbfb6c803fa3f844f8b5c62ee96d2ebec0222dbf Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 27 Mar 2024 14:43:34 +0100 Subject: [PATCH 151/363] catch early exception in video transcoder --- .../services/AttachFileToConversationRunnable.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java index 1ddee27b54e8099d031cf8fba2fd117fbef9d201..5889ba3333e98e16b22d640aed4fbc9b756a0efe 100644 --- a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java +++ b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java @@ -99,17 +99,25 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis final boolean highQuality = "720".equals(getVideoCompression()); - final Future future = Transcoder.into(file.getAbsolutePath()). + final Future future; + try { + future = Transcoder.into(file.getAbsolutePath()). addDataSource(mXmppConnectionService, uri) .setVideoTrackStrategy(highQuality ? TranscoderStrategies.VIDEO_720P : TranscoderStrategies.VIDEO_360P) .setAudioTrackStrategy(highQuality ? TranscoderStrategies.AUDIO_HQ : TranscoderStrategies.AUDIO_MQ) .setListener(this) .transcode(); + } catch (final RuntimeException e) { + // transcode can already throw if there is an invalid file format or a platform bug + mXmppConnectionService.stopForcingForegroundNotification(); + processAsFile(); + return; + } try { future.get(); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { throw new AssertionError(e); - } catch (ExecutionException e) { + } catch (final ExecutionException e) { if (e.getCause() instanceof Error) { mXmppConnectionService.stopForcingForegroundNotification(); processAsFile(); From 378efe1a8a1207bdac436ad18ec29c089e8ec2c8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 27 Mar 2024 14:44:17 +0100 Subject: [PATCH 152/363] do not crash when error notification comes after service has shut down --- .../services/NotificationService.java | 67 ++++++++++++++----- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index fce5c3c493a38ee99598561b6795d30854ab44b2..041f8185f079804d186b240bb02c18e8e0daf17f 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.services; import static eu.siacs.conversations.utils.Compatibility.s; +import android.Manifest; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; @@ -10,6 +11,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.content.pm.ShortcutManager; import android.content.res.Resources; import android.graphics.Bitmap; @@ -29,6 +31,7 @@ import android.util.DisplayMetrics; import android.util.Log; import androidx.annotation.RequiresApi; +import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat.BigPictureStyle; import androidx.core.app.NotificationCompat.Builder; @@ -1694,8 +1697,7 @@ public class NotificationService { } private boolean wasHighlightedOrPrivate(final Message message) { - if (message.getConversation() instanceof Conversation) { - Conversation conversation = (Conversation) message.getConversation(); + if (message.getConversation() instanceof Conversation conversation) { final String nick = conversation.getMucOptions().getActualNick(); final Pattern highlight = generateNickHighlightPattern(nick); if (message.getBody() == null || nick == null) { @@ -1825,10 +1827,17 @@ public class NotificationService { } } if (mXmppConnectionService.foregroundNotificationNeedsUpdatingWhenErrorStateChanges()) { - notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification()); + try { + notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification()); + } catch (final RuntimeException e) { + Log.d( + Config.LOGTAG, + "not refreshing foreground service notification because service has died", + e); + } } final Notification.Builder mBuilder = new Notification.Builder(mXmppConnectionService); - if (errors.size() == 0) { + if (errors.isEmpty()) { cancel(ERROR_NOTIFICATION_ID); return; } else if (errors.size() == 1) { @@ -1840,10 +1849,23 @@ public class NotificationService { mXmppConnectionService.getString(R.string.problem_connecting_to_accounts)); mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_fix)); } - mBuilder.addAction( - R.drawable.ic_autorenew_white_24dp, - mXmppConnectionService.getString(R.string.try_again), - pendingServiceIntent(mXmppConnectionService, XmppConnectionService.ACTION_TRY_AGAIN, 45)); + try { + mBuilder.addAction( + R.drawable.ic_autorenew_white_24dp, + mXmppConnectionService.getString(R.string.try_again), + pendingServiceIntent( + mXmppConnectionService, XmppConnectionService.ACTION_TRY_AGAIN, 45)); + mBuilder.setDeleteIntent( + pendingServiceIntent( + mXmppConnectionService, + XmppConnectionService.ACTION_DISMISS_ERROR_NOTIFICATIONS, + 69)); + } catch (final RuntimeException e) { + Log.d( + Config.LOGTAG, + "not including some actions in error notification because service has died", + e); + } if (torNotAvailable) { if (TorServiceUtils.isOrbotInstalled(mXmppConnectionService)) { mBuilder.addAction( @@ -1871,7 +1893,6 @@ public class NotificationService { : PendingIntent.FLAG_UPDATE_CURRENT)); } } - mBuilder.setDeleteIntent(pendingServiceIntent(mXmppConnectionService,XmppConnectionService.ACTION_DISMISS_ERROR_NOTIFICATIONS, 69)); mBuilder.setVisibility(Notification.VISIBILITY_PRIVATE); mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp); mBuilder.setLocalOnly(true); @@ -1912,27 +1933,37 @@ public class NotificationService { notify(FOREGROUND_NOTIFICATION_ID, notification); } - private void notify(String tag, int id, Notification notification) { - final NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(mXmppConnectionService); + private void notify(final String tag, final int id, final Notification notification) { + if (ActivityCompat.checkSelfPermission( + mXmppConnectionService, Manifest.permission.POST_NOTIFICATIONS) + != PackageManager.PERMISSION_GRANTED) { + return; + } + final var notificationManager = + mXmppConnectionService.getSystemService(NotificationManager.class); try { notificationManager.notify(tag, id, notification); - } catch (RuntimeException e) { + } catch (final RuntimeException e) { Log.d(Config.LOGTAG, "unable to make notification", e); } } - public void notify(int id, Notification notification) { - final NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(mXmppConnectionService); + public void notify(final int id, final Notification notification) { + if (ActivityCompat.checkSelfPermission( + mXmppConnectionService, Manifest.permission.POST_NOTIFICATIONS) + != PackageManager.PERMISSION_GRANTED) { + return; + } + final var notificationManager = + mXmppConnectionService.getSystemService(NotificationManager.class); try { notificationManager.notify(id, notification); - } catch (RuntimeException e) { + } catch (final RuntimeException e) { Log.d(Config.LOGTAG, "unable to make notification", e); } } - public void cancel(int id) { + public void cancel(final int id) { final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mXmppConnectionService); try { From 26dde5370a8b6268831caff5d60b98a34ac44542 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 28 Mar 2024 13:53:29 +0100 Subject: [PATCH 153/363] use distinct notification id for video transcoder --- .../AttachFileToConversationRunnable.java | 12 ++--- .../services/NotificationService.java | 37 +++++++++---- .../services/XmppConnectionService.java | 52 ++++++++++++------- 3 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java index 5889ba3333e98e16b22d640aed4fbc9b756a0efe..5df423502ddf800bdf87f6ed97525a46d82179f5 100644 --- a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java +++ b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java @@ -90,7 +90,7 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis private void processAsVideo() throws FileNotFoundException { Log.d(Config.LOGTAG, "processing file as video"); - mXmppConnectionService.startForcingForegroundNotification(); + mXmppConnectionService.startOngoingVideoTranscodingForegroundNotification(); mXmppConnectionService.getFileBackend().setupRelativeFilePath(message, String.format("%s.%s", message.getUuid(), "mp4")); final DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message); if (Objects.requireNonNull(file.getParentFile()).mkdirs()) { @@ -109,7 +109,7 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis .transcode(); } catch (final RuntimeException e) { // transcode can already throw if there is an invalid file format or a platform bug - mXmppConnectionService.stopForcingForegroundNotification(); + mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification(); processAsFile(); return; } @@ -119,7 +119,7 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis throw new AssertionError(e); } catch (final ExecutionException e) { if (e.getCause() instanceof Error) { - mXmppConnectionService.stopForcingForegroundNotification(); + mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification(); processAsFile(); } else { Log.d(Config.LOGTAG, "ignoring execution exception. Should get handled by onTranscodeFiled() instead", e); @@ -138,7 +138,7 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis @Override public void onTranscodeCompleted(int successCode) { - mXmppConnectionService.stopForcingForegroundNotification(); + mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification(); final File file = mXmppConnectionService.getFileBackend().getFile(message); long convertedFileSize = mXmppConnectionService.getFileBackend().getFile(message).getSize(); Log.d(Config.LOGTAG, "originalFileSize=" + originalFileSize + " convertedFileSize=" + convertedFileSize); @@ -162,13 +162,13 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis @Override public void onTranscodeCanceled() { - mXmppConnectionService.stopForcingForegroundNotification(); + mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification(); processAsFile(); } @Override public void onTranscodeFailed(@NonNull @NotNull Throwable exception) { - mXmppConnectionService.stopForcingForegroundNotification(); + mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification(); Log.d(Config.LOGTAG, "video transcoding failed", exception); processAsFile(); } diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 041f8185f079804d186b240bb02c18e8e0daf17f..e054fe54a9bf352c4461cf2cba0ed21663d28308 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -30,6 +30,7 @@ import android.text.style.StyleSpan; import android.util.DisplayMetrics; import android.util.Log; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationCompat; @@ -108,6 +109,7 @@ public class NotificationService { public static final int ONGOING_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 10; public static final int MISSED_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 12; private static final int DELIVERY_FAILED_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 13; + public static final int ONGOING_VIDEO_TRANSCODING_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 14; private final XmppConnectionService mXmppConnectionService; private final LinkedHashMap> notifications = new LinkedHashMap<>(); private final HashMap mBacklogMessageCounter = new HashMap<>(); @@ -1919,18 +1921,33 @@ public class NotificationService { notify(ERROR_NOTIFICATION_ID, mBuilder.build()); } - void updateFileAddingNotification(int current, Message message) { - Notification.Builder mBuilder = new Notification.Builder(mXmppConnectionService); - mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.transcoding_video)); - mBuilder.setProgress(100, current, false); - mBuilder.setSmallIcon(R.drawable.ic_hourglass_empty_white_24dp); - mBuilder.setContentIntent(createContentIntent(message.getConversation())); - mBuilder.setOngoing(true); + void updateFileAddingNotification(final int current, final Message message) { + + final Notification notification = videoTranscoding(current, message); + notify(ONGOING_VIDEO_TRANSCODING_NOTIFICATION_ID, notification); + } + + private Notification videoTranscoding(final int current, @Nullable final Message message) { + final Notification.Builder builder = new Notification.Builder(mXmppConnectionService); + builder.setContentTitle(mXmppConnectionService.getString(R.string.transcoding_video)); + if (current >= 0) { + builder.setProgress(100, current, false); + } else { + builder.setProgress(100, 0, true); + } + builder.setSmallIcon(R.drawable.ic_hourglass_empty_white_24dp); + if (message != null) { + builder.setContentIntent(createContentIntent(message.getConversation())); + } + builder.setOngoing(true); if (Compatibility.runsTwentySix()) { - mBuilder.setChannelId("compression"); + builder.setChannelId("compression"); } - Notification notification = mBuilder.build(); - notify(FOREGROUND_NOTIFICATION_ID, notification); + return builder.build(); + } + + public Notification getIndeterminateVideoTranscoding() { + return videoTranscoding(-1, null); } private void notify(final String tag, final int id, final Notification notification) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index a581926b899379c968ad6a274ef478ce4c206794..4bc85a01697274952d68693259926028beebf84f 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -243,7 +243,7 @@ public class XmppConnectionService extends Service { private final ChannelDiscoveryService mChannelDiscoveryService = new ChannelDiscoveryService(this); private final ShortcutService mShortcutService = new ShortcutService(this); private final AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false); - private final AtomicBoolean mForceForegroundService = new AtomicBoolean(false); + private final AtomicBoolean mOngoingVideoTranscoding = new AtomicBoolean(false); private final AtomicBoolean mForceDuringOnCreate = new AtomicBoolean(false); private final AtomicReference ongoingCall = new AtomicReference<>(); private final OnMessagePacketReceived mMessageParser = new MessageParser(this); @@ -526,13 +526,13 @@ public class XmppConnectionService extends Service { } } - public void startForcingForegroundNotification() { - mForceForegroundService.set(true); + public void startOngoingVideoTranscodingForegroundNotification() { + mOngoingVideoTranscoding.set(true); toggleForegroundService(); } - public void stopForcingForegroundNotification() { - mForceForegroundService.set(false); + public void stopOngoingVideoTranscodingForegroundNotification() { + mOngoingVideoTranscoding.set(false); toggleForegroundService(); } @@ -1467,35 +1467,47 @@ public class XmppConnectionService extends Service { private void toggleForegroundService(final boolean force) { final boolean status; final OngoingCall ongoing = ongoingCall.get(); - if (force || mForceDuringOnCreate.get() || mForceForegroundService.get() || ongoing != null || (Compatibility.keepForegroundService(this) && hasEnabledAccounts())) { + final boolean ongoingVideoTranscoding = mOngoingVideoTranscoding.get(); + final int id; + if (force + || mForceDuringOnCreate.get() + || ongoingVideoTranscoding + || ongoing != null + || (Compatibility.keepForegroundService(this) && hasEnabledAccounts())) { final Notification notification; - final int id; if (ongoing != null) { notification = this.mNotificationService.getOngoingCallNotification(ongoing); id = NotificationService.ONGOING_CALL_NOTIFICATION_ID; startForegroundOrCatch(id, notification, true); - mNotificationService.cancel(NotificationService.FOREGROUND_NOTIFICATION_ID); + } else if (ongoingVideoTranscoding) { + notification = this.mNotificationService.getIndeterminateVideoTranscoding(); + id = NotificationService.ONGOING_VIDEO_TRANSCODING_NOTIFICATION_ID; + startForegroundOrCatch(id, notification, false); } else { notification = this.mNotificationService.createForegroundNotification(); id = NotificationService.FOREGROUND_NOTIFICATION_ID; startForegroundOrCatch(id, notification, false); } - - if (!mForceForegroundService.get()) { - mNotificationService.notify(id, notification); - } + mNotificationService.notify(id, notification); status = true; } else { + id = 0; stopForeground(true); status = false; } - if (!mForceForegroundService.get()) { - mNotificationService.cancel(NotificationService.FOREGROUND_NOTIFICATION_ID); - } - if (ongoing == null) { - mNotificationService.cancel(NotificationService.ONGOING_CALL_NOTIFICATION_ID); + + for (final int toBeRemoved : + Collections2.filter( + Arrays.asList( + NotificationService.FOREGROUND_NOTIFICATION_ID, + NotificationService.ONGOING_CALL_NOTIFICATION_ID, + NotificationService.ONGOING_VIDEO_TRANSCODING_NOTIFICATION_ID), + i -> i != id)) { + mNotificationService.cancel(toBeRemoved); } - Log.d(Config.LOGTAG, "ForegroundService: " + (status ? "on" : "off")); + Log.d( + Config.LOGTAG, + "ForegroundService: " + (status ? "on" : "off") + ", notification: " + id); } private void startForegroundOrCatch( @@ -1531,13 +1543,13 @@ public class XmppConnectionService extends Service { } public boolean foregroundNotificationNeedsUpdatingWhenErrorStateChanges() { - return !mForceForegroundService.get() && ongoingCall.get() == null && Compatibility.keepForegroundService(this) && hasEnabledAccounts(); + return !mOngoingVideoTranscoding.get() && ongoingCall.get() == null && Compatibility.keepForegroundService(this) && hasEnabledAccounts(); } @Override public void onTaskRemoved(final Intent rootIntent) { super.onTaskRemoved(rootIntent); - if ((Compatibility.keepForegroundService(this) && hasEnabledAccounts()) || mForceForegroundService.get() || ongoingCall.get() != null) { + if ((Compatibility.keepForegroundService(this) && hasEnabledAccounts()) || mOngoingVideoTranscoding.get() || ongoingCall.get() != null) { Log.d(Config.LOGTAG, "ignoring onTaskRemoved because foreground service is activated"); } else { this.logoutAndSave(false); From 283f363088349c4709fc72d018b18345f2b53e8f Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 29 Mar 2024 06:55:24 +0100 Subject: [PATCH 154/363] use voice call stream to play connect sound --- .../services/AppRTCAudioManager.java | 2 +- .../services/CallIntegration.java | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java index d9eaeee4871a948e25bfce7c1e7b4edb05b31f62..0aa0c0cd094e80601753c5a1a72b8665c9521a52 100644 --- a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java +++ b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java @@ -466,7 +466,7 @@ public class AppRTCAudioManager { final var toneGenerator = new ToneGenerator( AudioManager.STREAM_MUSIC, - CallIntegration.DEFAULT_VOLUME); + CallIntegration.DEFAULT_TONE_VOLUME); toneGenerator.startTone(ToneGenerator.TONE_CDMA_DIAL_TONE_LITE, 750); }, 0, diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index 67ea605f57328db94c7bda9ce510d820143fafe9..93b25e25f194442324d627d07101956fbfa09360 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.services; import android.content.Context; import android.content.pm.PackageManager; +import android.media.AudioAttributes; import android.media.AudioManager; import android.media.ToneGenerator; import android.net.Uri; @@ -35,7 +36,8 @@ import java.util.concurrent.atomic.AtomicBoolean; public class CallIntegration extends Connection { - public static final int DEFAULT_VOLUME = 80; + public static final int DEFAULT_TONE_VOLUME = 60; + private static final int DEFAULT_MEDIA_PLAYER_VOLUME = 90; private final Context context; @@ -338,14 +340,25 @@ public class CallIntegration extends Connection { } private void playConnectedSound() { - final var mediaPlayer = MediaPlayer.create(context, R.raw.connected); - mediaPlayer.setVolume(DEFAULT_VOLUME / 100f, DEFAULT_VOLUME / 100f); + final var audioAttributes = + new AudioAttributes.Builder() + .setLegacyStreamType(AudioManager.STREAM_VOICE_CALL) + .build(); + final var mediaPlayer = + MediaPlayer.create( + context, + R.raw.connected, + audioAttributes, + AudioManager.AUDIO_SESSION_ID_GENERATE); + mediaPlayer.setVolume( + DEFAULT_MEDIA_PLAYER_VOLUME / 100f, DEFAULT_MEDIA_PLAYER_VOLUME / 100f); mediaPlayer.start(); } public void success() { Log.d(Config.LOGTAG, "CallIntegration.success()"); - final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, DEFAULT_VOLUME); + final var toneGenerator = + new ToneGenerator(AudioManager.STREAM_VOICE_CALL, DEFAULT_TONE_VOLUME); toneGenerator.startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375); this.destroyWithDelay(new DisconnectCause(DisconnectCause.LOCAL, null), 375); } @@ -361,7 +374,8 @@ public class CallIntegration extends Connection { public void error() { Log.d(Config.LOGTAG, "CallIntegration.error()"); - final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, DEFAULT_VOLUME); + final var toneGenerator = + new ToneGenerator(AudioManager.STREAM_VOICE_CALL, DEFAULT_TONE_VOLUME); toneGenerator.startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375); this.destroyWithDelay(new DisconnectCause(DisconnectCause.ERROR, null), 375); } @@ -379,7 +393,7 @@ public class CallIntegration extends Connection { public void busy() { Log.d(Config.LOGTAG, "CallIntegration.busy()"); - final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 80); + final var toneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, 80); toneGenerator.startTone(ToneGenerator.TONE_CDMA_NETWORK_BUSY, 2500); this.destroyWithDelay(new DisconnectCause(DisconnectCause.BUSY, null), 2500); } From c415e7d1d52cbdf1659ec1773dd2119ec4ea0b46 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 29 Mar 2024 15:59:53 +0100 Subject: [PATCH 155/363] add 0490 (mds) to doap file --- conversations.doap | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/conversations.doap b/conversations.doap index 486baed404255f278a705e34867d1b2e44b63a63..231859b2bd8d2e3e477b52daa362520f709e94db 100644 --- a/conversations.doap +++ b/conversations.doap @@ -481,12 +481,19 @@ 0.1.0 + + + + complete + 0.1.0 + + - 2.13.4 - 2024-02-20 - + 2.14.0 + 2024-03-22 + From 14fb9035226a0e0e2d5daf8a5a6b48d10c2625f4 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 29 Mar 2024 21:22:53 +0100 Subject: [PATCH 156/363] use fake 'tel:0' address on Android 8 --- .../CallIntegrationConnectionService.java | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index d47a28a22bf6a183f271f790577498e802b57e8d..b7cf00474ee98f695a7b9adaea39bc082ce31120 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -40,6 +40,7 @@ import eu.siacs.conversations.xmpp.jingle.Media; import eu.siacs.conversations.xmpp.jingle.RtpEndUserState; import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Set; @@ -52,6 +53,9 @@ import java.util.concurrent.TimeoutException; public class CallIntegrationConnectionService extends ConnectionService { + private static final String EXTRA_ADDRESS = "eu.siacs.conversations.address"; + private static final String EXTRA_SESSION_ID = "eu.siacs.conversations.sid"; + private static final ExecutorService ACCOUNT_REGISTRATION_EXECUTOR = Executors.newSingleThreadExecutor(); @@ -160,8 +164,17 @@ public class CallIntegrationConnectionService extends ConnectionService { final PhoneAccountHandle phoneAccountHandle, final ConnectionRequest request) { Log.d(Config.LOGTAG, "onCreateOutgoingConnection(" + request.getAddress() + ")"); final var uri = request.getAddress(); - final var jid = Jid.ofEscaped(uri.getSchemeSpecificPart()); final var extras = request.getExtras(); + if (uri == null || !Arrays.asList("xmpp", "tel").contains(uri.getScheme())) { + return Connection.createFailedConnection( + new DisconnectCause(DisconnectCause.ERROR, "invalid address")); + } + final Jid jid; + if ("tel".equals(uri.getScheme())) { + jid = Jid.ofEscaped(extras.getString(EXTRA_ADDRESS)); + } else { + jid = Jid.ofEscaped(uri.getSchemeSpecificPart()); + } final int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE); final Set media = videoState == VideoProfile.STATE_AUDIO_ONLY @@ -183,7 +196,7 @@ public class CallIntegrationConnectionService extends ConnectionService { final Bundle extraExtras = extras.getBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS); final String incomingCallAddress = extras.getString(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS); - final String sid = extraExtras == null ? null : extraExtras.getString("sid"); + final String sid = extraExtras == null ? null : extraExtras.getString(EXTRA_SESSION_ID); Log.d(Config.LOGTAG, "sid " + sid); final Uri uri = incomingCallAddress == null ? null : Uri.parse(incomingCallAddress); Log.d(Config.LOGTAG, "uri=" + uri); @@ -329,9 +342,19 @@ public class CallIntegrationConnectionService extends ConnectionService { .show(); return; } + final Uri address; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + // Android 9+ supports putting xmpp uris into the address + address = CallIntegration.address(with); + } else { + // for Android 8 we need to put in a fake tel uri + final var outgoingCallExtras = new Bundle(); + outgoingCallExtras.putString(EXTRA_ADDRESS, with.toEscapedString()); + extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, outgoingCallExtras); + address = Uri.parse("tel:0"); + } try { - service.getSystemService(TelecomManager.class) - .placeCall(CallIntegration.address(with), extras); + service.getSystemService(TelecomManager.class).placeCall(address, extras); } catch (final SecurityException e) { Toast.makeText(service, R.string.call_integration_not_available, Toast.LENGTH_LONG) .show(); @@ -363,7 +386,7 @@ public class CallIntegrationConnectionService extends ConnectionService { TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, CallIntegration.address(id.with).toString()); final var extras = new Bundle(); - extras.putString("sid", id.sessionId); + extras.putString(EXTRA_SESSION_ID, id.sessionId); bundle.putBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras); try { context.getSystemService(TelecomManager.class) From c527e763377e6957054c483ecf4c714ad8c80299 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 30 Mar 2024 08:48:09 +0100 Subject: [PATCH 157/363] parse invalid jingle actions --- .../xmpp/jingle/JingleConnectionManager.java | 20 +++++++++++-------- .../xmpp/jingle/stanzas/JinglePacket.java | 11 ++++++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index b70463eb016cf03a9bdc5049fd197f6519ac2b72..c94ed0ab1c02a6eaad366761fc5a52ff4a75cc63 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -76,15 +76,20 @@ public class JingleConnectionManager extends AbstractConnectionManager { public void deliverPacket(final Account account, final JinglePacket packet) { final String sessionId = packet.getSessionId(); + final JinglePacket.Action action = packet.getAction(); if (sessionId == null) { respondWithJingleError(account, packet, "unknown-session", "item-not-found", "cancel"); return; } + if (action == null) { + respondWithJingleError(account, packet, null, "bad-request", "cancel"); + return; + } final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, packet); final AbstractJingleConnection existingJingleConnection = connections.get(id); if (existingJingleConnection != null) { existingJingleConnection.deliverPacket(packet); - } else if (packet.getAction() == JinglePacket.Action.SESSION_INITIATE) { + } else if (action == JinglePacket.Action.SESSION_INITIATE) { final Jid from = packet.getFrom(); final Content content = packet.getJingleContent(); final String descriptionNamespace = @@ -153,7 +158,8 @@ public class JingleConnectionManager extends AbstractConnectionManager { rtpConnection.integrationFailure(); } - private void sendSessionTerminate(final Account account, final IqPacket request, final AbstractJingleConnection.Id id) { + private void sendSessionTerminate( + final Account account, final IqPacket request, final AbstractJingleConnection.Id id) { mXmppConnectionService.sendIqPacket( account, request.generateResponse(IqPacket.TYPE.RESULT), null); final JinglePacket sessionTermination = @@ -255,9 +261,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { void respondWithJingleError( final Account account, final IqPacket original, - String jingleCondition, - String condition, - String conditionType) { + final String jingleCondition, + final String condition, + final String conditionType) { final IqPacket response = original.generateResponse(IqPacket.TYPE.ERROR); final Element error = response.addChild("error"); error.setAttribute("type", conditionType); @@ -1171,8 +1177,6 @@ public class JingleConnectionManager extends AbstractConnectionManager { public void onCallIntegrationAnswer() {} @Override - public void onCallIntegrationSilence() { - - } + public void onCallIntegrationSilence() {} } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java index 82c5b155c1d9666fb52b2a6e60c71de5bf626df5..a24040d3dbe82efcbfc558e0ca56c006cf4d82bd 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java @@ -142,8 +142,15 @@ public class JinglePacket extends IqPacket { TRANSPORT_REPLACE; public static Action of(final String value) { - // TODO handle invalid - return Action.valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, value)); + if (Strings.isNullOrEmpty(value)) { + return null; + } + try { + return Action.valueOf( + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, value)); + } catch (final IllegalArgumentException e) { + return null; + } } @Override From cb37321ecb39f04aa0c3b7f19010938304d11b3c Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 30 Mar 2024 11:15:41 +0100 Subject: [PATCH 158/363] rtp session propsoal: fix race condition with very fast 'busy' or 'error' --- .../CallIntegrationConnectionService.java | 1 + .../conversations/ui/RtpSessionActivity.java | 22 ++++++-- .../xmpp/jingle/JingleConnectionManager.java | 52 +++++++++++++++---- .../xmpp/jingle/JingleRtpConnection.java | 10 ++-- 4 files changed, 67 insertions(+), 18 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index b7cf00474ee98f695a7b9adaea39bc082ce31120..e86f6c4f8f6f1d7a20a310bb062331ff45c5ef64 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -131,6 +131,7 @@ public class CallIntegrationConnectionService extends ConnectionService { intent.putExtra( RtpSessionActivity.EXTRA_LAST_REPORTED_STATE, RtpEndUserState.FINDING_DEVICE.toString()); + intent.putExtra(RtpSessionActivity.EXTRA_PROPOSED_SESSION_ID, proposal.sessionId); callIntegration = proposal.getCallIntegration(); } if (Media.audioOnly(media)) { diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 793ef16f90f847dfe64a549676e17d4d1f450593..871b7c259dd4be5b7b6598e0c631b89975fc9369 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -80,6 +80,7 @@ public class RtpSessionActivity extends XmppActivity public static final String EXTRA_WITH = "with"; public static final String EXTRA_SESSION_ID = "session_id"; + public static final String EXTRA_PROPOSED_SESSION_ID = "proposed_session_id"; public static final String EXTRA_LAST_REPORTED_STATE = "last_reported_state"; public static final String EXTRA_LAST_ACTION = "last_action"; public static final String ACTION_ACCEPT_CALL = "action_accept_call"; @@ -386,7 +387,6 @@ public class RtpSessionActivity extends XmppActivity } } - private void putScreenInCallMode() { putScreenInCallMode(requireRtpConnection().getMedia()); } @@ -509,6 +509,16 @@ public class RtpSessionActivity extends XmppActivity proposeJingleRtpSession(account, with, actionToMedia(action)); setWith(account.getRoster().getContact(with), null); } else if (Intent.ACTION_VIEW.equals(action)) { + final String proposedSessionId = intent.getStringExtra(EXTRA_PROPOSED_SESSION_ID); + final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession = + xmppConnectionService + .getJingleConnectionManager() + .getTerminalSessionState(with, proposedSessionId); + if (terminatedRtpSession != null) { + // termination (due to message error or 'busy' was faster than opening the activity + initializeWithTerminatedSessionState(account, with, terminatedRtpSession); + return; + } final String extraLastState = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE); final String lastAction = intent.getStringExtra(EXTRA_LAST_ACTION); final Set media = actionToMedia(lastAction); @@ -1007,7 +1017,7 @@ public class RtpSessionActivity extends XmppActivity private void updateInCallButtonConfiguration( final RtpEndUserState state, final Set media) { if (STATES_CONSIDERED_CONNECTED.contains(state) && !isPictureInPicture()) { - Preconditions.checkArgument(media.size() > 0, "Media must not be empty"); + Preconditions.checkArgument(!media.isEmpty(), "Media must not be empty"); if (media.contains(Media.VIDEO)) { final JingleRtpConnection rtpConnection = requireRtpConnection(); updateInCallButtonConfigurationVideo( @@ -1028,7 +1038,13 @@ public class RtpSessionActivity extends XmppActivity } else if (STATES_SHOWING_SPEAKER_CONFIGURATION.contains(state) && !isPictureInPicture() && Media.audioOnly(media)) { - final CallIntegration callIntegration = requireCallIntegration(); + final CallIntegration callIntegration; + try { + callIntegration = requireCallIntegration(); + } catch (final IllegalStateException e) { + Log.e(Config.LOGTAG, "can not update InCallButtonConfiguration in state " + state); + return; + } updateInCallButtonConfigurationSpeaker( callIntegration.getSelectedAudioDevice(), callIntegration.getAudioDevices().size()); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index c94ed0ab1c02a6eaad366761fc5a52ff4a75cc63..ce3628697d74e08d5756fc110f30068c48512d60 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -505,6 +505,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { getRtpSessionProposal(account, from.asBareJid(), sessionId); synchronized (rtpSessionProposals) { if (proposal != null) { + setTerminalSessionState(proposal, RtpEndUserState.DECLINED_OR_BUSY); rtpSessionProposals.remove(proposal); proposal.callIntegration.busy(); writeLogMissedOutgoing( @@ -938,7 +939,12 @@ public class JingleConnectionManager extends AbstractConnectionManager { final DeviceDiscoveryState currentState = sessionProposal == null ? null : rtpSessionProposals.get(sessionProposal); if (currentState == null) { - Log.d(Config.LOGTAG, "unable to find session proposal for session id " + sessionId); + Log.d( + Config.LOGTAG, + "unable to find session proposal for session id " + + sessionId + + " target=" + + target); return; } if (currentState == DeviceDiscoveryState.DISCOVERED) { @@ -947,14 +953,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { "session proposal already at discovered. not going to fall back"); return; } - this.rtpSessionProposals.put(sessionProposal, target); - final RtpEndUserState endUserState = target.toEndUserState(); - if (endUserState == RtpEndUserState.RINGING) { - sessionProposal.callIntegration.setDialing(); - } - // toneManager.transition(endUserState, sessionProposal.media); - mXmppConnectionService.notifyJingleRtpConnectionUpdate( - account, sessionProposal.with, sessionProposal.sessionId, endUserState); + Log.d( Config.LOGTAG, account.getJid().asBareJid() @@ -962,6 +961,30 @@ public class JingleConnectionManager extends AbstractConnectionManager { + sessionId + " as " + target); + + final RtpEndUserState endUserState = target.toEndUserState(); + + if (target == DeviceDiscoveryState.FAILED) { + Log.d(Config.LOGTAG, "removing session proposal after failure"); + setTerminalSessionState(sessionProposal, endUserState); + this.rtpSessionProposals.remove(sessionProposal); + sessionProposal.getCallIntegration().error(); + mXmppConnectionService.notifyJingleRtpConnectionUpdate( + account, + sessionProposal.with, + sessionProposal.sessionId, + endUserState); + return; + } + + this.rtpSessionProposals.put(sessionProposal, target); + + if (endUserState == RtpEndUserState.RINGING) { + sessionProposal.callIntegration.setDialing(); + } + + mXmppConnectionService.notifyJingleRtpConnectionUpdate( + account, sessionProposal.with, sessionProposal.sessionId, endUserState); } } @@ -1020,6 +1043,11 @@ public class JingleConnectionManager extends AbstractConnectionManager { PersistableSessionId.of(id), new TerminatedRtpSession(state, media)); } + void setTerminalSessionState(final RtpSessionProposal proposal, final RtpEndUserState state) { + this.terminatedSessions.put( + PersistableSessionId.of(proposal), new TerminatedRtpSession(state, proposal.media)); + } + public TerminatedRtpSession getTerminalSessionState(final Jid with, final String sessionId) { return this.terminatedSessions.getIfPresent(new PersistableSessionId(with, sessionId)); } @@ -1033,10 +1061,14 @@ public class JingleConnectionManager extends AbstractConnectionManager { this.sessionId = sessionId; } - public static PersistableSessionId of(AbstractJingleConnection.Id id) { + public static PersistableSessionId of(final AbstractJingleConnection.Id id) { return new PersistableSessionId(id.with, id.sessionId); } + public static PersistableSessionId of(final RtpSessionProposal proposal) { + return new PersistableSessionId(proposal.with, proposal.sessionId); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index ab965918568d28248bd83dc026278525a15b4a2b..bb7c70839ca10a025e06e0500d393f6ff66bfffd 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -2708,14 +2708,14 @@ public class JingleRtpConnection extends AbstractJingleConnection } @Override - public void onCallIntegrationShowIncomingCallUi() { + public synchronized void onCallIntegrationShowIncomingCallUi() { if (isTerminated()) { // there might be race conditions with the call integration service invoking this - // callback when the rtp session has already ended. It should be enough to just return - // instead of throwing an exception. however throwing an exception gives us a sense of - // if and how frequently this happens - throw new IllegalStateException( + // callback when the rtp session has already ended. + Log.w( + Config.LOGTAG, "CallIntegration requested incoming call UI but session was already terminated"); + return; } // TODO apparently this can be called too early as well? xmppConnectionService.getNotificationService().startRinging(id, getMedia()); From f22d19f7d07733c524730627b1dca72a7bdfaafb Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 30 Mar 2024 14:54:28 +0100 Subject: [PATCH 159/363] disable mds for bug fix release --- src/main/java/eu/siacs/conversations/Config.java | 2 ++ .../conversations/generator/AbstractGenerator.java | 10 ++++++---- .../eu/siacs/conversations/parser/MessageParser.java | 10 ++++++++-- .../eu/siacs/conversations/xmpp/XmppConnection.java | 4 +++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index da3628b6fcb4953a0dfdefd77ecfcb21d72662df..06434a20dabd7bffce187b6894c2f0eca8b66543 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -56,6 +56,8 @@ public final class Config { public static final boolean USE_RANDOM_RESOURCE_ON_EVERY_BIND = false; + public static final boolean MESSAGE_DISPLAYED_SYNCHRONIZATION = false; + public static final boolean ALLOW_NON_TLS_CONNECTIONS = false; // very dangerous. you should have a good reason to set this to true diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index 3f63edbd7dca8800af22b613884efcf4a6f805ab..905b08286149639b7dc4bea7ff97c63763e95105 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -24,7 +24,7 @@ import java.util.TimeZone; public abstract class AbstractGenerator { private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); - private final String[] FEATURES = { + private final String[] STATIC_FEATURES = { Namespace.JINGLE, Namespace.JINGLE_APPS_FILE_TRANSFER, Namespace.JINGLE_TRANSPORTS_S5B, @@ -40,8 +40,7 @@ public abstract class AbstractGenerator { Namespace.NICK + "+notify", "urn:xmpp:ping", "jabber:iq:version", - "http://jabber.org/protocol/chatstates", - Namespace.MDS_DISPLAYED + "+notify" + "http://jabber.org/protocol/chatstates" }; private final String[] MESSAGE_CONFIRMATION_FEATURES = { "urn:xmpp:chat-markers:0", "urn:xmpp:receipts" @@ -108,7 +107,10 @@ public abstract class AbstractGenerator { public List getFeatures(Account account) { final XmppConnection connection = account.getXmppConnection(); - final ArrayList features = new ArrayList<>(Arrays.asList(FEATURES)); + final ArrayList features = new ArrayList<>(Arrays.asList(STATIC_FEATURES)); + if (Config.MESSAGE_DISPLAYED_SYNCHRONIZATION) { + features.add(Namespace.MDS_DISPLAYED + "+notify"); + } if (mXmppConnectionService.confirmMessages()) { features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES)); } diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index ece8a78625f1224761487b93261db24b023909c1..a949b088d7d28442dc26c8200a3eef99ac1f6e60 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -271,11 +271,17 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece mXmppConnectionService.updateConversationUi(); } } - } else if (Namespace.MDS_DISPLAYED.equals(node) && account.getJid().asBareJid().equals(from)) { + } else if (Config.MESSAGE_DISPLAYED_SYNCHRONIZATION + && Namespace.MDS_DISPLAYED.equals(node) + && account.getJid().asBareJid().equals(from)) { final Element item = items.findChild("item"); mXmppConnectionService.processMdsItem(account, item); } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + " received pubsub notification for node=" + node); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + " received pubsub notification for node=" + + node); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index b29129ee2c657dfc32aa1314393ba5b4f0b7d01d..58f9e56446ef0ecaee1a83a850ed2f32f12df761 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -3074,7 +3074,9 @@ public class XmppConnection implements Runnable { } public boolean mds() { - return pepPublishOptions() && pepConfigNodeMax(); + return pepPublishOptions() + && pepConfigNodeMax() + && Config.MESSAGE_DISPLAYED_SYNCHRONIZATION; } public boolean mdsServerAssist() { From 429b190b8e7fb09fd9a24c7deb86779df96bb0bb Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 30 Mar 2024 15:00:32 +0100 Subject: [PATCH 160/363] version bump to 2.14.1 + changelog --- CHANGELOG.md | 6 ++++++ build.gradle | 4 ++-- fastlane/metadata/android/en-US/changelogs/4210404.txt | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/4210404.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index ccd607ce79c091921398b9b83a190c04173ff5cf..bcd66cb962e0000df1e03a4b71e2f9ca39fa04c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +### Version 2.14.1 + +* Fix A/V calls on Android 8 +* Fix race conditions in new call integration +* Fix video compression sticking around + ### Version 2.14.0 * Improve integration of A/V calls into the operating system diff --git a/build.gradle b/build.gradle index d7d71d99cbc8714430fbdef43d422ea856e1debe..70cecf8255abd4224c294930c330c4d790322938 100644 --- a/build.gradle +++ b/build.gradle @@ -97,8 +97,8 @@ android { defaultConfig { minSdkVersion 23 targetSdkVersion 34 - versionCode 42103 - versionName "2.14.0" + versionCode 42104 + versionName "2.14.1" archivesBaseName += "-$versionName" applicationId "eu.siacs.conversations" resValue "string", "applicationId", applicationId diff --git a/fastlane/metadata/android/en-US/changelogs/4210404.txt b/fastlane/metadata/android/en-US/changelogs/4210404.txt new file mode 100644 index 0000000000000000000000000000000000000000..0715795ad2aef418079b0bea633fd324c2ced77c --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/4210404.txt @@ -0,0 +1,3 @@ +* Fix A/V calls on Android 8 +* Fix race conditions in new call integration +* Fix video compression sticking around From 0e9f4e5265e61796cbad7f6e91a8690bf3f43bca Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 3 Apr 2024 08:33:21 +0200 Subject: [PATCH 161/363] Channel discovery service / okttp needs bundled letsencrypt too --- .../conversations/crypto/TrustManagers.java | 17 ++++++ .../services/ChannelDiscoveryService.java | 52 +++++++++++++++---- .../services/MemorizingTrustManager.java | 13 +---- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/crypto/TrustManagers.java b/src/main/java/eu/siacs/conversations/crypto/TrustManagers.java index 11fe182dde8449364c371b5005933f6a8e4d0d33..4fc11eecf349979d57a77b61c73a5015fcff7e18 100644 --- a/src/main/java/eu/siacs/conversations/crypto/TrustManagers.java +++ b/src/main/java/eu/siacs/conversations/crypto/TrustManagers.java @@ -1,17 +1,23 @@ package eu.siacs.conversations.crypto; +import android.content.Context; + import androidx.annotation.Nullable; import com.google.common.collect.Iterables; +import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.util.Arrays; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; +import eu.siacs.conversations.R; + public final class TrustManagers { private TrustManagers() { @@ -34,5 +40,16 @@ public final class TrustManagers { return createTrustManager(null); } + public static X509TrustManager defaultWithBundledLetsEncrypt(final Context context) + throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { + final BundledTrustManager bundleTrustManager = + BundledTrustManager.builder() + .loadKeyStore( + context.getResources().openRawResource(R.raw.letsencrypt), + "letsencrypt") + .build(); + return CombiningTrustManager.combineWithDefault(bundleTrustManager); + } + } diff --git a/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java b/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java index 2f9553bfce714bda47f3e438da7fa96c76e65ebf..3ca6bfde4cfecd9f76eebb2e6d13f055c7b7f418 100644 --- a/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java +++ b/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java @@ -1,5 +1,8 @@ package eu.siacs.conversations.services; +import static eu.siacs.conversations.utils.Random.SECURE_RANDOM; + +import android.os.Build; import android.util.Log; import androidx.annotation.NonNull; @@ -8,34 +11,45 @@ import com.google.common.base.Strings; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - import eu.siacs.conversations.Config; +import eu.siacs.conversations.crypto.TrustManagers; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Room; import eu.siacs.conversations.http.HttpConnectionManager; import eu.siacs.conversations.http.services.MuclumbusService; import eu.siacs.conversations.parser.IqParser; +import eu.siacs.conversations.utils.TLSSocketFactory; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.stanzas.IqPacket; + import okhttp3.OkHttpClient; import okhttp3.ResponseBody; + import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; + public class ChannelDiscoveryService { private final XmppConnectionService service; @@ -55,6 +69,24 @@ public class ChannelDiscoveryService { return; } final OkHttpClient.Builder builder = HttpConnectionManager.OK_HTTP_CLIENT.newBuilder(); + try { + final X509TrustManager trustManager; + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) { + trustManager = TrustManagers.defaultWithBundledLetsEncrypt(service); + } else { + trustManager = TrustManagers.createDefaultTrustManager(); + } + final SSLSocketFactory socketFactory = + new TLSSocketFactory(new X509TrustManager[] {trustManager}, SECURE_RANDOM); + builder.sslSocketFactory(socketFactory, trustManager); + } catch (final IOException + | KeyManagementException + | NoSuchAlgorithmException + | KeyStoreException + | CertificateException e) { + Log.d(Config.LOGTAG, "not reconfiguring service to work with bundled LetsEncrypt"); + throw new RuntimeException(e); + } if (service.useTorToConnect()) { builder.proxy(HttpConnectionManager.getProxy()); } diff --git a/src/main/java/eu/siacs/conversations/services/MemorizingTrustManager.java b/src/main/java/eu/siacs/conversations/services/MemorizingTrustManager.java index 81cfb951fb279f5940f4cdd2a4931a10fad422a6..d05fa4ac3dfe2b98d3d61f99427a12eea60a4f08 100644 --- a/src/main/java/eu/siacs/conversations/services/MemorizingTrustManager.java +++ b/src/main/java/eu/siacs/conversations/services/MemorizingTrustManager.java @@ -176,7 +176,7 @@ public class MemorizingTrustManager { this.appTrustManager = getTrustManager(appKeyStore); try { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) { - this.defaultTrustManager = defaultWithBundledLetsEncrypt(context); + this.defaultTrustManager = TrustManagers.defaultWithBundledLetsEncrypt(context); } else { this.defaultTrustManager = TrustManagers.createDefaultTrustManager(); } @@ -188,17 +188,6 @@ public class MemorizingTrustManager { } } - private static X509TrustManager defaultWithBundledLetsEncrypt(final Context context) - throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { - final BundledTrustManager bundleTrustManager = - BundledTrustManager.builder() - .loadKeyStore( - context.getResources().openRawResource(R.raw.letsencrypt), - "letsencrypt") - .build(); - return CombiningTrustManager.combineWithDefault(bundleTrustManager); - } - private static boolean isIp(final String server) { return server != null && (PATTERN_IPV4.matcher(server).matches() From a619cfe0d8c2bea4c3b6d1987a5f76a671e14228 Mon Sep 17 00:00:00 2001 From: Jonas Lochmann Date: Mon, 1 Apr 2024 02:00:00 +0200 Subject: [PATCH 162/363] Add MY_PACKAGE_REPLACED to the EventReceiver This ensures that Conversations is restarted after a update without waiting for the next eu.siacs.conversations.POST_CONNECTIVITY_CHANGE from the AlarmManager --- src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 6233770df0d66e352b7cf7ae61d8d0f75eabf3d2..b6e8a64576601d8ef9eea0f280b6391b3cbfa8b8 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -145,6 +145,7 @@ android:exported="false"> + From b90906b973a5b1c79bb194adf4a504308a9e2572 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 3 Apr 2024 20:51:48 +0200 Subject: [PATCH 163/363] make RtpSessionActivity onNewIntent and onBackendConnected run through the same code --- .../ui/ConversationFragment.java | 14 +++-- .../conversations/ui/RtpSessionActivity.java | 59 +++++++------------ 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 763bc37600639c2830c0a6a2b25bf7dd2a38d071..ae46b80759b5b9cc7c4f4a270a8813354d1f9a1d 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -127,6 +127,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleFileTransferConnection; import eu.siacs.conversations.xmpp.jingle.Media; import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession; import eu.siacs.conversations.xmpp.jingle.RtpCapability; +import eu.siacs.conversations.xmpp.jingle.RtpEndUserState; import org.jetbrains.annotations.NotNull; @@ -1556,20 +1557,25 @@ public class ConversationFragment extends XmppFragment if (ongoingRtpSession.isPresent()) { final OngoingRtpSession id = ongoingRtpSession.get(); final Intent intent = new Intent(getActivity(), RtpSessionActivity.class); + intent.setAction(Intent.ACTION_VIEW); intent.putExtra( RtpSessionActivity.EXTRA_ACCOUNT, id.getAccount().getJid().asBareJid().toEscapedString()); intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.getWith().toEscapedString()); if (id instanceof AbstractJingleConnection) { - intent.setAction(Intent.ACTION_VIEW); intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.getSessionId()); startActivity(intent); } else if (id instanceof JingleConnectionManager.RtpSessionProposal proposal) { - if (proposal.media.contains(Media.VIDEO)) { - intent.setAction(RtpSessionActivity.ACTION_MAKE_VIDEO_CALL); + if (Media.audioOnly(proposal.media)) { + intent.putExtra( + RtpSessionActivity.EXTRA_LAST_ACTION, + RtpSessionActivity.ACTION_MAKE_VOICE_CALL); } else { - intent.setAction(RtpSessionActivity.ACTION_MAKE_VOICE_CALL); + intent.putExtra( + RtpSessionActivity.EXTRA_LAST_ACTION, + RtpSessionActivity.ACTION_MAKE_VIDEO_CALL); } + intent.putExtra(RtpSessionActivity.EXTRA_PROPOSED_SESSION_ID, proposal.sessionId); startActivity(intent); } } diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 871b7c259dd4be5b7b6598e0c631b89975fc9369..97b2ccb52b54857b5b52136b419f44799ab11003 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -460,6 +460,9 @@ public class RtpSessionActivity extends XmppActivity public void onNewIntent(final Intent intent) { Log.d(Config.LOGTAG, this.getClass().getName() + ".onNewIntent()"); super.onNewIntent(intent); + if (intent == null) { + return; + } setIntent(intent); if (xmppConnectionService == null) { Log.d( @@ -467,32 +470,21 @@ public class RtpSessionActivity extends XmppActivity "RtpSessionActivity: background service wasn't bound in onNewIntent()"); return; } - final Account account = extractAccount(intent); - final String action = intent.getAction(); - final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH)); - final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID); - if (sessionId != null) { - Log.d(Config.LOGTAG, "reinitializing from onNewIntent()"); - if (initializeActivityWithRunningRtpSession(account, with, sessionId)) { - return; - } - if (ACTION_ACCEPT_CALL.equals(intent.getAction())) { - Log.d(Config.LOGTAG, "accepting call from onNewIntent()"); - requestPermissionsAndAcceptCall(); - resetIntent(intent.getExtras()); - } - } else if (asList(ACTION_MAKE_VIDEO_CALL, ACTION_MAKE_VOICE_CALL).contains(action)) { - proposeJingleRtpSession(account, with, actionToMedia(action)); - setWith(account.getRoster().getContact(with), null); - } else { - throw new IllegalStateException("received onNewIntent without sessionId"); - } + initializeWithIntent(Event.ON_NEW_INTENT, intent); } @Override void onBackendConnected() { - final Intent intent = getIntent(); + final var intent = getIntent(); + if (intent == null) { + return; + } + initializeWithIntent(Event.ON_BACKEND_CONNECTED, intent); + } + + private void initializeWithIntent(final Event event, @NonNull final Intent intent) { final String action = intent.getAction(); + Log.d(Config.LOGTAG, "initializeWithIntent(" + event + "," + action + ")"); final Account account = extractAccount(intent); final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH)); final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID); @@ -505,9 +497,6 @@ public class RtpSessionActivity extends XmppActivity requestPermissionsAndAcceptCall(); resetIntent(intent.getExtras()); } - } else if (asList(ACTION_MAKE_VIDEO_CALL, ACTION_MAKE_VOICE_CALL).contains(action)) { - proposeJingleRtpSession(account, with, actionToMedia(action)); - setWith(account.getRoster().getContact(with), null); } else if (Intent.ACTION_VIEW.equals(action)) { final String proposedSessionId = intent.getStringExtra(EXTRA_PROPOSED_SESSION_ID); final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession = @@ -520,8 +509,6 @@ public class RtpSessionActivity extends XmppActivity return; } final String extraLastState = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE); - final String lastAction = intent.getStringExtra(EXTRA_LAST_ACTION); - final Set media = actionToMedia(lastAction); final RtpEndUserState state = extraLastState == null ? null : RtpEndUserState.valueOf(extraLastState); if (state != null) { @@ -541,6 +528,8 @@ public class RtpSessionActivity extends XmppActivity if (END_CARD.contains(state)) { return; } + final String lastAction = intent.getStringExtra(EXTRA_LAST_ACTION); + final Set media = actionToMedia(lastAction); if (xmppConnectionService .getJingleConnectionManager() .hasMatchingProposal(account, with)) { @@ -567,19 +556,6 @@ public class RtpSessionActivity extends XmppActivity } } - private void proposeJingleRtpSession( - final Account account, final Jid with, final Set media) { - if (with.isBareJid()) { - xmppConnectionService - .getJingleConnectionManager() - .proposeJingleRtpSession(account, with, media); - } else { - throw new IllegalStateException( - "We should not be initializing direct calls from the RtpSessionActivity. Go through CallIntegrationConnectionService.placeCall instead!"); - } - putScreenInCallMode(media); - } - @Override public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { @@ -1526,4 +1502,9 @@ public class RtpSessionActivity extends XmppActivity private static boolean emptyReference(final WeakReference weakReference) { return weakReference == null || weakReference.get() == null; } + + private enum Event { + ON_BACKEND_CONNECTED, + ON_NEW_INTENT + } } From c294a24f4e470baaea23aebd2e3d579d5b1cd1ff Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 4 Apr 2024 09:57:01 +0200 Subject: [PATCH 164/363] add some call integration debug aids --- build.gradle | 2 +- .../CallIntegrationConnectionService.java | 1 + .../conversations/ui/AboutPreference.java | 23 ++++++++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 70cecf8255abd4224c294930c330c4d790322938..6b36db34aa92a7344faa46d9664c69c2a46666f0 100644 --- a/build.gradle +++ b/build.gradle @@ -82,7 +82,7 @@ dependencies { implementation 'com.google.guava:guava:32.1.3-android' quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.13.28' - implementation 'im.conversations.webrtc:webrtc-android:119.0.0' + implementation 'im.conversations.webrtc:webrtc-android:119.0.1' } ext { diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java index e86f6c4f8f6f1d7a20a310bb062331ff45c5ef64..ce4d9fb8a192ebd1a8b5c9e403a7fbc595e258a9 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java @@ -357,6 +357,7 @@ public class CallIntegrationConnectionService extends ConnectionService { try { service.getSystemService(TelecomManager.class).placeCall(address, extras); } catch (final SecurityException e) { + Log.e(Config.LOGTAG, "call integration not available", e); Toast.makeText(service, R.string.call_integration_not_available, Toast.LENGTH_LONG) .show(); } diff --git a/src/main/java/eu/siacs/conversations/ui/AboutPreference.java b/src/main/java/eu/siacs/conversations/ui/AboutPreference.java index f2cd1e34855e87d3a63c462e1b54edccad5c3c08..acfa0be8c0f733bb0506ccad37fb5ab1401f6d83 100644 --- a/src/main/java/eu/siacs/conversations/ui/AboutPreference.java +++ b/src/main/java/eu/siacs/conversations/ui/AboutPreference.java @@ -2,26 +2,28 @@ package eu.siacs.conversations.ui; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.preference.Preference; import android.util.AttributeSet; +import com.google.common.base.Strings; + import eu.siacs.conversations.BuildConfig; import eu.siacs.conversations.R; -import eu.siacs.conversations.utils.PhoneHelper; public class AboutPreference extends Preference { - public AboutPreference(final Context context, final AttributeSet attrs, final int defStyle) { - super(context, attrs, defStyle); + public AboutPreference(final Context context, final AttributeSet attrs, final int defStyle) { + super(context, attrs, defStyle); setSummaryAndTitle(context); - } + } - public AboutPreference(final Context context, final AttributeSet attrs) { - super(context, attrs); - setSummaryAndTitle(context); - } + public AboutPreference(final Context context, final AttributeSet attrs) { + super(context, attrs); + setSummaryAndTitle(context); + } - private void setSummaryAndTitle(final Context context) { - setSummary(String.format("%s %s", BuildConfig.APP_NAME, BuildConfig.VERSION_NAME)); + private void setSummaryAndTitle(final Context context) { + setSummary(String.format("%s%s %s (%s)", BuildConfig.APP_NAME, BuildConfig.VERSION_NAME, im.conversations.webrtc.BuildConfig.WEBRTC_VERSION, Strings.nullToEmpty(Build.DEVICE))); setTitle(context.getString(R.string.title_activity_about_x, BuildConfig.APP_NAME)); } @@ -32,4 +34,3 @@ public class AboutPreference extends Preference { getContext().startActivity(intent); } } - From 4968bde77468b609298cd179341c36cda12c0130 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 4 Apr 2024 11:07:53 +0200 Subject: [PATCH 165/363] version bump to 2.14.2 + changelog --- CHANGELOG.md | 5 +++++ build.gradle | 4 ++-- fastlane/metadata/android/en-US/changelogs/4210504.txt | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/4210504.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index bcd66cb962e0000df1e03a4b71e2f9ca39fa04c0..86167b5a4597088605fc2ff2a3a5772a37af6ff1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +### Version 2.14.2 + +* Restore access to Channel Discovery for Android 6+7 +* Improve logging for failed call integration + ### Version 2.14.1 * Fix A/V calls on Android 8 diff --git a/build.gradle b/build.gradle index 6b36db34aa92a7344faa46d9664c69c2a46666f0..908b80c17704a1fbe77a1eb9f56b4faebbe58aec 100644 --- a/build.gradle +++ b/build.gradle @@ -97,8 +97,8 @@ android { defaultConfig { minSdkVersion 23 targetSdkVersion 34 - versionCode 42104 - versionName "2.14.1" + versionCode 42105 + versionName "2.14.2" archivesBaseName += "-$versionName" applicationId "eu.siacs.conversations" resValue "string", "applicationId", applicationId diff --git a/fastlane/metadata/android/en-US/changelogs/4210504.txt b/fastlane/metadata/android/en-US/changelogs/4210504.txt new file mode 100644 index 0000000000000000000000000000000000000000..b4c432acc363e18b06d2e4d41b26411e548e4f1d --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/4210504.txt @@ -0,0 +1,2 @@ +* Restore access to Channel Discovery for Android 6+7 +* Improve logging for failed call integration From 6e432481353442d7192e94c77ba259b5e31d0ef6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 4 Apr 2024 11:38:27 +0200 Subject: [PATCH 166/363] apply Material 3 theme to all activites --- build.gradle | 2 +- src/conversations/AndroidManifest.xml | 1 + .../services/ImportBackupService.java | 4 +- .../ui/EasyOnboardingInviteActivity.java | 68 ++- .../ui/ImportBackupActivity.java | 21 +- .../conversations/ui/MagicCreateActivity.java | 141 +++--- .../ui/ManageAccountActivity.java | 111 +++-- .../conversations/ui/PickServerActivity.java | 15 +- .../ui/ShareViaAccountActivity.java | 9 - .../conversations/ui/WelcomeActivity.java | 94 ++-- .../ui/adapter/BackupFileAdapter.java | 13 +- .../drawable-hdpi/ic_unarchive_white_24dp.png | Bin 258 -> 0 bytes .../drawable-mdpi/ic_unarchive_white_24dp.png | Bin 181 -> 0 bytes .../ic_unarchive_white_24dp.png | Bin 273 -> 0 bytes .../ic_unarchive_white_24dp.png | Bin 391 -> 0 bytes .../ic_unarchive_white_24dp.png | Bin 503 -> 0 bytes .../res/layout/activity_easy_invite.xml | 32 +- .../res/layout/activity_import_backup.xml | 27 +- ...c_create.xml => activity_magic_create.xml} | 54 ++- .../res/layout/activity_pick_server.xml | 38 +- .../res/layout/activity_welcome.xml | 41 +- .../res/layout/dialog_enter_password.xml | 32 +- .../res/menu/easy_onboarding_invite.xml | 2 +- src/conversations/res/menu/manageaccounts.xml | 53 ++- src/conversations/res/menu/welcome_menu.xml | 2 +- .../res/values/colors-themed.xml | 63 +++ src/main/AndroidManifest.xml | 28 +- .../java/eu/siacs/conversations/Config.java | 2 - .../eu/siacs/conversations/Conversations.java | 67 +++ .../entities/RtpSessionStatus.java | 10 +- .../services/BarcodeProvider.java | 10 +- .../services/ExportBackupService.java | 6 +- .../services/NotificationService.java | 57 +-- .../services/XmppConnectionService.java | 3 - .../siacs/conversations/ui/AboutActivity.java | 22 +- .../AbstractSearchableListItemActivity.java | 6 +- .../conversations/ui/ActionBarActivity.java | 2 +- .../eu/siacs/conversations/ui/Activities.java | 47 ++ .../siacs/conversations/ui/BaseActivity.java | 53 +++ .../conversations/ui/BlockContactDialog.java | 4 +- .../ui/ChangePasswordActivity.java | 98 ++-- .../ui/ChannelDiscoveryActivity.java | 28 +- ...hooseAccountForProfilePictureActivity.java | 25 +- .../ui/ChooseContactActivity.java | 24 +- .../ui/ConferenceDetailsActivity.java | 47 +- .../ui/ContactDetailsActivity.java | 32 +- .../ui/ConversationActivity.java | 6 - .../ui/ConversationFragment.java | 24 +- .../ui/ConversationsActivity.java | 45 +- .../ui/ConversationsOverviewFragment.java | 12 +- .../ui/CreatePrivateGroupChatDialog.java | 12 +- .../ui/CreatePublicChannelDialog.java | 25 +- .../conversations/ui/EditAccountActivity.java | 45 +- .../conversations/ui/EnterJidDialog.java | 58 +-- .../ui/JoinConferenceDialog.java | 9 +- .../conversations/ui/LocationActivity.java | 5 +- .../ui/MediaBrowserActivity.java | 1 + .../conversations/ui/MemorizingActivity.java | 8 +- .../conversations/ui/MucUsersActivity.java | 3 +- .../siacs/conversations/ui/OmemoActivity.java | 20 +- ...ublishGroupChatProfilePictureActivity.java | 2 + .../ui/PublishProfilePictureActivity.java | 15 +- .../conversations/ui/RecordingActivity.java | 17 +- .../conversations/ui/RtpSessionActivity.java | 37 +- .../siacs/conversations/ui/ScanActivity.java | 1 - .../conversations/ui/SearchActivity.java | 18 +- .../conversations/ui/SettingsActivity.java | 90 ++-- .../ui/ShareLocationActivity.java | 39 +- .../conversations/ui/ShareWithActivity.java | 120 +++-- .../conversations/ui/ShortcutActivity.java | 2 +- .../ui/ShowLocationActivity.java | 2 + .../ui/StartConversationActivity.java | 60 ++- .../conversations/ui/TrustKeysActivity.java | 39 +- .../conversations/ui/UriHandlerActivity.java | 2 +- .../siacs/conversations/ui/XmppActivity.java | 95 ++-- .../ui/adapter/AccountAdapter.java | 26 +- .../adapter/ChannelSearchResultAdapter.java | 14 +- .../ui/adapter/ConversationAdapter.java | 115 +---- .../ui/adapter/KnownHostsAdapter.java | 57 ++- .../ui/adapter/ListItemAdapter.java | 30 +- .../ui/adapter/MediaAdapter.java | 180 +++++--- .../ui/adapter/MediaPreviewAdapter.java | 83 ++-- .../ui/adapter/MessageAdapter.java | 290 +++++++----- .../conversations/ui/adapter/UserAdapter.java | 10 +- .../ui/adapter/UserPreviewAdapter.java | 47 +- .../ui/forms/FormBooleanFieldWrapper.java | 80 ---- .../ui/forms/FormFieldFactory.java | 30 -- .../ui/forms/FormFieldWrapper.java | 93 ---- .../ui/forms/FormJidSingleFieldWrapper.java | 43 -- .../ui/forms/FormTextFieldWrapper.java | 97 ---- .../conversations/ui/forms/FormWrapper.java | 72 --- .../conversations/ui/service/AudioPlayer.java | 170 ++++--- .../conversations/ui/util/ActionBarUtil.java | 88 ---- .../conversations/ui/util/Attachment.java | 83 +++- .../ui/util/ConversationMenuConfigurator.java | 8 +- .../ui/util/MucDetailsContextMenuHelper.java | 4 +- .../ui/util/PresenceSelector.java | 8 +- .../conversations/ui/util/SendButtonTool.java | 236 ++++------ .../ui/util/StyledAttributes.java | 59 --- .../conversations/ui/util/ToolbarUtils.java | 166 +++++++ .../widget/ImmediateAutoCompleteTextView.java | 4 +- .../conversations/ui/widget/ScannerView.java | 19 +- .../ui/widget/SwipeRefreshListFragment.java | 5 +- .../ui/widget/UnreadCountCustomView.java | 9 +- .../conversations/utils/AccountUtils.java | 6 +- .../conversations/utils/ExceptionHelper.java | 7 +- .../utils/IrregularUnicodeDetector.java | 7 +- .../siacs/conversations/utils/MimeUtils.java | 2 + .../conversations/utils/StylingHelper.java | 44 +- .../conversations/utils/ThemeHelper.java | 117 ----- .../drawable-hdpi/baseline_tour_black_48.png | Bin 369 -> 0 bytes .../drawable-hdpi/baseline_tour_white_48.png | Bin 372 -> 0 bytes .../res/drawable-hdpi/date_bubble_grey.9.png | Bin 657 -> 0 bytes .../res/drawable-hdpi/date_bubble_white.9.png | Bin 689 -> 0 bytes .../ic_account_box_white_24dp.png | Bin 337 -> 0 bytes .../res/drawable-hdpi/ic_action_reply.png | Bin 462 -> 0 bytes .../res/drawable-hdpi/ic_add_white_24dp.png | Bin 223 -> 0 bytes .../drawable-hdpi/ic_android_black_48dp.png | Bin 519 -> 0 bytes .../drawable-hdpi/ic_android_white_48dp.png | Bin 536 -> 0 bytes .../ic_announcement_white_24dp.png | Bin 251 -> 0 bytes .../drawable-hdpi/ic_archive_black_48dp.png | Bin 377 -> 0 bytes .../drawable-hdpi/ic_archive_white_24dp.png | Bin 247 -> 0 bytes .../drawable-hdpi/ic_archive_white_48dp.png | Bin 390 -> 0 bytes .../res/drawable-hdpi/ic_attach_camera.png | Bin 1235 -> 0 bytes .../drawable-hdpi/ic_attach_camera_white.png | Bin 636 -> 0 bytes .../res/drawable-hdpi/ic_attach_document.png | Bin 1060 -> 0 bytes .../ic_attach_document_white.png | Bin 393 -> 0 bytes .../ic_attach_file_white_24dp.png | Bin 452 -> 0 bytes .../res/drawable-hdpi/ic_attach_location.png | Bin 1280 -> 0 bytes .../ic_attach_location_white.png | Bin 665 -> 0 bytes .../res/drawable-hdpi/ic_attach_photo.png | Bin 1117 -> 0 bytes .../drawable-hdpi/ic_attach_photo_white.png | Bin 463 -> 0 bytes .../res/drawable-hdpi/ic_attach_record.png | Bin 1233 -> 0 bytes .../drawable-hdpi/ic_attach_record_white.png | Bin 614 -> 0 bytes .../res/drawable-hdpi/ic_attach_videocam.png | Bin 277 -> 0 bytes .../ic_attach_videocam_white.png | Bin 376 -> 0 bytes .../drawable-hdpi/ic_autorenew_white_24dp.png | Bin 489 -> 0 bytes .../drawable-hdpi/ic_backup_black_48dp.png | Bin 561 -> 0 bytes .../drawable-hdpi/ic_backup_white_48dp.png | Bin 589 -> 0 bytes .../res/drawable-hdpi/ic_block_white_24dp.png | Bin 606 -> 0 bytes .../ic_bluetooth_audio_black_24dp.png | Bin 420 -> 0 bytes .../res/drawable-hdpi/ic_book_black_48dp.png | Bin 278 -> 0 bytes .../res/drawable-hdpi/ic_book_white_48dp.png | Bin 283 -> 0 bytes .../res/drawable-hdpi/ic_call_black_24dp.png | Bin 326 -> 0 bytes .../drawable-hdpi/ic_call_end_white_48dp.png | Bin 553 -> 0 bytes .../drawable-hdpi/ic_call_made_black_18dp.png | Bin 159 -> 0 bytes .../drawable-hdpi/ic_call_made_white_18dp.png | Bin 174 -> 0 bytes .../ic_call_missed_black_18dp.png | Bin 179 -> 0 bytes .../ic_call_missed_outgoing_black_18dp.png | Bin 180 -> 0 bytes .../ic_call_missed_outgoing_white_18dp.png | Bin 180 -> 0 bytes .../ic_call_missed_white_18dp.png | Bin 191 -> 0 bytes .../ic_call_received_black_18dp.png | Bin 159 -> 0 bytes .../ic_call_received_white_18dp.png | Bin 169 -> 0 bytes .../res/drawable-hdpi/ic_call_white_24dp.png | Bin 340 -> 0 bytes .../res/drawable-hdpi/ic_call_white_48dp.png | Bin 597 -> 0 bytes .../ic_camera_alt_white_24dp.png | Bin 364 -> 0 bytes .../drawable-hdpi/ic_cancel_black_24dp.png | Bin 397 -> 0 bytes .../drawable-hdpi/ic_cancel_white_24dp.png | Bin 510 -> 0 bytes .../res/drawable-hdpi/ic_chat_white_24dp.png | Bin 168 -> 0 bytes .../res/drawable-hdpi/ic_clear_white_48dp.png | Bin 347 -> 0 bytes .../ic_cloud_download_white_24dp.png | Bin 353 -> 0 bytes .../ic_content_copy_white_24dp.png | Bin 203 -> 0 bytes .../res/drawable-hdpi/ic_crop_white_24dp.png | Bin 302 -> 0 bytes .../drawable-hdpi/ic_delete_black_24dp.png | Bin 155 -> 0 bytes .../drawable-hdpi/ic_delete_white_24dp.png | Bin 246 -> 0 bytes .../ic_description_black_48dp.png | Bin 279 -> 0 bytes .../ic_description_white_48dp.png | Bin 295 -> 0 bytes .../ic_directions_black_24dp.png | Bin 233 -> 0 bytes .../ic_directions_white_24dp.png | Bin 252 -> 0 bytes .../res/drawable-hdpi/ic_done_black_18dp.png | Bin 149 -> 0 bytes .../res/drawable-hdpi/ic_done_white_18dp.png | Bin 157 -> 0 bytes .../drawable-hdpi/ic_drafts_white_24dp.png | Bin 392 -> 0 bytes .../res/drawable-hdpi/ic_edit_black_24dp.png | Bin 202 -> 0 bytes .../res/drawable-hdpi/ic_edit_white_24dp.png | Bin 339 -> 0 bytes .../res/drawable-hdpi/ic_error_white_24dp.png | Bin 324 -> 0 bytes .../res/drawable-hdpi/ic_event_black_48dp.png | Bin 245 -> 0 bytes .../res/drawable-hdpi/ic_event_white_48dp.png | Bin 254 -> 0 bytes .../ic_file_download_white_24dp.png | Bin 277 -> 0 bytes .../ic_flip_camera_android_black_24dp.png | Bin 717 -> 0 bytes .../drawable-hdpi/ic_forward_white_24dp.png | Bin 139 -> 0 bytes .../drawable-hdpi/ic_gps_fixed_black_24dp.png | Bin 549 -> 0 bytes .../drawable-hdpi/ic_gps_fixed_white_24dp.png | Bin 546 -> 0 bytes .../ic_gps_not_fixed_black_24dp.png | Bin 472 -> 0 bytes .../ic_gps_not_fixed_white_24dp.png | Bin 468 -> 0 bytes .../drawable-hdpi/ic_group_add_white_24dp.png | Bin 396 -> 0 bytes .../res/drawable-hdpi/ic_group_white_24dp.png | Bin 417 -> 0 bytes .../drawable-hdpi/ic_headset_black_24dp.png | Bin 349 -> 0 bytes .../drawable-hdpi/ic_headset_black_48dp.png | Bin 586 -> 0 bytes .../drawable-hdpi/ic_headset_white_48dp.png | Bin 610 -> 0 bytes .../res/drawable-hdpi/ic_help_black_48dp.png | Bin 834 -> 0 bytes .../res/drawable-hdpi/ic_help_white_24dp.png | Bin 476 -> 0 bytes .../res/drawable-hdpi/ic_help_white_48dp.png | Bin 842 -> 0 bytes .../ic_hourglass_empty_white_24dp.png | Bin 159 -> 0 bytes .../res/drawable-hdpi/ic_image_black_48dp.png | Bin 424 -> 0 bytes .../res/drawable-hdpi/ic_image_white_48dp.png | Bin 450 -> 0 bytes .../res/drawable-hdpi/ic_input_white_24dp.png | Bin 226 -> 0 bytes .../drawable-hdpi/ic_link_off_white_24dp.png | Bin 415 -> 0 bytes .../res/drawable-hdpi/ic_link_white_24dp.png | Bin 430 -> 0 bytes .../res/drawable-hdpi/ic_lock_black_18dp.png | Bin 275 -> 0 bytes .../drawable-hdpi/ic_lock_open_white_24dp.png | Bin 400 -> 0 bytes .../res/drawable-hdpi/ic_lock_white_18dp.png | Bin 281 -> 0 bytes .../res/drawable-hdpi/ic_lock_white_24dp.png | Bin 399 -> 0 bytes .../res/drawable-hdpi/ic_mic_black_24dp.png | Bin 344 -> 0 bytes .../res/drawable-hdpi/ic_mic_black_48dp.png | Bin 581 -> 0 bytes .../drawable-hdpi/ic_mic_off_black_24dp.png | Bin 402 -> 0 bytes .../res/drawable-hdpi/ic_mic_white_48dp.png | Bin 606 -> 0 bytes .../drawable-hdpi/ic_mode_edit_black_18dp.png | Bin 264 -> 0 bytes .../drawable-hdpi/ic_mode_edit_white_18dp.png | Bin 299 -> 0 bytes .../ic_new_releases_black_24dp.png | Bin 384 -> 0 bytes .../ic_new_releases_white_24dp.png | Bin 395 -> 0 bytes .../ic_no_results_background_black.png | Bin 3757 -> 0 bytes .../ic_no_results_background_white.png | Bin 4625 -> 0 bytes .../ic_notifications_black_24dp.png | Bin 236 -> 0 bytes .../ic_notifications_none_black_24dp.png | Bin 295 -> 0 bytes .../ic_notifications_none_white80.png | Bin 574 -> 0 bytes .../ic_notifications_none_white_24dp.png | Bin 285 -> 0 bytes .../ic_notifications_off_black_24dp.png | Bin 390 -> 0 bytes .../ic_notifications_off_white80.png | Bin 762 -> 0 bytes .../ic_notifications_off_white_24dp.png | Bin 385 -> 0 bytes .../ic_notifications_paused_black_24dp.png | Bin 308 -> 0 bytes .../ic_notifications_paused_white80.png | Bin 610 -> 0 bytes .../ic_notifications_paused_white_24dp.png | Bin 309 -> 0 bytes .../ic_notifications_white80.png | Bin 471 -> 0 bytes .../ic_notifications_white_24dp.png | Bin 234 -> 0 bytes .../res/drawable-hdpi/ic_pause_black_36dp.png | Bin 123 -> 0 bytes .../res/drawable-hdpi/ic_pause_white_36dp.png | Bin 124 -> 0 bytes .../ic_person_add_white_24dp.png | Bin 383 -> 0 bytes .../drawable-hdpi/ic_person_black_48dp.png | Bin 428 -> 0 bytes .../drawable-hdpi/ic_person_white_48dp.png | Bin 440 -> 0 bytes .../ic_phone_in_talk_black_18dp.png | Bin 374 -> 0 bytes .../ic_phone_in_talk_white_18dp.png | Bin 393 -> 0 bytes .../ic_phone_in_talk_white_24dp.png | Bin 483 -> 0 bytes .../ic_play_arrow_black_36dp.png | Bin 236 -> 0 bytes .../ic_play_arrow_white_36dp.png | Bin 242 -> 0 bytes .../ic_play_circle_filled_white_48dp.png | Bin 666 -> 0 bytes src/main/res/drawable-hdpi/ic_profile.png | Bin 999 -> 0 bytes .../drawable-hdpi/ic_public_white_24dp.png | Bin 504 -> 0 bytes .../ic_qr_code_scan_white_24dp.png | Bin 461 -> 0 bytes .../ic_question_answer_white_24dp.png | Bin 192 -> 0 bytes .../drawable-hdpi/ic_refresh_black_24dp.png | Bin 391 -> 0 bytes .../drawable-hdpi/ic_refresh_white_24dp.png | Bin 387 -> 0 bytes .../drawable-hdpi/ic_replay_white_48dp.png | Bin 675 -> 0 bytes .../res/drawable-hdpi/ic_reply_white_24dp.png | Bin 253 -> 0 bytes .../res/drawable-hdpi/ic_room_black_48dp.png | Bin 683 -> 0 bytes .../res/drawable-hdpi/ic_room_white_24dp.png | Bin 494 -> 0 bytes .../res/drawable-hdpi/ic_room_white_48dp.png | Bin 675 -> 0 bytes .../res/drawable-hdpi/ic_save_black_24dp.png | Bin 240 -> 0 bytes .../res/drawable-hdpi/ic_save_white_24dp.png | Bin 247 -> 0 bytes .../ic_search_background_black.png | Bin 3220 -> 0 bytes .../ic_search_background_white.png | Bin 4077 -> 0 bytes .../drawable-hdpi/ic_search_white_24dp.png | Bin 504 -> 0 bytes .../res/drawable-hdpi/ic_send_cancel_away.png | Bin 1396 -> 0 bytes .../res/drawable-hdpi/ic_send_cancel_dnd.png | Bin 1587 -> 0 bytes .../drawable-hdpi/ic_send_cancel_offline.png | Bin 1206 -> 0 bytes .../ic_send_cancel_offline_dark.png | Bin 1163 -> 0 bytes .../ic_send_cancel_offline_white.png | Bin 1205 -> 0 bytes .../drawable-hdpi/ic_send_cancel_online.png | Bin 1550 -> 0 bytes .../drawable-hdpi/ic_send_file_offline.png | Bin 412 -> 0 bytes .../ic_send_file_offline_white.png | Bin 356 -> 0 bytes .../drawable-hdpi/ic_send_location_away.png | Bin 1159 -> 0 bytes .../drawable-hdpi/ic_send_location_dnd.png | Bin 1333 -> 0 bytes .../ic_send_location_offline.png | Bin 971 -> 0 bytes .../ic_send_location_offline_dark.png | Bin 945 -> 0 bytes .../ic_send_location_offline_white.png | Bin 1010 -> 0 bytes .../drawable-hdpi/ic_send_location_online.png | Bin 1345 -> 0 bytes .../res/drawable-hdpi/ic_send_photo_away.png | Bin 1203 -> 0 bytes .../res/drawable-hdpi/ic_send_photo_dnd.png | Bin 1383 -> 0 bytes .../drawable-hdpi/ic_send_photo_offline.png | Bin 987 -> 0 bytes .../ic_send_photo_offline_dark.png | Bin 978 -> 0 bytes .../ic_send_photo_offline_white.png | Bin 1033 -> 0 bytes .../drawable-hdpi/ic_send_photo_online.png | Bin 1402 -> 0 bytes .../drawable-hdpi/ic_send_picture_away.png | Bin 790 -> 0 bytes .../res/drawable-hdpi/ic_send_picture_dnd.png | Bin 884 -> 0 bytes .../drawable-hdpi/ic_send_picture_offline.png | Bin 657 -> 0 bytes .../ic_send_picture_offline_dark.png | Bin 639 -> 0 bytes .../ic_send_picture_offline_white.png | Bin 675 -> 0 bytes .../drawable-hdpi/ic_send_picture_online.png | Bin 887 -> 0 bytes .../res/drawable-hdpi/ic_send_text_away.png | Bin 982 -> 0 bytes .../res/drawable-hdpi/ic_send_text_dnd.png | Bin 1128 -> 0 bytes .../drawable-hdpi/ic_send_text_offline.png | Bin 800 -> 0 bytes .../ic_send_text_offline_dark.png | Bin 765 -> 0 bytes .../ic_send_text_offline_white.png | Bin 844 -> 0 bytes .../res/drawable-hdpi/ic_send_text_online.png | Bin 1121 -> 0 bytes .../drawable-hdpi/ic_send_videocam_away.png | Bin 487 -> 0 bytes .../drawable-hdpi/ic_send_videocam_dnd.png | Bin 540 -> 0 bytes .../ic_send_videocam_offline.png | Bin 435 -> 0 bytes .../ic_send_videocam_offline_white.png | Bin 447 -> 0 bytes .../drawable-hdpi/ic_send_videocam_online.png | Bin 540 -> 0 bytes .../res/drawable-hdpi/ic_send_voice_away.png | Bin 1017 -> 0 bytes .../res/drawable-hdpi/ic_send_voice_dnd.png | Bin 1161 -> 0 bytes .../drawable-hdpi/ic_send_voice_offline.png | Bin 844 -> 0 bytes .../ic_send_voice_offline_dark.png | Bin 824 -> 0 bytes .../ic_send_voice_offline_white.png | Bin 878 -> 0 bytes .../drawable-hdpi/ic_send_voice_online.png | Bin 1170 -> 0 bytes .../drawable-hdpi/ic_settings_black_24dp.png | Bin 453 -> 0 bytes .../drawable-hdpi/ic_settings_white_24dp.png | Bin 460 -> 0 bytes .../res/drawable-hdpi/ic_share_white_24dp.png | Bin 506 -> 0 bytes .../res/drawable-hdpi/ic_star_black_24dp.png | Bin 369 -> 0 bytes .../res/drawable-hdpi/ic_star_white_24dp.png | Bin 370 -> 0 bytes .../drawable-hdpi/ic_stat_alert_warning.png | Bin 757 -> 0 bytes .../ic_stat_communication_import_export.png | Bin 620 -> 0 bytes .../drawable-hdpi/ic_verified_fingerprint.png | Bin 1324 -> 0 bytes .../ic_verified_user_black_18dp.png | Bin 320 -> 0 bytes .../ic_verified_user_white_18dp.png | Bin 329 -> 0 bytes .../drawable-hdpi/ic_videocam_black_24dp.png | Bin 169 -> 0 bytes .../ic_videocam_off_black_24dp.png | Bin 260 -> 0 bytes .../drawable-hdpi/ic_videocam_white_24dp.png | Bin 173 -> 0 bytes .../drawable-hdpi/ic_voicemail_white_24dp.png | Bin 478 -> 0 bytes .../ic_volume_off_black_24dp.png | Bin 407 -> 0 bytes .../drawable-hdpi/ic_volume_up_black_24dp.png | Bin 364 -> 0 bytes .../drawable-hdpi/ic_warning_white_24dp.png | Bin 421 -> 0 bytes .../drawable-hdpi/ic_warning_white_48dp.png | Bin 714 -> 0 bytes src/main/res/drawable-hdpi/ic_wear_reply.png | Bin 518 -> 0 bytes .../message_bubble_received.9.png | Bin 772 -> 0 bytes .../message_bubble_received_dark.9.png | Bin 773 -> 0 bytes .../message_bubble_received_grey.9.png | Bin 750 -> 0 bytes .../message_bubble_received_warning.9.png | Bin 776 -> 0 bytes .../message_bubble_received_white.9.png | Bin 779 -> 0 bytes .../drawable-hdpi/message_bubble_sent.9.png | Bin 687 -> 0 bytes .../message_bubble_sent_grey.9.png | Bin 707 -> 0 bytes .../drawable-mdpi/baseline_tour_black_48.png | Bin 273 -> 0 bytes .../drawable-mdpi/baseline_tour_white_48.png | Bin 277 -> 0 bytes .../res/drawable-mdpi/date_bubble_grey.9.png | Bin 514 -> 0 bytes .../res/drawable-mdpi/date_bubble_white.9.png | Bin 525 -> 0 bytes .../ic_account_box_white_24dp.png | Bin 290 -> 0 bytes .../res/drawable-mdpi/ic_action_reply.png | Bin 343 -> 0 bytes .../res/drawable-mdpi/ic_add_white_24dp.png | Bin 174 -> 0 bytes .../drawable-mdpi/ic_android_black_48dp.png | Bin 343 -> 0 bytes .../drawable-mdpi/ic_android_white_48dp.png | Bin 356 -> 0 bytes .../ic_announcement_white_24dp.png | Bin 214 -> 0 bytes .../drawable-mdpi/ic_archive_black_48dp.png | Bin 261 -> 0 bytes .../drawable-mdpi/ic_archive_white_24dp.png | Bin 181 -> 0 bytes .../drawable-mdpi/ic_archive_white_48dp.png | Bin 267 -> 0 bytes .../res/drawable-mdpi/ic_attach_camera.png | Bin 1114 -> 0 bytes .../drawable-mdpi/ic_attach_camera_white.png | Bin 453 -> 0 bytes .../res/drawable-mdpi/ic_attach_document.png | Bin 1021 -> 0 bytes .../ic_attach_document_white.png | Bin 322 -> 0 bytes .../ic_attach_file_white_24dp.png | Bin 332 -> 0 bytes .../res/drawable-mdpi/ic_attach_location.png | Bin 1146 -> 0 bytes .../ic_attach_location_white.png | Bin 486 -> 0 bytes .../res/drawable-mdpi/ic_attach_photo.png | Bin 1066 -> 0 bytes .../drawable-mdpi/ic_attach_photo_white.png | Bin 395 -> 0 bytes .../res/drawable-mdpi/ic_attach_record.png | Bin 1115 -> 0 bytes .../drawable-mdpi/ic_attach_record_white.png | Bin 451 -> 0 bytes .../res/drawable-mdpi/ic_attach_videocam.png | Bin 215 -> 0 bytes .../ic_attach_videocam_white.png | Bin 308 -> 0 bytes .../drawable-mdpi/ic_autorenew_white_24dp.png | Bin 353 -> 0 bytes .../drawable-mdpi/ic_backup_black_48dp.png | Bin 386 -> 0 bytes .../drawable-mdpi/ic_backup_white_48dp.png | Bin 405 -> 0 bytes .../res/drawable-mdpi/ic_block_white_24dp.png | Bin 428 -> 0 bytes .../ic_bluetooth_audio_black_24dp.png | Bin 283 -> 0 bytes .../res/drawable-mdpi/ic_book_black_48dp.png | Bin 201 -> 0 bytes .../res/drawable-mdpi/ic_book_white_48dp.png | Bin 197 -> 0 bytes .../res/drawable-mdpi/ic_call_black_24dp.png | Bin 244 -> 0 bytes .../drawable-mdpi/ic_call_end_white_48dp.png | Bin 389 -> 0 bytes .../drawable-mdpi/ic_call_made_black_18dp.png | Bin 132 -> 0 bytes .../drawable-mdpi/ic_call_made_white_18dp.png | Bin 135 -> 0 bytes .../ic_call_missed_black_18dp.png | Bin 141 -> 0 bytes .../ic_call_missed_outgoing_black_18dp.png | Bin 134 -> 0 bytes .../ic_call_missed_outgoing_white_18dp.png | Bin 136 -> 0 bytes .../ic_call_missed_white_18dp.png | Bin 147 -> 0 bytes .../ic_call_received_black_18dp.png | Bin 133 -> 0 bytes .../ic_call_received_white_18dp.png | Bin 140 -> 0 bytes .../res/drawable-mdpi/ic_call_white_24dp.png | Bin 246 -> 0 bytes .../res/drawable-mdpi/ic_call_white_48dp.png | Bin 420 -> 0 bytes .../ic_camera_alt_white_24dp.png | Bin 240 -> 0 bytes .../drawable-mdpi/ic_cancel_black_24dp.png | Bin 291 -> 0 bytes .../drawable-mdpi/ic_cancel_white_24dp.png | Bin 393 -> 0 bytes .../res/drawable-mdpi/ic_chat_white_24dp.png | Bin 133 -> 0 bytes .../res/drawable-mdpi/ic_clear_white_48dp.png | Bin 257 -> 0 bytes .../ic_cloud_download_white_24dp.png | Bin 242 -> 0 bytes .../ic_content_copy_white_24dp.png | Bin 134 -> 0 bytes .../res/drawable-mdpi/ic_crop_white_24dp.png | Bin 214 -> 0 bytes .../drawable-mdpi/ic_delete_black_24dp.png | Bin 111 -> 0 bytes .../drawable-mdpi/ic_delete_white_24dp.png | Bin 197 -> 0 bytes .../ic_description_black_48dp.png | Bin 202 -> 0 bytes .../ic_description_white_48dp.png | Bin 214 -> 0 bytes .../ic_directions_black_24dp.png | Bin 181 -> 0 bytes .../ic_directions_white_24dp.png | Bin 191 -> 0 bytes .../res/drawable-mdpi/ic_done_black_18dp.png | Bin 138 -> 0 bytes .../res/drawable-mdpi/ic_done_white_18dp.png | Bin 144 -> 0 bytes .../drawable-mdpi/ic_drafts_white_24dp.png | Bin 284 -> 0 bytes .../res/drawable-mdpi/ic_edit_black_24dp.png | Bin 160 -> 0 bytes .../res/drawable-mdpi/ic_edit_white_24dp.png | Bin 272 -> 0 bytes .../res/drawable-mdpi/ic_error_white_24dp.png | Bin 232 -> 0 bytes .../res/drawable-mdpi/ic_event_black_48dp.png | Bin 188 -> 0 bytes .../res/drawable-mdpi/ic_event_white_48dp.png | Bin 193 -> 0 bytes .../ic_file_download_white_24dp.png | Bin 210 -> 0 bytes .../ic_flip_camera_android_black_24dp.png | Bin 472 -> 0 bytes .../drawable-mdpi/ic_forward_white_24dp.png | Bin 117 -> 0 bytes .../drawable-mdpi/ic_gps_fixed_black_24dp.png | Bin 341 -> 0 bytes .../drawable-mdpi/ic_gps_fixed_white_24dp.png | Bin 350 -> 0 bytes .../ic_gps_not_fixed_black_24dp.png | Bin 295 -> 0 bytes .../ic_gps_not_fixed_white_24dp.png | Bin 298 -> 0 bytes .../drawable-mdpi/ic_group_add_white_24dp.png | Bin 313 -> 0 bytes .../res/drawable-mdpi/ic_group_white_24dp.png | Bin 296 -> 0 bytes .../drawable-mdpi/ic_headset_black_24dp.png | Bin 230 -> 0 bytes .../drawable-mdpi/ic_headset_black_48dp.png | Bin 412 -> 0 bytes .../drawable-mdpi/ic_headset_white_48dp.png | Bin 433 -> 0 bytes .../res/drawable-mdpi/ic_help_black_48dp.png | Bin 579 -> 0 bytes .../res/drawable-mdpi/ic_help_white_24dp.png | Bin 304 -> 0 bytes .../res/drawable-mdpi/ic_help_white_48dp.png | Bin 585 -> 0 bytes .../ic_hourglass_empty_white_24dp.png | Bin 135 -> 0 bytes .../res/drawable-mdpi/ic_image_black_48dp.png | Bin 295 -> 0 bytes .../res/drawable-mdpi/ic_image_white_48dp.png | Bin 304 -> 0 bytes .../res/drawable-mdpi/ic_input_white_24dp.png | Bin 148 -> 0 bytes .../drawable-mdpi/ic_link_off_white_24dp.png | Bin 273 -> 0 bytes .../res/drawable-mdpi/ic_link_white_24dp.png | Bin 297 -> 0 bytes .../res/drawable-mdpi/ic_lock_black_18dp.png | Bin 206 -> 0 bytes .../drawable-mdpi/ic_lock_open_white_24dp.png | Bin 290 -> 0 bytes .../res/drawable-mdpi/ic_lock_white_18dp.png | Bin 211 -> 0 bytes .../res/drawable-mdpi/ic_lock_white_24dp.png | Bin 296 -> 0 bytes .../res/drawable-mdpi/ic_mic_black_24dp.png | Bin 232 -> 0 bytes .../res/drawable-mdpi/ic_mic_black_48dp.png | Bin 418 -> 0 bytes .../drawable-mdpi/ic_mic_off_black_24dp.png | Bin 271 -> 0 bytes .../res/drawable-mdpi/ic_mic_white_48dp.png | Bin 436 -> 0 bytes .../drawable-mdpi/ic_mode_edit_black_18dp.png | Bin 229 -> 0 bytes .../drawable-mdpi/ic_mode_edit_white_18dp.png | Bin 249 -> 0 bytes .../ic_new_releases_black_24dp.png | Bin 277 -> 0 bytes .../ic_new_releases_white_24dp.png | Bin 287 -> 0 bytes .../ic_no_results_background_black.png | Bin 2359 -> 0 bytes .../ic_no_results_background_white.png | Bin 3019 -> 0 bytes .../ic_notifications_black_24dp.png | Bin 179 -> 0 bytes .../ic_notifications_none_black_24dp.png | Bin 206 -> 0 bytes .../ic_notifications_none_white80.png | Bin 409 -> 0 bytes .../ic_notifications_none_white_24dp.png | Bin 207 -> 0 bytes .../ic_notifications_off_black_24dp.png | Bin 290 -> 0 bytes .../ic_notifications_off_white80.png | Bin 505 -> 0 bytes .../ic_notifications_off_white_24dp.png | Bin 279 -> 0 bytes .../ic_notifications_paused_black_24dp.png | Bin 235 -> 0 bytes .../ic_notifications_paused_white80.png | Bin 466 -> 0 bytes .../ic_notifications_paused_white_24dp.png | Bin 235 -> 0 bytes .../ic_notifications_white80.png | Bin 369 -> 0 bytes .../ic_notifications_white_24dp.png | Bin 177 -> 0 bytes .../res/drawable-mdpi/ic_pause_black_36dp.png | Bin 102 -> 0 bytes .../res/drawable-mdpi/ic_pause_white_36dp.png | Bin 105 -> 0 bytes .../ic_person_add_white_24dp.png | Bin 289 -> 0 bytes .../drawable-mdpi/ic_person_black_48dp.png | Bin 307 -> 0 bytes .../drawable-mdpi/ic_person_white_48dp.png | Bin 312 -> 0 bytes .../ic_phone_in_talk_black_18dp.png | Bin 253 -> 0 bytes .../ic_phone_in_talk_white_18dp.png | Bin 261 -> 0 bytes .../ic_phone_in_talk_white_24dp.png | Bin 325 -> 0 bytes .../ic_play_arrow_black_36dp.png | Bin 194 -> 0 bytes .../ic_play_arrow_white_36dp.png | Bin 195 -> 0 bytes .../ic_play_circle_filled_white_48dp.png | Bin 464 -> 0 bytes src/main/res/drawable-mdpi/ic_profile.png | Bin 622 -> 0 bytes .../drawable-mdpi/ic_public_white_24dp.png | Bin 339 -> 0 bytes .../ic_qr_code_scan_white_24dp.png | Bin 309 -> 0 bytes .../ic_question_answer_white_24dp.png | Bin 137 -> 0 bytes .../drawable-mdpi/ic_refresh_black_24dp.png | Bin 249 -> 0 bytes .../drawable-mdpi/ic_refresh_white_24dp.png | Bin 254 -> 0 bytes .../drawable-mdpi/ic_replay_white_48dp.png | Bin 457 -> 0 bytes .../res/drawable-mdpi/ic_reply_white_24dp.png | Bin 186 -> 0 bytes .../res/drawable-mdpi/ic_room_black_48dp.png | Bin 457 -> 0 bytes .../res/drawable-mdpi/ic_room_white_24dp.png | Bin 362 -> 0 bytes .../res/drawable-mdpi/ic_room_white_48dp.png | Bin 456 -> 0 bytes .../res/drawable-mdpi/ic_save_black_24dp.png | Bin 167 -> 0 bytes .../res/drawable-mdpi/ic_save_white_24dp.png | Bin 168 -> 0 bytes .../ic_search_background_black.png | Bin 2080 -> 0 bytes .../ic_search_background_white.png | Bin 2344 -> 0 bytes .../drawable-mdpi/ic_search_white_24dp.png | Bin 346 -> 0 bytes .../res/drawable-mdpi/ic_send_cancel_away.png | Bin 901 -> 0 bytes .../res/drawable-mdpi/ic_send_cancel_dnd.png | Bin 1025 -> 0 bytes .../drawable-mdpi/ic_send_cancel_offline.png | Bin 837 -> 0 bytes .../ic_send_cancel_offline_dark.png | Bin 786 -> 0 bytes .../ic_send_cancel_offline_white.png | Bin 799 -> 0 bytes .../drawable-mdpi/ic_send_cancel_online.png | Bin 1029 -> 0 bytes .../drawable-mdpi/ic_send_file_offline.png | Bin 282 -> 0 bytes .../ic_send_file_offline_white.png | Bin 248 -> 0 bytes .../drawable-mdpi/ic_send_location_away.png | Bin 784 -> 0 bytes .../drawable-mdpi/ic_send_location_dnd.png | Bin 917 -> 0 bytes .../ic_send_location_offline.png | Bin 673 -> 0 bytes .../ic_send_location_offline_dark.png | Bin 681 -> 0 bytes .../ic_send_location_offline_white.png | Bin 699 -> 0 bytes .../drawable-mdpi/ic_send_location_online.png | Bin 928 -> 0 bytes .../res/drawable-mdpi/ic_send_photo_away.png | Bin 776 -> 0 bytes .../res/drawable-mdpi/ic_send_photo_dnd.png | Bin 915 -> 0 bytes .../drawable-mdpi/ic_send_photo_offline.png | Bin 666 -> 0 bytes .../ic_send_photo_offline_dark.png | Bin 649 -> 0 bytes .../ic_send_photo_offline_white.png | Bin 683 -> 0 bytes .../drawable-mdpi/ic_send_photo_online.png | Bin 917 -> 0 bytes .../drawable-mdpi/ic_send_picture_away.png | Bin 512 -> 0 bytes .../res/drawable-mdpi/ic_send_picture_dnd.png | Bin 585 -> 0 bytes .../drawable-mdpi/ic_send_picture_offline.png | Bin 456 -> 0 bytes .../ic_send_picture_offline_dark.png | Bin 436 -> 0 bytes .../ic_send_picture_offline_white.png | Bin 475 -> 0 bytes .../drawable-mdpi/ic_send_picture_online.png | Bin 594 -> 0 bytes .../res/drawable-mdpi/ic_send_text_away.png | Bin 677 -> 0 bytes .../res/drawable-mdpi/ic_send_text_dnd.png | Bin 781 -> 0 bytes .../drawable-mdpi/ic_send_text_offline.png | Bin 542 -> 0 bytes .../ic_send_text_offline_dark.png | Bin 557 -> 0 bytes .../ic_send_text_offline_white.png | Bin 583 -> 0 bytes .../res/drawable-mdpi/ic_send_text_online.png | Bin 781 -> 0 bytes .../drawable-mdpi/ic_send_videocam_away.png | Bin 365 -> 0 bytes .../drawable-mdpi/ic_send_videocam_dnd.png | Bin 397 -> 0 bytes .../ic_send_videocam_offline.png | Bin 341 -> 0 bytes .../ic_send_videocam_offline_white.png | Bin 340 -> 0 bytes .../drawable-mdpi/ic_send_videocam_online.png | Bin 403 -> 0 bytes .../res/drawable-mdpi/ic_send_voice_away.png | Bin 679 -> 0 bytes .../res/drawable-mdpi/ic_send_voice_dnd.png | Bin 795 -> 0 bytes .../drawable-mdpi/ic_send_voice_offline.png | Bin 575 -> 0 bytes .../ic_send_voice_offline_dark.png | Bin 576 -> 0 bytes .../ic_send_voice_offline_white.png | Bin 606 -> 0 bytes .../drawable-mdpi/ic_send_voice_online.png | Bin 796 -> 0 bytes .../drawable-mdpi/ic_settings_black_24dp.png | Bin 322 -> 0 bytes .../drawable-mdpi/ic_settings_white_24dp.png | Bin 326 -> 0 bytes .../res/drawable-mdpi/ic_share_white_24dp.png | Bin 361 -> 0 bytes .../res/drawable-mdpi/ic_star_black_24dp.png | Bin 263 -> 0 bytes .../res/drawable-mdpi/ic_star_white_24dp.png | Bin 271 -> 0 bytes .../drawable-mdpi/ic_stat_alert_warning.png | Bin 425 -> 0 bytes .../ic_stat_communication_import_export.png | Bin 392 -> 0 bytes .../drawable-mdpi/ic_verified_fingerprint.png | Bin 1022 -> 0 bytes .../ic_verified_user_black_18dp.png | Bin 238 -> 0 bytes .../ic_verified_user_white_18dp.png | Bin 241 -> 0 bytes .../drawable-mdpi/ic_videocam_black_24dp.png | Bin 127 -> 0 bytes .../ic_videocam_off_black_24dp.png | Bin 193 -> 0 bytes .../drawable-mdpi/ic_videocam_white_24dp.png | Bin 131 -> 0 bytes .../drawable-mdpi/ic_voicemail_white_24dp.png | Bin 221 -> 0 bytes .../ic_volume_off_black_24dp.png | Bin 279 -> 0 bytes .../drawable-mdpi/ic_volume_up_black_24dp.png | Bin 235 -> 0 bytes .../drawable-mdpi/ic_warning_white_24dp.png | Bin 328 -> 0 bytes .../drawable-mdpi/ic_warning_white_48dp.png | Bin 364 -> 0 bytes src/main/res/drawable-mdpi/ic_wear_reply.png | Bin 429 -> 0 bytes .../message_bubble_received.9.png | Bin 596 -> 0 bytes .../message_bubble_received_dark.9.png | Bin 617 -> 0 bytes .../message_bubble_received_grey.9.png | Bin 595 -> 0 bytes .../message_bubble_received_warning.9.png | Bin 599 -> 0 bytes .../message_bubble_received_white.9.png | Bin 610 -> 0 bytes .../drawable-mdpi/message_bubble_sent.9.png | Bin 558 -> 0 bytes .../message_bubble_sent_grey.9.png | Bin 568 -> 0 bytes src/main/res/drawable-mdpi/play_gif_black.png | Bin 584 -> 0 bytes src/main/res/drawable-mdpi/play_gif_white.png | Bin 612 -> 0 bytes .../res/drawable-mdpi/play_video_black.png | Bin 3204 -> 0 bytes .../res/drawable-mdpi/play_video_white.png | Bin 3383 -> 0 bytes .../drawable-xhdpi/baseline_tour_black_48.png | Bin 451 -> 0 bytes .../drawable-xhdpi/baseline_tour_white_48.png | Bin 451 -> 0 bytes .../res/drawable-xhdpi/date_bubble_grey.9.png | Bin 739 -> 0 bytes .../drawable-xhdpi/date_bubble_white.9.png | Bin 769 -> 0 bytes .../ic_account_box_white_24dp.png | Bin 431 -> 0 bytes .../res/drawable-xhdpi/ic_action_reply.png | Bin 561 -> 0 bytes .../res/drawable-xhdpi/ic_add_white_24dp.png | Bin 198 -> 0 bytes .../drawable-xhdpi/ic_android_black_48dp.png | Bin 641 -> 0 bytes .../drawable-xhdpi/ic_android_white_48dp.png | Bin 665 -> 0 bytes .../ic_announcement_white_24dp.png | Bin 285 -> 0 bytes .../drawable-xhdpi/ic_archive_black_48dp.png | Bin 483 -> 0 bytes .../drawable-xhdpi/ic_archive_white_24dp.png | Bin 267 -> 0 bytes .../drawable-xhdpi/ic_archive_white_48dp.png | Bin 489 -> 0 bytes .../res/drawable-xhdpi/ic_attach_camera.png | Bin 1313 -> 0 bytes .../drawable-xhdpi/ic_attach_camera_white.png | Bin 757 -> 0 bytes .../res/drawable-xhdpi/ic_attach_document.png | Bin 1097 -> 0 bytes .../ic_attach_document_white.png | Bin 427 -> 0 bytes .../ic_attach_file_white_24dp.png | Bin 576 -> 0 bytes .../res/drawable-xhdpi/ic_attach_location.png | Bin 1372 -> 0 bytes .../ic_attach_location_white.png | Bin 766 -> 0 bytes .../res/drawable-xhdpi/ic_attach_photo.png | Bin 1169 -> 0 bytes .../drawable-xhdpi/ic_attach_photo_white.png | Bin 543 -> 0 bytes .../res/drawable-xhdpi/ic_attach_record.png | Bin 1310 -> 0 bytes .../drawable-xhdpi/ic_attach_record_white.png | Bin 689 -> 0 bytes .../res/drawable-xhdpi/ic_attach_videocam.png | Bin 276 -> 0 bytes .../ic_attach_videocam_white.png | Bin 378 -> 0 bytes .../ic_autorenew_white_24dp.png | Bin 604 -> 0 bytes .../drawable-xhdpi/ic_backup_black_48dp.png | Bin 733 -> 0 bytes .../drawable-xhdpi/ic_backup_white_48dp.png | Bin 770 -> 0 bytes .../drawable-xhdpi/ic_block_white_24dp.png | Bin 796 -> 0 bytes .../ic_bluetooth_audio_black_24dp.png | Bin 479 -> 0 bytes .../res/drawable-xhdpi/ic_book_black_48dp.png | Bin 340 -> 0 bytes .../res/drawable-xhdpi/ic_book_white_48dp.png | Bin 350 -> 0 bytes .../res/drawable-xhdpi/ic_call_black_24dp.png | Bin 408 -> 0 bytes .../drawable-xhdpi/ic_call_end_white_48dp.png | Bin 712 -> 0 bytes .../ic_call_made_black_18dp.png | Bin 174 -> 0 bytes .../ic_call_made_white_18dp.png | Bin 189 -> 0 bytes .../ic_call_missed_black_18dp.png | Bin 201 -> 0 bytes .../ic_call_missed_outgoing_black_18dp.png | Bin 188 -> 0 bytes .../ic_call_missed_outgoing_white_18dp.png | Bin 193 -> 0 bytes .../ic_call_missed_white_18dp.png | Bin 215 -> 0 bytes .../ic_call_received_black_18dp.png | Bin 175 -> 0 bytes .../ic_call_received_white_18dp.png | Bin 189 -> 0 bytes .../res/drawable-xhdpi/ic_call_white_24dp.png | Bin 420 -> 0 bytes .../res/drawable-xhdpi/ic_call_white_48dp.png | Bin 778 -> 0 bytes .../ic_camera_alt_white_24dp.png | Bin 446 -> 0 bytes .../drawable-xhdpi/ic_cancel_black_24dp.png | Bin 517 -> 0 bytes .../drawable-xhdpi/ic_cancel_white_24dp.png | Bin 645 -> 0 bytes .../res/drawable-xhdpi/ic_chat_white_24dp.png | Bin 205 -> 0 bytes .../drawable-xhdpi/ic_clear_white_48dp.png | Bin 436 -> 0 bytes .../ic_cloud_download_white_24dp.png | Bin 417 -> 0 bytes .../ic_content_copy_white_24dp.png | Bin 188 -> 0 bytes .../res/drawable-xhdpi/ic_crop_white_24dp.png | Bin 272 -> 0 bytes .../drawable-xhdpi/ic_delete_black_24dp.png | Bin 148 -> 0 bytes .../drawable-xhdpi/ic_delete_white_24dp.png | Bin 270 -> 0 bytes .../ic_description_black_48dp.png | Bin 355 -> 0 bytes .../ic_description_white_48dp.png | Bin 378 -> 0 bytes .../ic_directions_black_24dp.png | Bin 274 -> 0 bytes .../ic_directions_white_24dp.png | Bin 307 -> 0 bytes .../res/drawable-xhdpi/ic_done_black_18dp.png | Bin 177 -> 0 bytes .../res/drawable-xhdpi/ic_done_white_18dp.png | Bin 188 -> 0 bytes .../drawable-xhdpi/ic_drafts_white_24dp.png | Bin 509 -> 0 bytes .../res/drawable-xhdpi/ic_edit_black_24dp.png | Bin 222 -> 0 bytes .../res/drawable-xhdpi/ic_edit_white_24dp.png | Bin 378 -> 0 bytes .../drawable-xhdpi/ic_error_white_24dp.png | Bin 431 -> 0 bytes .../drawable-xhdpi/ic_event_black_48dp.png | Bin 316 -> 0 bytes .../drawable-xhdpi/ic_event_white_48dp.png | Bin 326 -> 0 bytes .../ic_file_download_white_24dp.png | Bin 282 -> 0 bytes .../ic_flip_camera_android_black_24dp.png | Bin 915 -> 0 bytes .../drawable-xhdpi/ic_forward_white_24dp.png | Bin 159 -> 0 bytes .../ic_gps_fixed_black_24dp.png | Bin 660 -> 0 bytes .../ic_gps_fixed_white_24dp.png | Bin 687 -> 0 bytes .../ic_gps_not_fixed_black_24dp.png | Bin 561 -> 0 bytes .../ic_gps_not_fixed_white_24dp.png | Bin 577 -> 0 bytes .../ic_group_add_white_24dp.png | Bin 484 -> 0 bytes .../drawable-xhdpi/ic_group_white_24dp.png | Bin 464 -> 0 bytes .../drawable-xhdpi/ic_headset_black_24dp.png | Bin 412 -> 0 bytes .../drawable-xhdpi/ic_headset_black_48dp.png | Bin 786 -> 0 bytes .../drawable-xhdpi/ic_headset_white_48dp.png | Bin 838 -> 0 bytes .../res/drawable-xhdpi/ic_help_black_48dp.png | Bin 1122 -> 0 bytes .../res/drawable-xhdpi/ic_help_white_24dp.png | Bin 585 -> 0 bytes .../res/drawable-xhdpi/ic_help_white_48dp.png | Bin 1132 -> 0 bytes .../ic_hourglass_empty_white_24dp.png | Bin 174 -> 0 bytes .../drawable-xhdpi/ic_image_black_48dp.png | Bin 548 -> 0 bytes .../drawable-xhdpi/ic_image_white_48dp.png | Bin 570 -> 0 bytes .../drawable-xhdpi/ic_input_white_24dp.png | Bin 219 -> 0 bytes .../drawable-xhdpi/ic_link_off_white_24dp.png | Bin 472 -> 0 bytes .../res/drawable-xhdpi/ic_link_white_24dp.png | Bin 494 -> 0 bytes .../res/drawable-xhdpi/ic_lock_black_18dp.png | Bin 308 -> 0 bytes .../ic_lock_open_white_24dp.png | Bin 513 -> 0 bytes .../res/drawable-xhdpi/ic_lock_white_18dp.png | Bin 309 -> 0 bytes .../res/drawable-xhdpi/ic_lock_white_24dp.png | Bin 465 -> 0 bytes .../res/drawable-xhdpi/ic_mic_black_24dp.png | Bin 418 -> 0 bytes .../res/drawable-xhdpi/ic_mic_black_48dp.png | Bin 773 -> 0 bytes .../drawable-xhdpi/ic_mic_off_black_24dp.png | Bin 454 -> 0 bytes .../res/drawable-xhdpi/ic_mic_white_48dp.png | Bin 819 -> 0 bytes .../ic_mode_edit_black_18dp.png | Bin 291 -> 0 bytes .../ic_mode_edit_white_18dp.png | Bin 351 -> 0 bytes .../ic_new_releases_black_24dp.png | Bin 480 -> 0 bytes .../ic_new_releases_white_24dp.png | Bin 498 -> 0 bytes .../ic_no_results_background_black.png | Bin 5368 -> 0 bytes .../ic_no_results_background_white.png | Bin 6250 -> 0 bytes .../ic_notifications_black_24dp.png | Bin 272 -> 0 bytes .../ic_notifications_none_black_24dp.png | Bin 349 -> 0 bytes .../ic_notifications_none_white80.png | Bin 706 -> 0 bytes .../ic_notifications_none_white_24dp.png | Bin 344 -> 0 bytes .../ic_notifications_off_black_24dp.png | Bin 492 -> 0 bytes .../ic_notifications_off_white80.png | Bin 1014 -> 0 bytes .../ic_notifications_off_white_24dp.png | Bin 493 -> 0 bytes .../ic_notifications_paused_black_24dp.png | Bin 351 -> 0 bytes .../ic_notifications_paused_white80.png | Bin 745 -> 0 bytes .../ic_notifications_paused_white_24dp.png | Bin 351 -> 0 bytes .../ic_notifications_white80.png | Bin 583 -> 0 bytes .../ic_notifications_white_24dp.png | Bin 265 -> 0 bytes .../drawable-xhdpi/ic_pause_black_36dp.png | Bin 109 -> 0 bytes .../drawable-xhdpi/ic_pause_white_36dp.png | Bin 92 -> 0 bytes .../ic_person_add_white_24dp.png | Bin 423 -> 0 bytes .../drawable-xhdpi/ic_person_black_48dp.png | Bin 565 -> 0 bytes .../drawable-xhdpi/ic_person_white_48dp.png | Bin 577 -> 0 bytes .../ic_phone_in_talk_black_18dp.png | Bin 477 -> 0 bytes .../ic_phone_in_talk_white_18dp.png | Bin 483 -> 0 bytes .../ic_phone_in_talk_white_24dp.png | Bin 601 -> 0 bytes .../ic_play_arrow_black_36dp.png | Bin 265 -> 0 bytes .../ic_play_arrow_white_36dp.png | Bin 283 -> 0 bytes .../ic_play_circle_filled_white_48dp.png | Bin 883 -> 0 bytes src/main/res/drawable-xhdpi/ic_profile.png | Bin 1374 -> 0 bytes .../drawable-xhdpi/ic_public_white_24dp.png | Bin 661 -> 0 bytes .../ic_qr_code_scan_white_24dp.png | Bin 444 -> 0 bytes .../ic_question_answer_white_24dp.png | Bin 195 -> 0 bytes .../drawable-xhdpi/ic_refresh_black_24dp.png | Bin 506 -> 0 bytes .../drawable-xhdpi/ic_refresh_white_24dp.png | Bin 509 -> 0 bytes .../drawable-xhdpi/ic_replay_white_48dp.png | Bin 908 -> 0 bytes .../drawable-xhdpi/ic_reply_white_24dp.png | Bin 306 -> 0 bytes .../res/drawable-xhdpi/ic_room_black_48dp.png | Bin 868 -> 0 bytes .../res/drawable-xhdpi/ic_room_white_24dp.png | Bin 587 -> 0 bytes .../res/drawable-xhdpi/ic_room_white_48dp.png | Bin 869 -> 0 bytes .../res/drawable-xhdpi/ic_save_black_24dp.png | Bin 264 -> 0 bytes .../res/drawable-xhdpi/ic_save_white_24dp.png | Bin 273 -> 0 bytes .../ic_search_background_black.png | Bin 4465 -> 0 bytes .../ic_search_background_white.png | Bin 5477 -> 0 bytes .../drawable-xhdpi/ic_search_white_24dp.png | Bin 591 -> 0 bytes .../drawable-xhdpi/ic_send_cancel_away.png | Bin 1724 -> 0 bytes .../res/drawable-xhdpi/ic_send_cancel_dnd.png | Bin 1958 -> 0 bytes .../drawable-xhdpi/ic_send_cancel_offline.png | Bin 1519 -> 0 bytes .../ic_send_cancel_offline_dark.png | Bin 1458 -> 0 bytes .../ic_send_cancel_offline_white.png | Bin 1494 -> 0 bytes .../drawable-xhdpi/ic_send_cancel_online.png | Bin 1972 -> 0 bytes .../drawable-xhdpi/ic_send_file_offline.png | Bin 429 -> 0 bytes .../ic_send_file_offline_white.png | Bin 380 -> 0 bytes .../drawable-xhdpi/ic_send_location_away.png | Bin 1507 -> 0 bytes .../drawable-xhdpi/ic_send_location_dnd.png | Bin 1753 -> 0 bytes .../ic_send_location_offline.png | Bin 1278 -> 0 bytes .../ic_send_location_offline_dark.png | Bin 1250 -> 0 bytes .../ic_send_location_offline_white.png | Bin 1311 -> 0 bytes .../ic_send_location_online.png | Bin 1773 -> 0 bytes .../res/drawable-xhdpi/ic_send_photo_away.png | Bin 1516 -> 0 bytes .../res/drawable-xhdpi/ic_send_photo_dnd.png | Bin 1756 -> 0 bytes .../drawable-xhdpi/ic_send_photo_offline.png | Bin 1247 -> 0 bytes .../ic_send_photo_offline_dark.png | Bin 1191 -> 0 bytes .../ic_send_photo_offline_white.png | Bin 1290 -> 0 bytes .../drawable-xhdpi/ic_send_photo_online.png | Bin 1758 -> 0 bytes .../drawable-xhdpi/ic_send_picture_away.png | Bin 899 -> 0 bytes .../drawable-xhdpi/ic_send_picture_dnd.png | Bin 1016 -> 0 bytes .../ic_send_picture_offline.png | Bin 799 -> 0 bytes .../ic_send_picture_offline_dark.png | Bin 752 -> 0 bytes .../ic_send_picture_offline_white.png | Bin 828 -> 0 bytes .../drawable-xhdpi/ic_send_picture_online.png | Bin 1006 -> 0 bytes .../res/drawable-xhdpi/ic_send_text_away.png | Bin 1270 -> 0 bytes .../res/drawable-xhdpi/ic_send_text_dnd.png | Bin 1430 -> 0 bytes .../drawable-xhdpi/ic_send_text_offline.png | Bin 1007 -> 0 bytes .../ic_send_text_offline_dark.png | Bin 958 -> 0 bytes .../ic_send_text_offline_white.png | Bin 1079 -> 0 bytes .../drawable-xhdpi/ic_send_text_online.png | Bin 1429 -> 0 bytes .../drawable-xhdpi/ic_send_videocam_away.png | Bin 502 -> 0 bytes .../drawable-xhdpi/ic_send_videocam_dnd.png | Bin 542 -> 0 bytes .../ic_send_videocam_offline.png | Bin 479 -> 0 bytes .../ic_send_videocam_offline_white.png | Bin 470 -> 0 bytes .../ic_send_videocam_online.png | Bin 539 -> 0 bytes .../res/drawable-xhdpi/ic_send_voice_away.png | Bin 1257 -> 0 bytes .../res/drawable-xhdpi/ic_send_voice_dnd.png | Bin 1419 -> 0 bytes .../drawable-xhdpi/ic_send_voice_offline.png | Bin 1075 -> 0 bytes .../ic_send_voice_offline_dark.png | Bin 1034 -> 0 bytes .../ic_send_voice_offline_white.png | Bin 1109 -> 0 bytes .../drawable-xhdpi/ic_send_voice_online.png | Bin 1433 -> 0 bytes .../drawable-xhdpi/ic_settings_black_24dp.png | Bin 557 -> 0 bytes .../drawable-xhdpi/ic_settings_white_24dp.png | Bin 562 -> 0 bytes .../drawable-xhdpi/ic_share_white_24dp.png | Bin 625 -> 0 bytes .../res/drawable-xhdpi/ic_star_black_24dp.png | Bin 467 -> 0 bytes .../res/drawable-xhdpi/ic_star_white_24dp.png | Bin 475 -> 0 bytes .../drawable-xhdpi/ic_stat_alert_warning.png | Bin 1175 -> 0 bytes .../ic_stat_communication_import_export.png | Bin 972 -> 0 bytes .../ic_verified_fingerprint.png | Bin 1551 -> 0 bytes .../ic_verified_user_black_18dp.png | Bin 390 -> 0 bytes .../ic_verified_user_white_18dp.png | Bin 407 -> 0 bytes .../drawable-xhdpi/ic_videocam_black_24dp.png | Bin 171 -> 0 bytes .../ic_videocam_off_black_24dp.png | Bin 274 -> 0 bytes .../drawable-xhdpi/ic_videocam_white_24dp.png | Bin 178 -> 0 bytes .../ic_voicemail_white_24dp.png | Bin 487 -> 0 bytes .../ic_volume_off_black_24dp.png | Bin 493 -> 0 bytes .../ic_volume_up_black_24dp.png | Bin 434 -> 0 bytes .../drawable-xhdpi/ic_warning_white_24dp.png | Bin 460 -> 0 bytes .../drawable-xhdpi/ic_warning_white_48dp.png | Bin 590 -> 0 bytes src/main/res/drawable-xhdpi/ic_wear_reply.png | Bin 564 -> 0 bytes .../message_bubble_received.9.png | Bin 936 -> 0 bytes .../message_bubble_received_dark.9.png | Bin 926 -> 0 bytes .../message_bubble_received_grey.9.png | Bin 915 -> 0 bytes .../message_bubble_received_warning.9.png | Bin 916 -> 0 bytes .../message_bubble_received_white.9.png | Bin 935 -> 0 bytes .../drawable-xhdpi/message_bubble_sent.9.png | Bin 857 -> 0 bytes .../message_bubble_sent_grey.9.png | Bin 842 -> 0 bytes .../baseline_tour_black_48.png | Bin 662 -> 0 bytes .../baseline_tour_white_48.png | Bin 662 -> 0 bytes .../drawable-xxhdpi/date_bubble_grey.9.png | Bin 1072 -> 0 bytes .../drawable-xxhdpi/date_bubble_white.9.png | Bin 1127 -> 0 bytes .../ic_account_box_white_24dp.png | Bin 578 -> 0 bytes .../res/drawable-xxhdpi/ic_action_reply.png | Bin 775 -> 0 bytes .../res/drawable-xxhdpi/ic_add_white_24dp.png | Bin 222 -> 0 bytes .../drawable-xxhdpi/ic_android_black_48dp.png | Bin 975 -> 0 bytes .../drawable-xxhdpi/ic_android_white_48dp.png | Bin 1012 -> 0 bytes .../ic_announcement_white_24dp.png | Bin 355 -> 0 bytes .../drawable-xxhdpi/ic_archive_black_48dp.png | Bin 718 -> 0 bytes .../drawable-xxhdpi/ic_archive_white_24dp.png | Bin 390 -> 0 bytes .../drawable-xxhdpi/ic_archive_white_48dp.png | Bin 738 -> 0 bytes .../res/drawable-xxhdpi/ic_attach_camera.png | Bin 1552 -> 0 bytes .../ic_attach_camera_white.png | Bin 1085 -> 0 bytes .../drawable-xxhdpi/ic_attach_document.png | Bin 1183 -> 0 bytes .../ic_attach_document_white.png | Bin 518 -> 0 bytes .../ic_attach_file_white_24dp.png | Bin 870 -> 0 bytes .../drawable-xxhdpi/ic_attach_location.png | Bin 1628 -> 0 bytes .../ic_attach_location_white.png | Bin 1117 -> 0 bytes .../res/drawable-xxhdpi/ic_attach_photo.png | Bin 1324 -> 0 bytes .../drawable-xxhdpi/ic_attach_photo_white.png | Bin 716 -> 0 bytes .../res/drawable-xxhdpi/ic_attach_record.png | Bin 1494 -> 0 bytes .../ic_attach_record_white.png | Bin 913 -> 0 bytes .../drawable-xxhdpi/ic_attach_videocam.png | Bin 373 -> 0 bytes .../ic_attach_videocam_white.png | Bin 469 -> 0 bytes .../ic_autorenew_white_24dp.png | Bin 869 -> 0 bytes .../drawable-xxhdpi/ic_backup_black_48dp.png | Bin 1116 -> 0 bytes .../drawable-xxhdpi/ic_backup_white_48dp.png | Bin 1177 -> 0 bytes .../drawable-xxhdpi/ic_block_white_24dp.png | Bin 1194 -> 0 bytes .../ic_bluetooth_audio_black_24dp.png | Bin 724 -> 0 bytes .../drawable-xxhdpi/ic_book_black_48dp.png | Bin 516 -> 0 bytes .../drawable-xxhdpi/ic_book_white_48dp.png | Bin 532 -> 0 bytes .../drawable-xxhdpi/ic_call_black_24dp.png | Bin 574 -> 0 bytes .../ic_call_end_white_48dp.png | Bin 1039 -> 0 bytes .../ic_call_made_black_18dp.png | Bin 202 -> 0 bytes .../ic_call_made_white_18dp.png | Bin 225 -> 0 bytes .../ic_call_missed_black_18dp.png | Bin 247 -> 0 bytes .../ic_call_missed_outgoing_black_18dp.png | Bin 235 -> 0 bytes .../ic_call_missed_outgoing_white_18dp.png | Bin 235 -> 0 bytes .../ic_call_missed_white_18dp.png | Bin 263 -> 0 bytes .../ic_call_received_black_18dp.png | Bin 202 -> 0 bytes .../ic_call_received_white_18dp.png | Bin 228 -> 0 bytes .../drawable-xxhdpi/ic_call_white_24dp.png | Bin 597 -> 0 bytes .../drawable-xxhdpi/ic_call_white_48dp.png | Bin 1134 -> 0 bytes .../ic_camera_alt_white_24dp.png | Bin 666 -> 0 bytes .../drawable-xxhdpi/ic_cancel_black_24dp.png | Bin 721 -> 0 bytes .../drawable-xxhdpi/ic_cancel_white_24dp.png | Bin 893 -> 0 bytes .../drawable-xxhdpi/ic_chat_white_24dp.png | Bin 270 -> 0 bytes .../drawable-xxhdpi/ic_clear_white_48dp.png | Bin 524 -> 0 bytes .../ic_cloud_download_white_24dp.png | Bin 610 -> 0 bytes .../ic_content_copy_white_24dp.png | Bin 266 -> 0 bytes .../drawable-xxhdpi/ic_crop_white_24dp.png | Bin 326 -> 0 bytes .../drawable-xxhdpi/ic_delete_black_24dp.png | Bin 191 -> 0 bytes .../drawable-xxhdpi/ic_delete_white_24dp.png | Bin 338 -> 0 bytes .../ic_description_black_48dp.png | Bin 514 -> 0 bytes .../ic_description_white_48dp.png | Bin 563 -> 0 bytes .../ic_directions_black_24dp.png | Bin 393 -> 0 bytes .../ic_directions_white_24dp.png | Bin 444 -> 0 bytes .../drawable-xxhdpi/ic_done_black_18dp.png | Bin 199 -> 0 bytes .../drawable-xxhdpi/ic_done_white_18dp.png | Bin 217 -> 0 bytes .../drawable-xxhdpi/ic_drafts_white_24dp.png | Bin 717 -> 0 bytes .../drawable-xxhdpi/ic_edit_black_24dp.png | Bin 269 -> 0 bytes .../drawable-xxhdpi/ic_edit_white_24dp.png | Bin 490 -> 0 bytes .../drawable-xxhdpi/ic_error_white_24dp.png | Bin 614 -> 0 bytes .../drawable-xxhdpi/ic_event_black_48dp.png | Bin 484 -> 0 bytes .../drawable-xxhdpi/ic_event_white_48dp.png | Bin 513 -> 0 bytes .../ic_file_download_white_24dp.png | Bin 351 -> 0 bytes .../ic_flip_camera_android_black_24dp.png | Bin 1340 -> 0 bytes .../drawable-xxhdpi/ic_forward_white_24dp.png | Bin 204 -> 0 bytes .../ic_gps_fixed_black_24dp.png | Bin 976 -> 0 bytes .../ic_gps_fixed_white_24dp.png | Bin 1012 -> 0 bytes .../ic_gps_not_fixed_black_24dp.png | Bin 803 -> 0 bytes .../ic_gps_not_fixed_white_24dp.png | Bin 830 -> 0 bytes .../ic_group_add_white_24dp.png | Bin 722 -> 0 bytes .../drawable-xxhdpi/ic_group_white_24dp.png | Bin 599 -> 0 bytes .../drawable-xxhdpi/ic_headset_black_24dp.png | Bin 586 -> 0 bytes .../drawable-xxhdpi/ic_headset_black_48dp.png | Bin 1183 -> 0 bytes .../drawable-xxhdpi/ic_headset_white_48dp.png | Bin 1246 -> 0 bytes .../drawable-xxhdpi/ic_help_black_48dp.png | Bin 1652 -> 0 bytes .../drawable-xxhdpi/ic_help_white_24dp.png | Bin 842 -> 0 bytes .../drawable-xxhdpi/ic_help_white_48dp.png | Bin 1677 -> 0 bytes .../ic_hourglass_empty_white_24dp.png | Bin 255 -> 0 bytes .../drawable-xxhdpi/ic_image_black_48dp.png | Bin 807 -> 0 bytes .../drawable-xxhdpi/ic_image_white_48dp.png | Bin 859 -> 0 bytes .../drawable-xxhdpi/ic_input_white_24dp.png | Bin 303 -> 0 bytes .../ic_link_off_white_24dp.png | Bin 650 -> 0 bytes .../drawable-xxhdpi/ic_link_white_24dp.png | Bin 670 -> 0 bytes .../drawable-xxhdpi/ic_lock_black_18dp.png | Bin 453 -> 0 bytes .../ic_lock_open_white_24dp.png | Bin 739 -> 0 bytes .../drawable-xxhdpi/ic_lock_white_18dp.png | Bin 463 -> 0 bytes .../drawable-xxhdpi/ic_lock_white_24dp.png | Bin 760 -> 0 bytes .../res/drawable-xxhdpi/ic_mic_black_24dp.png | Bin 581 -> 0 bytes .../res/drawable-xxhdpi/ic_mic_black_48dp.png | Bin 1146 -> 0 bytes .../drawable-xxhdpi/ic_mic_off_black_24dp.png | Bin 671 -> 0 bytes .../res/drawable-xxhdpi/ic_mic_white_48dp.png | Bin 1220 -> 0 bytes .../ic_mode_edit_black_18dp.png | Bin 336 -> 0 bytes .../ic_mode_edit_white_18dp.png | Bin 436 -> 0 bytes .../ic_new_releases_black_24dp.png | Bin 699 -> 0 bytes .../ic_new_releases_white_24dp.png | Bin 736 -> 0 bytes .../ic_no_results_background_black.png | Bin 8790 -> 0 bytes .../ic_no_results_background_white.png | Bin 9662 -> 0 bytes .../ic_notifications_black_24dp.png | Bin 381 -> 0 bytes .../ic_notifications_none_black_24dp.png | Bin 495 -> 0 bytes .../ic_notifications_none_white80.png | Bin 1033 -> 0 bytes .../ic_notifications_none_white_24dp.png | Bin 485 -> 0 bytes .../ic_notifications_off_black_24dp.png | Bin 690 -> 0 bytes .../ic_notifications_off_white80.png | Bin 1483 -> 0 bytes .../ic_notifications_off_white_24dp.png | Bin 683 -> 0 bytes .../ic_notifications_paused_black_24dp.png | Bin 482 -> 0 bytes .../ic_notifications_paused_white80.png | Bin 1049 -> 0 bytes .../ic_notifications_paused_white_24dp.png | Bin 482 -> 0 bytes .../ic_notifications_white80.png | Bin 831 -> 0 bytes .../ic_notifications_white_24dp.png | Bin 378 -> 0 bytes .../drawable-xxhdpi/ic_pause_black_36dp.png | Bin 143 -> 0 bytes .../drawable-xxhdpi/ic_pause_white_36dp.png | Bin 158 -> 0 bytes .../ic_person_add_white_24dp.png | Bin 683 -> 0 bytes .../drawable-xxhdpi/ic_person_black_48dp.png | Bin 846 -> 0 bytes .../drawable-xxhdpi/ic_person_white_48dp.png | Bin 863 -> 0 bytes .../ic_phone_in_talk_black_18dp.png | Bin 685 -> 0 bytes .../ic_phone_in_talk_white_18dp.png | Bin 704 -> 0 bytes .../ic_phone_in_talk_white_24dp.png | Bin 882 -> 0 bytes .../ic_play_arrow_black_36dp.png | Bin 366 -> 0 bytes .../ic_play_arrow_white_36dp.png | Bin 390 -> 0 bytes .../ic_play_circle_filled_white_48dp.png | Bin 1331 -> 0 bytes src/main/res/drawable-xxhdpi/ic_profile.png | Bin 2137 -> 0 bytes .../drawable-xxhdpi/ic_public_white_24dp.png | Bin 982 -> 0 bytes .../ic_qr_code_scan_white_24dp.png | Bin 599 -> 0 bytes .../ic_question_answer_white_24dp.png | Bin 256 -> 0 bytes .../drawable-xxhdpi/ic_refresh_black_24dp.png | Bin 721 -> 0 bytes .../drawable-xxhdpi/ic_refresh_white_24dp.png | Bin 734 -> 0 bytes .../drawable-xxhdpi/ic_replay_white_48dp.png | Bin 1390 -> 0 bytes .../drawable-xxhdpi/ic_reply_white_24dp.png | Bin 436 -> 0 bytes .../drawable-xxhdpi/ic_room_black_48dp.png | Bin 1264 -> 0 bytes .../drawable-xxhdpi/ic_room_white_24dp.png | Bin 861 -> 0 bytes .../drawable-xxhdpi/ic_room_white_48dp.png | Bin 1270 -> 0 bytes .../drawable-xxhdpi/ic_save_black_24dp.png | Bin 368 -> 0 bytes .../drawable-xxhdpi/ic_save_white_24dp.png | Bin 391 -> 0 bytes .../ic_search_background_black.png | Bin 7248 -> 0 bytes .../ic_search_background_white.png | Bin 9102 -> 0 bytes .../drawable-xxhdpi/ic_search_white_24dp.png | Bin 871 -> 0 bytes .../drawable-xxhdpi/ic_send_cancel_away.png | Bin 2583 -> 0 bytes .../drawable-xxhdpi/ic_send_cancel_dnd.png | Bin 2983 -> 0 bytes .../ic_send_cancel_offline.png | Bin 2290 -> 0 bytes .../ic_send_cancel_offline_white.png | Bin 2239 -> 0 bytes .../drawable-xxhdpi/ic_send_cancel_online.png | Bin 2974 -> 0 bytes .../drawable-xxhdpi/ic_send_file_offline.png | Bin 750 -> 0 bytes .../ic_send_file_offline_white.png | Bin 669 -> 0 bytes .../drawable-xxhdpi/ic_send_location_away.png | Bin 2229 -> 0 bytes .../drawable-xxhdpi/ic_send_location_dnd.png | Bin 2589 -> 0 bytes .../ic_send_location_offline.png | Bin 1881 -> 0 bytes .../ic_send_location_offline_white.png | Bin 1940 -> 0 bytes .../ic_send_location_online.png | Bin 2607 -> 0 bytes .../drawable-xxhdpi/ic_send_photo_away.png | Bin 2256 -> 0 bytes .../res/drawable-xxhdpi/ic_send_photo_dnd.png | Bin 2618 -> 0 bytes .../drawable-xxhdpi/ic_send_photo_offline.png | Bin 1802 -> 0 bytes .../ic_send_photo_offline_white.png | Bin 1943 -> 0 bytes .../drawable-xxhdpi/ic_send_photo_online.png | Bin 2636 -> 0 bytes .../drawable-xxhdpi/ic_send_picture_away.png | Bin 1327 -> 0 bytes .../drawable-xxhdpi/ic_send_picture_dnd.png | Bin 1516 -> 0 bytes .../ic_send_picture_offline.png | Bin 1159 -> 0 bytes .../ic_send_picture_offline_white.png | Bin 1216 -> 0 bytes .../ic_send_picture_online.png | Bin 1515 -> 0 bytes .../res/drawable-xxhdpi/ic_send_text_away.png | Bin 1776 -> 0 bytes .../res/drawable-xxhdpi/ic_send_text_dnd.png | Bin 2005 -> 0 bytes .../drawable-xxhdpi/ic_send_text_offline.png | Bin 1410 -> 0 bytes .../ic_send_text_offline_white.png | Bin 1486 -> 0 bytes .../drawable-xxhdpi/ic_send_text_online.png | Bin 1970 -> 0 bytes .../drawable-xxhdpi/ic_send_videocam_away.png | Bin 705 -> 0 bytes .../drawable-xxhdpi/ic_send_videocam_dnd.png | Bin 777 -> 0 bytes .../ic_send_videocam_offline.png | Bin 650 -> 0 bytes .../ic_send_videocam_offline_white.png | Bin 670 -> 0 bytes .../ic_send_videocam_online.png | Bin 776 -> 0 bytes .../drawable-xxhdpi/ic_send_voice_away.png | Bin 1895 -> 0 bytes .../res/drawable-xxhdpi/ic_send_voice_dnd.png | Bin 2200 -> 0 bytes .../drawable-xxhdpi/ic_send_voice_offline.png | Bin 1621 -> 0 bytes .../ic_send_voice_offline_white.png | Bin 1644 -> 0 bytes .../drawable-xxhdpi/ic_send_voice_online.png | Bin 2187 -> 0 bytes .../ic_settings_black_24dp.png | Bin 827 -> 0 bytes .../ic_settings_white_24dp.png | Bin 843 -> 0 bytes .../drawable-xxhdpi/ic_share_white_24dp.png | Bin 857 -> 0 bytes .../drawable-xxhdpi/ic_star_black_24dp.png | Bin 668 -> 0 bytes .../drawable-xxhdpi/ic_star_white_24dp.png | Bin 676 -> 0 bytes .../drawable-xxhdpi/ic_stat_alert_warning.png | Bin 2276 -> 0 bytes .../ic_stat_communication_import_export.png | Bin 1860 -> 0 bytes .../ic_verified_fingerprint.png | Bin 2306 -> 0 bytes .../ic_verified_user_black_18dp.png | Bin 516 -> 0 bytes .../ic_verified_user_white_18dp.png | Bin 538 -> 0 bytes .../ic_videocam_black_24dp.png | Bin 224 -> 0 bytes .../ic_videocam_off_black_24dp.png | Bin 375 -> 0 bytes .../ic_videocam_white_24dp.png | Bin 234 -> 0 bytes .../ic_voicemail_white_24dp.png | Bin 625 -> 0 bytes .../ic_volume_off_black_24dp.png | Bin 704 -> 0 bytes .../ic_volume_up_black_24dp.png | Bin 626 -> 0 bytes .../drawable-xxhdpi/ic_warning_white_24dp.png | Bin 639 -> 0 bytes .../drawable-xxhdpi/ic_warning_white_48dp.png | Bin 843 -> 0 bytes .../res/drawable-xxhdpi/ic_wear_reply.png | Bin 866 -> 0 bytes .../message_bubble_received.9.png | Bin 1319 -> 0 bytes .../message_bubble_received_dark.9.png | Bin 1319 -> 0 bytes .../message_bubble_received_grey.9.png | Bin 1301 -> 0 bytes .../message_bubble_received_warning.9.png | Bin 1332 -> 0 bytes .../message_bubble_received_white.9.png | Bin 1344 -> 0 bytes .../drawable-xxhdpi/message_bubble_sent.9.png | Bin 1190 -> 0 bytes .../message_bubble_sent_grey.9.png | Bin 1173 -> 0 bytes .../baseline_tour_black_48.png | Bin 809 -> 0 bytes .../baseline_tour_white_48.png | Bin 809 -> 0 bytes .../drawable-xxxhdpi/date_bubble_grey.9.png | Bin 1392 -> 0 bytes .../drawable-xxxhdpi/date_bubble_white.9.png | Bin 1430 -> 0 bytes .../ic_account_box_white_24dp.png | Bin 894 -> 0 bytes .../drawable-xxxhdpi/ic_add_white_24dp.png | Bin 269 -> 0 bytes .../ic_android_black_48dp.png | Bin 1295 -> 0 bytes .../ic_android_white_48dp.png | Bin 1350 -> 0 bytes .../ic_announcement_white_24dp.png | Bin 519 -> 0 bytes .../ic_archive_black_48dp.png | Bin 945 -> 0 bytes .../ic_archive_white_24dp.png | Bin 489 -> 0 bytes .../ic_archive_white_48dp.png | Bin 971 -> 0 bytes .../res/drawable-xxxhdpi/ic_attach_camera.png | Bin 1790 -> 0 bytes .../ic_attach_camera_white.png | Bin 1416 -> 0 bytes .../drawable-xxxhdpi/ic_attach_document.png | Bin 1276 -> 0 bytes .../ic_attach_document_white.png | Bin 627 -> 0 bytes .../ic_attach_file_white_24dp.png | Bin 1039 -> 0 bytes .../drawable-xxxhdpi/ic_attach_location.png | Bin 1872 -> 0 bytes .../ic_attach_location_white.png | Bin 1395 -> 0 bytes .../res/drawable-xxxhdpi/ic_attach_photo.png | Bin 1454 -> 0 bytes .../ic_attach_photo_white.png | Bin 900 -> 0 bytes .../res/drawable-xxxhdpi/ic_attach_record.png | Bin 1703 -> 0 bytes .../ic_attach_record_white.png | Bin 1170 -> 0 bytes .../drawable-xxxhdpi/ic_attach_videocam.png | Bin 429 -> 0 bytes .../ic_attach_videocam_white.png | Bin 547 -> 0 bytes .../ic_autorenew_white_24dp.png | Bin 1114 -> 0 bytes .../drawable-xxxhdpi/ic_backup_black_48dp.png | Bin 1465 -> 0 bytes .../drawable-xxxhdpi/ic_backup_white_48dp.png | Bin 1562 -> 0 bytes .../drawable-xxxhdpi/ic_block_white_24dp.png | Bin 1497 -> 0 bytes .../ic_bluetooth_audio_black_24dp.png | Bin 867 -> 0 bytes .../drawable-xxxhdpi/ic_book_black_48dp.png | Bin 750 -> 0 bytes .../drawable-xxxhdpi/ic_book_white_48dp.png | Bin 766 -> 0 bytes .../drawable-xxxhdpi/ic_call_black_24dp.png | Bin 758 -> 0 bytes .../ic_call_end_white_48dp.png | Bin 1355 -> 0 bytes .../ic_call_made_black_18dp.png | Bin 212 -> 0 bytes .../ic_call_made_white_18dp.png | Bin 247 -> 0 bytes .../ic_call_missed_black_18dp.png | Bin 267 -> 0 bytes .../ic_call_missed_outgoing_black_18dp.png | Bin 257 -> 0 bytes .../ic_call_missed_outgoing_white_18dp.png | Bin 248 -> 0 bytes .../ic_call_missed_white_18dp.png | Bin 291 -> 0 bytes .../ic_call_received_black_18dp.png | Bin 214 -> 0 bytes .../ic_call_received_white_18dp.png | Bin 257 -> 0 bytes .../drawable-xxxhdpi/ic_call_white_24dp.png | Bin 778 -> 0 bytes .../drawable-xxxhdpi/ic_call_white_48dp.png | Bin 1529 -> 0 bytes .../ic_camera_alt_white_24dp.png | Bin 894 -> 0 bytes .../drawable-xxxhdpi/ic_cancel_black_24dp.png | Bin 963 -> 0 bytes .../drawable-xxxhdpi/ic_cancel_white_24dp.png | Bin 1179 -> 0 bytes .../drawable-xxxhdpi/ic_chat_white_24dp.png | Bin 344 -> 0 bytes .../drawable-xxxhdpi/ic_clear_white_48dp.png | Bin 702 -> 0 bytes .../ic_cloud_download_white_24dp.png | Bin 789 -> 0 bytes .../ic_content_copy_white_24dp.png | Bin 329 -> 0 bytes .../drawable-xxxhdpi/ic_crop_white_24dp.png | Bin 394 -> 0 bytes .../drawable-xxxhdpi/ic_delete_black_24dp.png | Bin 237 -> 0 bytes .../drawable-xxxhdpi/ic_delete_white_24dp.png | Bin 397 -> 0 bytes .../ic_description_black_48dp.png | Bin 726 -> 0 bytes .../ic_description_white_48dp.png | Bin 788 -> 0 bytes .../ic_directions_black_24dp.png | Bin 491 -> 0 bytes .../ic_directions_white_24dp.png | Bin 554 -> 0 bytes .../drawable-xxxhdpi/ic_done_black_18dp.png | Bin 227 -> 0 bytes .../drawable-xxxhdpi/ic_done_white_18dp.png | Bin 255 -> 0 bytes .../drawable-xxxhdpi/ic_drafts_white_24dp.png | Bin 953 -> 0 bytes .../drawable-xxxhdpi/ic_edit_black_24dp.png | Bin 319 -> 0 bytes .../drawable-xxxhdpi/ic_edit_white_24dp.png | Bin 632 -> 0 bytes .../drawable-xxxhdpi/ic_error_white_24dp.png | Bin 814 -> 0 bytes .../drawable-xxxhdpi/ic_event_black_48dp.png | Bin 713 -> 0 bytes .../drawable-xxxhdpi/ic_event_white_48dp.png | Bin 744 -> 0 bytes .../ic_file_download_white_24dp.png | Bin 433 -> 0 bytes .../ic_flip_camera_android_black_24dp.png | Bin 1780 -> 0 bytes .../ic_forward_white_24dp.png | Bin 236 -> 0 bytes .../ic_gps_fixed_black_24dp.png | Bin 1334 -> 0 bytes .../ic_gps_fixed_white_24dp.png | Bin 1379 -> 0 bytes .../ic_gps_not_fixed_black_24dp.png | Bin 1102 -> 0 bytes .../ic_gps_not_fixed_white_24dp.png | Bin 1139 -> 0 bytes .../ic_group_add_white_24dp.png | Bin 935 -> 0 bytes .../drawable-xxxhdpi/ic_group_white_24dp.png | Bin 759 -> 0 bytes .../ic_headset_black_24dp.png | Bin 786 -> 0 bytes .../ic_headset_black_48dp.png | Bin 1574 -> 0 bytes .../ic_headset_white_48dp.png | Bin 1736 -> 0 bytes .../drawable-xxxhdpi/ic_help_black_48dp.png | Bin 2217 -> 0 bytes .../drawable-xxxhdpi/ic_help_white_24dp.png | Bin 1132 -> 0 bytes .../drawable-xxxhdpi/ic_help_white_48dp.png | Bin 2356 -> 0 bytes .../ic_hourglass_empty_white_24dp.png | Bin 273 -> 0 bytes .../drawable-xxxhdpi/ic_image_black_48dp.png | Bin 1108 -> 0 bytes .../drawable-xxxhdpi/ic_image_white_48dp.png | Bin 1178 -> 0 bytes .../drawable-xxxhdpi/ic_input_white_24dp.png | Bin 382 -> 0 bytes .../ic_link_off_white_24dp.png | Bin 811 -> 0 bytes .../drawable-xxxhdpi/ic_link_white_24dp.png | Bin 865 -> 0 bytes .../drawable-xxxhdpi/ic_lock_black_18dp.png | Bin 527 -> 0 bytes .../ic_lock_open_white_24dp.png | Bin 946 -> 0 bytes .../drawable-xxxhdpi/ic_lock_white_18dp.png | Bin 540 -> 0 bytes .../drawable-xxxhdpi/ic_lock_white_24dp.png | Bin 971 -> 0 bytes .../drawable-xxxhdpi/ic_mic_black_24dp.png | Bin 773 -> 0 bytes .../drawable-xxxhdpi/ic_mic_black_48dp.png | Bin 1529 -> 0 bytes .../ic_mic_off_black_24dp.png | Bin 832 -> 0 bytes .../drawable-xxxhdpi/ic_mic_white_48dp.png | Bin 1664 -> 0 bytes .../ic_mode_edit_black_18dp.png | Bin 366 -> 0 bytes .../ic_mode_edit_white_18dp.png | Bin 490 -> 0 bytes .../ic_new_releases_black_24dp.png | Bin 897 -> 0 bytes .../ic_new_releases_white_24dp.png | Bin 932 -> 0 bytes .../ic_no_results_background_black.png | Bin 12852 -> 0 bytes .../ic_no_results_background_white.png | Bin 13693 -> 0 bytes .../ic_notifications_black_24dp.png | Bin 493 -> 0 bytes .../ic_notifications_none_black_24dp.png | Bin 641 -> 0 bytes .../ic_notifications_none_white80.png | Bin 1288 -> 0 bytes .../ic_notifications_none_white_24dp.png | Bin 633 -> 0 bytes .../ic_notifications_off_black_24dp.png | Bin 885 -> 0 bytes .../ic_notifications_off_white80.png | Bin 1903 -> 0 bytes .../ic_notifications_off_white_24dp.png | Bin 877 -> 0 bytes .../ic_notifications_paused_black_24dp.png | Bin 618 -> 0 bytes .../ic_notifications_paused_white80.png | Bin 1308 -> 0 bytes .../ic_notifications_paused_white_24dp.png | Bin 616 -> 0 bytes .../ic_notifications_white80.png | Bin 1053 -> 0 bytes .../ic_notifications_white_24dp.png | Bin 482 -> 0 bytes .../drawable-xxxhdpi/ic_pause_black_36dp.png | Bin 127 -> 0 bytes .../drawable-xxxhdpi/ic_pause_white_36dp.png | Bin 110 -> 0 bytes .../ic_person_add_white_24dp.png | Bin 909 -> 0 bytes .../drawable-xxxhdpi/ic_person_black_48dp.png | Bin 1121 -> 0 bytes .../drawable-xxxhdpi/ic_person_white_48dp.png | Bin 1140 -> 0 bytes .../ic_phone_in_talk_black_18dp.png | Bin 868 -> 0 bytes .../ic_phone_in_talk_white_18dp.png | Bin 882 -> 0 bytes .../ic_phone_in_talk_white_24dp.png | Bin 1162 -> 0 bytes .../ic_play_arrow_black_36dp.png | Bin 394 -> 0 bytes .../ic_play_arrow_white_36dp.png | Bin 461 -> 0 bytes .../ic_play_circle_filled_white_48dp.png | Bin 1826 -> 0 bytes .../drawable-xxxhdpi/ic_public_white_24dp.png | Bin 1288 -> 0 bytes .../ic_qr_code_scan_white_24dp.png | Bin 754 -> 0 bytes .../ic_question_answer_white_24dp.png | Bin 320 -> 0 bytes .../ic_refresh_black_24dp.png | Bin 948 -> 0 bytes .../ic_refresh_white_24dp.png | Bin 967 -> 0 bytes .../drawable-xxxhdpi/ic_replay_white_48dp.png | Bin 1885 -> 0 bytes .../drawable-xxxhdpi/ic_reply_white_24dp.png | Bin 579 -> 0 bytes .../drawable-xxxhdpi/ic_room_black_48dp.png | Bin 1691 -> 0 bytes .../drawable-xxxhdpi/ic_room_white_24dp.png | Bin 1093 -> 0 bytes .../drawable-xxxhdpi/ic_room_white_48dp.png | Bin 1709 -> 0 bytes .../drawable-xxxhdpi/ic_save_black_24dp.png | Bin 477 -> 0 bytes .../drawable-xxxhdpi/ic_save_white_24dp.png | Bin 504 -> 0 bytes .../ic_search_background_black.png | Bin 10595 -> 0 bytes .../ic_search_background_white.png | Bin 12848 -> 0 bytes .../drawable-xxxhdpi/ic_search_white_24dp.png | Bin 1090 -> 0 bytes .../drawable-xxxhdpi/ic_send_cancel_away.png | Bin 3377 -> 0 bytes .../drawable-xxxhdpi/ic_send_cancel_dnd.png | Bin 3862 -> 0 bytes .../ic_send_cancel_offline.png | Bin 2956 -> 0 bytes .../ic_send_cancel_offline_white.png | Bin 2987 -> 0 bytes .../ic_send_cancel_online.png | Bin 3861 -> 0 bytes .../ic_send_location_away.png | Bin 3015 -> 0 bytes .../drawable-xxxhdpi/ic_send_location_dnd.png | Bin 3514 -> 0 bytes .../ic_send_location_offline.png | Bin 2523 -> 0 bytes .../ic_send_location_offline_white.png | Bin 2563 -> 0 bytes .../ic_send_location_online.png | Bin 3519 -> 0 bytes .../drawable-xxxhdpi/ic_send_photo_away.png | Bin 3040 -> 0 bytes .../drawable-xxxhdpi/ic_send_photo_dnd.png | Bin 3569 -> 0 bytes .../ic_send_photo_offline.png | Bin 2491 -> 0 bytes .../ic_send_photo_offline_white.png | Bin 2631 -> 0 bytes .../drawable-xxxhdpi/ic_send_photo_online.png | Bin 3567 -> 0 bytes .../drawable-xxxhdpi/ic_send_picture_away.png | Bin 1632 -> 0 bytes .../drawable-xxxhdpi/ic_send_picture_dnd.png | Bin 1784 -> 0 bytes .../ic_send_picture_offline.png | Bin 1428 -> 0 bytes .../ic_send_picture_offline_white.png | Bin 1483 -> 0 bytes .../ic_send_picture_online.png | Bin 1777 -> 0 bytes .../drawable-xxxhdpi/ic_send_text_away.png | Bin 2311 -> 0 bytes .../res/drawable-xxxhdpi/ic_send_text_dnd.png | Bin 2592 -> 0 bytes .../drawable-xxxhdpi/ic_send_text_offline.png | Bin 1862 -> 0 bytes .../ic_send_text_offline_white.png | Bin 1974 -> 0 bytes .../drawable-xxxhdpi/ic_send_text_online.png | Bin 2665 -> 0 bytes .../ic_send_videocam_away.png | Bin 821 -> 0 bytes .../drawable-xxxhdpi/ic_send_videocam_dnd.png | Bin 884 -> 0 bytes .../ic_send_videocam_offline.png | Bin 772 -> 0 bytes .../ic_send_videocam_offline_white.png | Bin 777 -> 0 bytes .../ic_send_videocam_online.png | Bin 894 -> 0 bytes .../drawable-xxxhdpi/ic_send_voice_away.png | Bin 2491 -> 0 bytes .../drawable-xxxhdpi/ic_send_voice_dnd.png | Bin 2849 -> 0 bytes .../ic_send_voice_offline.png | Bin 2092 -> 0 bytes .../ic_send_voice_offline_white.png | Bin 2181 -> 0 bytes .../drawable-xxxhdpi/ic_send_voice_online.png | Bin 2895 -> 0 bytes .../ic_settings_black_24dp.png | Bin 1073 -> 0 bytes .../ic_settings_white_24dp.png | Bin 1074 -> 0 bytes .../drawable-xxxhdpi/ic_share_white_24dp.png | Bin 1115 -> 0 bytes .../drawable-xxxhdpi/ic_star_black_24dp.png | Bin 887 -> 0 bytes .../drawable-xxxhdpi/ic_star_white_24dp.png | Bin 890 -> 0 bytes .../ic_verified_fingerprint.png | Bin 2773 -> 0 bytes .../ic_verified_user_black_18dp.png | Bin 634 -> 0 bytes .../ic_verified_user_white_18dp.png | Bin 668 -> 0 bytes .../ic_videocam_black_24dp.png | Bin 270 -> 0 bytes .../ic_videocam_off_black_24dp.png | Bin 447 -> 0 bytes .../ic_videocam_white_24dp.png | Bin 290 -> 0 bytes .../ic_voicemail_white_24dp.png | Bin 971 -> 0 bytes .../ic_volume_off_black_24dp.png | Bin 924 -> 0 bytes .../ic_volume_up_black_24dp.png | Bin 828 -> 0 bytes .../ic_warning_white_24dp.png | Bin 887 -> 0 bytes .../ic_warning_white_48dp.png | Bin 1044 -> 0 bytes .../message_bubble_received.9.png | Bin 1713 -> 0 bytes .../message_bubble_received_dark.9.png | Bin 1691 -> 0 bytes .../message_bubble_received_grey.9.png | Bin 1670 -> 0 bytes .../message_bubble_received_warning.9.png | Bin 1696 -> 0 bytes .../message_bubble_received_white.9.png | Bin 1705 -> 0 bytes .../message_bubble_sent.9.png | Bin 1499 -> 0 bytes .../message_bubble_sent_grey.9.png | Bin 1468 -> 0 bytes ...=> background_account_profile_picture.xml} | 6 +- .../res/drawable/background_list_item.xml | 4 + .../drawable/background_message_bubble.xml | 5 + ...y.xml => background_message_separator.xml} | 8 +- .../res/drawable/background_no_results.xml | 7 + src/main/res/drawable/background_search.xml | 7 + .../background_selectable_list_item.xml | 5 + .../background_selected_item_conversation.xml | 7 + ...round.xml => background_splash_screen.xml} | 2 +- .../drawable/background_surface_container.xml | 5 + .../res/drawable/es_slidingpane_shadow.xml | 12 - src/main/res/drawable/greybackground.xml | 6 - src/main/res/drawable/ic_adb_48dp.xml | 5 + ...croll_to_end_black.xml => ic_add_24dp.xml} | 9 +- .../res/drawable/ic_announcement_24dp.xml | 13 + src/main/res/drawable/ic_archive_24dp.xml | 12 + src/main/res/drawable/ic_archive_48dp.xml | 12 + src/main/res/drawable/ic_attach_file_24dp.xml | 10 + src/main/res/drawable/ic_autorenew_24dp.xml | 12 + src/main/res/drawable/ic_backup_48dp.xml | 12 + .../res/drawable/ic_bluetooth_audio_24dp.xml | 12 + src/main/res/drawable/ic_book_48dp.xml | 5 + src/main/res/drawable/ic_call_24dp.xml | 12 + .../res/drawable/ic_call_black54_24dp.xml | 4 - src/main/res/drawable/ic_call_end_24dp.xml | 12 + src/main/res/drawable/ic_call_made_24dp.xml | 5 + src/main/res/drawable/ic_call_missed_24db.xml | 11 + src/main/res/drawable/ic_call_missed_24dp.xml | 5 + .../drawable/ic_call_missed_outgoing_24dp.xml | 5 + .../drawable/ic_call_missed_white_24db.xml | 5 - .../res/drawable/ic_call_received_24dp.xml | 5 + .../res/drawable/ic_call_white70_24dp.xml | 4 - src/main/res/drawable/ic_camera_alt_24dp.xml | 13 + src/main/res/drawable/ic_cancel_24dp.xml | 12 + src/main/res/drawable/ic_cancel_96dp.xml | 12 + src/main/res/drawable/ic_chat_24dp.xml | 13 + src/main/res/drawable/ic_check_24dp.xml | 10 + src/main/res/drawable/ic_clear_24dp.xml | 12 + .../res/drawable/ic_cloud_download_24dp.xml | 12 + src/main/res/drawable/ic_code_48dp.xml | 12 + .../res/drawable/ic_content_copy_24dp.xml | 12 + src/main/res/drawable/ic_delete_24dp.xml | 10 + src/main/res/drawable/ic_description_24dp.xml | 10 + src/main/res/drawable/ic_description_48dp.xml | 5 + src/main/res/drawable/ic_directions_24dp.xml | 5 + .../res/drawable/ic_directions_black_24dp.xml | 9 - src/main/res/drawable/ic_done_24dp.xml | 10 + src/main/res/drawable/ic_download_24dp.xml | 5 + src/main/res/drawable/ic_edit_24dp.xml | 10 + src/main/res/drawable/ic_email_48dp.xml | 12 + src/main/res/drawable/ic_error_24dp.xml | 12 + src/main/res/drawable/ic_event_48dp.xml | 12 + .../drawable/ic_flip_camera_android_24dp.xml | 20 + src/main/res/drawable/ic_forum_24dp.xml | 12 + src/main/res/drawable/ic_gps_fixed_24dp.xml | 5 + .../res/drawable/ic_gps_fixed_black_24dp.xml | 9 - .../res/drawable/ic_gps_not_fixed_24dp.xml | 5 + .../drawable/ic_gps_not_fixed_black_24dp.xml | 9 - src/main/res/drawable/ic_group_24dp.xml | 5 + src/main/res/drawable/ic_headphones_48dp.xml | 12 + src/main/res/drawable/ic_headset_mic_24dp.xml | 12 + src/main/res/drawable/ic_help_24dp.xml | 13 + src/main/res/drawable/ic_help_center_48dp.xml | 5 + .../res/drawable/ic_hourglass_top_24dp.xml | 5 + src/main/res/drawable/ic_image_24dp.xml | 10 + src/main/res/drawable/ic_image_48dp.xml | 5 + .../ic_keyboard_double_arrow_down_24dp.xml | 16 + src/main/res/drawable/ic_link_24dp.xml | 12 + src/main/res/drawable/ic_link_off_24dp.xml | 12 + .../res/drawable/ic_location_pin_24dp.xml | 10 + .../res/drawable/ic_location_pin_48dp.xml | 5 + src/main/res/drawable/ic_lock_24dp.xml | 10 + .../drawable/ic_lock_open_outline_24dp.xml | 10 + src/main/res/drawable/ic_login_24dp.xml | 5 + src/main/res/drawable/ic_logout_24dp.xml | 11 + .../res/drawable/ic_logout_white_24dp.xml | 5 - .../res/drawable/ic_mark_chat_read_24dp.xml | 12 + src/main/res/drawable/ic_mic_24dp.xml | 10 + src/main/res/drawable/ic_mic_48dp.xml | 5 + src/main/res/drawable/ic_mic_off_24dp.xml | 12 + src/main/res/drawable/ic_movie_48dp.xml | 12 + .../res/drawable/ic_navigate_next_24dp.xml | 13 + ..._check_24.xml => ic_new_releases_24dp.xml} | 2 +- .../res/drawable/ic_notifications_24dp.xml | 12 + .../drawable/ic_notifications_none_24dp.xml | 12 + .../drawable/ic_notifications_off_24dp.xml | 12 + .../drawable/ic_notifications_paused_24dp.xml | 12 + src/main/res/drawable/ic_pause_24dp.xml | 10 + src/main/res/drawable/ic_person_24dp.xml | 5 + src/main/res/drawable/ic_person_48dp.xml | 12 + src/main/res/drawable/ic_person_add_24dp.xml | 12 + .../res/drawable/ic_phone_in_talk_24dp.xml | 10 + src/main/res/drawable/ic_place_black_24dp.xml | 9 - src/main/res/drawable/ic_play_arrow_24dp.xml | 10 + src/main/res/drawable/ic_play_circle_24dp.xml | 5 + src/main/res/drawable/ic_play_lesson_48dp.xml | 13 + .../res/drawable/ic_play_lesson_black_24.xml | 6 - .../drawable/ic_play_lesson_white_48dp.xml | 6 - src/main/res/drawable/ic_public_24dp.xml | 5 + src/main/res/drawable/ic_qr_code_24dp.xml | 10 + .../res/drawable/ic_qr_code_black_24dp.xml | 40 -- .../res/drawable/ic_qr_code_scanner_24dp.xml | 10 + .../res/drawable/ic_qr_code_white_24dp.xml | 40 -- src/main/res/drawable/ic_refresh_24dp.xml | 5 + src/main/res/drawable/ic_replay_24dp.xml | 12 + src/main/res/drawable/ic_reply_24dp.xml | 13 + src/main/res/drawable/ic_save_24dp.xml | 12 + .../res/drawable/ic_scroll_to_end_white.xml | 36 -- src/main/res/drawable/ic_search_128dp.xml | 12 + src/main/res/drawable/ic_search_24dp.xml | 12 + src/main/res/drawable/ic_send_24dp.xml | 13 + src/main/res/drawable/ic_settings_24dp.xml | 10 + src/main/res/drawable/ic_share_24dp.xml | 10 + src/main/res/drawable/ic_star_24dp.xml | 12 + src/main/res/drawable/ic_tour_48dp.xml | 5 + .../res/drawable/ic_travel_explore_24dp.xml | 5 + src/main/res/drawable/ic_unarchive_24dp.xml | 5 + .../res/drawable/ic_verified_user_24dp.xml | 10 + src/main/res/drawable/ic_videocam_24dp.xml | 11 + .../res/drawable/ic_videocam_black54_24dp.xml | 4 - .../res/drawable/ic_videocam_off_24dp.xml | 12 + .../res/drawable/ic_videocam_white70_24dp.xml | 4 - src/main/res/drawable/ic_voicemail_24dp.xml | 12 + src/main/res/drawable/ic_volume_off_24dp.xml | 13 + src/main/res/drawable/ic_volume_up_24dp.xml | 13 + src/main/res/drawable/ic_warning_24dp.xml | 12 + .../drawable/list_item_background_dark.xml | 34 -- .../drawable/list_item_background_light.xml | 34 -- .../drawable/no_results_background_dark.xml | 41 -- .../drawable/no_results_background_light.xml | 41 -- .../no_results_primary_background_dark.xml | 41 -- .../no_results_primary_background_light.xml | 41 -- .../res/drawable/search_background_dark.xml | 41 -- .../res/drawable/search_background_light.xml | 41 -- src/main/res/drawable/snackbar.xml | 4 +- .../drawable/visibility_toggle_drawable.xml | 5 - src/main/res/drawable/white_cursor.xml | 33 -- .../layout-w945dp/activity_conversations.xml | 18 +- src/main/res/layout/actionview_search.xml | 2 +- src/main/res/layout/activity_about.xml | 48 +- .../res/layout/activity_change_password.xml | 168 +++---- .../res/layout/activity_channel_discovery.xml | 19 +- .../res/layout/activity_choose_contact.xml | 26 +- .../res/layout/activity_contact_details.xml | 107 ++--- .../res/layout/activity_conversations.xml | 43 +- src/main/res/layout/activity_edit_account.xml | 227 +++++---- .../res/layout/activity_manage_accounts.xml | 39 +- .../res/layout/activity_media_browser.xml | 20 +- src/main/res/layout/activity_muc_details.xml | 170 +++---- src/main/res/layout/activity_muc_users.xml | 19 +- .../activity_publish_profile_picture.xml | 77 ++-- src/main/res/layout/activity_recording.xml | 43 +- src/main/res/layout/activity_rtp_session.xml | 95 ++-- src/main/res/layout/activity_search.xml | 45 +- src/main/res/layout/activity_settings.xml | 32 +- .../res/layout/activity_share_location.xml | 55 ++- src/main/res/layout/activity_share_with.xml | 42 +- .../res/layout/activity_show_location.xml | 29 +- .../layout/activity_start_conversation.xml | 51 +- src/main/res/layout/activity_trust_keys.xml | 174 +++---- src/main/res/layout/activity_uri_handler.xml | 8 +- src/main/res/layout/captcha.xml | 12 +- src/main/res/layout/contact_key.xml | 37 +- .../res/layout/create_conference_dialog.xml | 40 -- src/main/res/layout/dialog_block_contact.xml | 10 +- src/main/res/layout/dialog_clear_history.xml | 16 +- .../res/layout/dialog_create_conference.xml | 36 ++ ...g.xml => dialog_create_public_channel.xml} | 36 +- src/main/res/layout/dialog_delete_account.xml | 16 +- src/main/res/layout/dialog_enter_jid.xml | 40 ++ .../res/layout/dialog_join_conference.xml | 39 +- src/main/res/layout/dialog_presence.xml | 31 +- src/main/res/layout/dialog_quickedit.xml | 10 +- .../res/layout/dialog_verify_fingerprints.xml | 10 +- src/main/res/layout/enter_jid_dialog.xml | 42 -- src/main/res/layout/form_boolean.xml | 11 - src/main/res/layout/form_text.xml | 17 - src/main/res/layout/fragment_conversation.xml | 92 ++-- .../fragment_conversations_overview.xml | 13 +- .../{account_row.xml => item_account.xml} | 19 +- src/main/res/layout/item_autocomplete.xml | 11 + ...lt_item.xml => item_channel_discovery.xml} | 23 +- .../layout/{contact.xml => item_contact.xml} | 31 +- ...ion_list_row.xml => item_conversation.xml} | 101 ++-- .../res/layout/{media.xml => item_media.xml} | 10 +- ...dia_preview.xml => item_media_preview.xml} | 15 +- src/main/res/layout/item_message_content.xml | 68 +++ .../res/layout/item_message_date_bubble.xml | 30 ++ src/main/res/layout/item_message_received.xml | 97 ++++ .../res/layout/item_message_rtp_session.xml | 42 ++ src/main/res/layout/item_message_sent.xml | 105 +++++ src/main/res/layout/item_message_status.xml | 48 ++ ...user_preview.xml => item_user_preview.xml} | 11 +- src/main/res/layout/keys_card.xml | 20 +- src/main/res/layout/list_item_tag.xml | 21 +- src/main/res/layout/message_content.xml | 66 --- src/main/res/layout/message_date_bubble.xml | 27 -- src/main/res/layout/message_received.xml | 94 ---- src/main/res/layout/message_rtp_session.xml | 38 -- src/main/res/layout/message_sent.xml | 108 ----- src/main/res/layout/message_status.xml | 46 -- src/main/res/layout/presence_template.xml | 47 -- src/main/res/layout/simple_list_item.xml | 25 - src/main/res/layout/toolbar.xml | 11 - src/main/res/menu/activity_conversations.xml | 4 +- src/main/res/menu/activity_rtp_session.xml | 15 +- src/main/res/menu/activity_search.xml | 2 +- .../res/menu/channel_discovery_activity.xml | 2 +- src/main/res/menu/choose_contact.xml | 6 +- src/main/res/menu/contact_details.xml | 4 +- src/main/res/menu/editaccount.xml | 4 +- src/main/res/menu/fragment_conversation.xml | 26 +- src/main/res/menu/import_backup.xml | 2 +- src/main/res/menu/menu_show_location.xml | 4 +- src/main/res/menu/muc_details.xml | 4 +- src/main/res/menu/muc_users_activity.xml | 2 +- src/main/res/menu/share_with.xml | 2 +- src/main/res/menu/start_conversation.xml | 4 +- .../menu/start_conversation_fab_submenu.xml | 16 +- src/main/res/menu/trust_keys.xml | 2 +- src/main/res/values-ar/strings.xml | 7 - src/main/res/values-bg/strings.xml | 8 - src/main/res/values-bn-rIN/strings.xml | 1 - src/main/res/values-ca/strings.xml | 8 - src/main/res/values-cs/strings.xml | 8 - src/main/res/values-da-rDK/strings.xml | 8 - src/main/res/values-de/strings.xml | 9 - src/main/res/values-el/strings.xml | 8 - src/main/res/values-es/strings.xml | 9 - src/main/res/values-eu/strings.xml | 8 - src/main/res/values-fa-rIR/strings.xml | 8 - src/main/res/values-fi/strings.xml | 7 - src/main/res/values-fr/strings.xml | 8 - src/main/res/values-gl/strings.xml | 9 - src/main/res/values-hr/strings.xml | 1 - src/main/res/values-hu/strings.xml | 8 - src/main/res/values-id/strings.xml | 6 - src/main/res/values-it/strings.xml | 8 - src/main/res/values-iw/strings.xml | 1 - src/main/res/values-ja/strings.xml | 9 - src/main/res/values-ko/strings.xml | 4 - src/main/res/values-nb-rNO/strings.xml | 4 - src/main/res/values-night/colors.xml | 5 - src/main/res/values-night/themes.xml | 32 ++ src/main/res/values-nl/strings.xml | 8 - src/main/res/values-pl/strings.xml | 9 - src/main/res/values-pt-rBR/strings.xml | 8 - src/main/res/values-pt/strings.xml | 4 - src/main/res/values-ro-rRO/strings.xml | 9 - src/main/res/values-ru/strings.xml | 9 - src/main/res/values-sk/strings.xml | 4 - src/main/res/values-sq-rAL/strings.xml | 8 - src/main/res/values-sr/strings.xml | 5 - src/main/res/values-sv/strings.xml | 8 - src/main/res/values-szl/strings.xml | 8 - src/main/res/values-tr-rTR/strings.xml | 8 - src/main/res/values-uk/strings.xml | 9 - src/main/res/values-vi/strings.xml | 8 - src/main/res/values-zh-rCN/strings.xml | 9 - src/main/res/values-zh-rTW/strings.xml | 8 - src/main/res/values/arrays.xml | 11 - src/main/res/values/attrs.xml | 127 +---- src/main/res/values/colors-md.xml | 43 ++ src/main/res/values/colors.xml | 47 -- src/main/res/values/strings.xml | 17 +- src/main/res/values/styles.xml | 162 ------- src/main/res/values/themes.xml | 436 ++---------------- src/main/res/xml/preferences.xml | 37 +- .../ui/ChooseCountryActivity.java | 7 +- .../conversations/ui/EnterNameActivity.java | 4 +- .../ui/EnterPhoneNumberActivity.java | 3 +- .../siacs/conversations/ui/TosActivity.java | 26 +- .../conversations/ui/VerifyActivity.java | 7 +- .../ui/drawable/TextDrawable.java | 7 +- .../res/layout/activity_choose_country.xml | 20 +- .../res/layout/activity_enter_name.xml | 44 +- .../res/layout/activity_enter_number.xml | 33 +- src/quicksy/res/layout/activity_tos.xml | 144 +++--- src/quicksy/res/layout/activity_verify.xml | 34 +- src/quicksy/res/layout/country_item.xml | 13 +- ...se_country.xml => item_choose_country.xml} | 2 +- src/quicksy/res/values/colors-themed.xml | 64 +++ 1427 files changed, 5140 insertions(+), 5848 deletions(-) delete mode 100644 src/conversations/res/drawable-hdpi/ic_unarchive_white_24dp.png delete mode 100644 src/conversations/res/drawable-mdpi/ic_unarchive_white_24dp.png delete mode 100644 src/conversations/res/drawable-xhdpi/ic_unarchive_white_24dp.png delete mode 100644 src/conversations/res/drawable-xxhdpi/ic_unarchive_white_24dp.png delete mode 100644 src/conversations/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png rename src/conversations/res/layout/{magic_create.xml => activity_magic_create.xml} (64%) create mode 100644 src/conversations/res/values/colors-themed.xml create mode 100644 src/main/java/eu/siacs/conversations/Conversations.java create mode 100644 src/main/java/eu/siacs/conversations/ui/Activities.java create mode 100644 src/main/java/eu/siacs/conversations/ui/BaseActivity.java delete mode 100644 src/main/java/eu/siacs/conversations/ui/forms/FormBooleanFieldWrapper.java delete mode 100644 src/main/java/eu/siacs/conversations/ui/forms/FormFieldFactory.java delete mode 100644 src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java delete mode 100644 src/main/java/eu/siacs/conversations/ui/forms/FormJidSingleFieldWrapper.java delete mode 100644 src/main/java/eu/siacs/conversations/ui/forms/FormTextFieldWrapper.java delete mode 100644 src/main/java/eu/siacs/conversations/ui/forms/FormWrapper.java delete mode 100644 src/main/java/eu/siacs/conversations/ui/util/ActionBarUtil.java delete mode 100644 src/main/java/eu/siacs/conversations/ui/util/StyledAttributes.java create mode 100644 src/main/java/eu/siacs/conversations/ui/util/ToolbarUtils.java delete mode 100644 src/main/java/eu/siacs/conversations/utils/ThemeHelper.java delete mode 100644 src/main/res/drawable-hdpi/baseline_tour_black_48.png delete mode 100644 src/main/res/drawable-hdpi/baseline_tour_white_48.png delete mode 100644 src/main/res/drawable-hdpi/date_bubble_grey.9.png delete mode 100644 src/main/res/drawable-hdpi/date_bubble_white.9.png delete mode 100644 src/main/res/drawable-hdpi/ic_account_box_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_action_reply.png delete mode 100644 src/main/res/drawable-hdpi/ic_add_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_android_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_android_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_announcement_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_archive_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_archive_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_archive_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_camera.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_camera_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_document.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_document_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_file_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_location.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_location_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_photo.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_photo_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_record.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_record_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_videocam.png delete mode 100644 src/main/res/drawable-hdpi/ic_attach_videocam_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_autorenew_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_backup_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_backup_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_block_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_bluetooth_audio_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_book_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_book_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_end_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_made_black_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_made_white_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_missed_black_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_missed_outgoing_black_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_missed_outgoing_white_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_missed_white_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_received_black_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_received_white_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_call_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_cancel_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_cancel_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_chat_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_clear_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_cloud_download_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_content_copy_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_crop_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_delete_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_delete_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_description_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_description_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_directions_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_directions_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_done_black_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_done_white_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_drafts_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_edit_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_edit_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_error_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_event_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_event_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_file_download_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_flip_camera_android_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_forward_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_gps_fixed_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_gps_fixed_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_gps_not_fixed_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_gps_not_fixed_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_group_add_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_group_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_headset_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_headset_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_headset_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_help_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_help_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_help_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_hourglass_empty_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_image_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_image_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_input_white_24dp.png delete mode 100755 src/main/res/drawable-hdpi/ic_link_off_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_link_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_lock_black_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_lock_open_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_lock_white_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_lock_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_mic_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_mic_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_mic_off_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_mic_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_mode_edit_black_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_mode_edit_white_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_new_releases_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_new_releases_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_no_results_background_black.png delete mode 100644 src/main/res/drawable-hdpi/ic_no_results_background_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_none_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_none_white80.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_none_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_off_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_off_white80.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_off_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_paused_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_paused_white80.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_paused_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_white80.png delete mode 100644 src/main/res/drawable-hdpi/ic_notifications_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_pause_black_36dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_pause_white_36dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_person_add_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_person_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_person_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_phone_in_talk_black_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_phone_in_talk_white_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_phone_in_talk_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_play_arrow_black_36dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_play_arrow_white_36dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_play_circle_filled_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_profile.png delete mode 100644 src/main/res/drawable-hdpi/ic_public_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_qr_code_scan_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_question_answer_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_refresh_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_refresh_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_replay_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_reply_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_room_black_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_room_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_room_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_save_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_save_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_search_background_black.png delete mode 100644 src/main/res/drawable-hdpi/ic_search_background_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_search_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_cancel_away.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_cancel_dnd.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_cancel_offline.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_cancel_offline_dark.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_cancel_offline_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_cancel_online.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_file_offline.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_file_offline_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_location_away.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_location_dnd.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_location_offline.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_location_offline_dark.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_location_offline_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_location_online.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_photo_away.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_photo_dnd.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_photo_offline.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_photo_offline_dark.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_photo_offline_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_photo_online.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_picture_away.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_picture_dnd.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_picture_offline.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_picture_offline_dark.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_picture_offline_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_picture_online.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_text_away.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_text_dnd.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_text_offline.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_text_offline_dark.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_text_offline_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_text_online.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_videocam_away.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_videocam_dnd.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_videocam_offline.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_videocam_offline_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_videocam_online.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_voice_away.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_voice_dnd.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_voice_offline.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_voice_offline_dark.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_voice_offline_white.png delete mode 100644 src/main/res/drawable-hdpi/ic_send_voice_online.png delete mode 100644 src/main/res/drawable-hdpi/ic_settings_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_settings_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_share_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_star_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_star_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_stat_alert_warning.png delete mode 100644 src/main/res/drawable-hdpi/ic_stat_communication_import_export.png delete mode 100644 src/main/res/drawable-hdpi/ic_verified_fingerprint.png delete mode 100644 src/main/res/drawable-hdpi/ic_verified_user_black_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_verified_user_white_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_videocam_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_videocam_off_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_videocam_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_voicemail_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_volume_off_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_volume_up_black_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_warning_white_24dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_warning_white_48dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_wear_reply.png delete mode 100644 src/main/res/drawable-hdpi/message_bubble_received.9.png delete mode 100644 src/main/res/drawable-hdpi/message_bubble_received_dark.9.png delete mode 100644 src/main/res/drawable-hdpi/message_bubble_received_grey.9.png delete mode 100644 src/main/res/drawable-hdpi/message_bubble_received_warning.9.png delete mode 100644 src/main/res/drawable-hdpi/message_bubble_received_white.9.png delete mode 100644 src/main/res/drawable-hdpi/message_bubble_sent.9.png delete mode 100644 src/main/res/drawable-hdpi/message_bubble_sent_grey.9.png delete mode 100644 src/main/res/drawable-mdpi/baseline_tour_black_48.png delete mode 100644 src/main/res/drawable-mdpi/baseline_tour_white_48.png delete mode 100644 src/main/res/drawable-mdpi/date_bubble_grey.9.png delete mode 100644 src/main/res/drawable-mdpi/date_bubble_white.9.png delete mode 100644 src/main/res/drawable-mdpi/ic_account_box_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_action_reply.png delete mode 100644 src/main/res/drawable-mdpi/ic_add_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_android_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_android_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_announcement_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_archive_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_archive_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_archive_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_camera.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_camera_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_document.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_document_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_file_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_location.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_location_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_photo.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_photo_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_record.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_record_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_videocam.png delete mode 100644 src/main/res/drawable-mdpi/ic_attach_videocam_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_autorenew_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_backup_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_backup_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_block_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_bluetooth_audio_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_book_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_book_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_end_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_made_black_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_made_white_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_missed_black_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_missed_outgoing_black_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_missed_outgoing_white_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_missed_white_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_received_black_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_received_white_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_call_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_cancel_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_cancel_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_chat_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_clear_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_cloud_download_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_content_copy_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_crop_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_delete_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_delete_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_description_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_description_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_directions_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_directions_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_done_black_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_done_white_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_drafts_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_edit_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_edit_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_error_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_event_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_event_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_file_download_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_flip_camera_android_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_forward_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_gps_fixed_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_gps_fixed_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_gps_not_fixed_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_gps_not_fixed_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_group_add_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_group_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_headset_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_headset_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_headset_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_help_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_help_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_help_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_hourglass_empty_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_image_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_image_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_input_white_24dp.png delete mode 100755 src/main/res/drawable-mdpi/ic_link_off_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_link_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_lock_black_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_lock_open_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_lock_white_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_lock_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_mic_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_mic_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_mic_off_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_mic_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_mode_edit_black_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_mode_edit_white_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_new_releases_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_new_releases_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_no_results_background_black.png delete mode 100644 src/main/res/drawable-mdpi/ic_no_results_background_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_none_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_none_white80.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_none_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_off_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_off_white80.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_off_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_paused_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_paused_white80.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_paused_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_white80.png delete mode 100644 src/main/res/drawable-mdpi/ic_notifications_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_pause_black_36dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_pause_white_36dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_person_add_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_person_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_person_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_phone_in_talk_black_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_phone_in_talk_white_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_phone_in_talk_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_play_arrow_black_36dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_play_arrow_white_36dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_play_circle_filled_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_profile.png delete mode 100644 src/main/res/drawable-mdpi/ic_public_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_qr_code_scan_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_question_answer_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_refresh_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_refresh_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_replay_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_reply_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_room_black_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_room_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_room_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_save_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_save_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_search_background_black.png delete mode 100644 src/main/res/drawable-mdpi/ic_search_background_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_search_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_cancel_away.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_cancel_dnd.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_cancel_offline.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_cancel_offline_dark.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_cancel_offline_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_cancel_online.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_file_offline.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_file_offline_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_location_away.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_location_dnd.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_location_offline.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_location_offline_dark.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_location_offline_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_location_online.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_photo_away.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_photo_dnd.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_photo_offline.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_photo_offline_dark.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_photo_offline_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_photo_online.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_picture_away.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_picture_dnd.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_picture_offline.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_picture_offline_dark.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_picture_offline_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_picture_online.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_text_away.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_text_dnd.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_text_offline.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_text_offline_dark.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_text_offline_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_text_online.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_videocam_away.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_videocam_dnd.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_videocam_offline.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_videocam_offline_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_videocam_online.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_voice_away.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_voice_dnd.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_voice_offline.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_voice_offline_dark.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_voice_offline_white.png delete mode 100644 src/main/res/drawable-mdpi/ic_send_voice_online.png delete mode 100644 src/main/res/drawable-mdpi/ic_settings_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_settings_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_share_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_star_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_star_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_stat_alert_warning.png delete mode 100644 src/main/res/drawable-mdpi/ic_stat_communication_import_export.png delete mode 100644 src/main/res/drawable-mdpi/ic_verified_fingerprint.png delete mode 100644 src/main/res/drawable-mdpi/ic_verified_user_black_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_verified_user_white_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_videocam_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_videocam_off_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_videocam_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_voicemail_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_volume_off_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_volume_up_black_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_warning_white_24dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_warning_white_48dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_wear_reply.png delete mode 100644 src/main/res/drawable-mdpi/message_bubble_received.9.png delete mode 100644 src/main/res/drawable-mdpi/message_bubble_received_dark.9.png delete mode 100644 src/main/res/drawable-mdpi/message_bubble_received_grey.9.png delete mode 100644 src/main/res/drawable-mdpi/message_bubble_received_warning.9.png delete mode 100644 src/main/res/drawable-mdpi/message_bubble_received_white.9.png delete mode 100644 src/main/res/drawable-mdpi/message_bubble_sent.9.png delete mode 100644 src/main/res/drawable-mdpi/message_bubble_sent_grey.9.png delete mode 100644 src/main/res/drawable-mdpi/play_gif_black.png delete mode 100644 src/main/res/drawable-mdpi/play_gif_white.png delete mode 100644 src/main/res/drawable-mdpi/play_video_black.png delete mode 100644 src/main/res/drawable-mdpi/play_video_white.png delete mode 100644 src/main/res/drawable-xhdpi/baseline_tour_black_48.png delete mode 100644 src/main/res/drawable-xhdpi/baseline_tour_white_48.png delete mode 100644 src/main/res/drawable-xhdpi/date_bubble_grey.9.png delete mode 100644 src/main/res/drawable-xhdpi/date_bubble_white.9.png delete mode 100644 src/main/res/drawable-xhdpi/ic_account_box_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_action_reply.png delete mode 100644 src/main/res/drawable-xhdpi/ic_add_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_android_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_android_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_announcement_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_archive_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_archive_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_archive_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_camera.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_camera_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_document.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_document_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_file_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_location.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_location_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_photo.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_photo_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_record.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_record_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_videocam.png delete mode 100644 src/main/res/drawable-xhdpi/ic_attach_videocam_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_autorenew_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_backup_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_backup_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_block_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_bluetooth_audio_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_book_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_book_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_end_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_made_black_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_made_white_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_missed_black_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_missed_outgoing_black_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_missed_outgoing_white_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_missed_white_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_received_black_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_received_white_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_call_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_camera_alt_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_cancel_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_cancel_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_chat_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_clear_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_cloud_download_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_content_copy_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_crop_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_delete_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_delete_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_description_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_description_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_directions_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_directions_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_done_black_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_done_white_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_drafts_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_edit_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_edit_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_error_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_event_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_event_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_file_download_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_flip_camera_android_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_forward_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_gps_fixed_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_gps_fixed_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_gps_not_fixed_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_gps_not_fixed_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_group_add_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_group_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_headset_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_headset_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_headset_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_help_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_help_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_help_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_hourglass_empty_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_image_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_image_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_input_white_24dp.png delete mode 100755 src/main/res/drawable-xhdpi/ic_link_off_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_link_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_lock_black_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_lock_open_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_lock_white_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_lock_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_mic_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_mic_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_mic_off_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_mic_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_mode_edit_black_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_mode_edit_white_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_new_releases_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_new_releases_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_no_results_background_black.png delete mode 100644 src/main/res/drawable-xhdpi/ic_no_results_background_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_none_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_none_white80.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_none_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_off_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_off_white80.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_off_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_paused_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_paused_white80.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_paused_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_white80.png delete mode 100644 src/main/res/drawable-xhdpi/ic_notifications_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_pause_black_36dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_pause_white_36dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_person_add_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_person_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_person_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_phone_in_talk_black_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_phone_in_talk_white_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_phone_in_talk_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_play_arrow_black_36dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_play_arrow_white_36dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_play_circle_filled_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_profile.png delete mode 100644 src/main/res/drawable-xhdpi/ic_public_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_qr_code_scan_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_question_answer_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_refresh_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_replay_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_reply_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_room_black_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_room_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_room_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_save_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_save_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_search_background_black.png delete mode 100644 src/main/res/drawable-xhdpi/ic_search_background_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_search_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_cancel_away.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_cancel_dnd.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_cancel_offline.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_cancel_offline_dark.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_cancel_offline_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_cancel_online.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_file_offline.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_file_offline_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_location_away.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_location_dnd.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_location_offline.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_location_offline_dark.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_location_offline_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_location_online.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_photo_away.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_photo_dnd.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_photo_offline.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_photo_offline_dark.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_photo_offline_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_photo_online.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_picture_away.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_picture_dnd.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_picture_offline.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_picture_offline_dark.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_picture_offline_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_picture_online.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_text_away.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_text_dnd.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_text_offline.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_text_offline_dark.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_text_offline_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_text_online.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_videocam_away.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_videocam_dnd.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_videocam_offline.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_videocam_offline_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_videocam_online.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_voice_away.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_voice_dnd.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_voice_offline.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_voice_offline_dark.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_voice_offline_white.png delete mode 100644 src/main/res/drawable-xhdpi/ic_send_voice_online.png delete mode 100644 src/main/res/drawable-xhdpi/ic_settings_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_settings_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_share_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_star_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_star_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_stat_alert_warning.png delete mode 100644 src/main/res/drawable-xhdpi/ic_stat_communication_import_export.png delete mode 100644 src/main/res/drawable-xhdpi/ic_verified_fingerprint.png delete mode 100644 src/main/res/drawable-xhdpi/ic_verified_user_black_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_verified_user_white_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_videocam_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_videocam_off_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_videocam_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_voicemail_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_volume_off_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_volume_up_black_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_warning_white_24dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_warning_white_48dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_wear_reply.png delete mode 100644 src/main/res/drawable-xhdpi/message_bubble_received.9.png delete mode 100644 src/main/res/drawable-xhdpi/message_bubble_received_dark.9.png delete mode 100644 src/main/res/drawable-xhdpi/message_bubble_received_grey.9.png delete mode 100644 src/main/res/drawable-xhdpi/message_bubble_received_warning.9.png delete mode 100644 src/main/res/drawable-xhdpi/message_bubble_received_white.9.png delete mode 100644 src/main/res/drawable-xhdpi/message_bubble_sent.9.png delete mode 100644 src/main/res/drawable-xhdpi/message_bubble_sent_grey.9.png delete mode 100644 src/main/res/drawable-xxhdpi/baseline_tour_black_48.png delete mode 100644 src/main/res/drawable-xxhdpi/baseline_tour_white_48.png delete mode 100644 src/main/res/drawable-xxhdpi/date_bubble_grey.9.png delete mode 100644 src/main/res/drawable-xxhdpi/date_bubble_white.9.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_account_box_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_action_reply.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_add_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_android_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_android_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_announcement_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_archive_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_camera.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_camera_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_document.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_document_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_file_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_location.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_location_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_photo.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_photo_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_record.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_record_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_videocam.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_attach_videocam_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_autorenew_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_backup_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_backup_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_block_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_bluetooth_audio_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_book_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_book_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_end_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_made_black_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_made_white_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_missed_black_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_missed_outgoing_black_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_missed_outgoing_white_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_missed_white_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_received_black_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_received_white_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_call_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_cancel_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_cancel_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_chat_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_clear_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_cloud_download_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_content_copy_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_crop_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_delete_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_description_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_description_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_directions_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_directions_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_done_black_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_done_white_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_drafts_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_edit_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_edit_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_error_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_event_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_event_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_file_download_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_flip_camera_android_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_forward_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_gps_fixed_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_gps_fixed_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_gps_not_fixed_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_gps_not_fixed_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_group_add_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_group_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_headset_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_headset_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_headset_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_help_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_help_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_help_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_hourglass_empty_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_image_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_image_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_input_white_24dp.png delete mode 100755 src/main/res/drawable-xxhdpi/ic_link_off_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_link_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_lock_black_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_lock_open_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_lock_white_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_mic_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_mic_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_mic_off_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_mode_edit_black_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_mode_edit_white_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_new_releases_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_new_releases_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_no_results_background_black.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_no_results_background_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_none_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_none_white80.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_none_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_off_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_off_white80.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_off_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_paused_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_paused_white80.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_paused_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_white80.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_notifications_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_pause_black_36dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_pause_white_36dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_person_add_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_person_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_person_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_phone_in_talk_black_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_phone_in_talk_white_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_phone_in_talk_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_play_arrow_black_36dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_play_circle_filled_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_profile.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_public_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_qr_code_scan_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_question_answer_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_refresh_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_replay_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_reply_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_room_black_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_room_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_room_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_save_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_save_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_search_background_black.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_search_background_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_search_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_cancel_away.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_cancel_dnd.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_cancel_offline.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_cancel_offline_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_cancel_online.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_file_offline.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_file_offline_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_location_away.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_location_dnd.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_location_offline.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_location_offline_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_location_online.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_photo_away.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_photo_dnd.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_photo_offline.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_photo_offline_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_photo_online.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_picture_away.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_picture_dnd.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_picture_offline.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_picture_offline_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_picture_online.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_text_away.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_text_dnd.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_text_offline.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_text_offline_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_text_online.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_videocam_away.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_videocam_dnd.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_videocam_offline.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_videocam_offline_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_videocam_online.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_voice_away.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_voice_dnd.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_voice_offline.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_voice_offline_white.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_send_voice_online.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_settings_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_share_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_star_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_star_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_stat_alert_warning.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_stat_communication_import_export.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_verified_fingerprint.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_verified_user_black_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_verified_user_white_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_videocam_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_videocam_off_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_videocam_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_voicemail_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_volume_off_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_volume_up_black_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_warning_white_24dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_warning_white_48dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_wear_reply.png delete mode 100644 src/main/res/drawable-xxhdpi/message_bubble_received.9.png delete mode 100644 src/main/res/drawable-xxhdpi/message_bubble_received_dark.9.png delete mode 100644 src/main/res/drawable-xxhdpi/message_bubble_received_grey.9.png delete mode 100644 src/main/res/drawable-xxhdpi/message_bubble_received_warning.9.png delete mode 100644 src/main/res/drawable-xxhdpi/message_bubble_received_white.9.png delete mode 100644 src/main/res/drawable-xxhdpi/message_bubble_sent.9.png delete mode 100644 src/main/res/drawable-xxhdpi/message_bubble_sent_grey.9.png delete mode 100644 src/main/res/drawable-xxxhdpi/baseline_tour_black_48.png delete mode 100644 src/main/res/drawable-xxxhdpi/baseline_tour_white_48.png delete mode 100644 src/main/res/drawable-xxxhdpi/date_bubble_grey.9.png delete mode 100644 src/main/res/drawable-xxxhdpi/date_bubble_white.9.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_account_box_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_android_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_android_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_announcement_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_archive_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_camera.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_camera_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_document.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_document_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_file_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_location.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_location_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_photo.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_photo_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_record.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_record_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_videocam.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_attach_videocam_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_autorenew_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_backup_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_backup_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_block_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_bluetooth_audio_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_book_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_book_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_end_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_made_black_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_made_white_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_missed_black_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_missed_outgoing_black_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_missed_outgoing_white_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_missed_white_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_received_black_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_received_white_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_call_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_cancel_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_cancel_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_chat_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_clear_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_cloud_download_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_content_copy_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_crop_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_delete_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_description_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_description_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_directions_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_directions_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_done_black_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_done_white_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_drafts_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_edit_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_edit_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_error_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_event_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_event_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_file_download_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_flip_camera_android_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_forward_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_gps_fixed_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_gps_fixed_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_gps_not_fixed_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_gps_not_fixed_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_group_add_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_group_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_headset_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_headset_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_headset_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_help_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_help_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_help_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_hourglass_empty_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_image_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_image_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_input_white_24dp.png delete mode 100755 src/main/res/drawable-xxxhdpi/ic_link_off_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_link_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_lock_black_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_lock_open_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_lock_white_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_mic_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_mic_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_mic_off_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_mode_edit_black_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_mode_edit_white_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_new_releases_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_new_releases_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_no_results_background_black.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_no_results_background_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_none_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_none_white80.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_none_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_off_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_off_white80.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_off_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_paused_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_paused_white80.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_paused_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_white80.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_notifications_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_pause_black_36dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_pause_white_36dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_person_add_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_person_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_person_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_phone_in_talk_black_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_phone_in_talk_white_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_phone_in_talk_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_play_arrow_black_36dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_play_circle_filled_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_public_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_qr_code_scan_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_question_answer_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_refresh_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_replay_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_reply_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_room_black_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_room_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_room_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_save_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_save_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_search_background_black.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_search_background_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_cancel_away.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_cancel_dnd.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_cancel_offline.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_cancel_offline_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_cancel_online.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_location_away.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_location_dnd.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_location_offline.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_location_offline_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_location_online.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_photo_away.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_photo_dnd.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_photo_offline.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_photo_offline_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_photo_online.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_picture_away.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_picture_dnd.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_picture_offline.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_picture_offline_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_picture_online.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_text_away.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_text_dnd.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_text_offline.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_text_offline_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_text_online.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_videocam_away.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_videocam_dnd.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_videocam_offline.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_videocam_offline_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_videocam_online.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_voice_away.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_voice_dnd.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_voice_offline.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_voice_offline_white.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_send_voice_online.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_settings_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_star_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_star_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_verified_fingerprint.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_verified_user_black_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_verified_user_white_18dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_videocam_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_videocam_off_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_videocam_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_voicemail_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_volume_off_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_volume_up_black_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_warning_white_24dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_warning_white_48dp.png delete mode 100644 src/main/res/drawable-xxxhdpi/message_bubble_received.9.png delete mode 100644 src/main/res/drawable-xxxhdpi/message_bubble_received_dark.9.png delete mode 100644 src/main/res/drawable-xxxhdpi/message_bubble_received_grey.9.png delete mode 100644 src/main/res/drawable-xxxhdpi/message_bubble_received_warning.9.png delete mode 100644 src/main/res/drawable-xxxhdpi/message_bubble_received_white.9.png delete mode 100644 src/main/res/drawable-xxxhdpi/message_bubble_sent.9.png delete mode 100644 src/main/res/drawable-xxxhdpi/message_bubble_sent_grey.9.png rename src/main/res/drawable/{account_image_border.xml => background_account_profile_picture.xml} (66%) create mode 100644 src/main/res/drawable/background_list_item.xml create mode 100644 src/main/res/drawable/background_message_bubble.xml rename src/main/res/drawable/{grey.xml => background_message_separator.xml} (51%) create mode 100644 src/main/res/drawable/background_no_results.xml create mode 100644 src/main/res/drawable/background_search.xml create mode 100644 src/main/res/drawable/background_selectable_list_item.xml create mode 100644 src/main/res/drawable/background_selected_item_conversation.xml rename src/main/res/drawable/{background.xml => background_splash_screen.xml} (79%) create mode 100644 src/main/res/drawable/background_surface_container.xml delete mode 100644 src/main/res/drawable/es_slidingpane_shadow.xml delete mode 100644 src/main/res/drawable/greybackground.xml create mode 100644 src/main/res/drawable/ic_adb_48dp.xml rename src/main/res/drawable/{ic_scroll_to_end_black.xml => ic_add_24dp.xml} (51%) create mode 100644 src/main/res/drawable/ic_announcement_24dp.xml create mode 100644 src/main/res/drawable/ic_archive_24dp.xml create mode 100644 src/main/res/drawable/ic_archive_48dp.xml create mode 100644 src/main/res/drawable/ic_attach_file_24dp.xml create mode 100644 src/main/res/drawable/ic_autorenew_24dp.xml create mode 100644 src/main/res/drawable/ic_backup_48dp.xml create mode 100644 src/main/res/drawable/ic_bluetooth_audio_24dp.xml create mode 100644 src/main/res/drawable/ic_book_48dp.xml create mode 100644 src/main/res/drawable/ic_call_24dp.xml delete mode 100644 src/main/res/drawable/ic_call_black54_24dp.xml create mode 100644 src/main/res/drawable/ic_call_end_24dp.xml create mode 100644 src/main/res/drawable/ic_call_made_24dp.xml create mode 100644 src/main/res/drawable/ic_call_missed_24db.xml create mode 100644 src/main/res/drawable/ic_call_missed_24dp.xml create mode 100644 src/main/res/drawable/ic_call_missed_outgoing_24dp.xml delete mode 100644 src/main/res/drawable/ic_call_missed_white_24db.xml create mode 100644 src/main/res/drawable/ic_call_received_24dp.xml delete mode 100644 src/main/res/drawable/ic_call_white70_24dp.xml create mode 100644 src/main/res/drawable/ic_camera_alt_24dp.xml create mode 100644 src/main/res/drawable/ic_cancel_24dp.xml create mode 100644 src/main/res/drawable/ic_cancel_96dp.xml create mode 100644 src/main/res/drawable/ic_chat_24dp.xml create mode 100644 src/main/res/drawable/ic_check_24dp.xml create mode 100644 src/main/res/drawable/ic_clear_24dp.xml create mode 100644 src/main/res/drawable/ic_cloud_download_24dp.xml create mode 100644 src/main/res/drawable/ic_code_48dp.xml create mode 100644 src/main/res/drawable/ic_content_copy_24dp.xml create mode 100644 src/main/res/drawable/ic_delete_24dp.xml create mode 100644 src/main/res/drawable/ic_description_24dp.xml create mode 100644 src/main/res/drawable/ic_description_48dp.xml create mode 100644 src/main/res/drawable/ic_directions_24dp.xml delete mode 100644 src/main/res/drawable/ic_directions_black_24dp.xml create mode 100644 src/main/res/drawable/ic_done_24dp.xml create mode 100644 src/main/res/drawable/ic_download_24dp.xml create mode 100644 src/main/res/drawable/ic_edit_24dp.xml create mode 100644 src/main/res/drawable/ic_email_48dp.xml create mode 100644 src/main/res/drawable/ic_error_24dp.xml create mode 100644 src/main/res/drawable/ic_event_48dp.xml create mode 100644 src/main/res/drawable/ic_flip_camera_android_24dp.xml create mode 100644 src/main/res/drawable/ic_forum_24dp.xml create mode 100644 src/main/res/drawable/ic_gps_fixed_24dp.xml delete mode 100644 src/main/res/drawable/ic_gps_fixed_black_24dp.xml create mode 100644 src/main/res/drawable/ic_gps_not_fixed_24dp.xml delete mode 100644 src/main/res/drawable/ic_gps_not_fixed_black_24dp.xml create mode 100644 src/main/res/drawable/ic_group_24dp.xml create mode 100644 src/main/res/drawable/ic_headphones_48dp.xml create mode 100644 src/main/res/drawable/ic_headset_mic_24dp.xml create mode 100644 src/main/res/drawable/ic_help_24dp.xml create mode 100644 src/main/res/drawable/ic_help_center_48dp.xml create mode 100644 src/main/res/drawable/ic_hourglass_top_24dp.xml create mode 100644 src/main/res/drawable/ic_image_24dp.xml create mode 100644 src/main/res/drawable/ic_image_48dp.xml create mode 100644 src/main/res/drawable/ic_keyboard_double_arrow_down_24dp.xml create mode 100644 src/main/res/drawable/ic_link_24dp.xml create mode 100644 src/main/res/drawable/ic_link_off_24dp.xml create mode 100644 src/main/res/drawable/ic_location_pin_24dp.xml create mode 100644 src/main/res/drawable/ic_location_pin_48dp.xml create mode 100644 src/main/res/drawable/ic_lock_24dp.xml create mode 100644 src/main/res/drawable/ic_lock_open_outline_24dp.xml create mode 100644 src/main/res/drawable/ic_login_24dp.xml create mode 100644 src/main/res/drawable/ic_logout_24dp.xml delete mode 100644 src/main/res/drawable/ic_logout_white_24dp.xml create mode 100644 src/main/res/drawable/ic_mark_chat_read_24dp.xml create mode 100644 src/main/res/drawable/ic_mic_24dp.xml create mode 100644 src/main/res/drawable/ic_mic_48dp.xml create mode 100644 src/main/res/drawable/ic_mic_off_24dp.xml create mode 100644 src/main/res/drawable/ic_movie_48dp.xml create mode 100644 src/main/res/drawable/ic_navigate_next_24dp.xml rename src/main/res/drawable/{ic_baseline_check_24.xml => ic_new_releases_24dp.xml} (52%) create mode 100644 src/main/res/drawable/ic_notifications_24dp.xml create mode 100644 src/main/res/drawable/ic_notifications_none_24dp.xml create mode 100644 src/main/res/drawable/ic_notifications_off_24dp.xml create mode 100644 src/main/res/drawable/ic_notifications_paused_24dp.xml create mode 100644 src/main/res/drawable/ic_pause_24dp.xml create mode 100644 src/main/res/drawable/ic_person_24dp.xml create mode 100644 src/main/res/drawable/ic_person_48dp.xml create mode 100644 src/main/res/drawable/ic_person_add_24dp.xml create mode 100644 src/main/res/drawable/ic_phone_in_talk_24dp.xml delete mode 100644 src/main/res/drawable/ic_place_black_24dp.xml create mode 100644 src/main/res/drawable/ic_play_arrow_24dp.xml create mode 100644 src/main/res/drawable/ic_play_circle_24dp.xml create mode 100644 src/main/res/drawable/ic_play_lesson_48dp.xml delete mode 100644 src/main/res/drawable/ic_play_lesson_black_24.xml delete mode 100644 src/main/res/drawable/ic_play_lesson_white_48dp.xml create mode 100644 src/main/res/drawable/ic_public_24dp.xml create mode 100644 src/main/res/drawable/ic_qr_code_24dp.xml delete mode 100644 src/main/res/drawable/ic_qr_code_black_24dp.xml create mode 100644 src/main/res/drawable/ic_qr_code_scanner_24dp.xml delete mode 100644 src/main/res/drawable/ic_qr_code_white_24dp.xml create mode 100644 src/main/res/drawable/ic_refresh_24dp.xml create mode 100644 src/main/res/drawable/ic_replay_24dp.xml create mode 100644 src/main/res/drawable/ic_reply_24dp.xml create mode 100644 src/main/res/drawable/ic_save_24dp.xml delete mode 100644 src/main/res/drawable/ic_scroll_to_end_white.xml create mode 100644 src/main/res/drawable/ic_search_128dp.xml create mode 100644 src/main/res/drawable/ic_search_24dp.xml create mode 100644 src/main/res/drawable/ic_send_24dp.xml create mode 100644 src/main/res/drawable/ic_settings_24dp.xml create mode 100644 src/main/res/drawable/ic_share_24dp.xml create mode 100644 src/main/res/drawable/ic_star_24dp.xml create mode 100644 src/main/res/drawable/ic_tour_48dp.xml create mode 100644 src/main/res/drawable/ic_travel_explore_24dp.xml create mode 100644 src/main/res/drawable/ic_unarchive_24dp.xml create mode 100644 src/main/res/drawable/ic_verified_user_24dp.xml create mode 100644 src/main/res/drawable/ic_videocam_24dp.xml delete mode 100644 src/main/res/drawable/ic_videocam_black54_24dp.xml create mode 100644 src/main/res/drawable/ic_videocam_off_24dp.xml delete mode 100644 src/main/res/drawable/ic_videocam_white70_24dp.xml create mode 100644 src/main/res/drawable/ic_voicemail_24dp.xml create mode 100644 src/main/res/drawable/ic_volume_off_24dp.xml create mode 100644 src/main/res/drawable/ic_volume_up_24dp.xml create mode 100644 src/main/res/drawable/ic_warning_24dp.xml delete mode 100644 src/main/res/drawable/list_item_background_dark.xml delete mode 100644 src/main/res/drawable/list_item_background_light.xml delete mode 100644 src/main/res/drawable/no_results_background_dark.xml delete mode 100644 src/main/res/drawable/no_results_background_light.xml delete mode 100644 src/main/res/drawable/no_results_primary_background_dark.xml delete mode 100644 src/main/res/drawable/no_results_primary_background_light.xml delete mode 100644 src/main/res/drawable/search_background_dark.xml delete mode 100644 src/main/res/drawable/search_background_light.xml delete mode 100644 src/main/res/drawable/visibility_toggle_drawable.xml delete mode 100644 src/main/res/drawable/white_cursor.xml delete mode 100644 src/main/res/layout/create_conference_dialog.xml create mode 100644 src/main/res/layout/dialog_create_conference.xml rename src/main/res/layout/{create_public_channel_dialog.xml => dialog_create_public_channel.xml} (60%) create mode 100644 src/main/res/layout/dialog_enter_jid.xml delete mode 100644 src/main/res/layout/enter_jid_dialog.xml delete mode 100644 src/main/res/layout/form_boolean.xml delete mode 100644 src/main/res/layout/form_text.xml rename src/main/res/layout/{account_row.xml => item_account.xml} (76%) create mode 100644 src/main/res/layout/item_autocomplete.xml rename src/main/res/layout/{search_result_item.xml => item_channel_discovery.xml} (69%) rename src/main/res/layout/{contact.xml => item_contact.xml} (61%) rename src/main/res/layout/{conversation_list_row.xml => item_conversation.xml} (59%) rename src/main/res/layout/{media.xml => item_media.xml} (69%) rename src/main/res/layout/{media_preview.xml => item_media_preview.xml} (74%) create mode 100644 src/main/res/layout/item_message_content.xml create mode 100644 src/main/res/layout/item_message_date_bubble.xml create mode 100644 src/main/res/layout/item_message_received.xml create mode 100644 src/main/res/layout/item_message_rtp_session.xml create mode 100644 src/main/res/layout/item_message_sent.xml create mode 100644 src/main/res/layout/item_message_status.xml rename src/main/res/layout/{user_preview.xml => item_user_preview.xml} (71%) delete mode 100644 src/main/res/layout/message_content.xml delete mode 100644 src/main/res/layout/message_date_bubble.xml delete mode 100644 src/main/res/layout/message_received.xml delete mode 100644 src/main/res/layout/message_rtp_session.xml delete mode 100644 src/main/res/layout/message_sent.xml delete mode 100644 src/main/res/layout/message_status.xml delete mode 100644 src/main/res/layout/presence_template.xml delete mode 100644 src/main/res/layout/simple_list_item.xml delete mode 100644 src/main/res/layout/toolbar.xml delete mode 100644 src/main/res/values-night/colors.xml create mode 100644 src/main/res/values-night/themes.xml create mode 100644 src/main/res/values/colors-md.xml delete mode 100644 src/main/res/values/colors.xml delete mode 100644 src/main/res/values/styles.xml rename src/quicksy/res/menu/{choose_country.xml => item_choose_country.xml} (88%) create mode 100644 src/quicksy/res/values/colors-themed.xml diff --git a/build.gradle b/build.gradle index 908b80c17704a1fbe77a1eb9f56b4faebbe58aec..2540a7836f94d5606604e5bedc4f7a43b5b5f98a 100644 --- a/build.gradle +++ b/build.gradle @@ -74,7 +74,7 @@ dependencies { implementation 'org.hsluv:hsluv:0.2' implementation 'org.conscrypt:conscrypt-android:2.5.2' implementation 'me.drakeet.support:toastcompat:1.1.0' - implementation "com.leinardi.android:speed-dial:3.2.0" + implementation "com.leinardi.android:speed-dial:3.3.0" implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.retrofit2:converter-gson:2.9.0" diff --git a/src/conversations/AndroidManifest.xml b/src/conversations/AndroidManifest.xml index 87c925fe6183b4ea89e53d29a30f50307d7f0410..5b111101e32d68a31b66cac0df703df02a0dd514 100644 --- a/src/conversations/AndroidManifest.xml +++ b/src/conversations/AndroidManifest.xml @@ -5,6 +5,7 @@ share()); if (bundle != null && bundle.containsKey("invite")) { this.easyOnboardingInvite = bundle.getParcelable("invite"); @@ -65,11 +69,11 @@ public class EasyOnboardingInviteActivity extends XmppActivity implements EasyOn } private void share() { - final String shareText = getString( - R.string.easy_invite_share_text, - easyOnboardingInvite.getDomain(), - easyOnboardingInvite.getShareableLink() - ); + final String shareText = + getString( + R.string.easy_invite_share_text, + easyOnboardingInvite.getDomain(), + easyOnboardingInvite.getShareableLink()); final Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, shareText); @@ -95,16 +99,47 @@ public class EasyOnboardingInviteActivity extends XmppActivity implements EasyOn private void showInvite(final EasyOnboardingInvite invite) { this.binding.inProgress.setVisibility(View.GONE); this.binding.invite.setVisibility(View.VISIBLE); - this.binding.tapToShare.setText(getString(R.string.tap_share_button_send_invite, invite.getDomain())); + this.binding.tapToShare.setText( + getString(R.string.tap_share_button_send_invite, invite.getDomain())); final Point size = new Point(); getWindowManager().getDefaultDisplay().getSize(size); final int width = Math.min(size.x, size.y); - final Bitmap bitmap = BarcodeProvider.create2dBarcodeBitmap(invite.getShareableLink(), width); + final boolean nightMode = + (this.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) + == Configuration.UI_MODE_NIGHT_YES; + final int black; + final int white; + if (nightMode) { + black = + MaterialColors.getColor( + this, + com.google.android.material.R.attr.colorSurface, + "No surface color configured"); + white = + MaterialColors.getColor( + this, + com.google.android.material.R.attr.colorSurfaceInverse, + "No inverse surface color configured"); + } else { + black = + MaterialColors.getColor( + this, + com.google.android.material.R.attr.colorSurfaceInverse, + "No inverse surface color configured"); + white = + MaterialColors.getColor( + this, + com.google.android.material.R.attr.colorSurface, + "No surface color configured"); + } + final Bitmap bitmap = + BarcodeProvider.create2dBarcodeBitmap( + invite.getShareableLink(), width, black, white); binding.qrCode.setImageBitmap(bitmap); } @Override - public void onSaveInstanceState(Bundle bundle) { + public void onSaveInstanceState(@NonNull Bundle bundle) { super.onSaveInstanceState(bundle); if (easyOnboardingInvite != null) { bundle.putParcelable("invite", easyOnboardingInvite); @@ -141,11 +176,12 @@ public class EasyOnboardingInviteActivity extends XmppActivity implements EasyOn @Override public void inviteRequestFailed(final String message) { - runOnUiThread(() -> { - if (!Strings.isNullOrEmpty(message)) { - Toast.makeText(this, message, Toast.LENGTH_LONG).show(); - } - finish(); - }); + runOnUiThread( + () -> { + if (!Strings.isNullOrEmpty(message)) { + Toast.makeText(this, message, Toast.LENGTH_LONG).show(); + } + finish(); + }); } } diff --git a/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java b/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java index ed998677b31ab2e45c915b88decf48cdadb708dd..1f1040c09c175f54eddd8138e55b52d2a4682136 100644 --- a/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java +++ b/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java @@ -18,6 +18,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.databinding.DataBindingUtil; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import java.io.IOException; @@ -31,7 +32,6 @@ import eu.siacs.conversations.services.ImportBackupService; import eu.siacs.conversations.ui.adapter.BackupFileAdapter; import eu.siacs.conversations.ui.util.SettingsUtils; import eu.siacs.conversations.utils.BackupFileHeader; -import eu.siacs.conversations.utils.ThemeHelper; public class ImportBackupActivity extends ActionBarActivity implements ServiceConnection, ImportBackupService.OnBackupFilesLoaded, BackupFileAdapter.OnItemClickedListener, ImportBackupService.OnBackupProcessed { @@ -46,22 +46,15 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo @Override protected void onCreate(final Bundle savedInstanceState) { - this.mTheme = ThemeHelper.find(this); - setTheme(this.mTheme); super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.activity_import_backup); + Activities.setStatusAndNavigationBarColors(this, binding.getRoot()); setSupportActionBar(binding.toolbar); setLoadingState(savedInstanceState != null && savedInstanceState.getBoolean("loading_state", false)); this.backupFileAdapter = new BackupFileAdapter(); this.binding.list.setAdapter(this.backupFileAdapter); this.backupFileAdapter.setOnItemClickedListener(this); } - - @Override - protected void onResume(){ - super.onResume(); - SettingsUtils.applyScreenshotPreventionSetting(this); - } @Override public boolean onCreateOptionsMenu(final Menu menu) { @@ -80,12 +73,7 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo @Override public void onStart() { super.onStart(); - final int theme = ThemeHelper.find(this); - if (this.mTheme != theme) { - recreate(); - } else { - bindService(new Intent(this, ImportBackupService.class), this, Context.BIND_AUTO_CREATE); - } + bindService(new Intent(this, ImportBackupService.class), this, Context.BIND_AUTO_CREATE); final Intent intent = getIntent(); if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction()) && !this.mLoadingState) { Uri uri = intent.getData(); @@ -146,7 +134,7 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo final DialogEnterPasswordBinding enterPasswordBinding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.dialog_enter_password, null, false); Log.d(Config.LOGTAG, "attempting to import " + backupFile.getUri()); enterPasswordBinding.explain.setText(getString(R.string.enter_password_to_restore, backupFile.getHeader().getJid().toString())); - AlertDialog.Builder builder = new AlertDialog.Builder(this); + final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); builder.setView(enterPasswordBinding.getRoot()); builder.setTitle(R.string.enter_password); builder.setNegativeButton(R.string.cancel, (dialog, which) -> { @@ -186,6 +174,7 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo binding.coordinator.setVisibility(loadingState ? View.GONE : View.VISIBLE); binding.inProgress.setVisibility(loadingState ? View.VISIBLE : View.GONE); setTitle(loadingState ? R.string.restoring_backup : R.string.restore_backup); + Activities.setStatusAndNavigationBarColors(this, binding.getRoot()); configureActionBar(getSupportActionBar(), !loadingState); this.mLoadingState = loadingState; invalidateOptionsMenu(); diff --git a/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java b/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java index 38761befdfdb02c6e23629c87ea51e67b90caff9..5a301f769886ca4d7992e90f94e9693e4768e0a7 100644 --- a/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java +++ b/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java @@ -10,45 +10,32 @@ import android.widget.Toast; import androidx.databinding.DataBindingUtil; -import java.security.SecureRandom; - import eu.siacs.conversations.Config; import eu.siacs.conversations.R; -import eu.siacs.conversations.databinding.MagicCreateBinding; +import eu.siacs.conversations.databinding.ActivityMagicCreateBinding; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.InstallReferrerUtils; import eu.siacs.conversations.xmpp.Jid; +import java.security.SecureRandom; + public class MagicCreateActivity extends XmppActivity implements TextWatcher { public static final String EXTRA_DOMAIN = "domain"; public static final String EXTRA_PRE_AUTH = "pre_auth"; public static final String EXTRA_USERNAME = "username"; - private MagicCreateBinding binding; + private ActivityMagicCreateBinding binding; private String domain; private String username; private String preAuth; @Override - protected void refreshUiReal() { - - } + protected void refreshUiReal() {} @Override - void onBackendConnected() { - - } - - @Override - public void onStart() { - super.onStart(); - final int theme = findTheme(); - if (this.mTheme != theme) { - recreate(); - } - } + void onBackendConnected() {} @Override protected void onCreate(final Bundle savedInstanceState) { @@ -60,7 +47,8 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } super.onCreate(savedInstanceState); - this.binding = DataBindingUtil.setContentView(this, R.layout.magic_create); + this.binding = DataBindingUtil.setContentView(this, R.layout.activity_magic_create); + Activities.setStatusAndNavigationBarColors(this, binding.getRoot()); setSupportActionBar(this.binding.toolbar); configureActionBar(getSupportActionBar(), this.domain == null); if (username != null && domain != null) { @@ -72,51 +60,64 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher { } else if (domain != null) { binding.instructions.setText(getString(R.string.magic_create_text_on_x, domain)); } - binding.createAccount.setOnClickListener(v -> { - try { - final String username = binding.username.getText().toString(); - final Jid jid; - final boolean fixedUsername; - if (this.domain != null && this.username != null) { - fixedUsername = true; - jid = Jid.ofLocalAndDomainEscaped(this.username, this.domain); - } else if (this.domain != null) { - fixedUsername = false; - jid = Jid.ofLocalAndDomainEscaped(username, this.domain); - } else { - fixedUsername = false; - jid = Jid.ofLocalAndDomainEscaped(username, Config.MAGIC_CREATE_DOMAIN); - } - if (!jid.getEscapedLocal().equals(jid.getLocal()) || (this.username == null && username.length() < 3)) { - binding.username.setError(getString(R.string.invalid_username)); - binding.username.requestFocus(); - } else { - binding.username.setError(null); - Account account = xmppConnectionService.findAccountByJid(jid); - if (account == null) { - account = new Account(jid, CryptoHelper.createPassword(new SecureRandom())); - account.setOption(Account.OPTION_REGISTER, true); - account.setOption(Account.OPTION_DISABLED, true); - account.setOption(Account.OPTION_MAGIC_CREATE, true); - account.setOption(Account.OPTION_FIXED_USERNAME, fixedUsername); - if (this.preAuth != null) { - account.setKey(Account.KEY_PRE_AUTH_REGISTRATION_TOKEN, this.preAuth); + binding.createAccount.setOnClickListener( + v -> { + try { + final String username = binding.username.getText().toString(); + final Jid jid; + final boolean fixedUsername; + if (this.domain != null && this.username != null) { + fixedUsername = true; + jid = Jid.ofLocalAndDomainEscaped(this.username, this.domain); + } else if (this.domain != null) { + fixedUsername = false; + jid = Jid.ofLocalAndDomainEscaped(username, this.domain); + } else { + fixedUsername = false; + jid = Jid.ofLocalAndDomainEscaped(username, Config.MAGIC_CREATE_DOMAIN); + } + if (!jid.getEscapedLocal().equals(jid.getLocal()) + || (this.username == null && username.length() < 3)) { + binding.usernameLayout.setError(getString(R.string.invalid_username)); + binding.username.requestFocus(); + } else { + binding.usernameLayout.setError(null); + Account account = xmppConnectionService.findAccountByJid(jid); + if (account == null) { + account = + new Account( + jid, + CryptoHelper.createPassword(new SecureRandom())); + account.setOption(Account.OPTION_REGISTER, true); + account.setOption(Account.OPTION_DISABLED, true); + account.setOption(Account.OPTION_MAGIC_CREATE, true); + account.setOption(Account.OPTION_FIXED_USERNAME, fixedUsername); + if (this.preAuth != null) { + account.setKey( + Account.KEY_PRE_AUTH_REGISTRATION_TOKEN, this.preAuth); + } + xmppConnectionService.createAccount(account); + } + Intent intent = + new Intent(MagicCreateActivity.this, EditAccountActivity.class); + intent.putExtra("jid", account.getJid().asBareJid().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(); + StartConversationActivity.addInviteUri(intent, getIntent()); + startActivity(intent); } - xmppConnectionService.createAccount(account); + } catch (final IllegalArgumentException e) { + binding.usernameLayout.setError(getString(R.string.invalid_username)); + binding.username.requestFocus(); } - Intent intent = new Intent(MagicCreateActivity.this, EditAccountActivity.class); - intent.putExtra("jid", account.getJid().asBareJid().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(); - StartConversationActivity.addInviteUri(intent, getIntent()); - startActivity(intent); - } - } catch (IllegalArgumentException e) { - binding.username.setError(getString(R.string.invalid_username)); - binding.username.requestFocus(); - } - }); + }); binding.username.addTextChangedListener(this); } @@ -127,14 +128,10 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher { } @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - - } + public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(final Editable s) { @@ -153,8 +150,10 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher { } else { jid = Jid.ofLocalAndDomainEscaped(username, this.domain); } - binding.fullJid.setText(getString(R.string.your_full_jid_will_be, jid.toEscapedString())); - } catch (IllegalArgumentException e) { + binding.fullJid.setText( + getString(R.string.your_full_jid_will_be, jid.toEscapedString())); + binding.usernameLayout.setError(null); + } catch (final IllegalArgumentException e) { binding.fullJid.setVisibility(View.INVISIBLE); } } diff --git a/src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java index 4446acefe77c7bb6d3a8f17e5d5f73d8d3720db7..1a04210dd4c99bc05f14c79b944bc3aa00199cc3 100644 --- a/src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -1,10 +1,14 @@ package eu.siacs.conversations.ui; +import static eu.siacs.conversations.utils.PermissionUtils.allGranted; +import static eu.siacs.conversations.utils.PermissionUtils.writeGranted; + import android.content.ActivityNotFoundException; import android.content.Intent; import android.os.Bundle; import android.security.KeyChain; import android.security.KeyChainAliasCallback; +import android.util.Log; import android.util.Pair; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; @@ -12,23 +16,17 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.ListView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AlertDialog; +import androidx.databinding.DataBindingUtil; -import org.openintents.openpgp.util.OpenPgpApi; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; +import com.google.common.base.Strings; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; +import eu.siacs.conversations.databinding.ActivityManageAccountsBinding; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; @@ -37,10 +35,17 @@ import eu.siacs.conversations.ui.util.MenuDoubleTabUtil; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.XmppConnection; -import static eu.siacs.conversations.utils.PermissionUtils.allGranted; -import static eu.siacs.conversations.utils.PermissionUtils.writeGranted; +import org.openintents.openpgp.util.OpenPgpApi; -public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated, AccountAdapter.OnTglAccountState { +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +public class ManageAccountActivity extends XmppActivity + implements OnAccountUpdate, + KeyChainAliasCallback, + XmppConnectionService.OnAccountCreated, + AccountAdapter.OnTglAccountState { private final String STATE_SELECTED_ACCOUNT = "selected_account"; @@ -50,7 +55,6 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda protected Jid selectedAccountJid = null; protected final List accountList = new ArrayList<>(); - protected ListView accountListView; protected AccountAdapter mAccountAdapter; protected AtomicBoolean mInvokedAddAccount = new AtomicBoolean(false); @@ -67,7 +71,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda accountList.clear(); accountList.addAll(xmppConnectionService.getAccounts()); } - ActionBar actionBar = getSupportActionBar(); + final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setHomeButtonEnabled(this.accountList.size() > 0); actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0); @@ -81,8 +85,11 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda super.onCreate(savedInstanceState); - setContentView(R.layout.activity_manage_accounts); - setSupportActionBar(findViewById(R.id.toolbar)); + ActivityManageAccountsBinding binding = + DataBindingUtil.setContentView(this, R.layout.activity_manage_accounts); + + Activities.setStatusAndNavigationBarColors(this, binding.getRoot()); + setSupportActionBar(binding.toolbar); configureActionBar(getSupportActionBar()); if (savedInstanceState != null) { String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT); @@ -95,26 +102,19 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda } } - accountListView = findViewById(R.id.account_list); this.mAccountAdapter = new AccountAdapter(this, accountList); - accountListView.setAdapter(this.mAccountAdapter); - accountListView.setOnItemClickListener((arg0, view, position, arg3) -> switchToAccount(accountList.get(position))); - registerForContextMenu(accountListView); + binding.accountList.setAdapter(this.mAccountAdapter); + binding.accountList.setOnItemClickListener( + (arg0, view, position, arg3) -> switchToAccount(accountList.get(position))); + registerForContextMenu(binding.accountList); } - @Override - protected void onStart() { - super.onStart(); - final int theme = findTheme(); - if (this.mTheme != theme) { - recreate(); - } - } @Override - public void onSaveInstanceState(final Bundle savedInstanceState) { + public void onSaveInstanceState(@NonNull final Bundle savedInstanceState) { if (selectedAccount != null) { - savedInstanceState.putString(STATE_SELECTED_ACCOUNT, selectedAccount.getJid().asBareJid().toEscapedString()); + savedInstanceState.putString( + STATE_SELECTED_ACCOUNT, selectedAccount.getJid().asBareJid().toEscapedString()); } super.onSaveInstanceState(savedInstanceState); } @@ -122,8 +122,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); - ManageAccountActivity.this.getMenuInflater().inflate( - R.menu.manageaccounts_context, menu); + ManageAccountActivity.this.getMenuInflater().inflate(R.menu.manageaccounts_context, menu); AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo; this.selectedAccount = accountList.get(acmi.position); if (this.selectedAccount.isEnabled()) { @@ -144,9 +143,10 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda } refreshUiReal(); if (this.mPostponedActivityResult != null) { - this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second); + this.onActivityResult( + mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second); } - if (Config.X509_VERIFICATION && this.accountList.size() == 0) { + if (Config.X509_VERIFICATION && this.accountList.isEmpty()) { if (mInvokedAddAccount.compareAndSet(false, true)) { addAccountFromKey(); } @@ -233,9 +233,9 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda return super.onOptionsItemSelected(item); } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + public void onRequestPermissionsResult( + int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (grantResults.length > 0) { if (allGranted(grantResults)) { @@ -258,13 +258,14 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda @Override public boolean onNavigateUp() { if (xmppConnectionService.getConversations().size() == 0) { - Intent contactsIntent = new Intent(this, - StartConversationActivity.class); + 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 | + Intent.FLAG_ACTIVITY_CLEAR_TOP + | // otherwise, make a new task for it - Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_NEW_TASK + | // don't use the new activity animation; finish // animation runs instead Intent.FLAG_ACTIVITY_NO_ANIMATION); @@ -286,16 +287,17 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda } private void addAccountFromKey() { + Log.d(Config.LOGTAG, "add account from key"); 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(); + } catch (final 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 intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString()); startActivity(intent); } @@ -377,7 +379,6 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda } } - @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); @@ -385,7 +386,8 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda 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)); + selectedAccount.setPgpSignId( + data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID)); announcePgp(selectedAccount, null, null, onOpenPGPKeyPublished); } else { choosePgpSignId(selectedAccount); @@ -402,9 +404,17 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda @Override public void alias(final String alias) { - if (alias != null) { - xmppConnectionService.createAccountFromKey(alias, this); - } + if (Strings.isNullOrEmpty(alias)) { + runOnUiThread( + () -> + Toast.makeText( + this, + R.string.no_certificate_selected, + Toast.LENGTH_LONG) + .show()); + return; + } + xmppConnectionService.createAccountFromKey(alias, this); } @Override @@ -417,6 +427,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda @Override public void informUser(final int r) { - runOnUiThread(() -> Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show()); + runOnUiThread( + () -> Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show()); } } diff --git a/src/conversations/java/eu/siacs/conversations/ui/PickServerActivity.java b/src/conversations/java/eu/siacs/conversations/ui/PickServerActivity.java index 06320d33deea510e9edd2b139757ce2ec8c08373..cbdd21b36caef09dcbc709823c60dca0016de307 100644 --- a/src/conversations/java/eu/siacs/conversations/ui/PickServerActivity.java +++ b/src/conversations/java/eu/siacs/conversations/ui/PickServerActivity.java @@ -26,15 +26,6 @@ public class PickServerActivity extends XmppActivity { } - @Override - public void onStart() { - super.onStart(); - final int theme = findTheme(); - if (this.mTheme != theme) { - recreate(); - } - } - @Override public boolean onOptionsItemSelected(final MenuItem item) { @@ -53,7 +44,8 @@ public class PickServerActivity extends XmppActivity { } @Override - public void onNewIntent(Intent intent) { + public void onNewIntent(final Intent intent) { + super.onNewIntent(intent); if (intent != null) { setIntent(intent); } @@ -66,6 +58,7 @@ public class PickServerActivity extends XmppActivity { } super.onCreate(savedInstanceState); ActivityPickServerBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_pick_server); + Activities.setStatusAndNavigationBarColors(this, binding.getRoot()); setSupportActionBar(binding.toolbar); configureActionBar(getSupportActionBar()); binding.useCim.setOnClickListener(v -> { @@ -81,7 +74,7 @@ public class PickServerActivity extends XmppActivity { if (accounts.size() == 1) { intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString()); intent.putExtra("init", true); - } else if (accounts.size() >= 1) { + } else if (!accounts.isEmpty()) { intent = new Intent(this, ManageAccountActivity.class); } addInviteUri(intent); diff --git a/src/conversations/java/eu/siacs/conversations/ui/ShareViaAccountActivity.java b/src/conversations/java/eu/siacs/conversations/ui/ShareViaAccountActivity.java index 762dfbb422780c20293eec13c3acdcd846d2595e..66ed355c41a16b658322d45733823f735fdfc3a9 100644 --- a/src/conversations/java/eu/siacs/conversations/ui/ShareViaAccountActivity.java +++ b/src/conversations/java/eu/siacs/conversations/ui/ShareViaAccountActivity.java @@ -56,15 +56,6 @@ public class ShareViaAccountActivity extends XmppActivity { }); } - @Override - protected void onStart() { - super.onStart(); - final int theme = findTheme(); - if (this.mTheme != theme) { - recreate(); - } - } - @Override void onBackendConnected() { final int numAccounts = xmppConnectionService.getAccounts().size(); diff --git a/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java b/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java index d61c64a9c38dc1deadd04016483efc503d3b5d7f..24528fc164de65be189fec3420563f40b4ce9c94 100644 --- a/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java +++ b/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java @@ -34,7 +34,10 @@ import eu.siacs.conversations.xmpp.Jid; import static eu.siacs.conversations.utils.PermissionUtils.allGranted; import static eu.siacs.conversations.utils.PermissionUtils.writeGranted; -public class WelcomeActivity extends XmppActivity implements XmppConnectionService.OnAccountCreated, KeyChainAliasCallback { +import com.google.common.base.Strings; + +public class WelcomeActivity extends XmppActivity + implements XmppConnectionService.OnAccountCreated, KeyChainAliasCallback { private static final int REQUEST_IMPORT_BACKUP = 0x63fb; @@ -66,7 +69,8 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi final Intent intent; if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) { intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth); - } else if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) { + } else if (xmppUri.isAction(XmppUri.ACTION_ROSTER) + && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) { intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth); intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString()); } else { @@ -81,22 +85,14 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi } @Override - protected void refreshUiReal() { - - } + protected void refreshUiReal() {} @Override - void onBackendConnected() { - - } + void onBackendConnected() {} @Override public void onStart() { super.onStart(); - final int theme = findTheme(); - if (this.mTheme != theme) { - recreate(); - } new InstallReferrerUtils(this); } @@ -119,42 +115,44 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } super.onCreate(savedInstanceState); - ActivityWelcomeBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_welcome); + ActivityWelcomeBinding binding = + DataBindingUtil.setContentView(this, R.layout.activity_welcome); + Activities.setStatusAndNavigationBarColors(this, binding.getRoot()); setSupportActionBar(binding.toolbar); configureActionBar(getSupportActionBar(), false); - binding.registerNewAccount.setOnClickListener(v -> { - final Intent intent = new Intent(this, PickServerActivity.class); - addInviteUri(intent); - startActivity(intent); - }); - binding.useExisting.setOnClickListener(v -> { - final List accounts = xmppConnectionService.getAccounts(); - Intent intent = new Intent(WelcomeActivity.this, EditAccountActivity.class); - intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, false); - if (accounts.size() == 1) { - intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString()); - intent.putExtra("init", true); - } else if (accounts.size() >= 1) { - intent = new Intent(WelcomeActivity.this, ManageAccountActivity.class); - } - addInviteUri(intent); - startActivity(intent); - }); - + setTitle(null); + binding.registerNewAccount.setOnClickListener( + v -> { + final Intent intent = new Intent(this, PickServerActivity.class); + addInviteUri(intent); + startActivity(intent); + }); + binding.useExisting.setOnClickListener( + v -> { + final List accounts = xmppConnectionService.getAccounts(); + Intent intent = new Intent(this, EditAccountActivity.class); + intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, false); + if (accounts.size() == 1) { + intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString()); + intent.putExtra("init", true); + } else if (!accounts.isEmpty()) { + intent = new Intent(this, ManageAccountActivity.class); + } + addInviteUri(intent); + startActivity(intent); + }); } @Override - public boolean onCreateOptionsMenu(Menu menu) { + public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.welcome_menu, menu); final MenuItem scan = menu.findItem(R.id.action_scan_qr_code); scan.setVisible(Compatibility.hasFeatureCamera(this)); return super.onCreateOptionsMenu(menu); } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { + public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case R.id.action_import_backup: if (hasStoragePermission(REQUEST_IMPORT_BACKUP)) { @@ -174,16 +172,25 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi 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(); + } catch (final ActivityNotFoundException e) { + Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG) + .show(); } } @Override public void alias(final String alias) { - if (alias != null) { - xmppConnectionService.createAccountFromKey(alias, this); + if (Strings.isNullOrEmpty(alias)) { + runOnUiThread( + () -> + Toast.makeText( + this, + R.string.no_certificate_selected, + Toast.LENGTH_LONG) + .show()); + return; } + xmppConnectionService.createAccountFromKey(alias, this); } @Override @@ -201,7 +208,8 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi } @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + public void onRequestPermissionsResult( + int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); UriHandlerActivity.onRequestPermissionResult(this, requestCode, grantResults); if (grantResults.length > 0) { @@ -211,7 +219,8 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi startActivity(new Intent(this, ImportBackupActivity.class)); break; } - } else if (Arrays.asList(permissions).contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + } else if (Arrays.asList(permissions) + .contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show(); } } @@ -232,5 +241,4 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi to.putExtra(StartConversationActivity.EXTRA_INVITE_URI, this.inviteUri.toString()); } } - } diff --git a/src/conversations/java/eu/siacs/conversations/ui/adapter/BackupFileAdapter.java b/src/conversations/java/eu/siacs/conversations/ui/adapter/BackupFileAdapter.java index 9857dcd8ac34e664c38f8f68a99366d9f139bfe5..9f32352eed913b313667e48ed21f968e92e4c094 100644 --- a/src/conversations/java/eu/siacs/conversations/ui/adapter/BackupFileAdapter.java +++ b/src/conversations/java/eu/siacs/conversations/ui/adapter/BackupFileAdapter.java @@ -22,7 +22,7 @@ import java.util.List; import java.util.concurrent.RejectedExecutionException; import eu.siacs.conversations.R; -import eu.siacs.conversations.databinding.AccountRowBinding; +import eu.siacs.conversations.databinding.ItemAccountBinding; import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.ImportBackupService; import eu.siacs.conversations.utils.BackupFileHeader; @@ -39,7 +39,7 @@ public class BackupFileAdapter extends RecyclerView.Adapter(imageView); DisplayMetrics metrics = imageView.getContext().getResources().getDisplayMetrics(); this.size = ((int) (48 * metrics.density)); @@ -146,8 +146,7 @@ public class BackupFileAdapter extends RecyclerView.AdapterECHuegCV^l*HW)V~E;3QPf!hnW>r91l1D=W_Vb z5W(y4G{MkBcZZ3?)a0y#>li&PcZualU6`^s>)_+eKZ~whaF{%!&NbjKcL*=z|M*AY f|L&Jxcvj00UZF6vf&ID(&>ak(u6{1-oD!M+fBM5T$P+WqDqf*6fiTYQEJx&}*G=J0NWgzCp99W#r?rXGJn%$B z>pbv5M4LPi)c^)CmvK+P2;p9U(bwDqF#0w}14hXl2^jq{M*&8eIRY@6nOgy)9dip{ zR4~^9Mul?~kR&SJ4&V>og8^)aXvzZ*M6}8SC%n+wGm8XBh&2&JQp zm*xkIOkLACniS#=UAm>$D(CP);GQF^cC>4buxhJZg0$ntc##tO*11-DT24+#+IO9Q zzh?2&)W~TY&&!&|UA_h3|*nY@Hy z!WqLL29IYFnhYw%JtC||eNJ5rlJ2TI8%z59KCw$4?_AOhW%Vv;zGLZD#LD^KGR7mI zN$5#PlhBj}`ioYXUN8>qHNBu2*lTjZYQ2le(&dd@(R)}5*hEA_A|AJJnx0;fcBVtl imEBd(5#+G2och=Q?sSz#RHy+1n8DN4&t;ucLK6T>dYn`M diff --git a/src/conversations/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png b/src/conversations/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png deleted file mode 100644 index a789520baa7f4b65fd73ae78a57ec50b9d645e3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 503 zcmV9JZFUK03OixFK?s74p2SYCAYR}p1?>_^(85!gQmq9eh^_{-&k=SI_HVTg zh?@Ooznyspp8wN-!>=89()iz$2!_W(zjkExyoapP1JK?6aWn z7F@3dm~h4roiV8cct+cOVyBz&mbRxg01s(5V4Wa(uWz#a0m;&wUU1(`Z22Ye?}7v+GT zWU46#Bx{5K0}L?000Rs#zyP5!14IyLtN{IfOJf9xXotoI5b?(}CV+^)Ok)9v_{TH` zfQbK0*#RQ1m@)%IY%pa7h}dw-2oO;)Wdn#PoH7AKbeOULM0A`o07Pst4Gj>n7+Ap}a$4Sn!<>CMU002ovPDHLkV1mEb(=Px3 diff --git a/src/conversations/res/layout/activity_easy_invite.xml b/src/conversations/res/layout/activity_easy_invite.xml index 8bbf11c037e00c299f152d5084093335d7f5f607..6f1c8fc57d386f4a45f4ef781dc37446ce61cd1f 100644 --- a/src/conversations/res/layout/activity_easy_invite.xml +++ b/src/conversations/res/layout/activity_easy_invite.xml @@ -1,17 +1,23 @@ - + - + + + + + + android:textAppearance="?textAppearanceBodyMedium" /> + android:textAppearance="?textAppearanceBodyMedium" />