|
| 1 | +package io.agora.api.example.examples.basic_video_audio; |
| 2 | + |
| 3 | +import android.content.Context; |
| 4 | +import android.os.Bundle; |
| 5 | +import android.os.Handler; |
| 6 | +import android.util.Log; |
| 7 | +import android.view.LayoutInflater; |
| 8 | +import android.view.View; |
| 9 | +import android.view.ViewGroup; |
| 10 | +import android.widget.Button; |
| 11 | +import android.widget.EditText; |
| 12 | +import android.widget.Toast; |
| 13 | + |
| 14 | +import androidx.annotation.NonNull; |
| 15 | +import androidx.annotation.Nullable; |
| 16 | + |
| 17 | +import com.yanzhenjie.permission.AndPermission; |
| 18 | +import com.yanzhenjie.permission.runtime.Permission; |
| 19 | + |
| 20 | +import io.agora.api.example.R; |
| 21 | +import io.agora.api.example.annotation.Example; |
| 22 | +import io.agora.api.example.common.BaseFragment; |
| 23 | +import io.agora.rtc.Constants; |
| 24 | +import io.agora.rtc.IRtcEngineEventHandler; |
| 25 | +import io.agora.rtc.RtcEngine; |
| 26 | + |
| 27 | +@Example( |
| 28 | + group = "BASIC VIDEO/AUDIO", |
| 29 | + name = "AudioOnly", |
| 30 | + actionId = R.id.action_mainFragment_to_audioOnly |
| 31 | +) |
| 32 | +public class AudioOnly extends BaseFragment implements View.OnClickListener |
| 33 | +{ |
| 34 | + private Button join, mute, speaker; |
| 35 | + private EditText et_channel; |
| 36 | + |
| 37 | + private Handler handler; |
| 38 | + protected RtcEngine engine; |
| 39 | + protected int myUid; |
| 40 | + |
| 41 | + @Override |
| 42 | + public void onCreate(@Nullable Bundle savedInstanceState) |
| 43 | + { |
| 44 | + super.onCreate(savedInstanceState); |
| 45 | + handler = new Handler(); |
| 46 | + } |
| 47 | + |
| 48 | + @Nullable |
| 49 | + @Override |
| 50 | + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) |
| 51 | + { |
| 52 | + View view = inflater.inflate(R.layout.fragment_audio_only, container, false); |
| 53 | + initView(view); |
| 54 | + return view; |
| 55 | + } |
| 56 | + |
| 57 | + protected void initView(View view) |
| 58 | + { |
| 59 | + join = view.findViewById(R.id.btn_join); |
| 60 | + mute = view.findViewById(R.id.btn_mute); |
| 61 | + speaker = view.findViewById(R.id.btn_speaker); |
| 62 | + et_channel = view.findViewById(R.id.et_channel); |
| 63 | + view.findViewById(R.id.btn_join).setOnClickListener(this); |
| 64 | + } |
| 65 | + |
| 66 | + @Override |
| 67 | + public void onDestroy() |
| 68 | + { |
| 69 | + super.onDestroy(); |
| 70 | + // release the RtcEngine |
| 71 | + engine.leaveChannel(); |
| 72 | + handler.post(RtcEngine::destroy); |
| 73 | + engine = null; |
| 74 | + } |
| 75 | + |
| 76 | + protected void joinChannel(String channelId) |
| 77 | + { |
| 78 | + Context context = getContext(); |
| 79 | + if (context == null) return; |
| 80 | + |
| 81 | + try |
| 82 | + { |
| 83 | + engine = RtcEngine.create(getContext(), getString(R.string.agora_app_id), eventHandler); |
| 84 | + /** Sets the channel profile of the Agora RtcEngine. |
| 85 | + CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile. |
| 86 | + Use this profile in one-on-one calls or group calls, where all users can talk freely. |
| 87 | + CHANNEL_PROFILE_LIVE_BROADCASTING(1): The Live-Broadcast profile. Users in a live-broadcast |
| 88 | + channel have a role as either broadcaster or audience. A broadcaster can both send and receive streams; |
| 89 | + an audience can only receive streams.*/ |
| 90 | + engine.setChannelProfile(Constants.CHANNEL_PROFILE_COMMUNICATION); |
| 91 | + } |
| 92 | + catch (Exception e) |
| 93 | + { |
| 94 | + e.printStackTrace(); |
| 95 | + } |
| 96 | + |
| 97 | + /** Allows a user to join a channel. |
| 98 | + if you do not specify the uid, we will generate the uid for you*/ |
| 99 | + int res = engine.joinChannel(getString(R.string.agora_access_token), channelId, "Extra Optional Data", 0); |
| 100 | + if (res != 0) |
| 101 | + { |
| 102 | + // Usually happens with invalid parameters |
| 103 | + // Error code description can be found at: |
| 104 | + // en: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html |
| 105 | + // cn: https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html |
| 106 | + showAlert(RtcEngine.getErrorDescription(Math.abs(res))); |
| 107 | + return; |
| 108 | + } |
| 109 | + // Prevent repeated entry |
| 110 | + join.setClickable(false); |
| 111 | + } |
| 112 | + |
| 113 | + @Override |
| 114 | + public void onClick(View v) |
| 115 | + { |
| 116 | + if (v.getId() == R.id.btn_join) |
| 117 | + { |
| 118 | + // call when join button hit |
| 119 | + String channelId = et_channel.getText().toString(); |
| 120 | + // Check permission |
| 121 | + if (AndPermission.hasPermissions(this, Permission.Group.STORAGE, Permission.Group.MICROPHONE, Permission.Group.CAMERA)) |
| 122 | + { |
| 123 | + joinChannel(channelId); |
| 124 | + return; |
| 125 | + } |
| 126 | + // Request permission |
| 127 | + AndPermission.with(this).runtime().permission( |
| 128 | + Permission.Group.STORAGE, |
| 129 | + Permission.Group.MICROPHONE, |
| 130 | + Permission.Group.CAMERA |
| 131 | + ).onGranted(permissions -> |
| 132 | + { |
| 133 | + // Permissions Granted |
| 134 | + joinChannel(channelId); |
| 135 | + }).start(); |
| 136 | + } |
| 137 | + else if(v.getId() == R.id.btn_mute) |
| 138 | + { |
| 139 | + mute.setActivated(!mute.isActivated()); |
| 140 | + mute.setText(getString(mute.isActivated() ? R.string.openmicrophone : R.string.closemicrophone)); |
| 141 | + // Turn off / on the microphone, stop / start local audio collection and push streaming. |
| 142 | + engine.muteLocalAudioStream(mute.isActivated()); |
| 143 | + } |
| 144 | + else if(v.getId() == R.id.btn_speaker) |
| 145 | + { |
| 146 | + speaker.setActivated(!speaker.isActivated()); |
| 147 | + speaker.setText(getString(speaker.isActivated() ? R.string.earpiece : R.string.speaker)); |
| 148 | + // Turn off / on the speaker and change the audio playback route. |
| 149 | + engine.setEnableSpeakerphone(speaker.isActivated()); |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | + private IRtcEngineEventHandler eventHandler = new IRtcEngineEventHandler() |
| 154 | + { |
| 155 | + private final String TAG = IRtcEngineEventHandler.class.getSimpleName(); |
| 156 | + |
| 157 | + @Override |
| 158 | + public void onWarning(int warn) |
| 159 | + { |
| 160 | + Log.w(TAG, String.format("onWarning code %d message %s", warn, RtcEngine.getErrorDescription(warn))); |
| 161 | + } |
| 162 | + |
| 163 | + @Override |
| 164 | + public void onError(int err) |
| 165 | + { |
| 166 | + Log.e(TAG, String.format("onError code %d message %s", err, RtcEngine.getErrorDescription(err))); |
| 167 | + } |
| 168 | + |
| 169 | + @Override |
| 170 | + public void onJoinChannelSuccess(String channel, int uid, int elapsed) |
| 171 | + { |
| 172 | + Log.i(TAG, String.format("onJoinChannelSuccess channel %s uid %d", channel, uid)); |
| 173 | + showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel, uid)); |
| 174 | + myUid = uid; |
| 175 | + /** After successfully joining the channel, the user is allowed to switch |
| 176 | + * the audio route and switch the microphone.*/ |
| 177 | + mute.setOnClickListener(AudioOnly.this); |
| 178 | + speaker.setOnClickListener(AudioOnly.this); |
| 179 | + } |
| 180 | + |
| 181 | + /**Since v2.9.0. |
| 182 | + * This callback indicates the state change of the remote audio stream. |
| 183 | + * PS: This callback does not work properly when the number of users (in the Communication profile) or |
| 184 | + * broadcasters (in the Live-broadcast profile) in the channel exceeds 17. |
| 185 | + * @param uid ID of the user whose audio state changes. |
| 186 | + * @param state State of the remote audio |
| 187 | + * REMOTE_AUDIO_STATE_STOPPED(0): The remote audio is in the default state, probably due |
| 188 | + * to REMOTE_AUDIO_REASON_LOCAL_MUTED(3), REMOTE_AUDIO_REASON_REMOTE_MUTED(5), |
| 189 | + * or REMOTE_AUDIO_REASON_REMOTE_OFFLINE(7). |
| 190 | + * REMOTE_AUDIO_STATE_STARTING(1): The first remote audio packet is received. |
| 191 | + * REMOTE_AUDIO_STATE_DECODING(2): The remote audio stream is decoded and plays normally, |
| 192 | + * probably due to REMOTE_AUDIO_REASON_NETWORK_RECOVERY(2), |
| 193 | + * REMOTE_AUDIO_REASON_LOCAL_UNMUTED(4) or REMOTE_AUDIO_REASON_REMOTE_UNMUTED(6). |
| 194 | + * REMOTE_AUDIO_STATE_FROZEN(3): The remote audio is frozen, probably due to |
| 195 | + * REMOTE_AUDIO_REASON_NETWORK_CONGESTION(1). |
| 196 | + * REMOTE_AUDIO_STATE_FAILED(4): The remote audio fails to start, probably due to |
| 197 | + * REMOTE_AUDIO_REASON_INTERNAL(0). |
| 198 | + * @param reason The reason of the remote audio state change. |
| 199 | + * REMOTE_AUDIO_REASON_INTERNAL(0): Internal reasons. |
| 200 | + * REMOTE_AUDIO_REASON_NETWORK_CONGESTION(1): Network congestion. |
| 201 | + * REMOTE_AUDIO_REASON_NETWORK_RECOVERY(2): Network recovery. |
| 202 | + * REMOTE_AUDIO_REASON_LOCAL_MUTED(3): The local user stops receiving the remote audio |
| 203 | + * stream or disables the audio module. |
| 204 | + * REMOTE_AUDIO_REASON_LOCAL_UNMUTED(4): The local user resumes receiving the remote audio |
| 205 | + * stream or enables the audio module. |
| 206 | + * REMOTE_AUDIO_REASON_REMOTE_MUTED(5): The remote user stops sending the audio stream or |
| 207 | + * disables the audio module. |
| 208 | + * REMOTE_AUDIO_REASON_REMOTE_UNMUTED(6): The remote user resumes sending the audio stream |
| 209 | + * or enables the audio module. |
| 210 | + * REMOTE_AUDIO_REASON_REMOTE_OFFLINE(7): The remote user leaves the channel. |
| 211 | + * @param elapsed Time elapsed (ms) from the local user calling the joinChannel method |
| 212 | + * until the SDK triggers this callback.*/ |
| 213 | + @Override |
| 214 | + public void onRemoteAudioStateChanged(int uid, int state, int reason, int elapsed) |
| 215 | + { |
| 216 | + super.onRemoteAudioStateChanged(uid, state, reason, elapsed); |
| 217 | + Log.e(TAG, "onRemoteAudioStateChanged->" + uid + ", state->" + state + ", reason->" + reason); |
| 218 | + } |
| 219 | + |
| 220 | + /**Occurs when a remote user (Communication)/host (Live Broadcast) joins the channel. |
| 221 | + * @param uid ID of the user whose audio state changes. |
| 222 | + * @param elapsed Time delay (ms) from the local user calling joinChannel/setClientRole |
| 223 | + * until this callback is triggered.*/ |
| 224 | + @Override |
| 225 | + public void onUserJoined(int uid, int elapsed) |
| 226 | + { |
| 227 | + super.onUserJoined(uid, elapsed); |
| 228 | + Log.e(TAG, "onUserJoined->" + uid); |
| 229 | + showLongToast(String.format("user %d joined!", uid)); |
| 230 | + } |
| 231 | + |
| 232 | + /**Occurs when a remote user (Communication)/host (Live Broadcast) leaves the channel. |
| 233 | + * @param uid ID of the user whose audio state changes. |
| 234 | + * @param reason Reason why the user goes offline: |
| 235 | + * USER_OFFLINE_QUIT(0): The user left the current channel. |
| 236 | + * USER_OFFLINE_DROPPED(1): The SDK timed out and the user dropped offline because no data |
| 237 | + * packet was received within a certain period of time. If a user quits the |
| 238 | + * call and the message is not passed to the SDK (due to an unreliable channel), |
| 239 | + * the SDK assumes the user dropped offline. |
| 240 | + * USER_OFFLINE_BECOME_AUDIENCE(2): (Live broadcast only.) The client role switched from |
| 241 | + * the host to the audience.*/ |
| 242 | + @Override |
| 243 | + public void onUserOffline(int uid, int reason) |
| 244 | + { |
| 245 | + Log.i(TAG, String.format("user %d offline! reason:%d", uid, reason)); |
| 246 | + showLongToast(String.format("user %d offline! reason:%d", uid, reason)); |
| 247 | + } |
| 248 | + }; |
| 249 | + private final void showLongToast(final String msg) |
| 250 | + { |
| 251 | + if(getActivity() == null) |
| 252 | + {return;} |
| 253 | + getActivity().runOnUiThread(new Runnable() |
| 254 | + { |
| 255 | + @Override |
| 256 | + public void run() |
| 257 | + { |
| 258 | + Toast.makeText(getContext().getApplicationContext(), msg, Toast.LENGTH_LONG).show(); |
| 259 | + } |
| 260 | + }); |
| 261 | + } |
| 262 | +} |
0 commit comments