How to Add a Host PK Feature in a Live Streaming App | Android Tutorial

shuoxin wangshuoxin wang
4 min read

Many popular live streaming platforms include a “PK” feature (short for “Player Kill” or “versus battle”), where hosts compete to receive gifts from viewers, and the host who gets the most gifts wins. In some cases, a last-second gift from a fan can overturn what seemed like a sure result, providing a thrilling experience for the audience. For the streaming platform, the PK feature typically grants hosts advanced permissions and is a key driver of engagement.

This article will guide you through integrating a PK feature into an Android live streaming app.

We’ll build upon an existing live streaming setup using Tencent RTC’s TUILiveKit component.

Custom Functionality

Custom Battle waiting countdown style

If you need the custom battle waiting countdown style, please refer to the following path for changes:

// File location: tuilivekit/src/main/java/com/trtc/uikit/livekit/view/liveroom/view/anchor/component/livestreaming/battle/

├── BattleCountdownBackView.java    // Battle waiting countdown background style
└── BattleCountdownView.java        // Battle waiting countdown foreground style

Custom Definition Dual Battle Score Style

If you need the custom Definition Dual battle score style, please refer to the following path for changes:

// File location: tuilivekit/src/main/java/com/trtc/uikit/livekit/view/liveroom/view/common/battle/SingleBattleScoreView.java

public class SingleBattleScoreView extends FrameLayout {
    ...
}

Custom Definition Multi Battle Score Style

If you need the custom Definition Multi Battle score style, please refer to the following path for changes:

// File location: tuilivekit/src/main/java/com/trtc/uikit/livekit/view/liveroom/view/common/battle/BattleMemberInfoView.java

public class BattleMemberInfoView extends FrameLayout {
    ...
}

Custom Definition Battle Score Result Style

If you need a Custom Definition Battle Score Result Style, please refer to the following path for changes:

// File location: tuilivekit/src/main/java/com/trtc/uikit/livekit/view/liveroom/view/common/battle/BattleInfoView.java

public class BattleInfoView extends BasicView {
    ...
    private void showBattleResult(int type) {
        // Battle Result Display
    }
}

Key code

Anchor Battle

TUILiveKit Anchor battle feature is mainly based on BattleService. You can obtain the battle management object through store.serviceCenter.battleService and call the relevant battle API functions to implement the battle feature. For example, in the interaction between Anchor A and B, refer to the diagram below for the specific interaction sequence.

Anchor A initiates Battle

Anchor A initiates a battle by calling requestBattle, passing the maximum Battle duration in parameter config, whether the inviter needs to reply with accept/reject, and passing anchor B's userId in parameter userIdList, and passing the battle invitation wait duration in parameter timeout.

// File location: tuilivekit/src/main/java/com/trtc/uikit/livekit/manager/controller/BattleController.java 

public void requestBattle(List<String> roomIdList, int timeout) {
    TUILiveBattleManager.BattleConfig config = new TUILiveBattleManager.BattleConfig();
    config.duration = BattleState.BATTLE_DURATION;
    config.needResponse = mBattleState.mNeedResponse;
    config.extensionInfo = "";
    mLiveService.requestBattle(config, roomIdList, timeout, new TUILiveBattleManager.BattleRequestCallback() {
        @Override
        public void onSuccess(TUILiveBattleManager.BattleInfo battleInfo,
                              Map<String, TUILiveBattleManager.BattleCode> map) {
            mBattleState.mBattleId = battleInfo.battleId;
            mBattleState.mBattleConfig.copy(config);
            List<BattleState.BattleUser> sendRequests = mBattleState.mSentBattleRequests.get();
            for (Map.Entry<String, TUILiveBattleManager.BattleCode> entry : map.entrySet()) {
                String key = entry.getKey();
                TUILiveBattleManager.BattleCode code = entry.getValue();
                if (code == TUILiveBattleManager.BattleCode.SUCCESS) {
                    for (ConnectionState.ConnectionUser user : mConnectionState.connectedUsers.get()) {
                        if (TextUtils.equals(user.userId, key)) {
                            sendRequests.add(new BattleState.BattleUser(user));
                            break;
                        }
                    }
                } else {
                    notifyToast(convertCodeToString(entry.getValue()));
                }
            }
            mBattleState.mSentBattleRequests.set(sendRequests);
        }

        @Override
        public void onError(TUICommonDefine.Error error, String s) {
            ErrorHandler.onError(error);
            mBattleState.mSentBattleRequests.clear();
        }
    });
}

Anchor A can receive the request acceptance callback via onBattleRequestAccept.

Anchor receives a battle request

Anchor B receives the battle request callback via onBattleRequestReceived.

// File location: tuilivekit/src/main/java/com/trtc/uikit/livekit/manager/observer/LiveBattleManagerObserver.java

@Override
public void onBattleRequestReceived(BattleInfo battleInfo, BattleUser inviter, BattleUser invitee) {
    LiveKitLog.info(mTag + " onBattleRequestReceived:[battleInfo:" + new Gson().toJson(battleInfo)
        + ", inviter:" + new Gson().toJson(inviter) + ", invitee:" + new Gson().toJson(invitee) + "]");
    mBattleController.onBattleRequestReceived(battleInfo, inviter);
}

Anchor B accepts the battle request by calling acceptBattle.

// File location: tuilivekit/src/main/java/com/trtc/uikit/livekit/manager/controller/BattleController.java 

public void accept() {
   mLiveService.acceptBattle(mBattleState.mBattleId, new TUIRoomDefine.ActionCallback() {
       @Override
       public void onSuccess() {

       }

       @Override
       public void onError(TUICommonDefine.Error error, String s) {

       }
   });
}

Anchors A, B, and the audience in the room can receive the battle start callback through onBattleStarted.

// File location: tuilivekit/src/main/java/com/trtc/uikit/livekit/manager/observer/LiveBattleManagerObserver.java

@Override
public void onBattleStarted(BattleInfo battleInfo) {
    LiveKitLog.info(mTag + " onBattleStarted:[battleInfo:" + new Gson().toJson(battleInfo) + "]");
    mBattleController.onBattleStarted(battleInfo);
}

The anchor exits the battle

For example, when anchor B exits the battle, the interaction sequence can be referenced from the diagram below.

Anchor B calls exitBattle to exit the battle.

// File location: tuilivekit/src/main/java/com/trtc/uikit/livekit/manager/controller/BattleController.java 

public void exitBattle() {
    mLiveService.exitBattle(mBattleState.mBattleId, new TUIRoomDefine.ActionCallback() {
        @Override
        public void onSuccess() {
            mBattleState.mSentBattleRequests.clear();
            mBattleState.mBattledUsers.clear();
            removeBattleRequestReceived();
        }

        @Override
        public void onError(TUICommonDefine.Error error, String s) {

        }
    });
}

Anchors A, B, and the room audience receive the onBattleEnded callback and the battle end notification.

// File location: tuilivekit/src/main/java/com/trtc/uikit/livekit/manager/observer/LiveBattleManagerObserver.java

@Override
public void onBattleEnded(BattleInfo battleInfo, BattleStoppedReason reason) {
    LiveKitLog.info(mTag + " onBattleEnded:[battleInfo:"
        + new Gson().toJson(battleInfo) + ", reason:" + reason + "]");
    mBattleController.onBattleEnded(battleInfo);
}

Now you can add a host PK feature to your live streaming app! For more details on additional features, please refer to the documentation.

My name is Susie. I am a writer and Media Service Product Manager. I work with startups across the globe to build real-time communication solutions using SDK and APIs.

If you want to build face filter or other special effect into your app, welcome to contact us.

0
Subscribe to my newsletter

Read articles from shuoxin wang directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

shuoxin wang
shuoxin wang