Sinch 视频通话无法在后台运行
Sinch video call not working in background
我在当前的应用程序中实现了 Sinch 视频通话。当应用程序在前台时,来电一切正常。但是当我关闭应用程序并尝试接听来电时,它没有显示任何来电。当应用程序关闭来电时如何进行视频 Sinch 通话?以下是我的 FCMMessageReceiverService class.
public class FCMMessageReceiverService extends FirebaseMessagingService implements ServiceConnection {
Context context;
private SinchService.SinchServiceInterface mSinchServiceInterface;
HashMap dataHashMap;
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
context = this;
if (SinchHelpers.isSinchPushPayload(remoteMessage.getData())) {
Map data = remoteMessage.getData();
dataHashMap = (data instanceof HashMap) ? (HashMap) data : new HashMap<>(data);
if (SinchHelpers.isSinchPushPayload(dataHashMap)) {
getApplicationContext().bindService(new Intent(getApplicationContext(), SinchService.class), this, Context.BIND_AUTO_CREATE);
}
} else {
Intent intent;
PendingIntent pendingIntent = null;
if (remoteMessage.getData().size() > 0) {
String identifier = remoteMessage.getData().get("identifier");
if (identifier.equals("0")) {
intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder.setWhen(System.currentTimeMillis());
notificationBuilder.setContentTitle(remoteMessage.getNotification().getTitle());
notificationBuilder.setContentText(remoteMessage.getNotification().getBody());
notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
notificationBuilder.setDefaults(Notification.DEFAULT_ALL | Notification.DEFAULT_LIGHTS | Notification.FLAG_SHOW_LIGHTS | Notification.DEFAULT_SOUND);
notificationBuilder.setAutoCancel(true);
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher);
notificationBuilder.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}
}
public static boolean foregrounded() {
ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
ActivityManager.getMyMemoryState(appProcessInfo);
return (appProcessInfo.importance == appProcessInfo.IMPORTANCE_FOREGROUND || appProcessInfo.importance == appProcessInfo.IMPORTANCE_VISIBLE);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if (SinchService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = (SinchService.SinchServiceInterface) iBinder;
}
// it starts incoming call activity which does not show incoming caller name and picture
NotificationResult result = mSinchServiceInterface.relayRemotePushNotificationPayload(dataHashMap);
if (result.isValid() && result.isCall()) {
CallNotificationResult callResult = result.getCallResult();
if (callResult.isCallCanceled() || callResult.isTimedOut()) {
createNotification("Missed Call from : ", callResult.getRemoteUserId());
return;
} else {
if (callResult.isVideoOffered()) {
Intent intent = new Intent(this, IncomingCallScreenActivity.class);
intent.putExtra(SinchService.CALL_ID, callResult.getRemoteUserId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
}
}
} else if (result.isValid() && result.isMessage()) {
//i want to get message content here
MessageNotificationResult notificationResult = result.getMessageResult();
createNotification("Received Message from : ", notificationResult.getSenderId());
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
unbindService(this);
}
private void createNotification(String contentTitle, String userId) {
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainActivity.class), 0);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext()).setSmallIcon(R.mipmap.ic_launcher).setContentTitle(contentTitle).setContentText(userId);
mBuilder.setContentIntent(contentIntent);
mBuilder.setDefaults(Notification.DEFAULT_SOUND);
mBuilder.setAutoCancel(true);
NotificationManager mNotificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
}
}
这是我的服务代码:
public class SinchService extends Service {
private static final String APP_KEY = "***********";
private static final String APP_SECRET = "***********";
private static final String ENVIRONMENT = "clientapi.sinch.com";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private PersistedSettings mSettings;
private StartFailedListener mListener;
@Override
public void onCreate() {
super.onCreate();
mSettings = new PersistedSettings(getApplicationContext());
String userName = mSettings.getUsername();
if (!userName.isEmpty()) {
Intent intent=new Intent(this,placecallActvity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
start(userName);
}
}
@Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}
private void start(String userName) {
if (mSinchClient == null) {
mSettings.setUsername(userName);
mUserId = userName;
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(userName).applicationKey(APP_KEY).applicationSecret(APP_SECRET).environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.checkManifest();
mSinchClient.setSupportActiveConnectionInBackground(true);
mSinchClient.startListeningOnActiveConnection();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().setRespectNativeCalls(false);
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.getVideoController().setResizeBehaviour(VideoScalingType.ASPECT_FILL);
mSinchClient.start();
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminate();
mSinchClient = null;
}
mSettings.setUsername("");
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
@Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public Call callUserVideo(String userId) {
return mSinchClient.getCallClient().callUserVideo(userId);
}
public NotificationResult relayRemotePushNotificationPayload(final Map payload) {
if (mSinchClient == null && !mSettings.getUsername().isEmpty()) {
start(mSettings.getUsername());
} else if (mSinchClient == null && mSettings.getUsername().isEmpty()) {
return null;
}
return mSinchClient.relayRemotePushNotificationPayload(payload);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchService.this.isStarted();
}
public void startClient(String userName) {
start(userName);
}
public void stopClient() {
stop();
}
public void setStartListener(StartFailedListener listener) {
mListener = listener;
}
public Call getCall(String callId) {
return mSinchClient.getCallClient().getCall(callId);
}
public VideoController getVideoController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getVideoController();
}
public AudioController getAudioController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getAudioController();
}
}
public interface StartFailedListener {
void onStartFailed(SinchError error);
void onStarted();
}
private class MySinchClientListener implements SinchClientListener {
@Override
public void onClientFailed(SinchClient client, SinchError error) {
if (mListener != null) {
mListener.onStartFailed(error);
}
mSinchClient.terminate();
mSinchClient = null;
}
@Override
public void onClientStarted(SinchClient client) {
Log.d(TAG, "SinchClient started");
if (mListener != null) {
mListener.onStarted();
}
}
@Override
public void onClientStopped(SinchClient client) {
Log.d(TAG, "SinchClient stopped");
}
@Override
public void onLogMessage(int level, String area, String message) {
switch (level) {
case Log.DEBUG:
Log.d(area, message);
break;
case Log.ERROR:
Log.e(area, message);
break;
case Log.INFO:
Log.i(area, message);
break;
case Log.VERBOSE:
Log.v(area, message);
break;
case Log.WARN:
Log.w(area, message);
break;
}
}
@Override
public void onRegistrationCredentialsRequired(SinchClient client,
ClientRegistration clientRegistration) {
}
}
private class SinchCallClientListener implements CallClientListener {
@Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.d(TAG, "Incoming call");
Intent intent = new Intent(SinchService.this, IncomingCallScreenActivity.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
SinchService.this.startActivity(intent);
}
}
private class PersistedSettings {
private SharedPreferences mStore;
private static final String PREF_KEY = "Sinch";
public PersistedSettings(Context context) {
mStore = context.getSharedPreferences(PREF_KEY, MODE_PRIVATE);
}
public String getUsername() {
return mStore.getString("Username", "");
}
public void setUsername(String username) {
SharedPreferences.Editor editor = mStore.edit();
editor.putString("Username", username);
editor.commit();
}
}
}
在我们的 SDK 包中,您会在 Samples 文件夹下找到多个示例文件,名为 sinch-rtc-sample-video-push 的文件可以满足您的需求。
您还应该在此处查看我们的文档:
https://developers.sinch.com/docs/voice-android-push-notifications
此致
将 FCM 添加到您的项目中
添加以下代码
public class SinchService extends Service {
static final String APP_KEY = "************";
static final String APP_SECRET = "***********";
static final String ENVIRONMENT = "clientapi.sinch.com";
public static final int MESSAGE_PERMISSIONS_NEEDED = 1;
public static final String REQUIRED_PERMISSION = "REQUIRED_PESMISSION";
public static final String MESSENGER = "MESSENGER";
private Messenger messenger;
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private StartFailedListener mListener;
private PersistedSettings mSettings;
private AudioPlayer audioPlayer;
private Handler handler;
@Override
public void onCreate() {
super.onCreate();
super.onCreate();
mSettings = new PersistedSettings(getApplicationContext());
String userName = mSettings.getUsername();
if (!userName.isEmpty()) {
start(userName);
}
handler = new Handler();
}
private void createClient(String username) {
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(username)
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.setPushNotificationDisplayName("User " + username);
}
@Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminateGracefully();
}
super.onDestroy();
}
private boolean hasUsername() {
if (mSettings.getUsername().isEmpty()) {
Log.e(TAG, "Can't start a SinchClient as no username is available!");
return false;
}
return true;
}
private void createClientIfNecessary() {
if (mSinchClient != null)
return;
if (!hasUsername()) {
throw new IllegalStateException("Can't create a SinchClient as no username is available!");
}
createClient(mSettings.getUsername());
}
private void start(String userName) {
boolean permissionsGranted = true;
if (mSinchClient == null) {
mSettings.setUsername(userName);
createClient(userName);
}
createClientIfNecessary();
try {
//mandatory checks
mSinchClient.checkManifest();
if (getApplicationContext().checkCallingOrSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
throw new MissingPermissionException(Manifest.permission.CAMERA);
}
} catch (MissingPermissionException e) {
permissionsGranted = false;
if (messenger != null) {
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString(REQUIRED_PERMISSION, e.getRequiredPermission());
message.setData(bundle);
message.what = MESSAGE_PERMISSIONS_NEEDED;
try {
messenger.send(message);
} catch (RemoteException e1) {
e1.printStackTrace();
}
}
}
if (permissionsGranted) {
Log.d(TAG, "Starting SinchClient");
try {
mSinchClient.start();
} catch (IllegalStateException e) {
Log.w(TAG, "Can't start SinchClient - " + e.getMessage());
}
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminateGracefully();
mSinchClient = null;
}
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
@Override
public IBinder onBind(Intent intent) {
messenger = intent.getParcelableExtra(MESSENGER);
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public Call callUserVideo(String userId) {
return mSinchClient.getCallClient().callUserVideo(userId);
}
public String getUserName() {
return mSettings.getUsername();
}
public void setUsername(String username) {
mSettings.setUsername(username);
}
public void retryStartAfterPermissionGranted() {
SinchService.this.attemptAutoStart();
}
public boolean isStarted() {
return SinchService.this.isStarted();
}
public void startClient(String userName) {
start(userName);
}
public void stopClient() {
stop();
}
public void setStartListener(StartFailedListener listener) {
mListener = listener;
}
public Call getCall(String callId) {
return mSinchClient.getCallClient().getCall(callId);
}
public VideoController getVideoController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getVideoController();
}
public AudioController getAudioController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getAudioController();
}
public NotificationResult relayRemotePushNotificationPayload(final Map payload) {
if (!hasUsername()) {
Log.e(TAG, "Unable to relay the push notification!");
return null;
}
createClientIfNecessary();
return mSinchClient.relayRemotePushNotificationPayload(payload);
}
}
private void attemptAutoStart() {
}
public interface StartFailedListener {
void onStartFailed(SinchError error);
void onStarted();
}
private class MySinchClientListener implements SinchClientListener {
@Override
public void onClientFailed(SinchClient client, SinchError error) {
if (mListener != null) {
mListener.onStartFailed(error);
}
mSinchClient.terminate();
mSinchClient = null;
}
@Override
public void onClientStarted(SinchClient client) {
Log.d(TAG, "SinchClient started");
if (mListener != null) {
mListener.onStarted();
}
}
@Override
public void onClientStopped(SinchClient client) {
Log.d(TAG, "SinchClient stopped");
}
@Override
public void onLogMessage(int level, String area, String message) {
switch (level) {
case Log.DEBUG:
Log.d(area, message);
break;
case Log.ERROR:
Log.e(area, message);
break;
case Log.INFO:
Log.i(area, message);
break;
case Log.VERBOSE:
Log.v(area, message);
break;
case Log.WARN:
Log.w(area, message);
break;
}
}
@Override
public void onRegistrationCredentialsRequired(SinchClient client,
ClientRegistration clientRegistration) {
}
}
private class SinchCallClientListener implements CallClientListener {
@Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.d(TAG, "onIncomingCall: " + call.getCallId());
audioPlayer = AudioPlayer.getInstance(getApplicationContext());
call.addCallListener(new SinchCallListener());
Intent intent = new Intent(SinchService.this, IncomingCallScreenActivity.class);
intent.putExtra(EXTRA_ID, MESSAGE_ID);
intent.putExtra(CALL_ID, call.getCallId());
boolean inForeground = isAppOnForeground(getApplicationContext());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (inForeground) {
SinchService.this.startActivity(intent);
} else {
if (!audioPlayer.isPlayedRingtone()) {
audioPlayer.playRingtone();
}
((NotificationManager) Objects.requireNonNull(getSystemService(NOTIFICATION_SERVICE))).notify(MESSAGE_ID, createIncomingCallNotification(call.getRemoteUserId(), intent));
}
}
private boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = null;
if (activityManager != null) {
appProcesses = activityManager.getRunningAppProcesses();
}
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
private Bitmap getBitmap(Context context, int resId) {
int largeIconWidth = (int) context.getResources()
.getDimension(R.dimen.height);
int largeIconHeight = (int) context.getResources()
.getDimension(R.dimen.height);
Drawable d = context.getResources().getDrawable(resId);
Bitmap b = Bitmap.createBitmap(largeIconWidth, largeIconHeight, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
d.setBounds(0, 0, largeIconWidth, largeIconHeight);
d.draw(c);
return b;
}
private PendingIntent getPendingIntent(Intent intent, String action) {
intent.setAction(action);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 111, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
@TargetApi(29)
private Notification createIncomingCallNotification(String userId, Intent fullScreenIntent) {
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 112, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(getApplicationContext(), FcmListenerService.CHANNEL_ID)
.setContentTitle("Incoming call")
.setContentText(userId)
.setLargeIcon(getBitmap(getApplicationContext(), R.drawable.call_normal))
.setSmallIcon(R.drawable.call_pressed)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setFullScreenIntent(pendingIntent, true)
.addAction(R.drawable.button_accept, "Answer", getPendingIntent(fullScreenIntent, ACTION_ANSWER))
.addAction(R.drawable.button_decline, "Ignore", getPendingIntent(fullScreenIntent, ACTION_IGNORE))
.setOngoing(true);
return builder.build();
}
}
private class SinchCallListener implements CallListener {
@Override
public void onCallEnded(Call call) {
cancelNotification();
if (audioPlayer != null && audioPlayer.isPlayedRingtone()) {
audioPlayer.stopRingtone();
}
}
@Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
if (audioPlayer != null && audioPlayer.isPlayedRingtone()) {
audioPlayer.stopRingtone();
}
}
@Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
}
@Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
// no need to implement for managed push
}
}
public void cancelNotification() {
NotificationManager nMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (nMgr != null) {
nMgr.cancel(MESSAGE_ID);
}
}
private static class PersistedSettings {
private SharedPreferences mStore;
private static final String PREF_KEY = "Sinch";
public PersistedSettings(Context context) {
mStore = context.getSharedPreferences(PREF_KEY, MODE_PRIVATE);
}
public String getUsername() {
return mStore.getString("Username", "");
}
public void setUsername(String username) {
SharedPreferences.Editor editor = mStore.edit();
editor.putString("Username", username);
editor.apply();
}
}
}
并创建 FcmListenerService 并添加以下代码
public class FcmListenerService extends FirebaseMessagingService {
public static String CHANNEL_ID = "Sinch Push Notification Channel";
private final String PREFERENCE_FILE = "com.sinch.android.rtc.sample.push.shared_preferences";
SharedPreferences sharedPreferences;
@Override
public void onMessageReceived(RemoteMessage remoteMessage){
Map data = remoteMessage.getData();
if (SinchHelpers.isSinchPushPayload(data)) {
new ServiceConnection() {
private Map payload;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Context context = getApplicationContext();
sharedPreferences = context.getSharedPreferences(PREFERENCE_FILE, Context.MODE_PRIVATE);
if (payload != null) {
SinchService.SinchServiceInterface sinchService = (SinchService.SinchServiceInterface) service;
if (sinchService != null) {
NotificationResult result = sinchService.relayRemotePushNotificationPayload(payload);
// handle result, e.g. show a notification or similar
// here is example for notifying user about missed/canceled call:
if (result!=null && result.isValid() && result.isCall()) {
CallNotificationResult callResult = result.getCallResult();
if (callResult != null && result.getDisplayName() != null) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(callResult.getRemoteUserId(), result.getDisplayName());
editor.apply();
}
if (callResult != null && callResult.isCallCanceled()) {
String displayName = result.getDisplayName();
if (displayName == null) {
displayName = sharedPreferences.getString(callResult.getRemoteUserId(), "n/a");
}
createMissedCallNotification(!displayName.isEmpty() ? displayName : callResult.getRemoteUserId());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
context.deleteSharedPreferences(PREFERENCE_FILE);
}
}
}
}
}
payload = null;
}
@Override
public void onServiceDisconnected(ComponentName name) {}
public void relayMessageData(Map<String, String> data) {
payload = data;
createNotificationChannel(NotificationManager.IMPORTANCE_MAX);
getApplicationContext().bindService(new Intent(getApplicationContext(), SinchService.class), this, BIND_AUTO_CREATE);
}
}.relayMessageData(data);
}
}
private void createNotificationChannel(int importance) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Amar LPG";
String description = "Incoming call notification";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
}
}
}
private void createMissedCallNotification(String userId) {
createNotificationChannel(NotificationManager.IMPORTANCE_DEFAULT);
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0,
new Intent(getApplicationContext(), IncomingCallScreenActivity.class), 0);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
.setSmallIcon(R.drawable.icon)
.setContentTitle("Missed call from ")
.setContentText(userId)
.setContentIntent(contentIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true);
NotificationManager mNotificationManager =
(NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
mNotificationManager.notify(1, builder.build());
}
}
}
在来电界面添加以下代码
public class IncomingCallScreenActivity 扩展了 BaseActivity {
static final String TAG = IncomingCallScreenActivity.class.getSimpleName();
private String mCallId;
private AudioPlayer mAudioPlayer;
public static final String ACTION_ANSWER = "answer";
public static final String ACTION_IGNORE = "ignore";
public static final String EXTRA_ID = "id";
public static int MESSAGE_ID = 14;
private String mAction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_incoming_call_screen);
Button answer = (Button) findViewById(R.id.answerButton);
answer.setOnClickListener(mClickListener);
Button decline = (Button) findViewById(R.id.declineButton);
decline.setOnClickListener(mClickListener);
mAudioPlayer = new AudioPlayer(this);
mAudioPlayer.playRingtone();
mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
mAction = "";
}
@Override
protected void onResume() {
super.onResume();
Intent intent = getIntent();
if (intent != null) {
if (intent.getStringExtra(SinchService.CALL_ID) != null) {
mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
}
final int id = intent.getIntExtra(EXTRA_ID, -1);
if (id > 0) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(id);
}
mAction = intent.getAction();
}
}
@Override
protected void onServiceConnected() {
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.addCallListener(new SinchCallListener());
TextView remoteUser = (TextView) findViewById(R.id.remoteUser);
remoteUser.setText(call.getRemoteUserId());
if (ACTION_ANSWER.equals(mAction)) {
mAction = "";
answerClicked();
} else if (ACTION_IGNORE.equals(mAction)) {
mAction = "";
declineClicked();
}
} else {
Log.e(TAG, "Started with invalid callId, aborting");
finish();
}
}
private void answerClicked() {
if (mAudioPlayer!=null && mAudioPlayer.isPlayedRingtone()){
mAudioPlayer.stopRingtone();
}
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
Log.d(TAG, "Answering call");
call.answer();
Intent intent = new Intent(this, CallScreenActivity.class);
intent.putExtra(SinchService.CALL_ID, mCallId);
startActivity(intent);
} else {
finish();
}
}
private void declineClicked() {
if (mAudioPlayer!=null && mAudioPlayer.isPlayedRingtone()){
mAudioPlayer.stopRingtone();
}
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.hangup();
}
finish();
}
private class SinchCallListener implements VideoCallListener {
@Override
public void onCallEnded(Call call) {
CallEndCause cause = call.getDetails().getEndCause();
Log.d(TAG, "Call ended, cause: " + cause.toString());
mAudioPlayer.stopRingtone();
finish();
}
@Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
}
@Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
}
@Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
}
@Override
public void onVideoTrackAdded(Call call) {
// Display some kind of icon showing it's a video call
}
@Override
public void onVideoTrackPaused(Call call) {
}
@Override
public void onVideoTrackResumed(Call call) {
}
}
private View.OnClickListener mClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.answerButton:
answerClicked();
break;
case R.id.declineButton:
declineClicked();
break;
}
}
};
}
在登录activity中添加以下代码。
public class MainActivity extends BaseActivity implements SinchService.StartFailedListener, PushTokenRegistrationCallback, UserRegistrationCallback {
private Button mLoginButton;
private EditText mLoginName;
private ProgressDialog mSpinner;
private String mUserId;
private long mSigningSequence = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.READ_PHONE_STATE},100);
}
mLoginName = (EditText) findViewById(R.id.loginName);
mLoginButton = (Button) findViewById(R.id.loginButton);
mLoginButton.setEnabled(false);
mLoginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loginClicked();
}
});
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onServiceConnected() {
mLoginButton.setEnabled(true);
getSinchServiceInterface().setStartListener(this);
}
@Override
protected void onPause() {
if (mSpinner != null) {
mSpinner.dismiss();
}
super.onPause();
}
@Override
public void onStartFailed(SinchError error) {
Toast.makeText(this, error.toString(), Toast.LENGTH_LONG).show();
if (mSpinner != null) {
mSpinner.dismiss();
}
}
@Override
public void onStarted() {
openPlaceCallActivity();
}
private void loginClicked() {
String userName = mLoginName.getText().toString();
if (userName.isEmpty()) {
Toast.makeText(this, "Please enter a name", Toast.LENGTH_LONG).show();
return;
}
if (!getSinchServiceInterface().isStarted()) {
getSinchServiceInterface().startClient(userName);
showSpinner();
} else {
openPlaceCallActivity();
}
mUserId = userName;
UserController uc = Sinch.getUserControllerBuilder()
.context(getApplicationContext())
.applicationKey(APP_KEY)
.userId(mUserId)
.environmentHost(ENVIRONMENT)
.build();
uc.registerUser(this, this);
}
private void openPlaceCallActivity() {
Intent mainActivity = new Intent(this, PlaceCallActivity.class);
startActivity(mainActivity);
finish();
}
private void showSpinner() {
mSpinner = new ProgressDialog(this);
mSpinner.setTitle("Logging in");
mSpinner.setMessage("Please wait...");
mSpinner.show();
}
private void dismissSpinner() {
if (mSpinner != null) {
mSpinner.dismiss();
mSpinner = null;
}
}
@Override
public void tokenRegistered() {
dismissSpinner();
startClientAndOpenPlaceCallActivity();
}
private void startClientAndOpenPlaceCallActivity() {
// start Sinch Client, it'll result onStarted() callback from where the place call activity will be started
if (!getSinchServiceInterface().isStarted()) {
getSinchServiceInterface().startClient("");
showSpinner();
}
}
@Override
public void tokenRegistrationFailed(SinchError sinchError) {
dismissSpinner();
Toast.makeText(this, "Push token registration failed - incoming calls can't be received!", Toast.LENGTH_LONG).show();
}
@Override
public void onCredentialsRequired(ClientRegistration clientRegistration) {
String toSign = mUserId + APP_KEY + mSigningSequence + APP_SECRET;
String signature;
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-1");
byte[] hash = messageDigest.digest(toSign.getBytes("UTF-8"));
signature = Base64.encodeToString(hash, Base64.DEFAULT).trim();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e.getCause());
}
clientRegistration.register(signature, mSigningSequence++);
}
@Override
public void onUserRegistered() {
}
@Override
public void onUserRegistrationFailed(SinchError sinchError) {
dismissSpinner();
Toast.makeText(this, "Registration failed!", Toast.LENGTH_LONG).show();
}
}
我在当前的应用程序中实现了 Sinch 视频通话。当应用程序在前台时,来电一切正常。但是当我关闭应用程序并尝试接听来电时,它没有显示任何来电。当应用程序关闭来电时如何进行视频 Sinch 通话?以下是我的 FCMMessageReceiverService class.
public class FCMMessageReceiverService extends FirebaseMessagingService implements ServiceConnection {
Context context;
private SinchService.SinchServiceInterface mSinchServiceInterface;
HashMap dataHashMap;
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
context = this;
if (SinchHelpers.isSinchPushPayload(remoteMessage.getData())) {
Map data = remoteMessage.getData();
dataHashMap = (data instanceof HashMap) ? (HashMap) data : new HashMap<>(data);
if (SinchHelpers.isSinchPushPayload(dataHashMap)) {
getApplicationContext().bindService(new Intent(getApplicationContext(), SinchService.class), this, Context.BIND_AUTO_CREATE);
}
} else {
Intent intent;
PendingIntent pendingIntent = null;
if (remoteMessage.getData().size() > 0) {
String identifier = remoteMessage.getData().get("identifier");
if (identifier.equals("0")) {
intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder.setWhen(System.currentTimeMillis());
notificationBuilder.setContentTitle(remoteMessage.getNotification().getTitle());
notificationBuilder.setContentText(remoteMessage.getNotification().getBody());
notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
notificationBuilder.setDefaults(Notification.DEFAULT_ALL | Notification.DEFAULT_LIGHTS | Notification.FLAG_SHOW_LIGHTS | Notification.DEFAULT_SOUND);
notificationBuilder.setAutoCancel(true);
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher);
notificationBuilder.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}
}
public static boolean foregrounded() {
ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
ActivityManager.getMyMemoryState(appProcessInfo);
return (appProcessInfo.importance == appProcessInfo.IMPORTANCE_FOREGROUND || appProcessInfo.importance == appProcessInfo.IMPORTANCE_VISIBLE);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if (SinchService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = (SinchService.SinchServiceInterface) iBinder;
}
// it starts incoming call activity which does not show incoming caller name and picture
NotificationResult result = mSinchServiceInterface.relayRemotePushNotificationPayload(dataHashMap);
if (result.isValid() && result.isCall()) {
CallNotificationResult callResult = result.getCallResult();
if (callResult.isCallCanceled() || callResult.isTimedOut()) {
createNotification("Missed Call from : ", callResult.getRemoteUserId());
return;
} else {
if (callResult.isVideoOffered()) {
Intent intent = new Intent(this, IncomingCallScreenActivity.class);
intent.putExtra(SinchService.CALL_ID, callResult.getRemoteUserId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
}
}
} else if (result.isValid() && result.isMessage()) {
//i want to get message content here
MessageNotificationResult notificationResult = result.getMessageResult();
createNotification("Received Message from : ", notificationResult.getSenderId());
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
unbindService(this);
}
private void createNotification(String contentTitle, String userId) {
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainActivity.class), 0);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext()).setSmallIcon(R.mipmap.ic_launcher).setContentTitle(contentTitle).setContentText(userId);
mBuilder.setContentIntent(contentIntent);
mBuilder.setDefaults(Notification.DEFAULT_SOUND);
mBuilder.setAutoCancel(true);
NotificationManager mNotificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
}
}
这是我的服务代码:
public class SinchService extends Service {
private static final String APP_KEY = "***********";
private static final String APP_SECRET = "***********";
private static final String ENVIRONMENT = "clientapi.sinch.com";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private PersistedSettings mSettings;
private StartFailedListener mListener;
@Override
public void onCreate() {
super.onCreate();
mSettings = new PersistedSettings(getApplicationContext());
String userName = mSettings.getUsername();
if (!userName.isEmpty()) {
Intent intent=new Intent(this,placecallActvity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
start(userName);
}
}
@Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}
private void start(String userName) {
if (mSinchClient == null) {
mSettings.setUsername(userName);
mUserId = userName;
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(userName).applicationKey(APP_KEY).applicationSecret(APP_SECRET).environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.checkManifest();
mSinchClient.setSupportActiveConnectionInBackground(true);
mSinchClient.startListeningOnActiveConnection();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().setRespectNativeCalls(false);
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.getVideoController().setResizeBehaviour(VideoScalingType.ASPECT_FILL);
mSinchClient.start();
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminate();
mSinchClient = null;
}
mSettings.setUsername("");
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
@Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public Call callUserVideo(String userId) {
return mSinchClient.getCallClient().callUserVideo(userId);
}
public NotificationResult relayRemotePushNotificationPayload(final Map payload) {
if (mSinchClient == null && !mSettings.getUsername().isEmpty()) {
start(mSettings.getUsername());
} else if (mSinchClient == null && mSettings.getUsername().isEmpty()) {
return null;
}
return mSinchClient.relayRemotePushNotificationPayload(payload);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchService.this.isStarted();
}
public void startClient(String userName) {
start(userName);
}
public void stopClient() {
stop();
}
public void setStartListener(StartFailedListener listener) {
mListener = listener;
}
public Call getCall(String callId) {
return mSinchClient.getCallClient().getCall(callId);
}
public VideoController getVideoController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getVideoController();
}
public AudioController getAudioController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getAudioController();
}
}
public interface StartFailedListener {
void onStartFailed(SinchError error);
void onStarted();
}
private class MySinchClientListener implements SinchClientListener {
@Override
public void onClientFailed(SinchClient client, SinchError error) {
if (mListener != null) {
mListener.onStartFailed(error);
}
mSinchClient.terminate();
mSinchClient = null;
}
@Override
public void onClientStarted(SinchClient client) {
Log.d(TAG, "SinchClient started");
if (mListener != null) {
mListener.onStarted();
}
}
@Override
public void onClientStopped(SinchClient client) {
Log.d(TAG, "SinchClient stopped");
}
@Override
public void onLogMessage(int level, String area, String message) {
switch (level) {
case Log.DEBUG:
Log.d(area, message);
break;
case Log.ERROR:
Log.e(area, message);
break;
case Log.INFO:
Log.i(area, message);
break;
case Log.VERBOSE:
Log.v(area, message);
break;
case Log.WARN:
Log.w(area, message);
break;
}
}
@Override
public void onRegistrationCredentialsRequired(SinchClient client,
ClientRegistration clientRegistration) {
}
}
private class SinchCallClientListener implements CallClientListener {
@Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.d(TAG, "Incoming call");
Intent intent = new Intent(SinchService.this, IncomingCallScreenActivity.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
SinchService.this.startActivity(intent);
}
}
private class PersistedSettings {
private SharedPreferences mStore;
private static final String PREF_KEY = "Sinch";
public PersistedSettings(Context context) {
mStore = context.getSharedPreferences(PREF_KEY, MODE_PRIVATE);
}
public String getUsername() {
return mStore.getString("Username", "");
}
public void setUsername(String username) {
SharedPreferences.Editor editor = mStore.edit();
editor.putString("Username", username);
editor.commit();
}
}
}
在我们的 SDK 包中,您会在 Samples 文件夹下找到多个示例文件,名为 sinch-rtc-sample-video-push 的文件可以满足您的需求。 您还应该在此处查看我们的文档: https://developers.sinch.com/docs/voice-android-push-notifications
此致
将 FCM 添加到您的项目中
添加以下代码
public class SinchService extends Service {
static final String APP_KEY = "************";
static final String APP_SECRET = "***********";
static final String ENVIRONMENT = "clientapi.sinch.com";
public static final int MESSAGE_PERMISSIONS_NEEDED = 1;
public static final String REQUIRED_PERMISSION = "REQUIRED_PESMISSION";
public static final String MESSENGER = "MESSENGER";
private Messenger messenger;
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private StartFailedListener mListener;
private PersistedSettings mSettings;
private AudioPlayer audioPlayer;
private Handler handler;
@Override
public void onCreate() {
super.onCreate();
super.onCreate();
mSettings = new PersistedSettings(getApplicationContext());
String userName = mSettings.getUsername();
if (!userName.isEmpty()) {
start(userName);
}
handler = new Handler();
}
private void createClient(String username) {
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(username)
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.setPushNotificationDisplayName("User " + username);
}
@Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminateGracefully();
}
super.onDestroy();
}
private boolean hasUsername() {
if (mSettings.getUsername().isEmpty()) {
Log.e(TAG, "Can't start a SinchClient as no username is available!");
return false;
}
return true;
}
private void createClientIfNecessary() {
if (mSinchClient != null)
return;
if (!hasUsername()) {
throw new IllegalStateException("Can't create a SinchClient as no username is available!");
}
createClient(mSettings.getUsername());
}
private void start(String userName) {
boolean permissionsGranted = true;
if (mSinchClient == null) {
mSettings.setUsername(userName);
createClient(userName);
}
createClientIfNecessary();
try {
//mandatory checks
mSinchClient.checkManifest();
if (getApplicationContext().checkCallingOrSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
throw new MissingPermissionException(Manifest.permission.CAMERA);
}
} catch (MissingPermissionException e) {
permissionsGranted = false;
if (messenger != null) {
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString(REQUIRED_PERMISSION, e.getRequiredPermission());
message.setData(bundle);
message.what = MESSAGE_PERMISSIONS_NEEDED;
try {
messenger.send(message);
} catch (RemoteException e1) {
e1.printStackTrace();
}
}
}
if (permissionsGranted) {
Log.d(TAG, "Starting SinchClient");
try {
mSinchClient.start();
} catch (IllegalStateException e) {
Log.w(TAG, "Can't start SinchClient - " + e.getMessage());
}
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminateGracefully();
mSinchClient = null;
}
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
@Override
public IBinder onBind(Intent intent) {
messenger = intent.getParcelableExtra(MESSENGER);
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public Call callUserVideo(String userId) {
return mSinchClient.getCallClient().callUserVideo(userId);
}
public String getUserName() {
return mSettings.getUsername();
}
public void setUsername(String username) {
mSettings.setUsername(username);
}
public void retryStartAfterPermissionGranted() {
SinchService.this.attemptAutoStart();
}
public boolean isStarted() {
return SinchService.this.isStarted();
}
public void startClient(String userName) {
start(userName);
}
public void stopClient() {
stop();
}
public void setStartListener(StartFailedListener listener) {
mListener = listener;
}
public Call getCall(String callId) {
return mSinchClient.getCallClient().getCall(callId);
}
public VideoController getVideoController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getVideoController();
}
public AudioController getAudioController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getAudioController();
}
public NotificationResult relayRemotePushNotificationPayload(final Map payload) {
if (!hasUsername()) {
Log.e(TAG, "Unable to relay the push notification!");
return null;
}
createClientIfNecessary();
return mSinchClient.relayRemotePushNotificationPayload(payload);
}
}
private void attemptAutoStart() {
}
public interface StartFailedListener {
void onStartFailed(SinchError error);
void onStarted();
}
private class MySinchClientListener implements SinchClientListener {
@Override
public void onClientFailed(SinchClient client, SinchError error) {
if (mListener != null) {
mListener.onStartFailed(error);
}
mSinchClient.terminate();
mSinchClient = null;
}
@Override
public void onClientStarted(SinchClient client) {
Log.d(TAG, "SinchClient started");
if (mListener != null) {
mListener.onStarted();
}
}
@Override
public void onClientStopped(SinchClient client) {
Log.d(TAG, "SinchClient stopped");
}
@Override
public void onLogMessage(int level, String area, String message) {
switch (level) {
case Log.DEBUG:
Log.d(area, message);
break;
case Log.ERROR:
Log.e(area, message);
break;
case Log.INFO:
Log.i(area, message);
break;
case Log.VERBOSE:
Log.v(area, message);
break;
case Log.WARN:
Log.w(area, message);
break;
}
}
@Override
public void onRegistrationCredentialsRequired(SinchClient client,
ClientRegistration clientRegistration) {
}
}
private class SinchCallClientListener implements CallClientListener {
@Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.d(TAG, "onIncomingCall: " + call.getCallId());
audioPlayer = AudioPlayer.getInstance(getApplicationContext());
call.addCallListener(new SinchCallListener());
Intent intent = new Intent(SinchService.this, IncomingCallScreenActivity.class);
intent.putExtra(EXTRA_ID, MESSAGE_ID);
intent.putExtra(CALL_ID, call.getCallId());
boolean inForeground = isAppOnForeground(getApplicationContext());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (inForeground) {
SinchService.this.startActivity(intent);
} else {
if (!audioPlayer.isPlayedRingtone()) {
audioPlayer.playRingtone();
}
((NotificationManager) Objects.requireNonNull(getSystemService(NOTIFICATION_SERVICE))).notify(MESSAGE_ID, createIncomingCallNotification(call.getRemoteUserId(), intent));
}
}
private boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = null;
if (activityManager != null) {
appProcesses = activityManager.getRunningAppProcesses();
}
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
private Bitmap getBitmap(Context context, int resId) {
int largeIconWidth = (int) context.getResources()
.getDimension(R.dimen.height);
int largeIconHeight = (int) context.getResources()
.getDimension(R.dimen.height);
Drawable d = context.getResources().getDrawable(resId);
Bitmap b = Bitmap.createBitmap(largeIconWidth, largeIconHeight, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
d.setBounds(0, 0, largeIconWidth, largeIconHeight);
d.draw(c);
return b;
}
private PendingIntent getPendingIntent(Intent intent, String action) {
intent.setAction(action);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 111, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
@TargetApi(29)
private Notification createIncomingCallNotification(String userId, Intent fullScreenIntent) {
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 112, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(getApplicationContext(), FcmListenerService.CHANNEL_ID)
.setContentTitle("Incoming call")
.setContentText(userId)
.setLargeIcon(getBitmap(getApplicationContext(), R.drawable.call_normal))
.setSmallIcon(R.drawable.call_pressed)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setFullScreenIntent(pendingIntent, true)
.addAction(R.drawable.button_accept, "Answer", getPendingIntent(fullScreenIntent, ACTION_ANSWER))
.addAction(R.drawable.button_decline, "Ignore", getPendingIntent(fullScreenIntent, ACTION_IGNORE))
.setOngoing(true);
return builder.build();
}
}
private class SinchCallListener implements CallListener {
@Override
public void onCallEnded(Call call) {
cancelNotification();
if (audioPlayer != null && audioPlayer.isPlayedRingtone()) {
audioPlayer.stopRingtone();
}
}
@Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
if (audioPlayer != null && audioPlayer.isPlayedRingtone()) {
audioPlayer.stopRingtone();
}
}
@Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
}
@Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
// no need to implement for managed push
}
}
public void cancelNotification() {
NotificationManager nMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (nMgr != null) {
nMgr.cancel(MESSAGE_ID);
}
}
private static class PersistedSettings {
private SharedPreferences mStore;
private static final String PREF_KEY = "Sinch";
public PersistedSettings(Context context) {
mStore = context.getSharedPreferences(PREF_KEY, MODE_PRIVATE);
}
public String getUsername() {
return mStore.getString("Username", "");
}
public void setUsername(String username) {
SharedPreferences.Editor editor = mStore.edit();
editor.putString("Username", username);
editor.apply();
}
}
} 并创建 FcmListenerService 并添加以下代码
public class FcmListenerService extends FirebaseMessagingService {
public static String CHANNEL_ID = "Sinch Push Notification Channel";
private final String PREFERENCE_FILE = "com.sinch.android.rtc.sample.push.shared_preferences";
SharedPreferences sharedPreferences;
@Override
public void onMessageReceived(RemoteMessage remoteMessage){
Map data = remoteMessage.getData();
if (SinchHelpers.isSinchPushPayload(data)) {
new ServiceConnection() {
private Map payload;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Context context = getApplicationContext();
sharedPreferences = context.getSharedPreferences(PREFERENCE_FILE, Context.MODE_PRIVATE);
if (payload != null) {
SinchService.SinchServiceInterface sinchService = (SinchService.SinchServiceInterface) service;
if (sinchService != null) {
NotificationResult result = sinchService.relayRemotePushNotificationPayload(payload);
// handle result, e.g. show a notification or similar
// here is example for notifying user about missed/canceled call:
if (result!=null && result.isValid() && result.isCall()) {
CallNotificationResult callResult = result.getCallResult();
if (callResult != null && result.getDisplayName() != null) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(callResult.getRemoteUserId(), result.getDisplayName());
editor.apply();
}
if (callResult != null && callResult.isCallCanceled()) {
String displayName = result.getDisplayName();
if (displayName == null) {
displayName = sharedPreferences.getString(callResult.getRemoteUserId(), "n/a");
}
createMissedCallNotification(!displayName.isEmpty() ? displayName : callResult.getRemoteUserId());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
context.deleteSharedPreferences(PREFERENCE_FILE);
}
}
}
}
}
payload = null;
}
@Override
public void onServiceDisconnected(ComponentName name) {}
public void relayMessageData(Map<String, String> data) {
payload = data;
createNotificationChannel(NotificationManager.IMPORTANCE_MAX);
getApplicationContext().bindService(new Intent(getApplicationContext(), SinchService.class), this, BIND_AUTO_CREATE);
}
}.relayMessageData(data);
}
}
private void createNotificationChannel(int importance) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Amar LPG";
String description = "Incoming call notification";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
}
}
}
private void createMissedCallNotification(String userId) {
createNotificationChannel(NotificationManager.IMPORTANCE_DEFAULT);
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0,
new Intent(getApplicationContext(), IncomingCallScreenActivity.class), 0);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
.setSmallIcon(R.drawable.icon)
.setContentTitle("Missed call from ")
.setContentText(userId)
.setContentIntent(contentIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true);
NotificationManager mNotificationManager =
(NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
mNotificationManager.notify(1, builder.build());
}
}
}
在来电界面添加以下代码
public class IncomingCallScreenActivity 扩展了 BaseActivity {
static final String TAG = IncomingCallScreenActivity.class.getSimpleName();
private String mCallId;
private AudioPlayer mAudioPlayer;
public static final String ACTION_ANSWER = "answer";
public static final String ACTION_IGNORE = "ignore";
public static final String EXTRA_ID = "id";
public static int MESSAGE_ID = 14;
private String mAction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_incoming_call_screen);
Button answer = (Button) findViewById(R.id.answerButton);
answer.setOnClickListener(mClickListener);
Button decline = (Button) findViewById(R.id.declineButton);
decline.setOnClickListener(mClickListener);
mAudioPlayer = new AudioPlayer(this);
mAudioPlayer.playRingtone();
mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
mAction = "";
}
@Override
protected void onResume() {
super.onResume();
Intent intent = getIntent();
if (intent != null) {
if (intent.getStringExtra(SinchService.CALL_ID) != null) {
mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
}
final int id = intent.getIntExtra(EXTRA_ID, -1);
if (id > 0) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(id);
}
mAction = intent.getAction();
}
}
@Override
protected void onServiceConnected() {
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.addCallListener(new SinchCallListener());
TextView remoteUser = (TextView) findViewById(R.id.remoteUser);
remoteUser.setText(call.getRemoteUserId());
if (ACTION_ANSWER.equals(mAction)) {
mAction = "";
answerClicked();
} else if (ACTION_IGNORE.equals(mAction)) {
mAction = "";
declineClicked();
}
} else {
Log.e(TAG, "Started with invalid callId, aborting");
finish();
}
}
private void answerClicked() {
if (mAudioPlayer!=null && mAudioPlayer.isPlayedRingtone()){
mAudioPlayer.stopRingtone();
}
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
Log.d(TAG, "Answering call");
call.answer();
Intent intent = new Intent(this, CallScreenActivity.class);
intent.putExtra(SinchService.CALL_ID, mCallId);
startActivity(intent);
} else {
finish();
}
}
private void declineClicked() {
if (mAudioPlayer!=null && mAudioPlayer.isPlayedRingtone()){
mAudioPlayer.stopRingtone();
}
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.hangup();
}
finish();
}
private class SinchCallListener implements VideoCallListener {
@Override
public void onCallEnded(Call call) {
CallEndCause cause = call.getDetails().getEndCause();
Log.d(TAG, "Call ended, cause: " + cause.toString());
mAudioPlayer.stopRingtone();
finish();
}
@Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
}
@Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
}
@Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
}
@Override
public void onVideoTrackAdded(Call call) {
// Display some kind of icon showing it's a video call
}
@Override
public void onVideoTrackPaused(Call call) {
}
@Override
public void onVideoTrackResumed(Call call) {
}
}
private View.OnClickListener mClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.answerButton:
answerClicked();
break;
case R.id.declineButton:
declineClicked();
break;
}
}
};
}
在登录activity中添加以下代码。
public class MainActivity extends BaseActivity implements SinchService.StartFailedListener, PushTokenRegistrationCallback, UserRegistrationCallback {
private Button mLoginButton;
private EditText mLoginName;
private ProgressDialog mSpinner;
private String mUserId;
private long mSigningSequence = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.READ_PHONE_STATE},100);
}
mLoginName = (EditText) findViewById(R.id.loginName);
mLoginButton = (Button) findViewById(R.id.loginButton);
mLoginButton.setEnabled(false);
mLoginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loginClicked();
}
});
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onServiceConnected() {
mLoginButton.setEnabled(true);
getSinchServiceInterface().setStartListener(this);
}
@Override
protected void onPause() {
if (mSpinner != null) {
mSpinner.dismiss();
}
super.onPause();
}
@Override
public void onStartFailed(SinchError error) {
Toast.makeText(this, error.toString(), Toast.LENGTH_LONG).show();
if (mSpinner != null) {
mSpinner.dismiss();
}
}
@Override
public void onStarted() {
openPlaceCallActivity();
}
private void loginClicked() {
String userName = mLoginName.getText().toString();
if (userName.isEmpty()) {
Toast.makeText(this, "Please enter a name", Toast.LENGTH_LONG).show();
return;
}
if (!getSinchServiceInterface().isStarted()) {
getSinchServiceInterface().startClient(userName);
showSpinner();
} else {
openPlaceCallActivity();
}
mUserId = userName;
UserController uc = Sinch.getUserControllerBuilder()
.context(getApplicationContext())
.applicationKey(APP_KEY)
.userId(mUserId)
.environmentHost(ENVIRONMENT)
.build();
uc.registerUser(this, this);
}
private void openPlaceCallActivity() {
Intent mainActivity = new Intent(this, PlaceCallActivity.class);
startActivity(mainActivity);
finish();
}
private void showSpinner() {
mSpinner = new ProgressDialog(this);
mSpinner.setTitle("Logging in");
mSpinner.setMessage("Please wait...");
mSpinner.show();
}
private void dismissSpinner() {
if (mSpinner != null) {
mSpinner.dismiss();
mSpinner = null;
}
}
@Override
public void tokenRegistered() {
dismissSpinner();
startClientAndOpenPlaceCallActivity();
}
private void startClientAndOpenPlaceCallActivity() {
// start Sinch Client, it'll result onStarted() callback from where the place call activity will be started
if (!getSinchServiceInterface().isStarted()) {
getSinchServiceInterface().startClient("");
showSpinner();
}
}
@Override
public void tokenRegistrationFailed(SinchError sinchError) {
dismissSpinner();
Toast.makeText(this, "Push token registration failed - incoming calls can't be received!", Toast.LENGTH_LONG).show();
}
@Override
public void onCredentialsRequired(ClientRegistration clientRegistration) {
String toSign = mUserId + APP_KEY + mSigningSequence + APP_SECRET;
String signature;
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-1");
byte[] hash = messageDigest.digest(toSign.getBytes("UTF-8"));
signature = Base64.encodeToString(hash, Base64.DEFAULT).trim();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e.getCause());
}
clientRegistration.register(signature, mSigningSequence++);
}
@Override
public void onUserRegistered() {
}
@Override
public void onUserRegistrationFailed(SinchError sinchError) {
dismissSpinner();
Toast.makeText(this, "Registration failed!", Toast.LENGTH_LONG).show();
}
}