Android adopts message push to realize similar WeChat video answering function

1. Background requirements: The business needs to access the video review function, initiate a video call on the PC side, display the call interface on the mobile side and click to answer, and then conduct a 1-to-1 video call.

2. Solution: because the project has no IM function. Only Jiguang news push is integrated (Jiguang news push access refers to the official document: https://docs.jiguang.cn//jpush/guideline/intro/ ), after communicating with the demand, the call answering interface is called by using the message push. Then integrate Tencent real-time audio and video SDK (refer to the official document for the specific integration method: https://cloud.tencent.com/document/product/647). Finally, a function similar to WeChat's 1-to-1 call is realized.

3. Technical implementation:
A: Write a broadcast receiver and register it in AndroidManifest, which is a global broadcast receiver. When the application exits to the background or the application process is killed, as long as the push process of Jiguang is Live, it can receive the message and start the call answering interface.

/**
 * Created on 2018/3/29 16:19
 * @author baokang.jia
 * 极光推送广播接收器
 */
public class JiGuangPushReceiver extends BroadcastReceiver {
    private static final String TAG = "JPushReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle bundle = intent.getExtras();
        if (bundle == null) {
            return;
        }
        //拿到锁屏管理者
        KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
        if (km != null && km.isKeyguardLocked()) {   //为true就是锁屏状态下
            startLoginOrCallActivity(context,bundle);
        } else {
            if (JPushInterface.ACTION_REGISTRATION_ID.equals(intent.getAction())) {
                String regId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID);
                LogUtil.d(TAG, "[MyReceiver] 接收Registration Id : " + regId);
                //send the Registration Id to yours server...
            } else if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
                LogUtil.d(TAG, "[MyReceiver] 接收到推送下来的自定义消息: " + bundle.getString(JPushInterface.EXTRA_MESSAGE));
            } else if (JPushInterface.ACTION_NOTIFICATION_RECEIVED.equals(intent.getAction())) {//接收到推送下来的通知
                //启动通话界面
                startLoginOrCallActivity(context, bundle);
            } else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(intent.getAction())) {//点击通知栏
                //启动通话界面
                startLoginOrCallActivity(context, bundle);
                //清除所有状态的通知
                JPushInterface.clearAllNotifications(context);
            } else if (JPushInterface.ACTION_RICHPUSH_CALLBACK.equals(intent.getAction())) {
                LogUtil.d(TAG, "[MyReceiver] 用户收到到RICH PUSH CALLBACK: " + bundle.getString(JPushInterface.EXTRA_EXTRA));
                //在这里根据 JPushInterface.EXTRA_EXTRA 的内容处理代码,比如打开新的Activity, 打开一个网页等..
            }
        }
    }

    /**
     * 未登录跳转登录界面,
     * else 启动通话接听界面
     */
    private void startLoginOrCallActivity(Context context, Bundle bundle) {
        //EXTRA_EXTRA
        String extras = bundle.getString(JPushInterface.EXTRA_EXTRA);
        String userID = SPUtil.getString(context, Constants.LOGIN_USER_ID);
        if (TextUtils.isEmpty(userID)) {
            //启动登录界面
            Intent intent = new Intent(context, LoginActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        } else {
            //启动通话接听界面
            int notifyId = bundle.getInt(JPushInterface.EXTRA_NOTIFICATION_ID);
            if (!TextUtils.isEmpty(extras) && extras.contains("androidNotification extras key")){
                ReceiveTalkActivity.startReceiveTalkActivity(context, extras,notifyId);
            }
        }
    }
 }
//在AndroidManifest注册全局自定义广播接收器
<receiver
            android:name=".event.JiGuangP`在这里插入代码片`ushReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="cn.jpush.android.intent.REGISTRATION" /> <!-- Required  用户注册SDK的intent -->
                <action android:name="cn.jpush.android.intent.MESSAGE_RECEIVED" /> <!-- Required  用户接收SDK消息的intent -->
                <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED" /> <!-- Required  用户接收SDK通知栏信息的intent -->
                <action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED" /> <!-- Required  用户打开自定义通知栏的intent -->
                <action android:name="cn.jpush.android.intent.CONNECTION" /> <!-- 接收网络变化 连接/断开 since 1.6.3 -->
                <category android:name="${PACKAGE_NAME}" />
            </intent-filter>
        </receiver>

B: Start the call answering interface, and get the current mobile phone mode after starting the answering interface

AudioManager audio = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
//手机模式,振动,精英、响铃,更具不同模式振动或者响铃,具体可参考以下的实现代码。
//点击接听按钮后跳转腾讯视频通话界面
/**
 * Created on 2019/4/28 16:19
 * @author baokang.jia
 * 视频预审接听界面
 */
public class ReceiveTalkActivity extends BaseActivity {

    private static String PUSH_MSG_KEY = "push_msg_key";

    private static String NOTIFICATION_ID_KEY = "notification_id_key";

    /**
     * 腾讯云注册分配的appId
     */
    private int sdkAppId =

    /**
     * 检查运行时权限
     */
    private boolean mCheckPermissionResult = false;

    private PushMsgBean mPushMsgBean;
    /**
     * 媒体播放
     */
    private MediaPlayer mMediaPlayer;

    /**
     * 震动
     */
    private Vibrator mVibrator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Window window = getWindow();
        //悬浮窗
        WindowViewUtil.setWindowFloatAndScreenOn(window,this);
        super.onCreate(savedInstanceState);

        //初始化倒计时器
        initCountDownTimer();
        //请求权限
        requestMustPermission();
        initViews();
        //根据通知Id清除状态栏对应的通知
        JPushInterface.clearAllNotifications(this);
        //持续震动和响铃
        continuedVibratorAndMediaPlayer();
    }

    /**
     * 60秒后关闭activity
     */
    private void initCountDownTimer() {
        long time = 30000;
        long countDownInterval = 1000;
        CountDownTimer downTimer = new CountDownTimer(time, countDownInterval) {
            @Override
            public void onTick(long millisUntilFinished) {
            }

            @Override
            public void onFinish() {
                finish();
            }
        };
        downTimer.start();
    }

    @Override
    protected int getLayoutId() {
        return R.layout.activity_receive_talk;
    }

    @Override
    protected boolean initToolbar() {
        return false;
    }

    @Override
    protected void getIntent(Intent intent) {
        String pushMsg = getIntent().getStringExtra(PUSH_MSG_KEY);
        //notificationId = getIntent().getIntExtra(NOTIFICATION_ID_KEY, 0);
        parsePushMsg(pushMsg);
    }

    @Override
    protected void initViews() {
        Button btnCancel = findViewById(R.id.btn_cancel_call);
        Button btnAnswer = findViewById(R.id.btn_answer_call);

        btnCancel.setOnClickListener(v ->{
            mVibrator.cancel();
            mMediaPlayer.stop();
            finish();
        });

        btnAnswer.setOnClickListener(v -> {
            mVibrator.cancel();
            mMediaPlayer.stop();
            if (mCheckPermissionResult) {
                Intent intent = new Intent(this, TRTCMainActivity.class);
                intent.putExtra("roomId", Integer.valueOf(mPushMsgBean.getRoomId()));
                intent.putExtra("userId", mPushMsgBean.getUserId());
                intent.putExtra("sdkAppId", sdkAppId);
                intent.putExtra("userSig", mPushMsgBean.getUserSig());
                startActivity(intent);
                finish();
            } else {
                ToastUtil.longToast("需要的权限被拒绝,无法开启视频审核");
            }
        });
    }

    /**
     * 持续响铃和震动
     */
    private void continuedVibratorAndMediaPlayer() {

        //获取媒体播放器
        mMediaPlayer = new MediaPlayer();
        try {
            mMediaPlayer.setDataSource(this, RingtoneManager
                    .getDefaultUri(RingtoneManager.TYPE_RINGTONE));//这里我用的通知声音,还有其他的,大家可以点进去看
            mMediaPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //取得震动服务的句柄
        mVibrator = (Vibrator)this. getSystemService(VIBRATOR_SERVICE);

        //获取当前手机模式
        AudioManager audio = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        if (audio != null) {
            switch (audio.getRingerMode()) {
                case AudioManager.RINGER_MODE_SILENT://静音
                    //do sth
                    break;
                case AudioManager.RINGER_MODE_NORMAL://响铃
                    mMediaPlayer.start();
                    mMediaPlayer.setLooping(true); //循环播放
                    break;
                case AudioManager.RINGER_MODE_VIBRATE://震动
                    //数组参数意义:第一个参数为等待指定时间后开始震动,
                    //震动时间为第二个参数。后边的参数依次为等待震动和震动的时间
                    //第二个参数为重复次数,-1为不重复,0为一直震动
                    if (mVibrator != null) {
                        mVibrator.vibrate( new long[]{1000,1000},0);
                    }
                    break;
            }
        }
    }

    private void parsePushMsg(String pushMsg) {
        if (!TextUtils.isEmpty(pushMsg)) {
            CustomerMsg customerMsg = GsonUtil.fromJson(pushMsg, CustomerMsg.class);
            String pushMsgContent = customerMsg.getPushMsgContent();
            pushMsgContent = pushMsgContent.replace("\\", "");
            LogUtil.d(Constants.LOG,"pushMsgContent="+pushMsgContent);
            mPushMsgBean = GsonUtil.fromJson(pushMsgContent, PushMsgBean.class);
        }
    }

    /**
     * 申请应用必须的权限
     */
    private void requestMustPermission() {
        AndPermission.with(this)
                .requestCode(Constants.REQUEST_CODE_PERMISSION)
                .permission(
                        Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.CAMERA,
                        Manifest.permission.RECORD_AUDIO,
                        Manifest.permission.READ_EXTERNAL_STORAGE,
                        Manifest.permission.VIBRATE,
                        Manifest.permission.DISABLE_KEYGUARD,
                        Manifest.permission.WAKE_LOCK
                )
                .rationale((requestCode, rationale) ->
                        //再次申请被拒绝的权限
                        AlertDialog.newBuilder(this)
                                .setTitle(R.string.title_dialog)
                                .setMessage(R.string.message_permission_failed)
                                .setPositiveButton(R.string.ok, (dialog, which) -> {
                                    dialog.cancel();
                                    rationale.resume();
                                })
                                .setNegativeButton(R.string.no, (dialog, which) -> {
                                    dialog.cancel();
                                    rationale.cancel();
                                }).show())
                .callback(new PermissionListener() {
                    @Override
                    public void onSucceed(int requestCode, @NonNull List<String> grantPermissions) {
                        mCheckPermissionResult = true;
                    }

                    @Override
                    public void onFailed(int requestCode, @NonNull List<String> deniedPermissions) {
                        mCheckPermissionResult = false;
                    }
                })
                .start();
    }

    /**
     * 界面未销毁,启动此界面时回调
     */
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        String pushMsg = intent.getStringExtra(PUSH_MSG_KEY);
        //notificationId = intent.getIntExtra(NOTIFICATION_ID_KEY, 0);
        parsePushMsg(pushMsg);
    }

    /**
     * 提供给外部调用启动接听界面的activity
     *
     * @param cex      上下文对象
     * @param pushMsg  消息内容
     * @param notifyId 通知id
     */
    public static void startReceiveTalkActivity(Context cex, String pushMsg, int notifyId) {
        Intent calIntent = new Intent(cex, ReceiveTalkActivity.class);
        //携带数据
        calIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        calIntent.putExtra(PUSH_MSG_KEY, pushMsg);
        calIntent.putExtra(NOTIFICATION_ID_KEY, notifyId);
        cex.startActivity(calIntent);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMediaPlayer.stop();
        mVibrator.cancel();
    }
}

//注册ReceiveTalkActivity, android:launchMode="singleTask"
<activity android:name=".trtc.view.ReceiveTalkActivity"
            android:launchMode="singleTask"
            android:screenOrientation="portrait"
            />

Summary: Time and cost issues are considered in the project. There is no access to the IM function. The message push is unreliable, and the push process of Jiguang is killed, so the message cannot be received. When the app is opened, a lot of notifications will pop up. This is just a simple implementation of calling the mobile terminal on the PC to make a video call. There are many factors that have not been taken into account, so record it here first.

Guess you like

Origin blog.csdn.net/jiabaokang/article/details/91371123