Android/APIExample-Audio/.agent/skills/upsert-case/SKILL.md
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.
npx skillsauth add agoraio/api-examples upsert-caseInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Touch exactly 4 files (all paths relative to app/src/main/):
| File | What to add |
|---|---|
| java/.../examples/{basic\|advanced\|audio}/YourCaseName.java | Fragment class |
| res/layout/fragment_your_case_name.xml | XML layout |
| res/values/strings.xml | 2 strings |
| res/navigation/nav_graph.xml | 1 action + 1 destination |
Registration is automatic via reflection — no other files needed.
voice-sdk constraint: Do NOT call enableVideo(), setupLocalVideo(), VideoCanvas, or any video API — the module does not exist and will crash at runtime.
Before writing a single line, ask:
query-cases skill first; a collision causes silent ordering bugs at runtimeRECORD_AUDIO? — most audio cases only need RECORD_AUDIO; check if the API requires anything elseMANDATORY — READ ENTIRE FILE before writing any code:
references/fragment-template.java
Do NOT skip — the setParameters, handler.post, getPrivateCloudConfig() null-check, AudioSeatManager wiring, and voice-sdk constraints are only fully shown there and are required in every case.
Do NOT load any other reference files for this task.
Non-obvious points the template highlights:
setParameters(...) for app scenario reporting — required in every case, do not removehandler.post(RtcEngine::destroy) — NOT RtcEngine.destroy() directly; direct call blocks UI thread (ANR)getPrivateCloudConfig() null-check before setLocalAccessPoint() — returns null on non-private-cloud builds (NPE)IRtcEngineEventHandler callbacks run on a background thread — always runOnUIThread() for UIonActivityCreated → create engine; onDestroy → leaveChannel() then handler.post(RtcEngine::destroy)ChannelMediaOptions must NOT set publishCameraTrack or autoSubscribeVideo — voice-sdk has no video moduleAudioSeatManager (not VideoReportLayout) to visualize remote participantsTypical audio layout — channel input + join button + audio controls:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!-- audio status / waveform view goes here -->
<LinearLayout
android:id="@+id/ll_join"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="center_vertical"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/et_channel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:digits="@string/chanel_support_char"
android:hint="@string/channel_id" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_join"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/join" />
</LinearLayout>
</RelativeLayout>
For waveform visualization, copy the WaveformView pattern from fragment_join_channel_audio.xml.
File: res/navigation/nav_graph.xml
Action — inside <fragment android:id="@+id/Ready"> (NOT mainFragment — mainFragment only has one action, to Ready):
<action
android:id="@+id/action_mainFragment_to_yourCaseName"
app:destination="@id/yourCaseName" />
Destination — at root <navigation> level:
<fragment
android:id="@+id/yourCaseName"
android:name="io.agora.api.example.examples.advanced.YourCaseName"
android:label="@string/item_your_case_name"
tools:layout="@layout/fragment_your_case_name" />
action android:id must exactly match actionId in @Example.
Add one line to the case list in ARCHITECTURE.md under the correct directory section (basic/, advanced/, or audio/):
├── YourCaseName.java # [index] "Display Name" — key API description
Keep the format consistent with existing entries. This file is the fast-lookup index used by query-cases — keeping it current avoids full directory scans.
When modifying an existing case rather than creating a new one, identify which files need changes based on what you are updating:
| What changed | Files to touch |
|---|---|
| Implementation logic (API calls, event handling) | java/.../examples/{basic\|advanced\|audio}/CaseName.java |
| UI layout (views, controls) | res/layout/fragment_case_name.xml |
| Display name or tips text | res/values/strings.xml |
| Sort index or group (BASIC ↔ ADVANCED) | @Example annotation in the Fragment class |
| Navigation label | res/navigation/nav_graph.xml (fragment label attribute) |
| Class rename or package move | Fragment class, nav_graph.xml (android:name + destination id), @Example annotation (actionId), layout file name, ARCHITECTURE.md |
After making changes:
@Example annotation consistency — ensure index, group, name, actionId, and tipsId still match the actual string resources, nav action ID, and intended group/position. A mismatch causes the case to silently disappear from the list or navigate to the wrong screen.res/values/strings.xml if the display name or tips text changed.res/navigation/nav_graph.xml if the class name, package, or label changed.ARCHITECTURE.md — update the Directory Layout entry and the Case Index table row to reflect any changes to the case name, path, Key APIs, or description../gradlew assembleDebug
onJoinChannelSuccess fires in LogcatRtcEngine.destroy within ~2 seconds — if missing, there is a lifecycle bug in onDestroyARCHITECTURE.md Case Index table is updated — row added (new case) or row updated (modified case) with correct Case, Path, Key APIs, and Description@Example annotation fields (index, group, name, actionId, tipsId) are consistent with string resources and nav_graph entriesIf the case meets any of the following criteria, create a Spec rather than using this skill directly:
If none apply → use this skill directly; no Spec needed.
RECORD_AUDIO)APIExample-Audio@Example annotation parameters, nav_graph.xml action + destination, strings.xml key names (item_ prefix)ARCHITECTURE.md or use the query-cases skill to check existing indicesenableVideo, setupLocalVideo, setupRemoteVideo, VideoCanvas, startScreenCapture) — violations must be eliminated at design timeupsert-case skill, and provide skill input parametersenableVideo, setupLocalVideo, VideoCanvas) — voice-sdk has no video module; crash is immediate.<fragment id="mainFragment"> — it belongs in <fragment id="Ready">. mainFragment only routes to Ready; all case actions live in Ready. Wrong placement causes silent navigation failure at runtime.RtcEngine.destroy() directly on the main thread — always handler.post(RtcEngine::destroy). Direct call blocks the UI thread and causes ANR.setLocalAccessPoint() without null-checking getPrivateCloudConfig() first — it returns null on standard builds, causing NPE.IRtcEngineEventHandler callbacks — they run on a background thread. Always wrap with runOnUIThread().setParameters(...) — it's required for Agora backend usage reporting in every case; omitting it causes silent reporting failure even though the app appears to work normally.development
Add a new API example or modify an existing one. Covers both creation and modification scenarios, including dialog class structure, message map registration, and ARCHITECTURE.md updates.
development
Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
development
Add a new API example or modify an existing one. Covers both creation and modification scenarios, including file structure, registration, and ARCHITECTURE.md updates.
development
Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.