2017-12-08 18:35:59 +01:00
package org.telegram.messenger.voip ;
import android.Manifest ;
import android.annotation.SuppressLint ;
2018-07-30 04:07:02 +02:00
import android.annotation.TargetApi ;
2017-12-08 18:35:59 +01:00
import android.app.Activity ;
import android.app.Notification ;
2018-07-30 04:07:02 +02:00
import android.app.NotificationChannel ;
import android.app.NotificationManager ;
2017-12-08 18:35:59 +01:00
import android.app.PendingIntent ;
import android.app.Service ;
import android.bluetooth.BluetoothAdapter ;
2020-08-14 18:58:22 +02:00
import android.bluetooth.BluetoothDevice ;
2017-12-08 18:35:59 +01:00
import android.bluetooth.BluetoothHeadset ;
import android.bluetooth.BluetoothProfile ;
import android.content.BroadcastReceiver ;
import android.content.ComponentName ;
import android.content.Context ;
import android.content.Intent ;
import android.content.IntentFilter ;
2018-07-30 04:07:02 +02:00
import android.content.SharedPreferences ;
2017-12-08 18:35:59 +01:00
import android.content.pm.PackageManager ;
import android.graphics.Bitmap ;
import android.graphics.BitmapFactory ;
2018-07-30 04:07:02 +02:00
import android.graphics.Canvas ;
import android.graphics.Paint ;
import android.graphics.Path ;
import android.graphics.PorterDuff ;
import android.graphics.PorterDuffXfermode ;
2017-12-08 18:35:59 +01:00
import android.graphics.drawable.BitmapDrawable ;
2018-07-30 04:07:02 +02:00
import android.graphics.drawable.Icon ;
2017-12-08 18:35:59 +01:00
import android.hardware.Sensor ;
import android.hardware.SensorEvent ;
import android.hardware.SensorEventListener ;
import android.hardware.SensorManager ;
2019-03-03 21:40:48 +01:00
import android.media.AudioAttributes ;
2017-12-08 18:35:59 +01:00
import android.media.AudioFormat ;
import android.media.AudioManager ;
import android.media.AudioTrack ;
import android.media.MediaPlayer ;
2020-12-23 08:48:30 +01:00
import android.media.MediaRouter ;
2018-07-30 04:07:02 +02:00
import android.media.RingtoneManager ;
2017-12-08 18:35:59 +01:00
import android.media.SoundPool ;
import android.net.ConnectivityManager ;
import android.net.NetworkInfo ;
2018-07-30 04:07:02 +02:00
import android.net.Uri ;
2017-12-08 18:35:59 +01:00
import android.os.Build ;
2018-07-30 04:07:02 +02:00
import android.os.Bundle ;
2017-12-08 18:35:59 +01:00
import android.os.PowerManager ;
2020-03-30 14:00:09 +02:00
import android.os.SystemClock ;
2017-12-08 18:35:59 +01:00
import android.os.Vibrator ;
2018-07-30 04:07:02 +02:00
import android.telecom.CallAudioState ;
import android.telecom.Connection ;
import android.telecom.DisconnectCause ;
import android.telecom.PhoneAccount ;
import android.telecom.PhoneAccountHandle ;
import android.telecom.TelecomManager ;
2017-12-08 18:35:59 +01:00
import android.telephony.TelephonyManager ;
2018-07-30 04:07:02 +02:00
import android.text.SpannableString ;
import android.text.TextUtils ;
import android.text.style.ForegroundColorSpan ;
import android.view.View ;
2020-12-23 08:48:30 +01:00
import android.view.WindowManager ;
2018-07-30 04:07:02 +02:00
import android.widget.RemoteViews ;
2017-12-08 18:35:59 +01:00
2020-12-23 08:48:30 +01:00
import org.telegram.messenger.AccountInstance ;
2017-12-08 18:35:59 +01:00
import org.telegram.messenger.AndroidUtilities ;
2020-08-15 23:06:36 +02:00
import org.telegram.messenger.ApplicationLoader ;
2019-03-03 21:40:48 +01:00
import org.telegram.messenger.BuildConfig ;
2018-07-30 04:07:02 +02:00
import org.telegram.messenger.BuildVars ;
2020-12-23 08:48:30 +01:00
import org.telegram.messenger.ChatObject ;
2018-07-30 04:07:02 +02:00
import org.telegram.messenger.ContactsController ;
2017-12-08 18:35:59 +01:00
import org.telegram.messenger.FileLoader ;
import org.telegram.messenger.FileLog ;
import org.telegram.messenger.ImageLoader ;
import org.telegram.messenger.LocaleController ;
2018-07-30 04:07:02 +02:00
import org.telegram.messenger.MessagesController ;
2017-12-08 18:35:59 +01:00
import org.telegram.messenger.NotificationCenter ;
2018-07-30 04:07:02 +02:00
import org.telegram.messenger.NotificationsController ;
2017-12-08 18:35:59 +01:00
import org.telegram.messenger.R ;
2021-04-09 15:17:32 +02:00
import org.telegram.messenger.SharedConfig ;
2017-12-08 18:35:59 +01:00
import org.telegram.messenger.StatsController ;
2018-07-30 04:07:02 +02:00
import org.telegram.messenger.UserConfig ;
2020-12-23 08:48:30 +01:00
import org.telegram.messenger.Utilities ;
2017-12-08 18:35:59 +01:00
import org.telegram.tgnet.ConnectionsManager ;
2018-07-30 04:07:02 +02:00
import org.telegram.tgnet.TLObject ;
2017-12-08 18:35:59 +01:00
import org.telegram.tgnet.TLRPC ;
2018-07-30 04:07:02 +02:00
import org.telegram.ui.ActionBar.BottomSheet ;
import org.telegram.ui.ActionBar.Theme ;
import org.telegram.ui.Components.AvatarDrawable ;
2017-12-08 18:35:59 +01:00
import org.telegram.ui.Components.voip.VoIPHelper ;
2020-08-14 18:58:22 +02:00
import org.telegram.ui.LaunchActivity ;
2017-12-08 18:35:59 +01:00
import org.telegram.ui.VoIPPermissionActivity ;
2021-04-09 15:17:32 +02:00
import org.webrtc.voiceengine.WebRtcAudioTrack ;
2017-12-08 18:35:59 +01:00
import java.lang.reflect.Field ;
import java.lang.reflect.Method ;
import java.util.ArrayList ;
/ * *
* Created by grishka on 21 . 07 . 17 .
* /
2020-08-14 18:58:22 +02:00
@SuppressLint ( " NewApi " )
public abstract class VoIPBaseService extends Service implements SensorEventListener , AudioManager . OnAudioFocusChangeListener , VoIPController . ConnectionStateListener , NotificationCenter . NotificationCenterDelegate {
2017-12-08 18:35:59 +01:00
2018-07-30 04:07:02 +02:00
protected int currentAccount = - 1 ;
2020-08-14 18:58:22 +02:00
public static final int STATE_WAIT_INIT = Instance . STATE_WAIT_INIT ;
public static final int STATE_WAIT_INIT_ACK = Instance . STATE_WAIT_INIT_ACK ;
public static final int STATE_ESTABLISHED = Instance . STATE_ESTABLISHED ;
public static final int STATE_FAILED = Instance . STATE_FAILED ;
public static final int STATE_RECONNECTING = Instance . STATE_RECONNECTING ;
2020-12-23 08:48:30 +01:00
public static final int STATE_CREATING = 6 ;
2017-12-08 18:35:59 +01:00
public static final int STATE_ENDED = 11 ;
public static final String ACTION_HEADSET_PLUG = " android.intent.action.HEADSET_PLUG " ;
protected static final int ID_ONGOING_CALL_NOTIFICATION = 201 ;
protected static final int ID_INCOMING_CALL_NOTIFICATION = 202 ;
public static final int DISCARD_REASON_HANGUP = 1 ;
public static final int DISCARD_REASON_DISCONNECT = 2 ;
public static final int DISCARD_REASON_MISSED = 3 ;
public static final int DISCARD_REASON_LINE_BUSY = 4 ;
2020-08-14 18:58:22 +02:00
public static final int AUDIO_ROUTE_EARPIECE = 0 ;
public static final int AUDIO_ROUTE_SPEAKER = 1 ;
public static final int AUDIO_ROUTE_BLUETOOTH = 2 ;
2018-07-30 04:07:02 +02:00
2020-08-14 18:58:22 +02:00
protected static final boolean USE_CONNECTION_SERVICE = isDeviceCompatibleWithConnectionServiceAPI ( ) ;
2017-12-08 18:35:59 +01:00
protected static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32 ;
protected static VoIPBaseService sharedInstance ;
2020-12-23 08:48:30 +01:00
protected static Runnable setModeRunnable ;
protected static final Object sync = new Object ( ) ;
2017-12-08 18:35:59 +01:00
protected NetworkInfo lastNetInfo ;
protected int currentState = 0 ;
protected Notification ongoingCallNotification ;
2020-08-14 18:58:22 +02:00
protected NativeInstance tgVoip ;
2020-12-24 06:36:01 +01:00
protected boolean wasConnected ;
2020-12-23 08:48:30 +01:00
2021-03-19 11:25:58 +01:00
protected int currentStreamRequestId ;
2020-12-23 08:48:30 +01:00
protected TLRPC . Chat chat ;
2020-08-14 18:58:22 +02:00
protected boolean isVideoAvailable ;
2020-08-15 02:01:55 +02:00
protected boolean notificationsDisabled ;
2020-08-14 18:58:22 +02:00
protected boolean switchingCamera ;
protected boolean isFrontFaceCamera = true ;
2020-03-30 14:00:09 +02:00
protected String lastError ;
2017-12-08 18:35:59 +01:00
protected PowerManager . WakeLock proximityWakelock ;
protected PowerManager . WakeLock cpuWakelock ;
protected boolean isProximityNear ;
protected boolean isHeadsetPlugged ;
2021-02-23 12:53:38 +01:00
protected int previousAudioOutput = - 1 ;
2017-12-08 18:35:59 +01:00
protected ArrayList < StateListener > stateListeners = new ArrayList < > ( ) ;
protected MediaPlayer ringtonePlayer ;
protected Vibrator vibrator ;
protected SoundPool soundPool ;
protected int spRingbackID ;
protected int spFailedID ;
protected int spEndId ;
2020-12-23 08:48:30 +01:00
protected int spVoiceChatEndId ;
protected int spVoiceChatStartId ;
protected int spVoiceChatConnecting ;
2017-12-08 18:35:59 +01:00
protected int spBusyId ;
protected int spConnectingId ;
2021-03-19 11:25:58 +01:00
protected int spPlayId ;
protected int spStartRecordId ;
protected int spAllowTalkId ;
2017-12-08 18:35:59 +01:00
protected boolean needPlayEndSound ;
2020-12-24 06:36:01 +01:00
protected boolean hasAudioFocus ;
2017-12-08 18:35:59 +01:00
protected boolean micMute ;
2020-12-23 08:48:30 +01:00
protected boolean unmutedByHold ;
2017-12-08 18:35:59 +01:00
protected BluetoothAdapter btAdapter ;
2020-08-14 18:58:22 +02:00
protected Instance . TrafficStats prevTrafficStats ;
2017-12-08 18:35:59 +01:00
protected boolean isBtHeadsetConnected ;
2020-08-14 18:58:22 +02:00
protected boolean screenOn ;
2021-03-19 11:25:58 +01:00
protected Runnable updateNotificationRunnable ;
2020-12-23 08:48:30 +01:00
protected Runnable onDestroyRunnable ;
2021-04-09 15:17:32 +02:00
protected Runnable switchingStreamTimeoutRunnable ;
2020-12-23 08:48:30 +01:00
protected boolean playedConnectedSound ;
2021-03-19 11:25:58 +01:00
protected boolean switchingStream ;
2020-12-23 08:48:30 +01:00
2020-08-14 18:58:22 +02:00
protected int videoState = Instance . VIDEO_STATE_INACTIVE ;
2020-12-23 08:48:30 +01:00
public TLRPC . PhoneCall privateCall ;
public ChatObject . Call groupCall ;
2021-03-19 11:25:58 +01:00
public boolean currentGroupModeStreaming = false ;
2020-12-23 08:48:30 +01:00
protected int mySource ;
2021-03-19 11:25:58 +01:00
protected String myJson ;
2020-12-23 08:48:30 +01:00
protected boolean createGroupCall ;
2021-04-14 03:44:46 +02:00
protected int scheduleDate ;
2021-03-19 11:25:58 +01:00
protected TLRPC . InputPeer groupCallPeer ;
public boolean hasFewPeers ;
protected String joinHash ;
2020-08-14 18:58:22 +02:00
protected long callStartTime ;
protected boolean playingSound ;
protected boolean isOutgoing ;
2020-08-22 01:59:49 +02:00
public boolean videoCall ;
2020-08-14 18:58:22 +02:00
protected long videoCapturer ;
protected Runnable timeoutRunnable ;
2021-04-09 15:17:32 +02:00
protected int currentStreamType ;
2020-08-14 18:58:22 +02:00
private Boolean mHasEarpiece ;
private boolean wasEstablished ;
protected int signalBarCount ;
protected int currentAudioState = Instance . AUDIO_STATE_ACTIVE ;
protected int currentVideoState = Instance . VIDEO_STATE_INACTIVE ;
protected boolean audioConfigured ;
protected int audioRouteToSet = AUDIO_ROUTE_BLUETOOTH ;
protected boolean speakerphoneStateToSet ;
protected CallConnection systemCallConnection ;
protected int callDiscardReason ;
protected boolean bluetoothScoActive ;
protected boolean needSwitchToBluetoothAfterScoActivates ;
protected boolean didDeleteConnectionServiceContact ;
protected Runnable connectingSoundRunnable ;
private String currentBluetoothDeviceName ;
public final SharedUIParams sharedUIParams = new SharedUIParams ( ) ;
protected Runnable afterSoundRunnable = new Runnable ( ) {
2017-12-08 18:35:59 +01:00
@Override
2020-08-14 18:58:22 +02:00
public void run ( ) {
2020-12-23 08:48:30 +01:00
2020-08-14 18:58:22 +02:00
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
2020-12-23 08:48:30 +01:00
am . abandonAudioFocus ( VoIPBaseService . this ) ;
am . unregisterMediaButtonEventReceiver ( new ComponentName ( VoIPBaseService . this , VoIPMediaButtonReceiver . class ) ) ;
if ( ! USE_CONNECTION_SERVICE & & sharedInstance = = null ) {
if ( isBtHeadsetConnected ) {
am . stopBluetoothSco ( ) ;
am . setBluetoothScoOn ( false ) ;
bluetoothScoActive = false ;
}
am . setSpeakerphoneOn ( false ) ;
2020-08-14 18:58:22 +02:00
}
2020-12-23 08:48:30 +01:00
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . release ( ) ) ;
Utilities . globalQueue . postRunnable ( setModeRunnable = ( ) - > {
synchronized ( sync ) {
if ( setModeRunnable = = null ) {
return ;
}
setModeRunnable = null ;
}
try {
am . setMode ( AudioManager . MODE_NORMAL ) ;
} catch ( SecurityException x ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error setting audio more to normal " , x ) ;
}
}
} ) ;
2017-12-08 18:35:59 +01:00
}
} ;
2020-08-14 18:58:22 +02:00
boolean fetchingBluetoothDeviceName ;
private BluetoothProfile . ServiceListener serviceListener = new BluetoothProfile . ServiceListener ( ) {
@Override
public void onServiceDisconnected ( int profile ) {
}
@Override
public void onServiceConnected ( int profile , BluetoothProfile proxy ) {
for ( BluetoothDevice device : proxy . getConnectedDevices ( ) ) {
if ( proxy . getConnectionState ( device ) ! = BluetoothProfile . STATE_CONNECTED ) {
continue ;
}
currentBluetoothDeviceName = device . getName ( ) ;
break ;
}
BluetoothAdapter . getDefaultAdapter ( ) . closeProfileProxy ( profile , proxy ) ;
fetchingBluetoothDeviceName = false ;
}
} ;
2017-12-08 18:35:59 +01:00
protected BroadcastReceiver receiver = new BroadcastReceiver ( ) {
2020-08-14 18:58:22 +02:00
2017-12-08 18:35:59 +01:00
@Override
public void onReceive ( Context context , Intent intent ) {
if ( ACTION_HEADSET_PLUG . equals ( intent . getAction ( ) ) ) {
isHeadsetPlugged = intent . getIntExtra ( " state " , 0 ) = = 1 ;
if ( isHeadsetPlugged & & proximityWakelock ! = null & & proximityWakelock . isHeld ( ) ) {
proximityWakelock . release ( ) ;
}
2021-01-28 15:15:51 +01:00
if ( isHeadsetPlugged ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( am . isSpeakerphoneOn ( ) ) {
previousAudioOutput = 0 ;
} else if ( am . isBluetoothScoOn ( ) ) {
previousAudioOutput = 2 ;
} else {
previousAudioOutput = 1 ;
}
setAudioOutput ( 1 ) ;
} else {
if ( previousAudioOutput > = 0 ) {
setAudioOutput ( previousAudioOutput ) ;
previousAudioOutput = - 1 ;
}
}
2017-12-08 18:35:59 +01:00
isProximityNear = false ;
updateOutputGainControlState ( ) ;
} else if ( ConnectivityManager . CONNECTIVITY_ACTION . equals ( intent . getAction ( ) ) ) {
updateNetworkType ( ) ;
2020-08-14 18:58:22 +02:00
} else if ( BluetoothHeadset . ACTION_CONNECTION_STATE_CHANGED . equals ( intent . getAction ( ) ) ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " bt headset state = " + intent . getIntExtra ( BluetoothProfile . EXTRA_STATE , 0 ) ) ;
}
updateBluetoothHeadsetState ( intent . getIntExtra ( BluetoothProfile . EXTRA_STATE , BluetoothProfile . STATE_DISCONNECTED ) = = BluetoothProfile . STATE_CONNECTED ) ;
} else if ( AudioManager . ACTION_SCO_AUDIO_STATE_UPDATED . equals ( intent . getAction ( ) ) ) {
int state = intent . getIntExtra ( AudioManager . EXTRA_SCO_AUDIO_STATE , AudioManager . SCO_AUDIO_STATE_DISCONNECTED ) ;
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Bluetooth SCO state updated: " + state ) ;
}
if ( state = = AudioManager . SCO_AUDIO_STATE_DISCONNECTED & & isBtHeadsetConnected ) {
if ( ! btAdapter . isEnabled ( ) | | btAdapter . getProfileConnectionState ( BluetoothProfile . HEADSET ) ! = BluetoothProfile . STATE_CONNECTED ) {
2018-07-30 04:07:02 +02:00
updateBluetoothHeadsetState ( false ) ;
return ;
}
}
2020-08-14 18:58:22 +02:00
bluetoothScoActive = state = = AudioManager . SCO_AUDIO_STATE_CONNECTED ;
if ( bluetoothScoActive ) {
fetchBluetoothDeviceName ( ) ;
if ( needSwitchToBluetoothAfterScoActivates ) {
needSwitchToBluetoothAfterScoActivates = false ;
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
am . setSpeakerphoneOn ( false ) ;
am . setBluetoothScoOn ( true ) ;
}
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
for ( StateListener l : stateListeners ) {
2017-12-08 18:35:59 +01:00
l . onAudioSettingsChanged ( ) ;
2020-08-14 18:58:22 +02:00
}
} else if ( TelephonyManager . ACTION_PHONE_STATE_CHANGED . equals ( intent . getAction ( ) ) ) {
String state = intent . getStringExtra ( TelephonyManager . EXTRA_STATE ) ;
if ( TelephonyManager . EXTRA_STATE_OFFHOOK . equals ( state ) ) {
2017-12-08 18:35:59 +01:00
hangUp ( ) ;
}
2020-08-14 18:58:22 +02:00
} else if ( Intent . ACTION_SCREEN_ON . equals ( intent . getAction ( ) ) ) {
screenOn = true ;
for ( int i = 0 ; i < stateListeners . size ( ) ; i + + ) {
stateListeners . get ( i ) . onScreenOnChange ( screenOn ) ;
}
} else if ( Intent . ACTION_SCREEN_OFF . equals ( intent . getAction ( ) ) ) {
screenOn = false ;
for ( int i = 0 ; i < stateListeners . size ( ) ; i + + ) {
stateListeners . get ( i ) . onScreenOnChange ( screenOn ) ;
}
2017-12-08 18:35:59 +01:00
}
}
} ;
public boolean hasEarpiece ( ) {
2020-08-14 18:58:22 +02:00
if ( USE_CONNECTION_SERVICE ) {
if ( systemCallConnection ! = null & & systemCallConnection . getCallAudioState ( ) ! = null ) {
int routeMask = systemCallConnection . getCallAudioState ( ) . getSupportedRouteMask ( ) ;
return ( routeMask & ( CallAudioState . ROUTE_EARPIECE | CallAudioState . ROUTE_WIRED_HEADSET ) ) ! = 0 ;
2018-07-30 04:07:02 +02:00
}
}
2020-08-14 18:58:22 +02:00
if ( ( ( TelephonyManager ) getSystemService ( TELEPHONY_SERVICE ) ) . getPhoneType ( ) ! = TelephonyManager . PHONE_TYPE_NONE ) {
2017-12-08 18:35:59 +01:00
return true ;
2020-08-14 18:58:22 +02:00
}
2017-12-08 18:35:59 +01:00
if ( mHasEarpiece ! = null ) {
return mHasEarpiece ;
}
// not calculated yet, do it now
try {
2020-08-14 18:58:22 +02:00
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
2017-12-08 18:35:59 +01:00
Method method = AudioManager . class . getMethod ( " getDevicesForStream " , Integer . TYPE ) ;
Field field = AudioManager . class . getField ( " DEVICE_OUT_EARPIECE " ) ;
int earpieceFlag = field . getInt ( null ) ;
int bitmaskResult = ( int ) method . invoke ( am , AudioManager . STREAM_VOICE_CALL ) ;
// check if masked by the earpiece flag
if ( ( bitmaskResult & earpieceFlag ) = = earpieceFlag ) {
mHasEarpiece = Boolean . TRUE ;
} else {
mHasEarpiece = Boolean . FALSE ;
}
} catch ( Throwable error ) {
2018-07-30 04:07:02 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error while checking earpiece! " , error ) ;
}
2017-12-08 18:35:59 +01:00
mHasEarpiece = Boolean . TRUE ;
}
return mHasEarpiece ;
}
protected int getStatsNetworkType ( ) {
int netType = StatsController . TYPE_WIFI ;
if ( lastNetInfo ! = null ) {
2020-08-14 18:58:22 +02:00
if ( lastNetInfo . getType ( ) = = ConnectivityManager . TYPE_MOBILE ) {
2017-12-08 18:35:59 +01:00
netType = lastNetInfo . isRoaming ( ) ? StatsController . TYPE_ROAMING : StatsController . TYPE_MOBILE ;
2020-08-14 18:58:22 +02:00
}
2017-12-08 18:35:59 +01:00
}
return netType ;
}
2020-08-14 18:58:22 +02:00
protected void setSwitchingCamera ( boolean switching , boolean isFrontFace ) {
switchingCamera = switching ;
if ( ! switching ) {
isFrontFaceCamera = isFrontFace ;
for ( int a = 0 ; a < stateListeners . size ( ) ; a + + ) {
StateListener l = stateListeners . get ( a ) ;
l . onCameraSwitch ( isFrontFaceCamera ) ;
}
}
}
2017-12-08 18:35:59 +01:00
public void registerStateListener ( StateListener l ) {
2020-12-23 08:48:30 +01:00
if ( stateListeners . contains ( l ) ) {
return ;
}
2017-12-08 18:35:59 +01:00
stateListeners . add ( l ) ;
2020-08-14 18:58:22 +02:00
if ( currentState ! = 0 ) {
2017-12-08 18:35:59 +01:00
l . onStateChanged ( currentState ) ;
2020-08-14 18:58:22 +02:00
}
if ( signalBarCount ! = 0 ) {
2017-12-08 18:35:59 +01:00
l . onSignalBarsCountChanged ( signalBarCount ) ;
2020-08-14 18:58:22 +02:00
}
2017-12-08 18:35:59 +01:00
}
public void unregisterStateListener ( StateListener l ) {
stateListeners . remove ( l ) ;
}
2021-03-19 11:25:58 +01:00
public void editCallMember ( TLObject object , boolean mute , int volume , Boolean raiseHand ) {
if ( object = = null | | groupCall = = null ) {
2020-12-23 08:48:30 +01:00
return ;
}
2021-03-19 11:25:58 +01:00
TLRPC . TL_phone_editGroupCallParticipant req = new TLRPC . TL_phone_editGroupCallParticipant ( ) ;
2020-12-23 08:48:30 +01:00
req . call = groupCall . getInputGroupCall ( ) ;
if ( object instanceof TLRPC . User ) {
TLRPC . User user = ( TLRPC . User ) object ;
2021-03-19 11:25:58 +01:00
req . participant = MessagesController . getInputPeer ( user ) ;
2021-04-09 15:17:32 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " edit group call part id = " + req . participant . user_id + " access_hash = " + req . participant . user_id ) ;
}
2021-03-19 11:25:58 +01:00
} else if ( object instanceof TLRPC . Chat ) {
TLRPC . Chat chat = ( TLRPC . Chat ) object ;
req . participant = MessagesController . getInputPeer ( chat ) ;
2021-04-09 15:17:32 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " edit group call part id = " + ( req . participant . chat_id ! = 0 ? req . participant . chat_id : req . participant . channel_id ) + " access_hash = " + req . participant . access_hash ) ;
}
2020-12-23 08:48:30 +01:00
}
req . muted = mute ;
2021-01-28 15:15:51 +01:00
if ( volume > = 0 ) {
req . volume = volume ;
req . flags | = 2 ;
}
2021-03-19 11:25:58 +01:00
if ( raiseHand ! = null ) {
req . raise_hand = raiseHand ;
req . flags | = 4 ;
}
2021-04-09 15:17:32 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " edit group call flags = " + req . flags ) ;
}
2020-12-23 08:48:30 +01:00
int account = currentAccount ;
AccountInstance . getInstance ( account ) . getConnectionsManager ( ) . sendRequest ( req , ( response , error ) - > {
if ( response ! = null ) {
AccountInstance . getInstance ( account ) . getMessagesController ( ) . processUpdates ( ( TLRPC . Updates ) response , false ) ;
}
} ) ;
2017-12-08 18:35:59 +01:00
}
public boolean isMicMute ( ) {
return micMute ;
}
2020-12-23 08:48:30 +01:00
public void toggleSpeakerphoneOrShowRouteSheet ( Context context , boolean fromOverlayWindow ) {
2020-08-14 18:58:22 +02:00
if ( isBluetoothHeadsetConnected ( ) & & hasEarpiece ( ) ) {
2020-12-23 08:48:30 +01:00
BottomSheet . Builder builder = new BottomSheet . Builder ( context )
2020-08-14 18:58:22 +02:00
. setTitle ( LocaleController . getString ( " VoipOutputDevices " , R . string . VoipOutputDevices ) , true )
. setItems ( new CharSequence [ ] {
LocaleController . getString ( " VoipAudioRoutingSpeaker " , R . string . VoipAudioRoutingSpeaker ) ,
2020-08-15 02:01:55 +02:00
isHeadsetPlugged ? LocaleController . getString ( " VoipAudioRoutingHeadset " , R . string . VoipAudioRoutingHeadset ) : LocaleController . getString ( " VoipAudioRoutingEarpiece " , R . string . VoipAudioRoutingEarpiece ) ,
2020-08-14 18:58:22 +02:00
currentBluetoothDeviceName ! = null ? currentBluetoothDeviceName : LocaleController . getString ( " VoipAudioRoutingBluetooth " , R . string . VoipAudioRoutingBluetooth ) } ,
new int [ ] { R . drawable . calls_menu_speaker ,
2020-08-15 02:01:55 +02:00
isHeadsetPlugged ? R . drawable . calls_menu_headset : R . drawable . calls_menu_phone ,
2020-08-14 18:58:22 +02:00
R . drawable . calls_menu_bluetooth } , ( dialog , which ) - > {
if ( getSharedInstance ( ) = = null ) {
return ;
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
setAudioOutput ( which ) ;
2018-07-30 04:07:02 +02:00
} ) ;
2020-12-23 08:48:30 +01:00
BottomSheet bottomSheet = builder . create ( ) ;
if ( fromOverlayWindow ) {
if ( Build . VERSION . SDK_INT > = 26 ) {
bottomSheet . getWindow ( ) . setType ( WindowManager . LayoutParams . TYPE_APPLICATION_OVERLAY ) ;
} else {
bottomSheet . getWindow ( ) . setType ( WindowManager . LayoutParams . TYPE_SYSTEM_ALERT ) ;
}
}
2020-08-14 18:58:22 +02:00
builder . show ( ) ;
2018-07-30 04:07:02 +02:00
return ;
}
2020-08-14 18:58:22 +02:00
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null & & systemCallConnection . getCallAudioState ( ) ! = null ) {
if ( hasEarpiece ( ) ) {
systemCallConnection . setAudioRoute ( systemCallConnection . getCallAudioState ( ) . getRoute ( ) = = CallAudioState . ROUTE_SPEAKER ? CallAudioState . ROUTE_WIRED_OR_EARPIECE : CallAudioState . ROUTE_SPEAKER ) ;
} else {
systemCallConnection . setAudioRoute ( systemCallConnection . getCallAudioState ( ) . getRoute ( ) = = CallAudioState . ROUTE_BLUETOOTH ? CallAudioState . ROUTE_WIRED_OR_EARPIECE : CallAudioState . ROUTE_BLUETOOTH ) ;
}
} else if ( audioConfigured & & ! USE_CONNECTION_SERVICE ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( hasEarpiece ( ) ) {
2018-07-30 04:07:02 +02:00
am . setSpeakerphoneOn ( ! am . isSpeakerphoneOn ( ) ) ;
2020-08-14 18:58:22 +02:00
} else {
2018-07-30 04:07:02 +02:00
am . setBluetoothScoOn ( ! am . isBluetoothScoOn ( ) ) ;
}
updateOutputGainControlState ( ) ;
2020-08-14 18:58:22 +02:00
} else {
speakerphoneStateToSet = ! speakerphoneStateToSet ;
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
for ( StateListener l : stateListeners ) {
2018-07-30 04:07:02 +02:00
l . onAudioSettingsChanged ( ) ;
2020-08-14 18:58:22 +02:00
}
}
protected void setAudioOutput ( int which ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null ) {
switch ( which ) {
case 2 :
systemCallConnection . setAudioRoute ( CallAudioState . ROUTE_BLUETOOTH ) ;
break ;
case 1 :
systemCallConnection . setAudioRoute ( CallAudioState . ROUTE_WIRED_OR_EARPIECE ) ;
break ;
case 0 :
systemCallConnection . setAudioRoute ( CallAudioState . ROUTE_SPEAKER ) ;
break ;
}
} else if ( audioConfigured & & ! USE_CONNECTION_SERVICE ) {
switch ( which ) {
case 2 :
if ( ! bluetoothScoActive ) {
needSwitchToBluetoothAfterScoActivates = true ;
try {
am . startBluetoothSco ( ) ;
} catch ( Throwable ignore ) {
}
} else {
am . setBluetoothScoOn ( true ) ;
am . setSpeakerphoneOn ( false ) ;
}
break ;
case 1 :
if ( bluetoothScoActive ) {
am . stopBluetoothSco ( ) ;
bluetoothScoActive = false ;
}
am . setSpeakerphoneOn ( false ) ;
am . setBluetoothScoOn ( false ) ;
break ;
case 0 :
if ( bluetoothScoActive ) {
am . stopBluetoothSco ( ) ;
bluetoothScoActive = false ;
}
am . setBluetoothScoOn ( false ) ;
am . setSpeakerphoneOn ( true ) ;
break ;
}
updateOutputGainControlState ( ) ;
} else {
switch ( which ) {
case 2 :
audioRouteToSet = AUDIO_ROUTE_BLUETOOTH ;
speakerphoneStateToSet = false ;
break ;
case 1 :
audioRouteToSet = AUDIO_ROUTE_EARPIECE ;
speakerphoneStateToSet = false ;
break ;
case 0 :
audioRouteToSet = AUDIO_ROUTE_SPEAKER ;
speakerphoneStateToSet = true ;
break ;
}
}
for ( StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
public boolean isSpeakerphoneOn ( ) {
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null & & systemCallConnection . getCallAudioState ( ) ! = null ) {
int route = systemCallConnection . getCallAudioState ( ) . getRoute ( ) ;
return hasEarpiece ( ) ? route = = CallAudioState . ROUTE_SPEAKER : route = = CallAudioState . ROUTE_BLUETOOTH ;
} else if ( audioConfigured & & ! USE_CONNECTION_SERVICE ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
2018-07-30 04:07:02 +02:00
return hasEarpiece ( ) ? am . isSpeakerphoneOn ( ) : am . isBluetoothScoOn ( ) ;
}
return speakerphoneStateToSet ;
}
2020-08-14 18:58:22 +02:00
public int getCurrentAudioRoute ( ) {
if ( USE_CONNECTION_SERVICE ) {
if ( systemCallConnection ! = null & & systemCallConnection . getCallAudioState ( ) ! = null ) {
switch ( systemCallConnection . getCallAudioState ( ) . getRoute ( ) ) {
2018-07-30 04:07:02 +02:00
case CallAudioState . ROUTE_BLUETOOTH :
return AUDIO_ROUTE_BLUETOOTH ;
case CallAudioState . ROUTE_EARPIECE :
case CallAudioState . ROUTE_WIRED_HEADSET :
return AUDIO_ROUTE_EARPIECE ;
case CallAudioState . ROUTE_SPEAKER :
return AUDIO_ROUTE_SPEAKER ;
}
}
return audioRouteToSet ;
}
2020-08-14 18:58:22 +02:00
if ( audioConfigured ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( am . isBluetoothScoOn ( ) ) {
2018-07-30 04:07:02 +02:00
return AUDIO_ROUTE_BLUETOOTH ;
2020-08-14 18:58:22 +02:00
} else if ( am . isSpeakerphoneOn ( ) ) {
2018-07-30 04:07:02 +02:00
return AUDIO_ROUTE_SPEAKER ;
2020-08-14 18:58:22 +02:00
} else {
2018-07-30 04:07:02 +02:00
return AUDIO_ROUTE_EARPIECE ;
2020-08-14 18:58:22 +02:00
}
2018-07-30 04:07:02 +02:00
}
return audioRouteToSet ;
}
2017-12-08 18:35:59 +01:00
public String getDebugString ( ) {
2020-03-30 14:00:09 +02:00
return tgVoip ! = null ? tgVoip . getDebugInfo ( ) : " " ;
2017-12-08 18:35:59 +01:00
}
public long getCallDuration ( ) {
2020-03-30 14:00:09 +02:00
if ( callStartTime = = 0 ) {
return 0 ;
}
return SystemClock . elapsedRealtime ( ) - callStartTime ;
2017-12-08 18:35:59 +01:00
}
2020-08-14 18:58:22 +02:00
public static VoIPBaseService getSharedInstance ( ) {
2017-12-08 18:35:59 +01:00
return sharedInstance ;
}
public void stopRinging ( ) {
if ( ringtonePlayer ! = null ) {
ringtonePlayer . stop ( ) ;
ringtonePlayer . release ( ) ;
ringtonePlayer = null ;
}
if ( vibrator ! = null ) {
vibrator . cancel ( ) ;
vibrator = null ;
}
}
2020-08-14 18:58:22 +02:00
protected void showNotification ( String name , Bitmap photo ) {
2020-12-23 08:48:30 +01:00
Intent intent = new Intent ( this , LaunchActivity . class ) . setAction ( groupCall ! = null ? " voip_chat " : " voip " ) ;
if ( groupCall ! = null ) {
intent . putExtra ( " currentAccount " , currentAccount ) ;
}
2017-12-08 18:35:59 +01:00
Notification . Builder builder = new Notification . Builder ( this )
2020-12-23 08:48:30 +01:00
. setContentTitle ( groupCall ! = null ? LocaleController . getString ( " VoipVoiceChat " , R . string . VoipVoiceChat ) : LocaleController . getString ( " VoipOutgoingCall " , R . string . VoipOutgoingCall ) )
2017-12-08 18:35:59 +01:00
. setContentText ( name )
2020-12-23 08:48:30 +01:00
. setContentIntent ( PendingIntent . getActivity ( this , 50 , intent , 0 ) ) ;
if ( groupCall ! = null ) {
builder . setSmallIcon ( isMicMute ( ) ? R . drawable . voicechat_muted : R . drawable . voicechat_active ) ;
} else {
builder . setSmallIcon ( R . drawable . notification ) ;
}
2017-12-08 18:35:59 +01:00
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN ) {
Intent endIntent = new Intent ( this , VoIPActionsReceiver . class ) ;
endIntent . setAction ( getPackageName ( ) + " .END_CALL " ) ;
2020-12-23 08:48:30 +01:00
builder . addAction ( R . drawable . ic_call_end_white_24dp , groupCall ! = null ? LocaleController . getString ( " VoipGroupLeaveAlertTitle " , R . string . VoipGroupLeaveAlertTitle ) : LocaleController . getString ( " VoipEndCall " , R . string . VoipEndCall ) , PendingIntent . getBroadcast ( this , 0 , endIntent , PendingIntent . FLAG_UPDATE_CURRENT ) ) ;
2017-12-08 18:35:59 +01:00
builder . setPriority ( Notification . PRIORITY_MAX ) ;
}
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN_MR1 ) {
builder . setShowWhen ( false ) ;
}
2020-08-14 18:58:22 +02:00
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . O ) {
builder . setColor ( 0xff282e31 ) ;
builder . setColorized ( true ) ;
} else if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . LOLLIPOP ) {
2017-12-08 18:35:59 +01:00
builder . setColor ( 0xff2ca5e0 ) ;
}
2018-07-30 04:07:02 +02:00
if ( Build . VERSION . SDK_INT > = 26 ) {
2018-08-27 10:33:11 +02:00
NotificationsController . checkOtherNotificationsChannel ( ) ;
2018-07-30 04:07:02 +02:00
builder . setChannelId ( NotificationsController . OTHER_NOTIFICATIONS_CHANNEL ) ;
}
2020-08-14 18:58:22 +02:00
if ( photo ! = null ) {
builder . setLargeIcon ( photo ) ;
2017-12-08 18:35:59 +01:00
}
ongoingCallNotification = builder . getNotification ( ) ;
startForeground ( ID_ONGOING_CALL_NOTIFICATION , ongoingCallNotification ) ;
}
2020-08-14 18:58:22 +02:00
protected void startRingtoneAndVibration ( int chatID ) {
2018-07-30 04:07:02 +02:00
SharedPreferences prefs = MessagesController . getNotificationsSettings ( currentAccount ) ;
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
2020-08-14 18:58:22 +02:00
boolean needRing = am . getRingerMode ( ) ! = AudioManager . RINGER_MODE_SILENT ;
if ( needRing ) {
ringtonePlayer = new MediaPlayer ( ) ;
2021-04-09 15:17:32 +02:00
ringtonePlayer . setOnPreparedListener ( mediaPlayer - > {
try {
ringtonePlayer . start ( ) ;
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
} ) ;
2018-07-30 04:07:02 +02:00
ringtonePlayer . setLooping ( true ) ;
2020-08-15 02:01:55 +02:00
if ( isHeadsetPlugged ) {
ringtonePlayer . setAudioStreamType ( AudioManager . STREAM_VOICE_CALL ) ;
} else {
ringtonePlayer . setAudioStreamType ( AudioManager . STREAM_RING ) ;
if ( ! USE_CONNECTION_SERVICE ) {
am . requestAudioFocus ( this , AudioManager . STREAM_RING , AudioManager . AUDIOFOCUS_GAIN ) ;
}
}
2020-08-14 18:58:22 +02:00
try {
2018-07-30 04:07:02 +02:00
String notificationUri ;
2020-08-15 02:01:55 +02:00
if ( prefs . getBoolean ( " custom_ " + chatID , false ) ) {
2020-08-14 18:58:22 +02:00
notificationUri = prefs . getString ( " ringtone_path_ " + chatID , RingtoneManager . getDefaultUri ( RingtoneManager . TYPE_RINGTONE ) . toString ( ) ) ;
2020-08-15 02:01:55 +02:00
} else {
2020-08-14 18:58:22 +02:00
notificationUri = prefs . getString ( " CallsRingtonePath " , RingtoneManager . getDefaultUri ( RingtoneManager . TYPE_RINGTONE ) . toString ( ) ) ;
2020-08-15 02:01:55 +02:00
}
2018-07-30 04:07:02 +02:00
ringtonePlayer . setDataSource ( this , Uri . parse ( notificationUri ) ) ;
ringtonePlayer . prepareAsync ( ) ;
2020-08-14 18:58:22 +02:00
} catch ( Exception e ) {
2018-07-30 04:07:02 +02:00
FileLog . e ( e ) ;
2020-08-14 18:58:22 +02:00
if ( ringtonePlayer ! = null ) {
2018-07-30 04:07:02 +02:00
ringtonePlayer . release ( ) ;
2020-08-14 18:58:22 +02:00
ringtonePlayer = null ;
2018-07-30 04:07:02 +02:00
}
}
int vibrate ;
2020-08-14 18:58:22 +02:00
if ( prefs . getBoolean ( " custom_ " + chatID , false ) ) {
vibrate = prefs . getInt ( " calls_vibrate_ " + chatID , 0 ) ;
} else {
vibrate = prefs . getInt ( " vibrate_calls " , 0 ) ;
}
if ( ( vibrate ! = 2 & & vibrate ! = 4 & & ( am . getRingerMode ( ) = = AudioManager . RINGER_MODE_VIBRATE | | am . getRingerMode ( ) = = AudioManager . RINGER_MODE_NORMAL ) ) | | ( vibrate = = 4 & & am . getRingerMode ( ) = = AudioManager . RINGER_MODE_VIBRATE ) ) {
vibrator = ( Vibrator ) getSystemService ( VIBRATOR_SERVICE ) ;
long duration = 700 ;
if ( vibrate = = 1 ) {
duration / = 2 ;
} else if ( vibrate = = 3 ) {
duration * = 2 ;
}
2018-07-30 04:07:02 +02:00
vibrator . vibrate ( new long [ ] { 0 , duration , 500 } , 0 ) ;
}
}
}
2017-12-08 18:35:59 +01:00
@Override
public void onDestroy ( ) {
2018-07-30 04:07:02 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " =============== VoIPService STOPPING =============== " ) ;
}
2017-12-08 18:35:59 +01:00
stopForeground ( true ) ;
stopRinging ( ) ;
2020-08-15 23:06:36 +02:00
if ( ApplicationLoader . mainInterfacePaused | | ! ApplicationLoader . isScreenOn ) {
MessagesController . getInstance ( currentAccount ) . ignoreSetOnline = false ;
}
2018-07-30 04:07:02 +02:00
NotificationCenter . getInstance ( currentAccount ) . removeObserver ( this , NotificationCenter . appDidLogout ) ;
2017-12-08 18:35:59 +01:00
SensorManager sm = ( SensorManager ) getSystemService ( SENSOR_SERVICE ) ;
Sensor proximity = sm . getDefaultSensor ( Sensor . TYPE_PROXIMITY ) ;
if ( proximity ! = null ) {
sm . unregisterListener ( this ) ;
}
if ( proximityWakelock ! = null & & proximityWakelock . isHeld ( ) ) {
proximityWakelock . release ( ) ;
}
2020-12-23 08:48:30 +01:00
if ( updateNotificationRunnable ! = null ) {
Utilities . globalQueue . cancelRunnable ( updateNotificationRunnable ) ;
updateNotificationRunnable = null ;
}
2021-04-09 15:17:32 +02:00
if ( switchingStreamTimeoutRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( switchingStreamTimeoutRunnable ) ;
switchingStreamTimeoutRunnable = null ;
}
2017-12-08 18:35:59 +01:00
unregisterReceiver ( receiver ) ;
2020-08-14 18:58:22 +02:00
if ( timeoutRunnable ! = null ) {
2017-12-08 18:35:59 +01:00
AndroidUtilities . cancelRunOnUIThread ( timeoutRunnable ) ;
2020-08-14 18:58:22 +02:00
timeoutRunnable = null ;
2017-12-08 18:35:59 +01:00
}
super . onDestroy ( ) ;
sharedInstance = null ;
2020-08-14 18:58:22 +02:00
AndroidUtilities . runOnUIThread ( ( ) - > NotificationCenter . getGlobalInstance ( ) . postNotificationName ( NotificationCenter . didEndCall ) ) ;
2020-03-30 14:00:09 +02:00
if ( tgVoip ! = null ) {
StatsController . getInstance ( currentAccount ) . incrementTotalCallsTime ( getStatsNetworkType ( ) , ( int ) ( getCallDuration ( ) / 1000 ) % 5 ) ;
onTgVoipPreStop ( ) ;
2020-12-23 08:48:30 +01:00
if ( tgVoip . isGroup ( ) ) {
NativeInstance instance = tgVoip ;
Utilities . globalQueue . postRunnable ( instance : : stopGroup ) ;
2021-03-19 11:25:58 +01:00
AccountInstance . getInstance ( currentAccount ) . getConnectionsManager ( ) . cancelRequest ( currentStreamRequestId , true ) ;
currentStreamRequestId = 0 ;
2020-12-23 08:48:30 +01:00
} else {
Instance . FinalState state = tgVoip . stop ( ) ;
updateTrafficStats ( state . trafficStats ) ;
onTgVoipStop ( state ) ;
}
2020-03-30 14:00:09 +02:00
prevTrafficStats = null ;
callStartTime = 0 ;
tgVoip = null ;
2020-08-14 18:58:22 +02:00
Instance . destroyInstance ( ) ;
}
if ( videoCapturer ! = 0 ) {
NativeInstance . destroyVideoCapturer ( videoCapturer ) ;
videoCapturer = 0 ;
2017-12-08 18:35:59 +01:00
}
cpuWakelock . release ( ) ;
2020-12-23 08:48:30 +01:00
if ( ! playingSound ) {
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( ! USE_CONNECTION_SERVICE ) {
if ( isBtHeadsetConnected ) {
am . stopBluetoothSco ( ) ;
am . setBluetoothScoOn ( false ) ;
am . setSpeakerphoneOn ( false ) ;
bluetoothScoActive = false ;
2018-07-30 04:07:02 +02:00
}
2020-12-23 08:48:30 +01:00
if ( onDestroyRunnable = = null ) {
Utilities . globalQueue . postRunnable ( setModeRunnable = ( ) - > {
synchronized ( sync ) {
if ( setModeRunnable = = null ) {
return ;
}
setModeRunnable = null ;
}
try {
am . setMode ( AudioManager . MODE_NORMAL ) ;
} catch ( SecurityException x ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error setting audio more to normal " , x ) ;
}
}
} ) ;
}
am . abandonAudioFocus ( this ) ;
2018-07-30 04:07:02 +02:00
}
2020-12-23 08:48:30 +01:00
am . unregisterMediaButtonEventReceiver ( new ComponentName ( this , VoIPMediaButtonReceiver . class ) ) ;
2020-12-24 06:36:01 +01:00
if ( hasAudioFocus ) {
2020-12-23 08:48:30 +01:00
am . abandonAudioFocus ( this ) ;
}
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . release ( ) ) ;
2020-08-14 18:58:22 +02:00
}
2017-12-08 18:35:59 +01:00
2020-08-14 18:58:22 +02:00
if ( USE_CONNECTION_SERVICE ) {
if ( ! didDeleteConnectionServiceContact ) {
2019-01-23 18:03:33 +01:00
ContactsController . getInstance ( currentAccount ) . deleteConnectionServiceContact ( ) ;
2020-08-14 18:58:22 +02:00
}
if ( systemCallConnection ! = null & & ! playingSound ) {
2018-07-30 04:07:02 +02:00
systemCallConnection . destroy ( ) ;
}
}
ConnectionsManager . getInstance ( currentAccount ) . setAppPaused ( true , false ) ;
2020-12-23 08:48:30 +01:00
VoIPHelper . lastCallTime = SystemClock . elapsedRealtime ( ) ;
2017-12-08 18:35:59 +01:00
}
2020-08-14 18:58:22 +02:00
public abstract long getCallID ( ) ;
public abstract void hangUp ( ) ;
public abstract void hangUp ( Runnable onDone ) ;
public abstract void acceptIncomingCall ( ) ;
public abstract void declineIncomingCall ( int reason , Runnable onDone ) ;
public abstract void declineIncomingCall ( ) ;
protected abstract Class < ? extends Activity > getUIActivityClass ( ) ;
public abstract CallConnection getConnectionAndStartCall ( ) ;
protected abstract void startRinging ( ) ;
public abstract void startRingtoneAndVibration ( ) ;
protected abstract void updateServerConfig ( ) ;
protected abstract void showNotification ( ) ;
2020-03-30 14:00:09 +02:00
protected void onTgVoipPreStop ( ) {
2020-08-14 18:58:22 +02:00
2017-12-08 18:35:59 +01:00
}
2020-08-14 18:58:22 +02:00
protected void onTgVoipStop ( Instance . FinalState finalState ) {
2017-12-08 18:35:59 +01:00
}
2020-08-14 18:58:22 +02:00
protected void initializeAccountRelatedThings ( ) {
2018-07-30 04:07:02 +02:00
updateServerConfig ( ) ;
NotificationCenter . getInstance ( currentAccount ) . addObserver ( this , NotificationCenter . appDidLogout ) ;
ConnectionsManager . getInstance ( currentAccount ) . setAppPaused ( false , false ) ;
}
2020-08-14 18:58:22 +02:00
@SuppressLint ( " InvalidWakeLockTag " )
2017-12-08 18:35:59 +01:00
@Override
public void onCreate ( ) {
super . onCreate ( ) ;
2018-07-30 04:07:02 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " =============== VoIPService STARTING =============== " ) ;
}
2017-12-08 18:35:59 +01:00
try {
2020-03-30 14:00:09 +02:00
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
2020-08-14 18:58:22 +02:00
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN_MR1 & & am . getProperty ( AudioManager . PROPERTY_OUTPUT_FRAMES_PER_BUFFER ) ! = null ) {
2020-03-30 14:00:09 +02:00
int outFramesPerBuffer = Integer . parseInt ( am . getProperty ( AudioManager . PROPERTY_OUTPUT_FRAMES_PER_BUFFER ) ) ;
2020-08-14 18:58:22 +02:00
Instance . setBufferSize ( outFramesPerBuffer ) ;
2020-03-30 14:00:09 +02:00
} else {
2020-08-14 18:58:22 +02:00
Instance . setBufferSize ( AudioTrack . getMinBufferSize ( 48000 , AudioFormat . CHANNEL_OUT_MONO , AudioFormat . ENCODING_PCM_16BIT ) / 2 ) ;
2020-03-30 14:00:09 +02:00
}
2017-12-08 18:35:59 +01:00
cpuWakelock = ( ( PowerManager ) getSystemService ( POWER_SERVICE ) ) . newWakeLock ( PowerManager . PARTIAL_WAKE_LOCK , " telegram-voip " ) ;
cpuWakelock . acquire ( ) ;
2020-08-14 18:58:22 +02:00
btAdapter = am . isBluetoothScoAvailableOffCall ( ) ? BluetoothAdapter . getDefaultAdapter ( ) : null ;
2017-12-08 18:35:59 +01:00
IntentFilter filter = new IntentFilter ( ) ;
filter . addAction ( ConnectivityManager . CONNECTIVITY_ACTION ) ;
2020-08-14 18:58:22 +02:00
if ( ! USE_CONNECTION_SERVICE ) {
2018-07-30 04:07:02 +02:00
filter . addAction ( ACTION_HEADSET_PLUG ) ;
2020-08-14 18:58:22 +02:00
if ( btAdapter ! = null ) {
2018-07-30 04:07:02 +02:00
filter . addAction ( BluetoothHeadset . ACTION_CONNECTION_STATE_CHANGED ) ;
filter . addAction ( AudioManager . ACTION_SCO_AUDIO_STATE_UPDATED ) ;
}
filter . addAction ( TelephonyManager . ACTION_PHONE_STATE_CHANGED ) ;
2020-08-14 18:58:22 +02:00
filter . addAction ( Intent . ACTION_SCREEN_ON ) ;
filter . addAction ( Intent . ACTION_SCREEN_OFF ) ;
2017-12-08 18:35:59 +01:00
}
registerReceiver ( receiver , filter ) ;
2020-08-14 18:58:22 +02:00
fetchBluetoothDeviceName ( ) ;
2017-12-08 18:35:59 +01:00
am . registerMediaButtonEventReceiver ( new ComponentName ( this , VoIPMediaButtonReceiver . class ) ) ;
2020-08-14 18:58:22 +02:00
if ( ! USE_CONNECTION_SERVICE & & btAdapter ! = null & & btAdapter . isEnabled ( ) ) {
2020-12-23 08:48:30 +01:00
try {
MediaRouter mr = ( MediaRouter ) getSystemService ( Context . MEDIA_ROUTER_SERVICE ) ;
if ( Build . VERSION . SDK_INT < 24 ) {
int headsetState = btAdapter . getProfileConnectionState ( BluetoothProfile . HEADSET ) ;
updateBluetoothHeadsetState ( headsetState = = BluetoothProfile . STATE_CONNECTED ) ;
for ( StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
} else {
MediaRouter . RouteInfo ri = mr . getSelectedRoute ( MediaRouter . ROUTE_TYPE_LIVE_AUDIO ) ;
if ( ri . getDeviceType ( ) = = MediaRouter . RouteInfo . DEVICE_TYPE_BLUETOOTH ) {
int headsetState = btAdapter . getProfileConnectionState ( BluetoothProfile . HEADSET ) ;
updateBluetoothHeadsetState ( headsetState = = BluetoothProfile . STATE_CONNECTED ) ;
for ( StateListener l : stateListeners ) {
l . onAudioSettingsChanged ( ) ;
}
} else {
updateBluetoothHeadsetState ( false ) ;
}
}
} catch ( Throwable e ) {
FileLog . e ( e ) ;
2020-08-14 18:58:22 +02:00
}
2017-12-08 18:35:59 +01:00
}
} catch ( Exception x ) {
2018-07-30 04:07:02 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " error initializing voip controller " , x ) ;
}
2017-12-08 18:35:59 +01:00
callFailed ( ) ;
}
}
2021-04-09 15:17:32 +02:00
protected void loadResources ( ) {
if ( chat ! = null & & SharedConfig . useMediaStream ) {
currentStreamType = AudioManager . STREAM_MUSIC ;
if ( Build . VERSION . SDK_INT > = 21 ) {
WebRtcAudioTrack . setAudioTrackUsageAttribute ( AudioAttributes . USAGE_MEDIA ) ;
}
} else {
currentStreamType = AudioManager . STREAM_VOICE_CALL ;
if ( Build . VERSION . SDK_INT > = 21 ) {
WebRtcAudioTrack . setAudioTrackUsageAttribute ( AudioAttributes . USAGE_VOICE_COMMUNICATION ) ;
}
}
WebRtcAudioTrack . setAudioStreamType ( currentStreamType ) ;
Utilities . globalQueue . postRunnable ( ( ) - > {
soundPool = new SoundPool ( 1 , currentStreamType , 0 ) ;
spConnectingId = soundPool . load ( this , R . raw . voip_connecting , 1 ) ;
spRingbackID = soundPool . load ( this , R . raw . voip_ringback , 1 ) ;
spFailedID = soundPool . load ( this , R . raw . voip_failed , 1 ) ;
spEndId = soundPool . load ( this , R . raw . voip_end , 1 ) ;
spBusyId = soundPool . load ( this , R . raw . voip_busy , 1 ) ;
spVoiceChatEndId = soundPool . load ( this , R . raw . voicechat_leave , 1 ) ;
spVoiceChatStartId = soundPool . load ( this , R . raw . voicechat_join , 1 ) ;
spVoiceChatConnecting = soundPool . load ( this , R . raw . voicechat_connecting , 1 ) ;
spAllowTalkId = soundPool . load ( this , R . raw . voip_onallowtalk , 1 ) ;
spStartRecordId = soundPool . load ( this , R . raw . voip_recordstart , 1 ) ;
} ) ;
}
2017-12-08 18:35:59 +01:00
protected void dispatchStateChanged ( int state ) {
2018-07-30 04:07:02 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " == Call " + getCallID ( ) + " state changed to " + state + " == " ) ;
}
2017-12-08 18:35:59 +01:00
currentState = state ;
2020-08-14 18:58:22 +02:00
if ( USE_CONNECTION_SERVICE & & state = = STATE_ESTABLISHED /*&& !wasEstablished*/ & & systemCallConnection ! = null ) {
2018-07-30 04:07:02 +02:00
systemCallConnection . setActive ( ) ;
}
2017-12-08 18:35:59 +01:00
for ( int a = 0 ; a < stateListeners . size ( ) ; a + + ) {
StateListener l = stateListeners . get ( a ) ;
l . onStateChanged ( state ) ;
}
}
2020-08-15 23:06:36 +02:00
protected void updateTrafficStats ( Instance . TrafficStats trafficStats ) {
if ( trafficStats = = null ) {
trafficStats = tgVoip . getTrafficStats ( ) ;
}
2020-03-30 14:00:09 +02:00
final long wifiSentDiff = trafficStats . bytesSentWifi - ( prevTrafficStats ! = null ? prevTrafficStats . bytesSentWifi : 0 ) ;
final long wifiRecvdDiff = trafficStats . bytesReceivedWifi - ( prevTrafficStats ! = null ? prevTrafficStats . bytesReceivedWifi : 0 ) ;
final long mobileSentDiff = trafficStats . bytesSentMobile - ( prevTrafficStats ! = null ? prevTrafficStats . bytesSentMobile : 0 ) ;
final long mobileRecvdDiff = trafficStats . bytesReceivedMobile - ( prevTrafficStats ! = null ? prevTrafficStats . bytesReceivedMobile : 0 ) ;
prevTrafficStats = trafficStats ;
if ( wifiSentDiff > 0 ) {
2018-07-30 04:07:02 +02:00
StatsController . getInstance ( currentAccount ) . incrementSentBytesCount ( StatsController . TYPE_WIFI , StatsController . TYPE_CALLS , wifiSentDiff ) ;
2020-03-30 14:00:09 +02:00
}
if ( wifiRecvdDiff > 0 ) {
2018-07-30 04:07:02 +02:00
StatsController . getInstance ( currentAccount ) . incrementReceivedBytesCount ( StatsController . TYPE_WIFI , StatsController . TYPE_CALLS , wifiRecvdDiff ) ;
2020-03-30 14:00:09 +02:00
}
if ( mobileSentDiff > 0 ) {
2020-08-14 18:58:22 +02:00
StatsController . getInstance ( currentAccount ) . incrementSentBytesCount ( lastNetInfo ! = null & & lastNetInfo . isRoaming ( ) ? StatsController . TYPE_ROAMING : StatsController . TYPE_MOBILE , StatsController . TYPE_CALLS , mobileSentDiff ) ;
2020-03-30 14:00:09 +02:00
}
if ( mobileRecvdDiff > 0 ) {
2020-08-14 18:58:22 +02:00
StatsController . getInstance ( currentAccount ) . incrementReceivedBytesCount ( lastNetInfo ! = null & & lastNetInfo . isRoaming ( ) ? StatsController . TYPE_ROAMING : StatsController . TYPE_MOBILE , StatsController . TYPE_CALLS , mobileRecvdDiff ) ;
2020-03-30 14:00:09 +02:00
}
2017-12-08 18:35:59 +01:00
}
2020-08-14 18:58:22 +02:00
@SuppressLint ( " InvalidWakeLockTag " )
2017-12-08 18:35:59 +01:00
protected void configureDeviceForCall ( ) {
needPlayEndSound = true ;
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
2020-08-14 18:58:22 +02:00
if ( ! USE_CONNECTION_SERVICE ) {
2021-04-09 15:17:32 +02:00
if ( currentStreamType = = AudioManager . STREAM_VOICE_CALL ) {
Utilities . globalQueue . postRunnable ( ( ) - > {
try {
am . setMode ( AudioManager . MODE_IN_COMMUNICATION ) ;
} catch ( Exception e ) {
FileLog . e ( e ) ;
}
} ) ;
}
am . requestAudioFocus ( this , currentStreamType , AudioManager . AUDIOFOCUS_GAIN ) ;
2020-08-14 18:58:22 +02:00
if ( isBluetoothHeadsetConnected ( ) & & hasEarpiece ( ) ) {
switch ( audioRouteToSet ) {
2018-07-30 04:07:02 +02:00
case AUDIO_ROUTE_BLUETOOTH :
2020-08-14 18:58:22 +02:00
if ( ! bluetoothScoActive ) {
needSwitchToBluetoothAfterScoActivates = true ;
2018-08-27 10:33:11 +02:00
try {
am . startBluetoothSco ( ) ;
} catch ( Throwable ignore ) {
}
2020-08-14 18:58:22 +02:00
} else {
2018-08-27 10:33:11 +02:00
am . setBluetoothScoOn ( true ) ;
am . setSpeakerphoneOn ( false ) ;
}
2018-07-30 04:07:02 +02:00
break ;
case AUDIO_ROUTE_EARPIECE :
am . setBluetoothScoOn ( false ) ;
am . setSpeakerphoneOn ( false ) ;
break ;
case AUDIO_ROUTE_SPEAKER :
am . setBluetoothScoOn ( false ) ;
am . setSpeakerphoneOn ( true ) ;
break ;
}
2020-08-14 18:58:22 +02:00
} else if ( isBluetoothHeadsetConnected ( ) ) {
2018-07-30 04:07:02 +02:00
am . setBluetoothScoOn ( speakerphoneStateToSet ) ;
2020-08-14 18:58:22 +02:00
} else {
2018-07-30 04:07:02 +02:00
am . setSpeakerphoneOn ( speakerphoneStateToSet ) ;
}
2020-08-14 18:58:22 +02:00
}
2017-12-08 18:35:59 +01:00
updateOutputGainControlState ( ) ;
2020-08-14 18:58:22 +02:00
audioConfigured = true ;
2017-12-08 18:35:59 +01:00
SensorManager sm = ( SensorManager ) getSystemService ( SENSOR_SERVICE ) ;
Sensor proximity = sm . getDefaultSensor ( Sensor . TYPE_PROXIMITY ) ;
2020-08-14 18:58:22 +02:00
try {
if ( proximity ! = null ) {
proximityWakelock = ( ( PowerManager ) getSystemService ( Context . POWER_SERVICE ) ) . newWakeLock ( PROXIMITY_SCREEN_OFF_WAKE_LOCK , " telegram-voip-prx " ) ;
2017-12-08 18:35:59 +01:00
sm . registerListener ( this , proximity , SensorManager . SENSOR_DELAY_NORMAL ) ;
}
2020-08-14 18:58:22 +02:00
} catch ( Exception x ) {
2018-07-30 04:07:02 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . e ( " Error initializing proximity sensor " , x ) ;
}
2017-12-08 18:35:59 +01:00
}
}
2020-08-14 18:58:22 +02:00
private void fetchBluetoothDeviceName ( ) {
if ( fetchingBluetoothDeviceName ) {
return ;
}
try {
currentBluetoothDeviceName = null ;
fetchingBluetoothDeviceName = true ;
BluetoothAdapter . getDefaultAdapter ( ) . getProfileProxy ( this , serviceListener , BluetoothProfile . HEADSET ) ;
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
}
2017-12-08 18:35:59 +01:00
@SuppressLint ( " NewApi " )
@Override
public void onSensorChanged ( SensorEvent event ) {
2020-12-23 08:48:30 +01:00
if ( unmutedByHold | | currentVideoState = = Instance . VIDEO_STATE_ACTIVE | | videoState = = Instance . VIDEO_STATE_ACTIVE ) {
2020-08-14 18:58:22 +02:00
return ;
}
2017-12-08 18:35:59 +01:00
if ( event . sensor . getType ( ) = = Sensor . TYPE_PROXIMITY ) {
2020-08-14 18:58:22 +02:00
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
2017-12-08 18:35:59 +01:00
if ( isHeadsetPlugged | | am . isSpeakerphoneOn ( ) | | ( isBluetoothHeadsetConnected ( ) & & am . isBluetoothScoOn ( ) ) ) {
return ;
}
boolean newIsNear = event . values [ 0 ] < Math . min ( event . sensor . getMaximumRange ( ) , 3 ) ;
2020-08-14 18:58:22 +02:00
checkIsNear ( newIsNear ) ;
}
}
protected void checkIsNear ( ) {
if ( currentVideoState = = Instance . VIDEO_STATE_ACTIVE | | videoState = = Instance . VIDEO_STATE_ACTIVE ) {
checkIsNear ( false ) ;
}
}
private void checkIsNear ( boolean newIsNear ) {
if ( newIsNear ! = isProximityNear ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " proximity " + newIsNear ) ;
}
isProximityNear = newIsNear ;
try {
if ( isProximityNear ) {
proximityWakelock . acquire ( ) ;
} else {
proximityWakelock . release ( 1 ) ; // this is non-public API before L
2017-12-08 18:35:59 +01:00
}
2020-08-14 18:58:22 +02:00
} catch ( Exception x ) {
FileLog . e ( x ) ;
2017-12-08 18:35:59 +01:00
}
}
}
@Override
public void onAccuracyChanged ( Sensor sensor , int accuracy ) {
}
2020-08-14 18:58:22 +02:00
public boolean isBluetoothHeadsetConnected ( ) {
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null & & systemCallConnection . getCallAudioState ( ) ! = null ) {
return ( systemCallConnection . getCallAudioState ( ) . getSupportedRouteMask ( ) & CallAudioState . ROUTE_BLUETOOTH ) ! = 0 ;
}
2017-12-08 18:35:59 +01:00
return isBtHeadsetConnected ;
}
public void onAudioFocusChange ( int focusChange ) {
if ( focusChange = = AudioManager . AUDIOFOCUS_GAIN ) {
2020-12-24 06:36:01 +01:00
hasAudioFocus = true ;
2017-12-08 18:35:59 +01:00
} else {
2020-12-24 06:36:01 +01:00
hasAudioFocus = false ;
2017-12-08 18:35:59 +01:00
}
}
2020-08-14 18:58:22 +02:00
protected void updateBluetoothHeadsetState ( boolean connected ) {
if ( connected = = isBtHeadsetConnected ) {
2017-12-08 18:35:59 +01:00
return ;
2020-08-14 18:58:22 +02:00
}
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " updateBluetoothHeadsetState: " + connected ) ;
}
isBtHeadsetConnected = connected ;
final AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
if ( connected & & ! isRinging ( ) & & currentState ! = 0 ) {
if ( bluetoothScoActive ) {
if ( BuildVars . LOGS_ENABLED ) {
2018-07-30 04:07:02 +02:00
FileLog . d ( " SCO already active, setting audio routing " ) ;
2020-08-14 18:58:22 +02:00
}
2018-07-30 04:07:02 +02:00
am . setSpeakerphoneOn ( false ) ;
am . setBluetoothScoOn ( true ) ;
2020-08-14 18:58:22 +02:00
} else {
if ( BuildVars . LOGS_ENABLED ) {
2018-07-30 04:07:02 +02:00
FileLog . d ( " startBluetoothSco " ) ;
2020-08-14 18:58:22 +02:00
}
needSwitchToBluetoothAfterScoActivates = true ;
2018-07-30 04:07:02 +02:00
// some devices ignore startBluetoothSco when called immediately after the headset is connected, so delay it
2020-08-14 18:58:22 +02:00
AndroidUtilities . runOnUIThread ( ( ) - > {
try {
am . startBluetoothSco ( ) ;
} catch ( Throwable ignore ) {
2018-07-30 04:07:02 +02:00
}
} , 500 ) ;
}
2020-08-14 18:58:22 +02:00
} else {
bluetoothScoActive = false ;
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
for ( StateListener l : stateListeners ) {
2017-12-08 18:35:59 +01:00
l . onAudioSettingsChanged ( ) ;
2020-08-14 18:58:22 +02:00
}
2017-12-08 18:35:59 +01:00
}
2020-03-30 14:00:09 +02:00
public String getLastError ( ) {
2017-12-08 18:35:59 +01:00
return lastError ;
}
2020-08-14 18:58:22 +02:00
public int getCallState ( ) {
2017-12-08 18:35:59 +01:00
return currentState ;
}
2021-03-19 11:25:58 +01:00
public TLRPC . InputPeer getGroupCallPeer ( ) {
return groupCallPeer ;
}
2017-12-08 18:35:59 +01:00
protected void updateNetworkType ( ) {
2020-03-30 14:00:09 +02:00
if ( tgVoip ! = null ) {
2020-12-23 08:48:30 +01:00
if ( tgVoip . isGroup ( ) ) {
} else {
tgVoip . setNetworkType ( getNetworkType ( ) ) ;
}
2020-03-30 14:00:09 +02:00
} else {
lastNetInfo = getActiveNetworkInfo ( ) ;
}
}
protected int getNetworkType ( ) {
final NetworkInfo info = lastNetInfo = getActiveNetworkInfo ( ) ;
2020-08-14 18:58:22 +02:00
int type = Instance . NET_TYPE_UNKNOWN ;
2017-12-08 18:35:59 +01:00
if ( info ! = null ) {
switch ( info . getType ( ) ) {
case ConnectivityManager . TYPE_MOBILE :
switch ( info . getSubtype ( ) ) {
case TelephonyManager . NETWORK_TYPE_GPRS :
2020-08-14 18:58:22 +02:00
type = Instance . NET_TYPE_GPRS ;
2017-12-08 18:35:59 +01:00
break ;
case TelephonyManager . NETWORK_TYPE_EDGE :
case TelephonyManager . NETWORK_TYPE_1xRTT :
2020-08-14 18:58:22 +02:00
type = Instance . NET_TYPE_EDGE ;
2017-12-08 18:35:59 +01:00
break ;
case TelephonyManager . NETWORK_TYPE_UMTS :
case TelephonyManager . NETWORK_TYPE_EVDO_0 :
2020-08-14 18:58:22 +02:00
type = Instance . NET_TYPE_3G ;
2017-12-08 18:35:59 +01:00
break ;
case TelephonyManager . NETWORK_TYPE_HSDPA :
case TelephonyManager . NETWORK_TYPE_HSPA :
case TelephonyManager . NETWORK_TYPE_HSPAP :
case TelephonyManager . NETWORK_TYPE_HSUPA :
case TelephonyManager . NETWORK_TYPE_EVDO_A :
case TelephonyManager . NETWORK_TYPE_EVDO_B :
2020-08-14 18:58:22 +02:00
type = Instance . NET_TYPE_HSPA ;
2017-12-08 18:35:59 +01:00
break ;
case TelephonyManager . NETWORK_TYPE_LTE :
2020-08-14 18:58:22 +02:00
type = Instance . NET_TYPE_LTE ;
2017-12-08 18:35:59 +01:00
break ;
default :
2020-08-14 18:58:22 +02:00
type = Instance . NET_TYPE_OTHER_MOBILE ;
2017-12-08 18:35:59 +01:00
break ;
}
break ;
case ConnectivityManager . TYPE_WIFI :
2020-08-14 18:58:22 +02:00
type = Instance . NET_TYPE_WIFI ;
2017-12-08 18:35:59 +01:00
break ;
case ConnectivityManager . TYPE_ETHERNET :
2020-08-14 18:58:22 +02:00
type = Instance . NET_TYPE_ETHERNET ;
2017-12-08 18:35:59 +01:00
break ;
}
}
2020-03-30 14:00:09 +02:00
return type ;
}
protected NetworkInfo getActiveNetworkInfo ( ) {
return ( ( ConnectivityManager ) getSystemService ( CONNECTIVITY_SERVICE ) ) . getActiveNetworkInfo ( ) ;
2017-12-08 18:35:59 +01:00
}
protected void callFailed ( ) {
2020-08-14 18:58:22 +02:00
callFailed ( tgVoip ! = null ? tgVoip . getLastError ( ) : Instance . ERROR_UNKNOWN ) ;
}
protected Bitmap getRoundAvatarBitmap ( TLObject userOrChat ) {
Bitmap bitmap = null ;
2021-04-14 03:44:46 +02:00
try {
if ( userOrChat instanceof TLRPC . User ) {
TLRPC . User user = ( TLRPC . User ) userOrChat ;
if ( user . photo ! = null & & user . photo . photo_small ! = null ) {
BitmapDrawable img = ImageLoader . getInstance ( ) . getImageFromMemory ( user . photo . photo_small , null , " 50_50 " ) ;
if ( img ! = null ) {
bitmap = img . getBitmap ( ) . copy ( Bitmap . Config . ARGB_8888 , true ) ;
} else {
try {
BitmapFactory . Options opts = new BitmapFactory . Options ( ) ;
opts . inMutable = true ;
bitmap = BitmapFactory . decodeFile ( FileLoader . getPathToAttach ( user . photo . photo_small , true ) . toString ( ) , opts ) ;
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
2018-07-30 04:07:02 +02:00
}
}
2021-04-14 03:44:46 +02:00
} else {
TLRPC . Chat chat = ( TLRPC . Chat ) userOrChat ;
if ( chat . photo ! = null & & chat . photo . photo_small ! = null ) {
BitmapDrawable img = ImageLoader . getInstance ( ) . getImageFromMemory ( chat . photo . photo_small , null , " 50_50 " ) ;
if ( img ! = null ) {
bitmap = img . getBitmap ( ) . copy ( Bitmap . Config . ARGB_8888 , true ) ;
} else {
try {
BitmapFactory . Options opts = new BitmapFactory . Options ( ) ;
opts . inMutable = true ;
bitmap = BitmapFactory . decodeFile ( FileLoader . getPathToAttach ( chat . photo . photo_small , true ) . toString ( ) , opts ) ;
} catch ( Throwable e ) {
FileLog . e ( e ) ;
}
2018-07-30 04:07:02 +02:00
}
}
}
2021-04-14 03:44:46 +02:00
} catch ( Throwable e ) {
FileLog . e ( e ) ;
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
if ( bitmap = = null ) {
2018-07-30 04:07:02 +02:00
Theme . createDialogsResources ( this ) ;
AvatarDrawable placeholder ;
2020-08-14 18:58:22 +02:00
if ( userOrChat instanceof TLRPC . User ) {
placeholder = new AvatarDrawable ( ( TLRPC . User ) userOrChat ) ;
} else {
placeholder = new AvatarDrawable ( ( TLRPC . Chat ) userOrChat ) ;
}
bitmap = Bitmap . createBitmap ( AndroidUtilities . dp ( 42 ) , AndroidUtilities . dp ( 42 ) , Bitmap . Config . ARGB_8888 ) ;
2018-07-30 04:07:02 +02:00
placeholder . setBounds ( 0 , 0 , bitmap . getWidth ( ) , bitmap . getHeight ( ) ) ;
placeholder . draw ( new Canvas ( bitmap ) ) ;
}
2020-08-14 18:58:22 +02:00
Canvas canvas = new Canvas ( bitmap ) ;
Path circlePath = new Path ( ) ;
circlePath . addCircle ( bitmap . getWidth ( ) / 2 , bitmap . getHeight ( ) / 2 , bitmap . getWidth ( ) / 2 , Path . Direction . CW ) ;
2018-07-30 04:07:02 +02:00
circlePath . toggleInverseFillType ( ) ;
2020-08-14 18:58:22 +02:00
Paint paint = new Paint ( Paint . ANTI_ALIAS_FLAG ) ;
2018-07-30 04:07:02 +02:00
paint . setXfermode ( new PorterDuffXfermode ( PorterDuff . Mode . CLEAR ) ) ;
canvas . drawPath ( circlePath , paint ) ;
return bitmap ;
}
2020-08-14 18:58:22 +02:00
protected void showIncomingNotification ( String name , CharSequence subText , TLObject userOrChat , boolean video , int additionalMemberCount ) {
Intent intent = new Intent ( this , LaunchActivity . class ) ;
intent . setAction ( " voip " ) ;
2018-07-30 04:07:02 +02:00
Notification . Builder builder = new Notification . Builder ( this )
2020-08-14 18:58:22 +02:00
. setContentTitle ( video ? LocaleController . getString ( " VoipInVideoCallBranding " , R . string . VoipInVideoCallBranding ) : LocaleController . getString ( " VoipInCallBranding " , R . string . VoipInCallBranding ) )
2018-07-30 04:07:02 +02:00
. setContentText ( name )
. setSmallIcon ( R . drawable . notification )
. setSubText ( subText )
. setContentIntent ( PendingIntent . getActivity ( this , 0 , intent , 0 ) ) ;
2020-08-14 18:58:22 +02:00
Uri soundProviderUri = Uri . parse ( " content:// " + BuildConfig . APPLICATION_ID + " .call_sound_provider/start_ringing " ) ;
2018-07-30 04:07:02 +02:00
if ( Build . VERSION . SDK_INT > = 26 ) {
2020-08-14 18:58:22 +02:00
SharedPreferences nprefs = MessagesController . getGlobalNotificationsSettings ( ) ;
int chanIndex = nprefs . getInt ( " calls_notification_channel " , 0 ) ;
NotificationManager nm = ( NotificationManager ) getSystemService ( NOTIFICATION_SERVICE ) ;
2021-03-19 11:25:58 +01:00
NotificationChannel oldChannel = nm . getNotificationChannel ( " incoming_calls2 " + chanIndex ) ;
2020-08-14 18:58:22 +02:00
if ( oldChannel ! = null ) {
2019-03-03 21:40:48 +01:00
nm . deleteNotificationChannel ( oldChannel . getId ( ) ) ;
2020-08-14 18:58:22 +02:00
}
2021-03-19 11:25:58 +01:00
NotificationChannel existingChannel = nm . getNotificationChannel ( " incoming_calls3 " + chanIndex ) ;
2020-08-14 18:58:22 +02:00
boolean needCreate = true ;
if ( existingChannel ! = null ) {
if ( existingChannel . getImportance ( ) < NotificationManager . IMPORTANCE_HIGH | | ! soundProviderUri . equals ( existingChannel . getSound ( ) ) | | existingChannel . getVibrationPattern ( ) ! = null | | existingChannel . shouldVibrate ( ) ) {
if ( BuildVars . LOGS_ENABLED ) {
2019-03-03 21:40:48 +01:00
FileLog . d ( " User messed up the notification channel; deleting it and creating a proper one " ) ;
2020-08-14 18:58:22 +02:00
}
2021-03-19 11:25:58 +01:00
nm . deleteNotificationChannel ( " incoming_calls3 " + chanIndex ) ;
2018-07-30 04:07:02 +02:00
chanIndex + + ;
nprefs . edit ( ) . putInt ( " calls_notification_channel " , chanIndex ) . commit ( ) ;
2020-08-14 18:58:22 +02:00
} else {
needCreate = false ;
2018-07-30 04:07:02 +02:00
}
}
2020-08-14 18:58:22 +02:00
if ( needCreate ) {
AudioAttributes attrs = new AudioAttributes . Builder ( )
2021-03-19 11:25:58 +01:00
. setContentType ( AudioAttributes . CONTENT_TYPE_SONIFICATION )
. setLegacyStreamType ( AudioManager . STREAM_RING )
. setUsage ( AudioAttributes . USAGE_VOICE_COMMUNICATION )
2019-03-03 21:40:48 +01:00
. build ( ) ;
2021-03-19 11:25:58 +01:00
NotificationChannel chan = new NotificationChannel ( " incoming_calls3 " + chanIndex , LocaleController . getString ( " IncomingCalls " , R . string . IncomingCalls ) , NotificationManager . IMPORTANCE_HIGH ) ;
2019-03-03 21:40:48 +01:00
chan . setSound ( soundProviderUri , attrs ) ;
2018-07-30 04:07:02 +02:00
chan . enableVibration ( false ) ;
chan . enableLights ( false ) ;
2020-12-23 08:48:30 +01:00
chan . setBypassDnd ( true ) ;
2020-08-14 18:58:22 +02:00
try {
nm . createNotificationChannel ( chan ) ;
} catch ( Exception e ) {
FileLog . e ( e ) ;
this . stopSelf ( ) ;
return ;
}
2018-07-30 04:07:02 +02:00
}
2021-03-19 11:25:58 +01:00
builder . setChannelId ( " incoming_calls3 " + chanIndex ) ;
2020-08-14 18:58:22 +02:00
} else if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . LOLLIPOP ) {
2019-03-03 21:40:48 +01:00
builder . setSound ( soundProviderUri , AudioManager . STREAM_RING ) ;
2018-07-30 04:07:02 +02:00
}
Intent endIntent = new Intent ( this , VoIPActionsReceiver . class ) ;
endIntent . setAction ( getPackageName ( ) + " .DECLINE_CALL " ) ;
endIntent . putExtra ( " call_id " , getCallID ( ) ) ;
2020-08-14 18:58:22 +02:00
CharSequence endTitle = LocaleController . getString ( " VoipDeclineCall " , R . string . VoipDeclineCall ) ;
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . N ) {
endTitle = new SpannableString ( endTitle ) ;
( ( SpannableString ) endTitle ) . setSpan ( new ForegroundColorSpan ( 0xFFF44336 ) , 0 , endTitle . length ( ) , 0 ) ;
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
PendingIntent endPendingIntent = PendingIntent . getBroadcast ( this , 0 , endIntent , PendingIntent . FLAG_CANCEL_CURRENT ) ;
2018-07-30 04:07:02 +02:00
builder . addAction ( R . drawable . ic_call_end_white_24dp , endTitle , endPendingIntent ) ;
Intent answerIntent = new Intent ( this , VoIPActionsReceiver . class ) ;
answerIntent . setAction ( getPackageName ( ) + " .ANSWER_CALL " ) ;
answerIntent . putExtra ( " call_id " , getCallID ( ) ) ;
2020-08-14 18:58:22 +02:00
CharSequence answerTitle = LocaleController . getString ( " VoipAnswerCall " , R . string . VoipAnswerCall ) ;
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . N ) {
answerTitle = new SpannableString ( answerTitle ) ;
( ( SpannableString ) answerTitle ) . setSpan ( new ForegroundColorSpan ( 0xFF00AA00 ) , 0 , answerTitle . length ( ) , 0 ) ;
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
PendingIntent answerPendingIntent = PendingIntent . getBroadcast ( this , 0 , answerIntent , PendingIntent . FLAG_CANCEL_CURRENT ) ;
2019-05-14 14:08:05 +02:00
builder . addAction ( R . drawable . ic_call , answerTitle , answerPendingIntent ) ;
2018-07-30 04:07:02 +02:00
builder . setPriority ( Notification . PRIORITY_MAX ) ;
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN_MR1 ) {
builder . setShowWhen ( false ) ;
}
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . LOLLIPOP ) {
builder . setColor ( 0xff2ca5e0 ) ;
builder . setVibrate ( new long [ 0 ] ) ;
builder . setCategory ( Notification . CATEGORY_CALL ) ;
builder . setFullScreenIntent ( PendingIntent . getActivity ( this , 0 , intent , 0 ) , true ) ;
2020-08-14 18:58:22 +02:00
if ( userOrChat instanceof TLRPC . User ) {
TLRPC . User user = ( TLRPC . User ) userOrChat ;
if ( ! TextUtils . isEmpty ( user . phone ) ) {
builder . addPerson ( " tel: " + user . phone ) ;
2019-03-03 21:40:48 +01:00
}
}
2018-07-30 04:07:02 +02:00
}
Notification incomingNotification = builder . getNotification ( ) ;
2020-08-14 18:58:22 +02:00
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . LOLLIPOP ) {
RemoteViews customView = new RemoteViews ( getPackageName ( ) , LocaleController . isRTL ? R . layout . call_notification_rtl : R . layout . call_notification ) ;
2018-07-30 04:07:02 +02:00
customView . setTextViewText ( R . id . name , name ) ;
2020-08-14 18:58:22 +02:00
boolean subtitleVisible = true ;
if ( TextUtils . isEmpty ( subText ) ) {
2018-07-30 04:07:02 +02:00
customView . setViewVisibility ( R . id . subtitle , View . GONE ) ;
2020-08-14 18:58:22 +02:00
if ( UserConfig . getActivatedAccountsCount ( ) > 1 ) {
TLRPC . User self = UserConfig . getInstance ( currentAccount ) . getCurrentUser ( ) ;
customView . setTextViewText ( R . id . title , video ? LocaleController . formatString ( " VoipInVideoCallBrandingWithName " , R . string . VoipInVideoCallBrandingWithName , ContactsController . formatName ( self . first_name , self . last_name ) ) : LocaleController . formatString ( " VoipInCallBrandingWithName " , R . string . VoipInCallBrandingWithName , ContactsController . formatName ( self . first_name , self . last_name ) ) ) ;
} else {
customView . setTextViewText ( R . id . title , video ? LocaleController . getString ( " VoipInVideoCallBranding " , R . string . VoipInVideoCallBranding ) : LocaleController . getString ( " VoipInCallBranding " , R . string . VoipInCallBranding ) ) ;
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
} else {
if ( UserConfig . getActivatedAccountsCount ( ) > 1 ) {
TLRPC . User self = UserConfig . getInstance ( currentAccount ) . getCurrentUser ( ) ;
2018-07-30 04:07:02 +02:00
customView . setTextViewText ( R . id . subtitle , LocaleController . formatString ( " VoipAnsweringAsAccount " , R . string . VoipAnsweringAsAccount , ContactsController . formatName ( self . first_name , self . last_name ) ) ) ;
2020-08-14 18:58:22 +02:00
} else {
2018-07-30 04:07:02 +02:00
customView . setViewVisibility ( R . id . subtitle , View . GONE ) ;
}
customView . setTextViewText ( R . id . title , subText ) ;
}
2020-08-14 18:58:22 +02:00
Bitmap avatar = getRoundAvatarBitmap ( userOrChat ) ;
2018-07-30 04:07:02 +02:00
customView . setTextViewText ( R . id . answer_text , LocaleController . getString ( " VoipAnswerCall " , R . string . VoipAnswerCall ) ) ;
customView . setTextViewText ( R . id . decline_text , LocaleController . getString ( " VoipDeclineCall " , R . string . VoipDeclineCall ) ) ;
2019-03-03 21:40:48 +01:00
customView . setImageViewBitmap ( R . id . photo , avatar ) ;
2018-07-30 04:07:02 +02:00
customView . setOnClickPendingIntent ( R . id . answer_btn , answerPendingIntent ) ;
customView . setOnClickPendingIntent ( R . id . decline_btn , endPendingIntent ) ;
2019-03-03 21:40:48 +01:00
builder . setLargeIcon ( avatar ) ;
2018-07-30 04:07:02 +02:00
2020-08-14 18:58:22 +02:00
incomingNotification . headsUpContentView = incomingNotification . bigContentView = customView ;
2018-07-30 04:07:02 +02:00
}
startForeground ( ID_INCOMING_CALL_NOTIFICATION , incomingNotification ) ;
2021-03-19 11:25:58 +01:00
startRingtoneAndVibration ( ) ;
2018-07-30 04:07:02 +02:00
}
2020-03-30 14:00:09 +02:00
protected void callFailed ( String error ) {
try {
throw new Exception ( " Call " + getCallID ( ) + " failed with error: " + error ) ;
} catch ( Exception x ) {
2017-12-08 18:35:59 +01:00
FileLog . e ( x ) ;
}
2020-03-30 14:00:09 +02:00
lastError = error ;
2020-12-23 08:48:30 +01:00
AndroidUtilities . runOnUIThread ( ( ) - > dispatchStateChanged ( STATE_FAILED ) ) ;
2020-08-14 18:58:22 +02:00
if ( TextUtils . equals ( error , Instance . ERROR_LOCALIZED ) & & soundPool ! = null ) {
2020-03-30 14:00:09 +02:00
playingSound = true ;
2020-12-23 08:48:30 +01:00
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spFailedID , 1 , 1 , 0 , 0 , 1 ) ) ;
2017-12-08 18:35:59 +01:00
AndroidUtilities . runOnUIThread ( afterSoundRunnable , 1000 ) ;
}
2020-03-30 14:00:09 +02:00
if ( USE_CONNECTION_SERVICE & & systemCallConnection ! = null ) {
2018-07-30 04:07:02 +02:00
systemCallConnection . setDisconnected ( new DisconnectCause ( DisconnectCause . ERROR ) ) ;
systemCallConnection . destroy ( ) ;
2020-03-30 14:00:09 +02:00
systemCallConnection = null ;
2018-07-30 04:07:02 +02:00
}
2017-12-08 18:35:59 +01:00
stopSelf ( ) ;
}
2020-08-14 18:58:22 +02:00
void callFailedFromConnectionService ( ) {
if ( isOutgoing ) {
callFailed ( Instance . ERROR_CONNECTION_SERVICE ) ;
} else {
2018-07-30 04:07:02 +02:00
hangUp ( ) ;
2020-08-14 18:58:22 +02:00
}
2018-07-30 04:07:02 +02:00
}
2017-12-08 18:35:59 +01:00
@Override
2021-03-19 11:25:58 +01:00
public void onConnectionStateChanged ( int newState , boolean inTransition ) {
2017-12-08 18:35:59 +01:00
if ( newState = = STATE_FAILED ) {
callFailed ( ) ;
return ;
}
if ( newState = = STATE_ESTABLISHED ) {
2020-08-14 18:58:22 +02:00
if ( connectingSoundRunnable ! = null ) {
2019-01-23 18:03:33 +01:00
AndroidUtilities . cancelRunOnUIThread ( connectingSoundRunnable ) ;
2020-08-14 18:58:22 +02:00
connectingSoundRunnable = null ;
2019-01-23 18:03:33 +01:00
}
2020-12-23 08:48:30 +01:00
Utilities . globalQueue . postRunnable ( ( ) - > {
2021-03-19 11:25:58 +01:00
if ( spPlayId ! = 0 ) {
soundPool . stop ( spPlayId ) ;
spPlayId = 0 ;
2020-12-23 08:48:30 +01:00
}
} ) ;
if ( groupCall = = null & & ! wasEstablished ) {
2020-08-14 18:58:22 +02:00
wasEstablished = true ;
2020-12-23 08:48:30 +01:00
if ( ! isProximityNear & & ! privateCall . video ) {
2020-08-14 18:58:22 +02:00
Vibrator vibrator = ( Vibrator ) getSystemService ( VIBRATOR_SERVICE ) ;
if ( vibrator . hasVibrator ( ) ) {
2017-12-08 18:35:59 +01:00
vibrator . vibrate ( 100 ) ;
2020-08-14 18:58:22 +02:00
}
2017-12-08 18:35:59 +01:00
}
2020-08-14 18:58:22 +02:00
AndroidUtilities . runOnUIThread ( new Runnable ( ) {
2017-12-08 18:35:59 +01:00
@Override
2020-08-14 18:58:22 +02:00
public void run ( ) {
2020-03-30 14:00:09 +02:00
if ( tgVoip ! = null ) {
StatsController . getInstance ( currentAccount ) . incrementTotalCallsTime ( getStatsNetworkType ( ) , 5 ) ;
AndroidUtilities . runOnUIThread ( this , 5000 ) ;
}
2017-12-08 18:35:59 +01:00
}
} , 5000 ) ;
2020-08-14 18:58:22 +02:00
if ( isOutgoing ) {
2018-07-30 04:07:02 +02:00
StatsController . getInstance ( currentAccount ) . incrementSentItemsCount ( getStatsNetworkType ( ) , StatsController . TYPE_CALLS , 1 ) ;
2020-08-14 18:58:22 +02:00
} else {
2018-07-30 04:07:02 +02:00
StatsController . getInstance ( currentAccount ) . incrementReceivedItemsCount ( getStatsNetworkType ( ) , StatsController . TYPE_CALLS , 1 ) ;
2020-08-14 18:58:22 +02:00
}
2017-12-08 18:35:59 +01:00
}
}
2020-08-14 18:58:22 +02:00
if ( newState = = STATE_RECONNECTING ) {
2020-12-23 08:48:30 +01:00
Utilities . globalQueue . postRunnable ( ( ) - > {
2021-03-19 11:25:58 +01:00
if ( spPlayId ! = 0 ) {
soundPool . stop ( spPlayId ) ;
2020-12-23 08:48:30 +01:00
}
2021-03-19 11:25:58 +01:00
spPlayId = soundPool . play ( groupCall ! = null ? spVoiceChatConnecting : spConnectingId , 1 , 1 , 0 , - 1 , 1 ) ;
2020-12-23 08:48:30 +01:00
} ) ;
2017-12-08 18:35:59 +01:00
}
dispatchStateChanged ( newState ) ;
}
2021-03-19 11:25:58 +01:00
public void playStartRecordSound ( ) {
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spStartRecordId , 0 . 5f , 0 . 5f , 0 , 0 , 1 ) ) ;
}
public void playAllowTalkSound ( ) {
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spAllowTalkId , 0 . 5f , 0 . 5f , 0 , 0 , 1 ) ) ;
}
2017-12-08 18:35:59 +01:00
@Override
2020-08-14 18:58:22 +02:00
public void onSignalBarCountChanged ( int newCount ) {
2020-12-23 08:48:30 +01:00
AndroidUtilities . runOnUIThread ( ( ) - > {
signalBarCount = newCount ;
for ( int a = 0 ; a < stateListeners . size ( ) ; a + + ) {
StateListener l = stateListeners . get ( a ) ;
l . onSignalBarsCountChanged ( newCount ) ;
}
} ) ;
2017-12-08 18:35:59 +01:00
}
2020-08-14 18:58:22 +02:00
public boolean isBluetoothOn ( ) {
final AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
return am . isBluetoothScoOn ( ) ;
}
2020-12-23 08:48:30 +01:00
public boolean isBluetoothWillOn ( ) {
return needSwitchToBluetoothAfterScoActivates ;
}
2020-08-15 02:01:55 +02:00
public boolean isHeadsetPlugged ( ) {
return isHeadsetPlugged ;
}
2020-08-14 18:58:22 +02:00
public void onMediaStateUpdated ( int audioState , int videoState ) {
AndroidUtilities . runOnUIThread ( ( ) - > {
currentAudioState = audioState ;
currentVideoState = videoState ;
checkIsNear ( ) ;
for ( int a = 0 ; a < stateListeners . size ( ) ; a + + ) {
StateListener l = stateListeners . get ( a ) ;
l . onMediaStateUpdated ( audioState , videoState ) ;
}
} ) ;
}
2017-12-08 18:35:59 +01:00
protected void callEnded ( ) {
2018-07-30 04:07:02 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " Call " + getCallID ( ) + " ended " ) ;
}
2020-12-23 08:48:30 +01:00
if ( groupCall ! = null & & ( ! playedConnectedSound | | onDestroyRunnable ! = null ) ) {
needPlayEndSound = false ;
}
AndroidUtilities . runOnUIThread ( ( ) - > dispatchStateChanged ( STATE_ENDED ) ) ;
int delay = 700 ;
Utilities . globalQueue . postRunnable ( ( ) - > {
2021-03-19 11:25:58 +01:00
if ( spPlayId ! = 0 ) {
soundPool . stop ( spPlayId ) ;
spPlayId = 0 ;
2020-12-23 08:48:30 +01:00
}
} ) ;
if ( connectingSoundRunnable ! = null ) {
AndroidUtilities . cancelRunOnUIThread ( connectingSoundRunnable ) ;
connectingSoundRunnable = null ;
}
2017-12-08 18:35:59 +01:00
if ( needPlayEndSound ) {
playingSound = true ;
2020-12-23 08:48:30 +01:00
if ( groupCall = = null ) {
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spEndId , 1 , 1 , 0 , 0 , 1 ) ) ;
} else {
2021-01-28 15:15:51 +01:00
Utilities . globalQueue . postRunnable ( ( ) - > soundPool . play ( spVoiceChatEndId , 1 . 0f , 1 . 0f , 0 , 0 , 1 ) , 100 ) ;
delay = 500 ;
2020-12-23 08:48:30 +01:00
}
AndroidUtilities . runOnUIThread ( afterSoundRunnable , delay ) ;
2017-12-08 18:35:59 +01:00
}
2020-08-14 18:58:22 +02:00
if ( timeoutRunnable ! = null ) {
2017-12-08 18:35:59 +01:00
AndroidUtilities . cancelRunOnUIThread ( timeoutRunnable ) ;
2020-08-14 18:58:22 +02:00
timeoutRunnable = null ;
2017-12-08 18:35:59 +01:00
}
2020-12-23 08:48:30 +01:00
endConnectionServiceCall ( needPlayEndSound ? delay : 0 ) ;
2019-01-23 18:03:33 +01:00
stopSelf ( ) ;
}
2020-08-14 18:58:22 +02:00
protected void endConnectionServiceCall ( long delay ) {
if ( USE_CONNECTION_SERVICE ) {
Runnable r = ( ) - > {
if ( systemCallConnection ! = null ) {
switch ( callDiscardReason ) {
case DISCARD_REASON_HANGUP :
systemCallConnection . setDisconnected ( new DisconnectCause ( isOutgoing ? DisconnectCause . LOCAL : DisconnectCause . REJECTED ) ) ;
break ;
case DISCARD_REASON_DISCONNECT :
systemCallConnection . setDisconnected ( new DisconnectCause ( DisconnectCause . ERROR ) ) ;
break ;
case DISCARD_REASON_LINE_BUSY :
systemCallConnection . setDisconnected ( new DisconnectCause ( DisconnectCause . BUSY ) ) ;
break ;
case DISCARD_REASON_MISSED :
systemCallConnection . setDisconnected ( new DisconnectCause ( isOutgoing ? DisconnectCause . CANCELED : DisconnectCause . MISSED ) ) ;
break ;
default :
systemCallConnection . setDisconnected ( new DisconnectCause ( DisconnectCause . REMOTE ) ) ;
break ;
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
systemCallConnection . destroy ( ) ;
systemCallConnection = null ;
2018-07-30 04:07:02 +02:00
}
} ;
2020-08-14 18:58:22 +02:00
if ( delay > 0 ) {
2019-01-23 18:03:33 +01:00
AndroidUtilities . runOnUIThread ( r , delay ) ;
2020-08-14 18:58:22 +02:00
} else {
2018-07-30 04:07:02 +02:00
r . run ( ) ;
2020-08-14 18:58:22 +02:00
}
2018-07-30 04:07:02 +02:00
}
2017-12-08 18:35:59 +01:00
}
2020-08-14 18:58:22 +02:00
public boolean isOutgoing ( ) {
2017-12-08 18:35:59 +01:00
return isOutgoing ;
}
2020-08-14 18:58:22 +02:00
public void handleNotificationAction ( Intent intent ) {
2017-12-08 18:35:59 +01:00
if ( ( getPackageName ( ) + " .END_CALL " ) . equals ( intent . getAction ( ) ) ) {
stopForeground ( true ) ;
hangUp ( ) ;
} else if ( ( getPackageName ( ) + " .DECLINE_CALL " ) . equals ( intent . getAction ( ) ) ) {
stopForeground ( true ) ;
declineIncomingCall ( DISCARD_REASON_LINE_BUSY , null ) ;
} else if ( ( getPackageName ( ) + " .ANSWER_CALL " ) . equals ( intent . getAction ( ) ) ) {
2018-07-30 04:07:02 +02:00
acceptIncomingCallFromNotification ( ) ;
}
}
2020-08-14 18:58:22 +02:00
private void acceptIncomingCallFromNotification ( ) {
2018-07-30 04:07:02 +02:00
showNotification ( ) ;
2020-12-23 08:48:30 +01:00
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . M & & ( checkSelfPermission ( Manifest . permission . RECORD_AUDIO ) ! = PackageManager . PERMISSION_GRANTED | | privateCall . video & & checkSelfPermission ( Manifest . permission . CAMERA ) ! = PackageManager . PERMISSION_GRANTED ) ) {
2020-08-14 18:58:22 +02:00
try {
2018-07-30 04:07:02 +02:00
PendingIntent . getActivity ( VoIPBaseService . this , 0 , new Intent ( VoIPBaseService . this , VoIPPermissionActivity . class ) . addFlags ( Intent . FLAG_ACTIVITY_NEW_TASK ) , 0 ) . send ( ) ;
2020-08-14 18:58:22 +02:00
} catch ( Exception x ) {
2018-07-30 04:07:02 +02:00
if ( BuildVars . LOGS_ENABLED ) {
2017-12-08 18:35:59 +01:00
FileLog . e ( " Error starting permission activity " , x ) ;
}
}
2018-07-30 04:07:02 +02:00
return ;
}
acceptIncomingCall ( ) ;
2020-08-14 18:58:22 +02:00
try {
PendingIntent . getActivity ( VoIPBaseService . this , 0 , new Intent ( VoIPBaseService . this , getUIActivityClass ( ) ) . setAction ( " voip " ) , 0 ) . send ( ) ;
} catch ( Exception x ) {
2018-07-30 04:07:02 +02:00
if ( BuildVars . LOGS_ENABLED ) {
2017-12-08 18:35:59 +01:00
FileLog . e ( " Error starting incall activity " , x ) ;
}
}
}
2020-08-14 18:58:22 +02:00
public void updateOutputGainControlState ( ) {
2020-03-30 14:00:09 +02:00
if ( tgVoip ! = null ) {
if ( ! USE_CONNECTION_SERVICE ) {
final AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE ) ;
tgVoip . setAudioOutputGainControlEnabled ( hasEarpiece ( ) & & ! am . isSpeakerphoneOn ( ) & & ! am . isBluetoothScoOn ( ) & & ! isHeadsetPlugged ) ;
tgVoip . setEchoCancellationStrength ( isHeadsetPlugged | | ( hasEarpiece ( ) & & ! am . isSpeakerphoneOn ( ) & & ! am . isBluetoothScoOn ( ) & & ! isHeadsetPlugged ) ? 0 : 1 ) ;
} else {
final boolean isEarpiece = systemCallConnection . getCallAudioState ( ) . getRoute ( ) = = CallAudioState . ROUTE_EARPIECE ;
tgVoip . setAudioOutputGainControlEnabled ( isEarpiece ) ;
tgVoip . setEchoCancellationStrength ( isEarpiece ? 0 : 1 ) ;
}
2018-07-30 04:07:02 +02:00
}
}
2020-08-14 18:58:22 +02:00
public int getAccount ( ) {
2018-07-30 04:07:02 +02:00
return currentAccount ;
}
@Override
2020-08-14 18:58:22 +02:00
public void didReceivedNotification ( int id , int account , Object . . . args ) {
if ( id = = NotificationCenter . appDidLogout ) {
2018-07-30 04:07:02 +02:00
callEnded ( ) ;
}
}
2020-08-14 18:58:22 +02:00
public static boolean isAnyKindOfCallActive ( ) {
if ( VoIPService . getSharedInstance ( ) ! = null ) {
return VoIPService . getSharedInstance ( ) . getCallState ( ) ! = VoIPService . STATE_WAITING_INCOMING ;
2018-07-30 04:07:02 +02:00
}
return false ;
}
2020-08-14 18:58:22 +02:00
protected boolean isFinished ( ) {
return currentState = = STATE_ENDED | | currentState = = STATE_FAILED ;
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
protected boolean isRinging ( ) {
2018-08-27 10:33:11 +02:00
return false ;
}
2020-08-14 18:58:22 +02:00
public int getCurrentAudioState ( ) {
return currentAudioState ;
}
public int getCurrentVideoState ( ) {
return currentVideoState ;
}
2018-07-30 04:07:02 +02:00
@TargetApi ( Build . VERSION_CODES . O )
2020-08-14 18:58:22 +02:00
protected PhoneAccountHandle addAccountToTelecomManager ( ) {
TelecomManager tm = ( TelecomManager ) getSystemService ( TELECOM_SERVICE ) ;
TLRPC . User self = UserConfig . getInstance ( currentAccount ) . getCurrentUser ( ) ;
PhoneAccountHandle handle = new PhoneAccountHandle ( new ComponentName ( this , TelegramConnectionService . class ) , " " + self . id ) ;
PhoneAccount account = new PhoneAccount . Builder ( handle , ContactsController . formatName ( self . first_name , self . last_name ) )
2018-07-30 04:07:02 +02:00
. setCapabilities ( PhoneAccount . CAPABILITY_SELF_MANAGED )
2019-05-14 14:08:05 +02:00
. setIcon ( Icon . createWithResource ( this , R . drawable . ic_launcher_dr ) )
2018-07-30 04:07:02 +02:00
. setHighlightColor ( 0xff2ca5e0 )
. addSupportedUriScheme ( " sip " )
. build ( ) ;
tm . registerPhoneAccount ( account ) ;
return handle ;
}
2020-08-14 18:58:22 +02:00
private static boolean isDeviceCompatibleWithConnectionServiceAPI ( ) {
if ( Build . VERSION . SDK_INT < Build . VERSION_CODES . O ) {
2018-07-30 04:07:02 +02:00
return false ;
2020-08-14 18:58:22 +02:00
}
2018-07-30 04:07:02 +02:00
// some non-Google devices don't implement the ConnectionService API correctly so, sadly,
// we'll have to whitelist only a handful of known-compatible devices for now
2020-08-22 01:59:49 +02:00
return false ; / * " angler " . equals ( Build . PRODUCT ) // Nexus 6P
2020-08-14 18:58:22 +02:00
| | " bullhead " . equals ( Build . PRODUCT ) // Nexus 5X
| | " sailfish " . equals ( Build . PRODUCT ) // Pixel
| | " marlin " . equals ( Build . PRODUCT ) // Pixel XL
| | " walleye " . equals ( Build . PRODUCT ) // Pixel 2
| | " taimen " . equals ( Build . PRODUCT ) // Pixel 2 XL
| | " blueline " . equals ( Build . PRODUCT ) // Pixel 3
| | " crosshatch " . equals ( Build . PRODUCT ) // Pixel 3 XL
2020-08-22 01:59:49 +02:00
| | MessagesController . getGlobalMainSettings ( ) . getBoolean ( " dbg_force_connection_service " , false ) ; * /
2017-12-08 18:35:59 +01:00
}
public interface StateListener {
2020-12-23 08:48:30 +01:00
default void onStateChanged ( int state ) {
}
default void onSignalBarsCountChanged ( int count ) {
}
default void onAudioSettingsChanged ( ) {
}
default void onMediaStateUpdated ( int audioState , int videoState ) {
}
default void onCameraSwitch ( boolean isFrontFace ) {
}
default void onVideoAvailableChange ( boolean isAvailable ) {
}
default void onScreenOnChange ( boolean screenOn ) {
}
2017-12-08 18:35:59 +01:00
}
2018-07-30 04:07:02 +02:00
2020-08-14 18:58:22 +02:00
public class CallConnection extends Connection {
public CallConnection ( ) {
2018-07-30 04:07:02 +02:00
setConnectionProperties ( PROPERTY_SELF_MANAGED ) ;
setAudioModeIsVoip ( true ) ;
}
@Override
2020-08-14 18:58:22 +02:00
public void onCallAudioStateChanged ( CallAudioState state ) {
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " ConnectionService call audio state changed: " + state ) ;
}
for ( StateListener l : stateListeners ) {
2018-07-30 04:07:02 +02:00
l . onAudioSettingsChanged ( ) ;
2020-08-14 18:58:22 +02:00
}
2018-07-30 04:07:02 +02:00
}
@Override
2020-08-14 18:58:22 +02:00
public void onDisconnect ( ) {
if ( BuildVars . LOGS_ENABLED ) {
2018-07-30 04:07:02 +02:00
FileLog . d ( " ConnectionService onDisconnect " ) ;
2020-08-14 18:58:22 +02:00
}
2018-07-30 04:07:02 +02:00
setDisconnected ( new DisconnectCause ( DisconnectCause . LOCAL ) ) ;
destroy ( ) ;
2020-08-14 18:58:22 +02:00
systemCallConnection = null ;
2018-07-30 04:07:02 +02:00
hangUp ( ) ;
}
@Override
2020-08-14 18:58:22 +02:00
public void onAnswer ( ) {
2018-07-30 04:07:02 +02:00
acceptIncomingCallFromNotification ( ) ;
}
@Override
2020-08-14 18:58:22 +02:00
public void onReject ( ) {
needPlayEndSound = false ;
2018-07-30 04:07:02 +02:00
declineIncomingCall ( DISCARD_REASON_HANGUP , null ) ;
}
@Override
2020-08-14 18:58:22 +02:00
public void onShowIncomingCallUi ( ) {
2018-07-30 04:07:02 +02:00
startRinging ( ) ;
}
@Override
2020-08-14 18:58:22 +02:00
public void onStateChanged ( int state ) {
2018-07-30 04:07:02 +02:00
super . onStateChanged ( state ) ;
2020-08-14 18:58:22 +02:00
if ( BuildVars . LOGS_ENABLED ) {
FileLog . d ( " ConnectionService onStateChanged " + stateToString ( state ) ) ;
}
if ( state = = Connection . STATE_ACTIVE ) {
2019-01-23 18:03:33 +01:00
ContactsController . getInstance ( currentAccount ) . deleteConnectionServiceContact ( ) ;
2020-08-14 18:58:22 +02:00
didDeleteConnectionServiceContact = true ;
2019-01-23 18:03:33 +01:00
}
2018-07-30 04:07:02 +02:00
}
@Override
2020-08-14 18:58:22 +02:00
public void onCallEvent ( String event , Bundle extras ) {
2018-07-30 04:07:02 +02:00
super . onCallEvent ( event , extras ) ;
2020-08-14 18:58:22 +02:00
if ( BuildVars . LOGS_ENABLED )
FileLog . d ( " ConnectionService onCallEvent " + event ) ;
2018-07-30 04:07:02 +02:00
}
2020-08-14 18:58:22 +02:00
//undocumented API
public void onSilence ( ) {
if ( BuildVars . LOGS_ENABLED ) {
2018-07-30 04:07:02 +02:00
FileLog . d ( " onSlience " ) ;
2020-08-14 18:58:22 +02:00
}
2018-07-30 04:07:02 +02:00
stopRinging ( ) ;
}
}
2020-08-14 18:58:22 +02:00
2020-08-15 23:06:36 +02:00
public static class SharedUIParams {
2020-08-14 18:58:22 +02:00
public boolean tapToVideoTooltipWasShowed ;
public boolean cameraAlertWasShowed ;
public boolean wasVideoCall ;
}
2017-12-08 18:35:59 +01:00
}