我们正在使用Sinch voip api。有一个绑定的服务从应用程序启动开始,我们初始化服务中的sinch客户端,并始终在后台运行。我尝试在通话屏幕活动中放置通知代码,因为此活动将始终显示为接受呼叫。我的目标是能够点击通知并像whatsapp一样重新打开此通话活动。通知恢复使用backpress后台进行的活动
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类是这样的:
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"
如果我backpress东西在CallScreenA上活动并单击通知,它将打开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.access$100(CallScreenActivity.java:51)
at com.app.activity.calls.CallScreenActivity$1.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)
所以,如果我使用的代码没有ST ackbuilder和我按主页按钮,然后按通知我的CallSCreenACtivity打开,但如果我按下CAllSCreenACtivity内的按钮,然后按主页按钮,然后我按通知,它打开主要acitivty而不是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>
您可以在'CallScreenActivity'中重写'onBackPressed()'并让它开始'MainAct'。是什么原因? “MainAct”活动是否已经在运行(即:在“CallScreenActivity”下面?您需要解释您的活动之间的导航。请注意,使用“TaskStackBuilder”可能不是您想要的,因为这将永远清除任务并转储任何(这可能不是你想要的) –
@DavidWasser我已经包含了应用程序的流程,你能帮我吗? – Ghanendra
当用户在“CallScreenActivity”中按下BACK时会发生什么?MainAct是静止的当'CallScreenActivity'正在运行时,我还是不明白你的问题 –