通知使用backpress

时间:2017-07-06 18:26:04

标签: android android-activity android-service android-notifications sinch

我们正在使用Sinch voip api。有一个绑定服务,从应用程序启动开始,我们初始化服务中的sinch客户端,它始终在后台运行。我尝试将通知代码放在呼叫屏幕活动中,因为此活动将始终显示以接受呼叫。我的目标是能够点击通知并重新打开此调用活动,如whatsapp。

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"

如果我在CallScreenActivity上进行反投影并单击通知,则会打开MainAct而不是CallScreenActivity。如何让它打开CallScreenActivity?

我尝试了一个创建堆栈构建器并将其传递给挂起意图的解决方案,如下所示:

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)

所以如果我使用没有stackbuilder的代码,我按下主页按钮,然后按通知我的CallSCreenACtivity打开,但如果我按下CAllSCreenACtivity内的后退按钮,然后按主页按钮然后我按通知,它打开主而不是CallSCreenACtivity。即使在callcreenactivty上按下后退按钮,我的呼叫仍然是活动的。

此外,我注意到,在回拨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>

1 个答案:

答案 0 :(得分:0)

没有必要在mainfest中将CallScreenActivity作为singletask或singletop,只需存储调用参数,如callid(帮助Sinch api识别特定的调用),在sharedpreferences或sqlite db中调用启动时间等,make通知意图如下:

    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,我们就可以通过点击通知来恢复通话。