com um clique
review-case
// Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
// Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
Add a new API example or modify an existing one. Covers both creation and modification scenarios, including file structure, per-example storyboard creation, registration, and ARCHITECTURE.md updates.
Add a new API example or modify an existing one. Covers both creation and modification scenarios, including dialog class structure, registration in APIExampleDlg, localization wiring, and ARCHITECTURE.md updates.
Add a new API example case or modify an existing one in the APIExample Android demo — creates or updates Fragment class, XML layout, string resources, and nav_graph registration. Use when: adding a new Agora RTC API demo screen, modifying an existing case's implementation or registration, implementing a new feature example in Java + XML layouts, registering a new case via @Example annotation, subclassing BaseFragment for a new demo screen, or updating an existing case's strings, layout, or nav entry. Keywords: add case, modify case, update case, new fragment, nav_graph, @Example, BaseFragment, APIExample, new screen, demo case, RTC API example.
Add a new audio API example case or modify an existing one in the APIExample-Audio Android demo — creates or updates Fragment class, XML layout, string resources, and nav_graph registration. Use when: adding a new Agora audio API demo screen, modifying an existing case's implementation or registration, implementing a new audio feature example in Java + XML layouts, registering a new case via @Example annotation, subclassing BaseFragment for a new audio demo screen, or updating an existing case's strings, layout, or nav entry. This project uses voice-sdk — no video APIs available. Keywords: add case, modify case, update case, new fragment, nav_graph, @Example, BaseFragment, APIExample-Audio, audio case, voice-sdk, new screen, audio demo, upsert case.
Add a new API example case or modify an existing one in the APIExample-Compose Android demo — creates or updates a Kotlin Composable file, registers or updates it in Examples.kt, and manages string resources. Use when: adding a new Agora RTC API demo screen in Jetpack Compose, modifying an existing case's implementation or registration, porting an existing APIExample case to Compose, implementing a new feature example in Kotlin + Compose UI, registering a new entry in BasicExampleList or AdvanceExampleList, or updating an existing case's strings or Examples.kt entry. Kotlin only — no XML layouts, no Fragments. Keywords: add case, modify case, update case, new composable, Examples.kt, BasicExampleList, AdvanceExampleList, APIExample-Compose, Compose case, new screen, Jetpack Compose, RTC API example, upsert case.
| name | review-case |
| description | Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly. |
| compatibility | ["Cursor","Kiro","Windsurf","Claude","Copilot"] |
| license | MIT |
| metadata | {"author":"APIExample Team","version":"1.0.0","platform":"Windows"} |
Use this skill when you need to:
Check:
InitializeAgoraEngine() or similarRtcEngineContextleaveChannel() is called before release()release() is called in the case's real cleanup pathCorrect Pattern A — standalone dialog teardown:
BOOL CExampleDlg::OnInitDialog() {
CDialogEx::OnInitDialog();
InitializeAgoraEngine(); // Create once
return TRUE;
}
void CExampleDlg::PostNcDestroy() {
LeaveChannel();
if (m_rtcEngine) {
m_rtcEngine->release();
m_rtcEngine = nullptr;
}
CDialogEx::PostNcDestroy();
delete this;
}
void CExampleDlg::JoinChannel() {
if (!m_rtcEngine) return;
m_rtcEngine->joinChannel(token, channelName, "", 0);
}
void CExampleDlg::LeaveChannel() {
if (!m_rtcEngine) return;
m_rtcEngine->leaveChannel();
}
Correct Pattern B — scene-switching dialog teardown:
bool CExampleDlg::InitAgora() {
m_rtcEngine = createAgoraRtcEngine();
// initialize once when the scene becomes active
return m_rtcEngine != nullptr;
}
void CExampleDlg::UnInitAgora() {
if (!m_rtcEngine) return;
if (m_joinChannel) {
m_rtcEngine->leaveChannel();
}
m_rtcEngine->release(nullptr);
m_rtcEngine = nullptr;
}
void CExampleDlg::OnShowWindow(BOOL bShow, UINT nStatus) {
CDialogEx::OnShowWindow(bShow, nStatus);
if (!bShow) {
UnInitAgora();
}
}
Accept either pattern as long as the dialog follows one lifecycle consistently and does not leak the engine across scene switches.
Incorrect Pattern:
See references/incorrect-lifecycle.cpp for common mistakes.
Check:
PostMessage()Correct Pattern:
// Event handler (may be called from background thread)
void CExampleRtcEngineEventHandler::onJoinChannelSuccess(const char* channel, uid_t uid, int elapsed) {
if (m_hMsgHandler) {
// Post message to main thread
::PostMessage(m_hMsgHandler, WM_MSGID(EID_JOIN_CHANNEL_SUCCESS), (WPARAM)uid, 0);
}
}
// Message handler (runs on main thread)
LRESULT CExampleDlg::OnMsgEngineEvent(WPARAM wParam, LPARAM lParam) {
// Safe to update UI here
m_statusText.SetWindowText(_T("Joined channel"));
return 0;
}
Incorrect Pattern:
See references/incorrect-thread-safety.cpp for common mistakes.
Check:
enableAudio()enableVideo()Correct Pattern:
void CExampleDlg::InitializeAgoraEngine() {
m_rtcEngine = createAgoraRtcEngine();
if (!m_rtcEngine) return;
RtcEngineContext context;
context.appId = CConfig::GetAppId();
context.eventHandler = &m_eventHandler;
m_eventHandler.SetMsgReceiver(m_hWnd);
m_rtcEngine->initialize(context);
// Check device availability
if (m_rtcEngine->enableVideo() == 0) {
// Video enabled successfully
}
if (m_rtcEngine->enableAudio() == 0) {
// Audio enabled successfully
}
}
Check:
joinChannel() return value checkedonError() callback implementedCorrect Pattern:
void CExampleDlg::JoinChannel() {
if (!m_rtcEngine) return;
const char* token = CConfig::GetToken("test");
int ret = m_rtcEngine->joinChannel(token, "test", "", 0);
if (ret != 0) {
// Handle error
MessageBox(_T("Failed to join channel"), _T("Error"));
}
}
void CExampleRtcEngineEventHandler::onError(int err) {
if (m_hMsgHandler) {
::PostMessage(m_hMsgHandler, WM_MSGID(EID_ERROR), (WPARAM)err, 0);
}
}
LRESULT CExampleDlg::OnMsgEngineEvent(WPARAM wParam, LPARAM lParam) {
if (wParam == EID_ERROR) {
int errorCode = (int)lParam;
// Handle error
}
return 0;
}
Check:
C<ExampleName>DlgC<ExampleName>RtcEngineEventHandlerm_ prefixCorrect Pattern:
// Header: CScreenShareDlg.h
class CScreenShareRtcEngineEventHandler : public IRtcEngineEventHandler {
// ...
};
class CScreenShareDlg : public CDialogEx {
DECLARE_DYNAMIC(CScreenShareDlg)
private:
IRtcEngine* m_rtcEngine = nullptr;
CScreenShareRtcEngineEventHandler m_eventHandler;
uid_t m_remoteUid = 0;
bool m_isJoined = false;
BEGIN_MESSAGE_MAP(CScreenShareDlg, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON_JOIN, &CScreenShareDlg::OnBnClickedButtonJoin)
END_MESSAGE_MAP()
};
Check:
Correct Pattern:
// Correct order: create -> initialize -> enable -> join
m_rtcEngine = createAgoraRtcEngine();
m_rtcEngine->initialize(context);
m_rtcEngine->enableVideo();
m_rtcEngine->enableAudio();
m_rtcEngine->joinChannel(token, channelName, "", 0);
Incorrect Pattern:
// ❌ Wrong order
m_rtcEngine->joinChannel(...); // Join first
m_rtcEngine->enableVideo(); // Enable after join (too late)
Check:
Correct Pattern:
void CExampleDlg::LeaveChannel() {
if (!m_rtcEngine) return;
m_rtcEngine->stopAudioMixing(); // Stop audio
m_rtcEngine->stopScreenCapture(); // Stop screen share
m_rtcEngine->leaveChannel();
m_isJoined = false;
}
void CExampleDlg::PostNcDestroy() {
LeaveChannel();
if (m_rtcEngine) {
m_rtcEngine->release();
m_rtcEngine = nullptr;
}
CDialogEx::PostNcDestroy();
delete this;
}
When reviewing, provide feedback in this format:
## Review Results
### ✅ Passed
- Engine lifecycle correctly managed
- Thread safety ensured with message map pattern
- Error handling implemented for joinChannel
### ⚠️ Issues Found
**[HIGH] Thread Safety Issue**
- File: `CScreenShareDlg.cpp`
- Line: 45
- Issue: Direct UI update in event handler without PostMessage
- Suggestion: Use PostMessage to post event to main thread
**[MEDIUM] Missing Error Handling**
- File: `CScreenShareDlg.cpp`
- Line: 78
- Issue: joinChannel() return value not checked
- Suggestion: Check return value and handle errors
### 🔧 Recommendations
- Add logging for debugging
- Consider adding retry logic for network failures
Check:
Correct Pattern:
// Windows: Use MFC
#include "stdafx.h"
#include "APIExample.h"
class CExampleDlg : public CDialogEx {
DECLARE_DYNAMIC(CExampleDlg)
BEGIN_MESSAGE_MAP(CExampleDlg, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON_JOIN, &CExampleDlg::OnBnClickedButtonJoin)
END_MESSAGE_MAP()
};
Incorrect Pattern:
// ❌ Non-MFC patterns
using namespace std; // Avoid in MFC
auto ptr = std::make_unique<IRtcEngine>(); // Modern C++ not typical in MFC
Do NOT accept:
leaveChannel() before release()APIExample/APIExample/[Basic|Advanced]/ structureUse this checklist when reviewing an example:
Lifecycle:
leaveChannel() called before release()release() called in PostNcDestroy()Thread Safety:
PostMessage()Permissions:
Error Handling:
Code Quality:
API Usage:
Resources:
Platform:
Cause: release() called without leaveChannel() first
Fix: Always call leaveChannel() before release()
Cause: Direct UI update from event handler Fix: Use PostMessage to post event to main thread
Cause: release() not called or engine recreated
Fix: Ensure release() in PostNcDestroy() and create engine once
Cause: No token refresh handling Fix: Implement token refresh in error handler
Cause: Device not available or not enabled
Fix: Check return values of enableAudio() / enableVideo()
APIExample/APIExample/Basic/JoinChannelVideoByToken/ for reference