恢复 activity 的通知已在后台使用 backpress
Notification to resume activity which has gone in background using backpress
我们正在使用 Sinch voip api。有一个在 app start 启动的绑定服务,我们在服务中初始化 sinch 客户端,它在后台总是 运行 。我尝试将通知代码放在呼叫屏幕 activity 中,因为此 activity 将始终显示接受呼叫。我的目标是能够像在 whatsapp 中一样单击通知并重新打开此调用 activity。
public class CallScreenActivity extends BaseActivity {
static final String TAG = CallScreenActivity.class.getSimpleName();
private AudioPlayer mAudioPlayer;
private Timer mTimer;
private UpdateCallDurationTask mDurationTask;
private String mCallId;
String mCaller, mReceiver;
String otherusername, myname;
private long mCallStart = 0;
private TextView mCallDuration;
private TextView mCallState;
private TextView mCallerName;
private ImageView mCallImg;
private String mk, mTimestamp;
private String mProfpic;
Button endCallButton;
Notification notification;
NotificationManager notificationManager;
private class UpdateCallDurationTask extends TimerTask {
@Override
public void run() {
CallScreenActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
updateCallDuration();
}
});
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| +WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| +WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
setContentView(R.layout.activity_callscreen);
mAudioPlayer = new AudioPlayer(this);
mCallDuration = (TextView) findViewById(R.id.callDuration);
mCallerName = (TextView) findViewById(R.id.remoteUser);
mCallState = (TextView) findViewById(R.id.callState);
mCallImg = (ImageView) findViewById(R.id.imgotherusr);
endCallButton = (Button) findViewById(R.id.hangupButton);
endCallButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
endCall();
}
});
mCallStart = System.currentTimeMillis();
mCallId = getIntent().getStringExtra(SinchCallService.CALL_ID);
UserSession us = new UserSession(this);
mk = us.getUserKey();
myname = us.getUsername();
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
System.out.println(mCaller+"on create call screen activity ongoing call" + mReceiver + otherusername);
showNotification();
}
private void showNotification() {
System.out.println("show notification callscreenactivity");
Intent notificationIntent = new Intent(this, CallScreenActivity.class);
notificationIntent.setAction("ongoingcall");
notificationIntent.putExtra("calleruid", mCaller);
notificationIntent.putExtra("receiveruid", mReceiver);
notificationIntent.putExtra("otherusername", otherusername);
notificationIntent.putExtra("timestamp", mTimestamp);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Intent hangupintent = new Intent(this, CallScreenActivity.class);
hangupintent.setAction("hangupcall");
hangupintent.setAction("ongoingcall");
hangupintent.putExtra("calleruid", mCaller);
hangupintent.putExtra("receiveruid", mReceiver);
hangupintent.putExtra("otherusername", otherusername);
hangupintent.putExtra("timestamp", mTimestamp);
PendingIntent phangupintent = PendingIntent.getService(this, 0,
hangupintent, 0);
notification = new NotificationCompat.Builder(this)
.setContentTitle("In call with " + otherusername)
.setContentText("Duration " + VoiceCallHelper.formatTimespan(System.currentTimeMillis() - mCallStart))
.setSmallIcon(R.drawable.iconcall)
.setContentIntent(pendingIntent)
.addAction(R.drawable.ic_arrow_back, "Hangup",
phangupintent).build();
notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111 /* ID of notification */, notification);
}
@Override
public void onServiceConnected() {
try {
doStuff();
} catch (NullPointerException e) {
//getSinchServiceInterface() in doStuff below throw null pointer error.
}
}
private void doStuff() {
final Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.addCallListener(new SinchCallListener());
mCallState.setText(call.getState().toString());
DBREF_USER_PROFILES.child(call.getRemoteUserId()).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
System.out.println("datasnapshot callscreenactivity otheruser" + dataSnapshot);
User u = User.parse(dataSnapshot);
mCallerName.setText(u.getName());
mProfpic = u.getProfpicurl();
Glide.with(CallScreenActivity.this).load(mProfpic).into(mCallImg);
} else {
mCallerName.setText(call.getHeaders().get("username"));
Glide.with(CallScreenActivity.this).load(R.drawable.whatsapplogo).into(mCallImg);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
} else {
Log.e(TAG, "Started with invalid callId, aborting.");
finish();
}
}
@Override
public void onPause() {
super.onPause();
mDurationTask.cancel();
mTimer.cancel();
}
@Override
public void onResume() {
super.onResume();
mTimer = new Timer();
mDurationTask = new UpdateCallDurationTask();
mTimer.schedule(mDurationTask, 0, 500);
if (getIntent() != null && getIntent().getAction() != null) {
switch (getIntent().getAction()) {
case "ongoingcall":
System.out.println("on resume call screen activity ongoing call" + mCaller + mReceiver + otherusername);
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
break;
case "hangupcall":
System.out.println("on resume call screen activity hangup call");
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
endCallButton.performClick();
break;
}
}
}
@Override
public void onBackPressed() {
startActivity(new Intent(CallScreenActivity.this, MainAct.class));
}
private void endCall() {
if (notification != null) {
System.out.println("cancelling notification in endCAll callscreenactivity");
notificationManager.cancel(111);
}
mAudioPlayer.stopProgressTone();
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.hangup();
}
finish();
}
private void updateCallDuration() {
if (mCallStart > 0) {
mCallDuration.setText(VoiceCallHelper.formatTimespan(System.currentTimeMillis() - mCallStart));
showNotification();
}
}
private class SinchCallListener implements CallListener {
@Override
public void onCallEnded(Call call) {
CallEndCause cause = call.getDetails().getEndCause();
Log.d(TAG, "Call ended. Reason: " + cause.toString() + mk + mCaller);
if (mk != null && mCaller != null && mk.matches(mCaller)) {
mAudioPlayer.stopProgressTone();
setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
String endMsg = "Call ended: " + call.getDetails().toString();
Long gt = GetTimeStamp.Id();
Toast.makeText(CallScreenActivity.this, endMsg, Toast.LENGTH_LONG).show();
System.out.println(endMsg + "mtimestamp" + mTimestamp);
String cau;
String oth;
if (call.getDetails().getDuration() > 0)
cau = "completed";
else
cau = cause.toString();
CallDetails cd1 = new CallDetails(String.valueOf(call.getDetails().getDuration()), mCaller, mReceiver, cau, String.valueOf(gt), mTimestamp, mProfpic, mCallerName.getText().toString());
CallDetails cd2 = new CallDetails(String.valueOf(call.getDetails().getDuration()), mCaller, mReceiver, cau, String.valueOf(gt), mTimestamp, mProfpic, myname);
System.out.println(mCaller + "end msg callscreenactivity" + mReceiver + " " + String.valueOf(gt));
System.out.println("end msg callscreenactivity" + mReceiver + " " + DBREF.child("VoiceCalls").child(mCaller).child(String.valueOf(gt)));
//setting in mCaller mykey node at voicecalls node firebase
DBREF_CALLS.child(mCaller).child(String.valueOf(gt)).setValue(cd1);
//setting in mReceiver otheruserkey node at voicecalls node firebase
DBREF_CALLS.child(mReceiver).child(String.valueOf(gt)).setValue(cd2);
}
endCall();
}
@Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
mAudioPlayer.stopProgressTone();
mCallState.setText(call.getState().toString());
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
mCallStart = System.currentTimeMillis();
mTimestamp = GetTimeStamp.timeStamp();
}
@Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
mAudioPlayer.playProgressTone();
}
@Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
// Send a push through your push provider here, e.g. GCM
}
}
}
我的 SinchCallService class 是这样的:
public class SinchCallService extends Service {
private static final String APP_KEY = SINCH_APPLICATION_KEY;
private static final String APP_SECRET = SINCH_SECRET_KEY;
private static final String ENVIRONMENT = "sandbox.sinch.com";
public static final String LOCATION = "LOCATION";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchCallService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private StartFailedListener mListener;
@Override
public void onCreate() {
super.onCreate();
UserSession us = new UserSession(this);
System.out.println("From sinchcall oncreate" + us.getUserKey());
if (!isStarted()) {
System.out.println("sinch not started callservice oncreate " + us.getUserKey());
start(us.getUserKey());
}
}
@Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}
public void start(String userName) {
System.out.println("sinch call service start " + userName);
if (mSinchClient == null) {
mUserId = userName;
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(userName)
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.startListeningOnActiveConnection();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.start();
System.out.println(" sinch client started");
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminate();
mSinchClient = null;
}
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
@Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public SinchCallService getService() {
return SinchCallService.this;
}
public Call callPhoneNumber(String phoneNumber) {
return mSinchClient.getCallClient().callPhoneNumber(phoneNumber);
}
public Call callUser(String userId) {
return mSinchClient.getCallClient().callUser(userId);
}
public Call callUser(String userId, Map<String, String> headers) {
if(!isStarted()){
UserSession us = new UserSession(getApplicationContext());
startClient(us.getUserKey());
}
return mSinchClient.getCallClient().callUser(userId, headers);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchCallService.this.isStarted();
}
public void startClient(String userName) {
System.out.println("startClient called sinchcallservice" + userName);
if (!isStarted()) {
System.out.println("startClient not started callservice " + 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 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.e(TAG, "Incoming call");
Intent intent = new Intent(SinchCallService.this, IncomingCallScreenActivity.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.putExtra(LOCATION, call.getHeaders().get("location"));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
SinchCallService.this.startActivity(intent);
}
}
}
以下是我的 BaseActivity.java :
public abstract class BaseActivity extends FragmentActivity implements ServiceConnection {
private SinchCallService.SinchServiceInterface mSinchServiceInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getApplicationContext().bindService(new Intent(this, SinchCallService.class), this,
BIND_AUTO_CREATE);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if (SinchCallService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = (SinchCallService.SinchServiceInterface) iBinder;
onServiceConnected();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
if (SinchCallService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = null;
onServiceDisconnected();
}
}
protected void onServiceConnected() {
// for subclasses
}
protected void onServiceDisconnected() {
// for subclasses
}
protected SinchCallService.SinchServiceInterface getSinchServiceInterface() {
return mSinchServiceInterface;
}
}
我试过设置
android:launchMode="singleTop"
如果我按下 CallScreenActivity 并单击通知,它会打开 MainAct 而不是 CallScreenActivity。如何让它打开CallScreenActivity?
我尝试了一种解决方案,即创建一个 stackbuilder 并将其传递给待定意图,如下所示:
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(CallScreenActivity.class);
stackBuilder.addNextIntent(notificationIntent);
PendingIntent pendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
并且还更改了清单如下:
<activity
android:launchMode="singleTop"
android:name=".activity.calls.CallScreenActivity"
android:screenOrientation="portrait"
android:parentActivityName=".activity.main.MainAct">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.main.MainAct"/>
</activity>
但上述更改导致应用程序崩溃并出现以下错误:
07-06 23:38:52.353 9182-9182/com.app E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke interface method 'com.sinch.android.rtc.calling.CallClient com.sinch.android.rtc.SinchClient.getCallClient()' on a null object reference
at com.app.services.SinchCallService$SinchServiceInterface.getCall(SinchCallService.java:150)
at com.app.activity.calls.CallScreenActivity.endCall(CallScreenActivity.java:265)
at com.app.activity.calls.CallScreenActivity.access0(CallScreenActivity.java:51)
at com.app.activity.calls.CallScreenActivity.onClick(CallScreenActivity.java:110)
at android.view.View.performClick(View.java:5207)
at android.view.View$PerformClick.run(View.java:21177)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5438)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
因此,如果我使用没有 stackbuilder 的代码,然后按下主页按钮,然后按下通知,我的 CallSCreenACtivity 打开,但是如果我按下 CAllSCreenACtivity 内的后退按钮,然后按下主页按钮,然后按下通知,它打开主要活动而不是 CallSCreenACtivity。即使按下 callscreenactivty 上的后退按钮,我的通话仍然活跃。
我还注意到通知中显示的通话时长在 CallScreenActivity 上按下后停止刷新,但通话仍然有效。
应用程序流程:
来电者:-
当应用程序启动时,MainAct 启动,用户从那里点击某人的个人资料,然后被带到 ProfilesAct,在那里他按下呼叫按钮呼叫那个人,然后被带到 CallScreenActivity。
MainAct->ProfilesACt->CallScreenACt
对于接收者:-
应用程序在后台,来电,SinchCallService 使 IncomingCAllAct 显示,当他接受呼叫时,他被带到 CallScreenActivity。
IncomingCallAct->CallSCreeenACtivity
或者假设接收者已经在使用该应用程序,那么
any activity(could be chat activity, main acitivty etc)->IncomingCallAct->CallSCreeenACtivity
应用清单:-
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.app.t">
<application
android:name=".TApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity
android:name=".SplashAct"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.userprofile.UserProfileAct"
android:screenOrientation="portrait" />
<activity
android:name=".activity.videocalls.VideoCallScreenActivity"
android:screenOrientation="portrait"
android:launchMode="singleTop"/>
<activity
android:name=".activity.videocalls.IncomingVideoCallScreenActivity"
android:screenOrientation="portrait"
android:noHistory="true"/>
<activity
android:name=".activity.main.MainAct"
android:launchMode="singleTop"
android:screenOrientation="portrait"></activity>
<activity android:name=".activity.chat.ChatActivity" />
<service android:name=".services.MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name=".services.MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<activity android:name=".activity.settings.SettingsmainActivity" />
<service android:name=".services.SinchCallService"></service>
<activity
android:name=".activity.calls.CallScreenActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".activity.calls.IncomingCallScreenActivity"
android:noHistory="true"
android:screenOrientation="portrait" />
<activity android:name=".activity.chat.NewChatActivity" />
<activity android:name=".activity.chat.ChatActivity1"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
</application>
</manifest>
mainfest中的CallScreenActivity不需要做成singletask或者singletop,只需要将callid(帮助Sinchapi识别具体调用),调用开始时间等调用参数保存在sharedpreferences 或 sqlite db,使通知意图如下:
Intent notificationIntent = new Intent(getApplicationContext(), CallScreenActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(getApplicationContext())
.setContentTitle("In call with " + otherusername)
.setSmallIcon(R.drawable.iconcall)
.setContentIntent(pendingIntent).build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111 /* ID of notification */, notification);
只要我们传递正确的callid,我们就可以通过点击通知恢复通话。
我们正在使用 Sinch voip api。有一个在 app start 启动的绑定服务,我们在服务中初始化 sinch 客户端,它在后台总是 运行 。我尝试将通知代码放在呼叫屏幕 activity 中,因为此 activity 将始终显示接受呼叫。我的目标是能够像在 whatsapp 中一样单击通知并重新打开此调用 activity。
public class CallScreenActivity extends BaseActivity {
static final String TAG = CallScreenActivity.class.getSimpleName();
private AudioPlayer mAudioPlayer;
private Timer mTimer;
private UpdateCallDurationTask mDurationTask;
private String mCallId;
String mCaller, mReceiver;
String otherusername, myname;
private long mCallStart = 0;
private TextView mCallDuration;
private TextView mCallState;
private TextView mCallerName;
private ImageView mCallImg;
private String mk, mTimestamp;
private String mProfpic;
Button endCallButton;
Notification notification;
NotificationManager notificationManager;
private class UpdateCallDurationTask extends TimerTask {
@Override
public void run() {
CallScreenActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
updateCallDuration();
}
});
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| +WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| +WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
setContentView(R.layout.activity_callscreen);
mAudioPlayer = new AudioPlayer(this);
mCallDuration = (TextView) findViewById(R.id.callDuration);
mCallerName = (TextView) findViewById(R.id.remoteUser);
mCallState = (TextView) findViewById(R.id.callState);
mCallImg = (ImageView) findViewById(R.id.imgotherusr);
endCallButton = (Button) findViewById(R.id.hangupButton);
endCallButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
endCall();
}
});
mCallStart = System.currentTimeMillis();
mCallId = getIntent().getStringExtra(SinchCallService.CALL_ID);
UserSession us = new UserSession(this);
mk = us.getUserKey();
myname = us.getUsername();
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
System.out.println(mCaller+"on create call screen activity ongoing call" + mReceiver + otherusername);
showNotification();
}
private void showNotification() {
System.out.println("show notification callscreenactivity");
Intent notificationIntent = new Intent(this, CallScreenActivity.class);
notificationIntent.setAction("ongoingcall");
notificationIntent.putExtra("calleruid", mCaller);
notificationIntent.putExtra("receiveruid", mReceiver);
notificationIntent.putExtra("otherusername", otherusername);
notificationIntent.putExtra("timestamp", mTimestamp);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Intent hangupintent = new Intent(this, CallScreenActivity.class);
hangupintent.setAction("hangupcall");
hangupintent.setAction("ongoingcall");
hangupintent.putExtra("calleruid", mCaller);
hangupintent.putExtra("receiveruid", mReceiver);
hangupintent.putExtra("otherusername", otherusername);
hangupintent.putExtra("timestamp", mTimestamp);
PendingIntent phangupintent = PendingIntent.getService(this, 0,
hangupintent, 0);
notification = new NotificationCompat.Builder(this)
.setContentTitle("In call with " + otherusername)
.setContentText("Duration " + VoiceCallHelper.formatTimespan(System.currentTimeMillis() - mCallStart))
.setSmallIcon(R.drawable.iconcall)
.setContentIntent(pendingIntent)
.addAction(R.drawable.ic_arrow_back, "Hangup",
phangupintent).build();
notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111 /* ID of notification */, notification);
}
@Override
public void onServiceConnected() {
try {
doStuff();
} catch (NullPointerException e) {
//getSinchServiceInterface() in doStuff below throw null pointer error.
}
}
private void doStuff() {
final Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.addCallListener(new SinchCallListener());
mCallState.setText(call.getState().toString());
DBREF_USER_PROFILES.child(call.getRemoteUserId()).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
System.out.println("datasnapshot callscreenactivity otheruser" + dataSnapshot);
User u = User.parse(dataSnapshot);
mCallerName.setText(u.getName());
mProfpic = u.getProfpicurl();
Glide.with(CallScreenActivity.this).load(mProfpic).into(mCallImg);
} else {
mCallerName.setText(call.getHeaders().get("username"));
Glide.with(CallScreenActivity.this).load(R.drawable.whatsapplogo).into(mCallImg);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
} else {
Log.e(TAG, "Started with invalid callId, aborting.");
finish();
}
}
@Override
public void onPause() {
super.onPause();
mDurationTask.cancel();
mTimer.cancel();
}
@Override
public void onResume() {
super.onResume();
mTimer = new Timer();
mDurationTask = new UpdateCallDurationTask();
mTimer.schedule(mDurationTask, 0, 500);
if (getIntent() != null && getIntent().getAction() != null) {
switch (getIntent().getAction()) {
case "ongoingcall":
System.out.println("on resume call screen activity ongoing call" + mCaller + mReceiver + otherusername);
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
break;
case "hangupcall":
System.out.println("on resume call screen activity hangup call");
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
endCallButton.performClick();
break;
}
}
}
@Override
public void onBackPressed() {
startActivity(new Intent(CallScreenActivity.this, MainAct.class));
}
private void endCall() {
if (notification != null) {
System.out.println("cancelling notification in endCAll callscreenactivity");
notificationManager.cancel(111);
}
mAudioPlayer.stopProgressTone();
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.hangup();
}
finish();
}
private void updateCallDuration() {
if (mCallStart > 0) {
mCallDuration.setText(VoiceCallHelper.formatTimespan(System.currentTimeMillis() - mCallStart));
showNotification();
}
}
private class SinchCallListener implements CallListener {
@Override
public void onCallEnded(Call call) {
CallEndCause cause = call.getDetails().getEndCause();
Log.d(TAG, "Call ended. Reason: " + cause.toString() + mk + mCaller);
if (mk != null && mCaller != null && mk.matches(mCaller)) {
mAudioPlayer.stopProgressTone();
setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
String endMsg = "Call ended: " + call.getDetails().toString();
Long gt = GetTimeStamp.Id();
Toast.makeText(CallScreenActivity.this, endMsg, Toast.LENGTH_LONG).show();
System.out.println(endMsg + "mtimestamp" + mTimestamp);
String cau;
String oth;
if (call.getDetails().getDuration() > 0)
cau = "completed";
else
cau = cause.toString();
CallDetails cd1 = new CallDetails(String.valueOf(call.getDetails().getDuration()), mCaller, mReceiver, cau, String.valueOf(gt), mTimestamp, mProfpic, mCallerName.getText().toString());
CallDetails cd2 = new CallDetails(String.valueOf(call.getDetails().getDuration()), mCaller, mReceiver, cau, String.valueOf(gt), mTimestamp, mProfpic, myname);
System.out.println(mCaller + "end msg callscreenactivity" + mReceiver + " " + String.valueOf(gt));
System.out.println("end msg callscreenactivity" + mReceiver + " " + DBREF.child("VoiceCalls").child(mCaller).child(String.valueOf(gt)));
//setting in mCaller mykey node at voicecalls node firebase
DBREF_CALLS.child(mCaller).child(String.valueOf(gt)).setValue(cd1);
//setting in mReceiver otheruserkey node at voicecalls node firebase
DBREF_CALLS.child(mReceiver).child(String.valueOf(gt)).setValue(cd2);
}
endCall();
}
@Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
mAudioPlayer.stopProgressTone();
mCallState.setText(call.getState().toString());
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
mCallStart = System.currentTimeMillis();
mTimestamp = GetTimeStamp.timeStamp();
}
@Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
mAudioPlayer.playProgressTone();
}
@Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
// Send a push through your push provider here, e.g. GCM
}
}
}
我的 SinchCallService class 是这样的:
public class SinchCallService extends Service {
private static final String APP_KEY = SINCH_APPLICATION_KEY;
private static final String APP_SECRET = SINCH_SECRET_KEY;
private static final String ENVIRONMENT = "sandbox.sinch.com";
public static final String LOCATION = "LOCATION";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchCallService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private StartFailedListener mListener;
@Override
public void onCreate() {
super.onCreate();
UserSession us = new UserSession(this);
System.out.println("From sinchcall oncreate" + us.getUserKey());
if (!isStarted()) {
System.out.println("sinch not started callservice oncreate " + us.getUserKey());
start(us.getUserKey());
}
}
@Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}
public void start(String userName) {
System.out.println("sinch call service start " + userName);
if (mSinchClient == null) {
mUserId = userName;
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(userName)
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.startListeningOnActiveConnection();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.start();
System.out.println(" sinch client started");
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminate();
mSinchClient = null;
}
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
@Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public SinchCallService getService() {
return SinchCallService.this;
}
public Call callPhoneNumber(String phoneNumber) {
return mSinchClient.getCallClient().callPhoneNumber(phoneNumber);
}
public Call callUser(String userId) {
return mSinchClient.getCallClient().callUser(userId);
}
public Call callUser(String userId, Map<String, String> headers) {
if(!isStarted()){
UserSession us = new UserSession(getApplicationContext());
startClient(us.getUserKey());
}
return mSinchClient.getCallClient().callUser(userId, headers);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchCallService.this.isStarted();
}
public void startClient(String userName) {
System.out.println("startClient called sinchcallservice" + userName);
if (!isStarted()) {
System.out.println("startClient not started callservice " + 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 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.e(TAG, "Incoming call");
Intent intent = new Intent(SinchCallService.this, IncomingCallScreenActivity.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.putExtra(LOCATION, call.getHeaders().get("location"));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
SinchCallService.this.startActivity(intent);
}
}
}
以下是我的 BaseActivity.java :
public abstract class BaseActivity extends FragmentActivity implements ServiceConnection {
private SinchCallService.SinchServiceInterface mSinchServiceInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getApplicationContext().bindService(new Intent(this, SinchCallService.class), this,
BIND_AUTO_CREATE);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if (SinchCallService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = (SinchCallService.SinchServiceInterface) iBinder;
onServiceConnected();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
if (SinchCallService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = null;
onServiceDisconnected();
}
}
protected void onServiceConnected() {
// for subclasses
}
protected void onServiceDisconnected() {
// for subclasses
}
protected SinchCallService.SinchServiceInterface getSinchServiceInterface() {
return mSinchServiceInterface;
}
}
我试过设置
android:launchMode="singleTop"
如果我按下 CallScreenActivity 并单击通知,它会打开 MainAct 而不是 CallScreenActivity。如何让它打开CallScreenActivity?
我尝试了一种解决方案,即创建一个 stackbuilder 并将其传递给待定意图,如下所示:
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(CallScreenActivity.class);
stackBuilder.addNextIntent(notificationIntent);
PendingIntent pendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
并且还更改了清单如下:
<activity
android:launchMode="singleTop"
android:name=".activity.calls.CallScreenActivity"
android:screenOrientation="portrait"
android:parentActivityName=".activity.main.MainAct">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.main.MainAct"/>
</activity>
但上述更改导致应用程序崩溃并出现以下错误:
07-06 23:38:52.353 9182-9182/com.app E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke interface method 'com.sinch.android.rtc.calling.CallClient com.sinch.android.rtc.SinchClient.getCallClient()' on a null object reference
at com.app.services.SinchCallService$SinchServiceInterface.getCall(SinchCallService.java:150)
at com.app.activity.calls.CallScreenActivity.endCall(CallScreenActivity.java:265)
at com.app.activity.calls.CallScreenActivity.access0(CallScreenActivity.java:51)
at com.app.activity.calls.CallScreenActivity.onClick(CallScreenActivity.java:110)
at android.view.View.performClick(View.java:5207)
at android.view.View$PerformClick.run(View.java:21177)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5438)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
因此,如果我使用没有 stackbuilder 的代码,然后按下主页按钮,然后按下通知,我的 CallSCreenACtivity 打开,但是如果我按下 CAllSCreenACtivity 内的后退按钮,然后按下主页按钮,然后按下通知,它打开主要活动而不是 CallSCreenACtivity。即使按下 callscreenactivty 上的后退按钮,我的通话仍然活跃。
我还注意到通知中显示的通话时长在 CallScreenActivity 上按下后停止刷新,但通话仍然有效。
应用程序流程:
来电者:- 当应用程序启动时,MainAct 启动,用户从那里点击某人的个人资料,然后被带到 ProfilesAct,在那里他按下呼叫按钮呼叫那个人,然后被带到 CallScreenActivity。
MainAct->ProfilesACt->CallScreenACt
对于接收者:- 应用程序在后台,来电,SinchCallService 使 IncomingCAllAct 显示,当他接受呼叫时,他被带到 CallScreenActivity。
IncomingCallAct->CallSCreeenACtivity
或者假设接收者已经在使用该应用程序,那么
any activity(could be chat activity, main acitivty etc)->IncomingCallAct->CallSCreeenACtivity
应用清单:-
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.app.t">
<application
android:name=".TApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity
android:name=".SplashAct"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.userprofile.UserProfileAct"
android:screenOrientation="portrait" />
<activity
android:name=".activity.videocalls.VideoCallScreenActivity"
android:screenOrientation="portrait"
android:launchMode="singleTop"/>
<activity
android:name=".activity.videocalls.IncomingVideoCallScreenActivity"
android:screenOrientation="portrait"
android:noHistory="true"/>
<activity
android:name=".activity.main.MainAct"
android:launchMode="singleTop"
android:screenOrientation="portrait"></activity>
<activity android:name=".activity.chat.ChatActivity" />
<service android:name=".services.MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name=".services.MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<activity android:name=".activity.settings.SettingsmainActivity" />
<service android:name=".services.SinchCallService"></service>
<activity
android:name=".activity.calls.CallScreenActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".activity.calls.IncomingCallScreenActivity"
android:noHistory="true"
android:screenOrientation="portrait" />
<activity android:name=".activity.chat.NewChatActivity" />
<activity android:name=".activity.chat.ChatActivity1"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
</application>
</manifest>
mainfest中的CallScreenActivity不需要做成singletask或者singletop,只需要将callid(帮助Sinchapi识别具体调用),调用开始时间等调用参数保存在sharedpreferences 或 sqlite db,使通知意图如下:
Intent notificationIntent = new Intent(getApplicationContext(), CallScreenActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(getApplicationContext())
.setContentTitle("In call with " + otherusername)
.setSmallIcon(R.drawable.iconcall)
.setContentIntent(pendingIntent).build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111 /* ID of notification */, notification);
只要我们传递正确的callid,我们就可以通过点击通知恢复通话。