如何使用视频SDK制作1对1的视频聊天Android Java应用
#java #android #开发人员

介绍

远程交流已成为大流行后交流中最重要的部分。它很有可能在未来也发挥重要作用。当今的移动应用程序通常包括语音或视频通话功能。但这非常复杂,需要大量时间来构建。这是Video SDK进入图片的地方。

视频SDK是一个平台,允许开发人员创建丰富的应用程序内体验,例如嵌入实时视频,语音,实时录制,实时流和实时消息。

Video SDK可用于JavaScriptReactJSReact-NativeIOSAndroidFlutter无缝集成。 Video SDK还提供了一个预先构建的SDK,它有机会仅用10分钟就可以将实时通信整合到您的应用程序中!

让我们使用视频SDK创建一对一的视频通话应用程序。
但是首先,我们需要创建视频SDK帐户并生成令牌。

先决条件

首先,您的开发环境应满足以下要求:

设置项目

步骤1:创建新项目

让我们从创建新项目开始。在Android Studio中,创建一个具有空活动的新项目。
然后,提供一个名称。我们将其命名为Onetoonedemo。

步骤2:集成视频SDK

  • 将存储库添加到settings.gradle文件。
dependencyResolutionManagement{
  repositories {
    // ...
    google()
    mavenCentral()
    maven { url 'https://jitpack.io' }
    maven { url "https://maven.aliyun.com/repository/jcenter" }
  }
}
  • build.gradle文件中添加以下依赖项。
dependencies {
  implementation 'live.videosdk:rtc-android-sdk:0.1.13'

  // library to perform Network call to generate a meeting id
  implementation 'com.amitshekhar.android:android-networking:1.0.2'

  // other app dependencies
  }

如果您的项目已设置了android.useAndroidX = true,则将android.enableJetifier = true设置在gradle.properties文件中以将您的项目迁移到Androidx并避免重复的类别冲突。

步骤3:将权限添加到您的项目中

/app/Manifests/AndroidManifest.xml中,添加</application>之后的以下权限。

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

开始使用代码!

项目的结构
我们将创建两个屏幕。第一个屏幕将是Joining screen,允许用户创建/加入会议,另一个是Meeting screen,它将显示诸如WhatsApp View之类的参与者。

我们的项目结构看起来像这样。

   app
   ├── java
       ├── packagename
            ├── JoinActivity
            ├── MeetingActivity
   ├── res
       ├── layout
           ├── activity_join.xml
           ├── activity_meeting.xml

您必须将JoinActivity设置为启动器活动。

创建加入屏幕

创建一个名为JoinActivity

的新活动

创建UI以加入屏幕

加入屏幕将包括:

  1. 创建按钮 - 创建新的会议。
  2. textfield的会议ID-包含您要加入的会议ID。
  3. 加入按钮 - 与提供的会议加入会议。

/app/res/layout/activity_join.xml文件中,用以下内容替换内容。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".JoinActivity">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/material_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:contentInsetStart="0dp"
        android:background="?attr/colorPrimary"
        app:titleTextColor="@color/white" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical">


        <Button
            android:id="@+id/btnCreateMeeting"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:text="Create Meeting" />

        <TextView
            style="@style/TextAppearance.AppCompat.Headline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="OR" />

        <com.google.android.material.textfield.TextInputLayout          style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginVertical="16dp"
            android:hint="Enter Meeting ID">

            <EditText
                android:id="@+id/etMeetingId"
                android:layout_width="250dp"
                android:layout_height="wrap_content" />
        </com.google.android.material.textfield.TextInputLayout>

        <Button
            android:id="@+id/btnJoinMeeting"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Join Meeting" />
    </LinearLayout>

</LinearLayout>

集成创建会议API

  • 您需要创建一个在JoinActivity中的字段采样,该字段可容纳Video SDK仪表板中生成的令牌。该令牌将用于视频SDK配置以及生成METICID。
public class JoinActivity extends AppCompatActivity {

  //Replace with the token you generated from the VideoSDK Dashboard
  private String sampleToken = ""; 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    //...
  }
}
  • 在JOIN BUTTEN onClick事件上,它将使用令牌和METICID进行Naviagte至MeetingActivity
public class JoinActivity extends AppCompatActivity {

  //Replace with the token you generated from the VideoSDK Dashboard
  private String sampleToken =""; 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_join);

    final Button btnCreate = findViewById(R.id.btnCreateMeeting);
    final Button btnJoin = findViewById(R.id.btnJoinMeeting);
    final EditText etMeetingId = findViewById(R.id.etMeetingId);

    //set title
    Toolbar toolbar = findViewById(R.id.material_toolbar);
    toolbar.setTitle("OneToOneDemo");
    setSupportActionBar(toolbar);

    btnCreate.setOnClickListener(v -> {
      // we will explore this method in the next step
      createMeeting(sampleToken);
    });

    btnJoin.setOnClickListener(v -> {
      Intent intent = new Intent(JoinActivity.this, MeetingActivity.class);
      intent.putExtra("token", sampleToken);
      intent.putExtra("meetingId", etMeetingId.getText().toString());
      startActivity(intent);
    });
  }

  private void createMeeting(String token) {
  }
}
  • 对于创建按钮,在createMeeting方法下,我们将通过呼叫API并通过令牌和生成的MEDICID来生成会议ID。
public class JoinActivity extends AppCompatActivity {
  //...onCreate

  private void createMeeting(String token) {
      // we will make an API call to VideoSDK Server to get a roomId
      AndroidNetworking.post("https://api.videosdk.live/v2/rooms")
        .addHeaders("Authorization", token) //we will pass the token in the Headers
        .build()
        .getAsJSONObject(new JSONObjectRequestListener() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    // response will contain `roomId`
                    final String meetingId = response.getString("roomId");

                    // starting the MeetingActivity with received roomId and our sampleToken
                    Intent intent = new Intent(JoinActivity.this, MeetingActivity.class);
                    intent.putExtra("token", sampleToken);
                    intent.putExtra("meetingId", meetingId);
                    startActivity(intent);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onError(ANError anError) {
                anError.printStackTrace();
                Toast.makeText(JoinActivity.this, anError.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
  }
}
  • 我们的应用程序完全基于音频和视频通勤,这就是为什么我们需要要求运行时RECORD_AUDIOCAMERA。因此,我们将在JoinActivity上实现权限逻辑。
public class JoinActivity extends AppCompatActivity {
  private static final int PERMISSION_REQ_ID = 22;

  private static final String[] REQUESTED_PERMISSIONS = {
    Manifest.permission.RECORD_AUDIO,
    Manifest.permission.CAMERA
  };

  private boolean checkSelfPermission(String permission, int requestCode) {
    if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
      ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode);
      return false;
    }
    return true;
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    //... button listeneres
    checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID);
    checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID);
  }
}

您会遇到Unresolved reference: MeetingActivity错误,但不用担心。创建MeetingActivity后,它将自动解决。

  • 我们现在完成了加入屏幕,现在是时候在会议屏幕上创建参与者的视图了。

步骤4:创建会议屏幕

创建一个名为MeetingActivity的新活动。

为会议屏幕创建UI

/app/res/layout/activity_meeting.xml文件中,用以下内容替换内容。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MeetingActivity">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/material_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/black"
        app:contentInsetStart="0dp"
        app:titleTextColor="@color/white">

        <LinearLayout
            android:id="@+id/meetingLayout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="14dp"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:id="@+id/txtMeetingId"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:fontFamily="sans-serif-medium"
                    android:textColor="@color/white"
                    android:textFontWeight="600"
                    android:textSize="16sp" />

                <ImageButton
                    android:id="@+id/btnCopyContent"
                    android:layout_width="22dp"
                    android:layout_height="22sp"
                    android:layout_marginLeft="7dp"
                    android:layout_toRightOf="@+id/txtMeetingId"
                    android:backgroundTint="@color/black"
                    android:src="@drawable/ic_outline_content_copy_24" />

            </RelativeLayout>

        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:layout_marginEnd="10dp">

            <ImageButton
                android:id="@+id/btnSwitchCameraMode"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:background="@color/black"
                android:contentDescription="Switch Camera mode"
                android:src="@drawable/ic_baseline_flip_camera_android_24" />

        </LinearLayout>

    </com.google.android.material.appbar.MaterialToolbar>

    <FrameLayout
        android:id="@+id/participants_frameLayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/black">

        <androidx.cardview.widget.CardView
            android:id="@+id/ParticipantCard"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="12dp"
            android:layout_marginTop="3dp"
            android:layout_marginRight="12dp"
            android:layout_marginBottom="3dp"
            android:backgroundTint="#2B3034"
            android:visibility="gone"
            app:cardCornerRadius="8dp"
            app:strokeColor="#2B3034">

            <ImageView
                android:layout_width="150dp"
                android:layout_height="150dp"
                android:layout_gravity="center"
                android:src="@drawable/ic_baseline_person_24" />

            <live.videosdk.rtc.android.VideoView
                android:id="@+id/participantView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:visibility="gone" />

        </androidx.cardview.widget.CardView>

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </FrameLayout>

        <androidx.cardview.widget.CardView
            android:id="@+id/LocalCard"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="12dp"
            android:layout_marginTop="3dp"
            android:layout_marginRight="12dp"
            android:layout_marginBottom="3dp"
            android:backgroundTint="#1A1C22"
            app:cardCornerRadius="8dp"
            app:strokeColor="#1A1C22">

            <ImageView
                android:id="@+id/localParticipant_img"
                android:layout_width="150dp"
                android:layout_height="150dp"
                android:layout_gravity="center"
                android:src="@drawable/ic_baseline_person_24" />

            <live.videosdk.rtc.android.VideoView
                android:id="@+id/localView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:visibility="gone" />

        </androidx.cardview.widget.CardView>

    </FrameLayout>

    <!-- add bottombar here-->

</LinearLayout>
<com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bottomAppbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true"
        android:backgroundTint="@color/black"
        android:gravity="center_horizontal"
        android:paddingVertical="5dp"
        tools:ignore="BottomAppBar">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingStart="16dp"
            android:paddingEnd="16dp">

            <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/btnLeave"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:contentDescription="Leave Meeting"
                android:src="@drawable/ic_end_call"
                app:backgroundTint="#FF5D5D"
                app:fabSize="normal"
                app:tint="@color/white" />

            <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/btnMic"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="90dp"
                android:layout_toEndOf="@+id/btnLeave"
                android:contentDescription="Toggle Mic"
                android:src="@drawable/ic_mic_off"
                app:backgroundTint="@color/white"
                app:borderWidth="1dp"
                app:fabSize="normal" />

            <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/btnWebcam"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="90dp"
                android:layout_toEndOf="@+id/btnMic"
                android:backgroundTint="@color/white"
                android:contentDescription="Toggle Camera"
                android:src="@drawable/ic_video_camera_off"
                app:backgroundTint="@color/white"
                app:borderWidth="1dp"
                app:fabSize="normal" />

        </RelativeLayout>

    </com.google.android.material.bottomappbar.BottomAppBar>

复制项目中所需的图标和您项目的res/drawable文件夹中的粘贴。

初始化会议

JoinActivity获得令牌和会议后,我们需要...

  • 初始化视频SDK
  • 配置视频SDK 带有令牌。
  • 用所需的参数(例如meetingIdparticipantNamemicEnabledmicEnabledwebcamEnabled,participantId)初始化会议。
  • 使用meeting.join()方法加入房间。
public class MeetingActivity extends AppCompatActivity {

    private static Meeting meeting;
    private boolean micEnabled = true;
    private boolean webcamEnabled = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_meeting);

        //
        Toolbar toolbar = findViewById(R.id.material_toolbar);
        toolbar.setTitle("");
        setSupportActionBar(toolbar);

        //
        String token = getIntent().getStringExtra("token");
        final String meetingId = getIntent().getStringExtra("meetingId");

        // set participant name
        String localParticipantName = "Alex";

        // Initialize VideoSDK
        VideoSDK.initialize(getApplicationContext());

        // pass the token generated from api server
        VideoSDK.config(token);

        // create a new meeting instance
        meeting = VideoSDK.initMeeting(
                MeetingActivity.this, meetingId, localParticipantName,
                micEnabled, webcamEnabled, null, null
        );

        // join the meeting
        if (meeting != null) meeting.join();

        //
        TextView textMeetingId = findViewById(R.id.txtMeetingId);
        textMeetingId.setText(meetingId);

     // copy meetingId to clipboard
         ((ImageButton) findViewById(R.id.btnCopyContent)).setOnClickListener(v -> copyTextToClipboard(meetingId));

   }

      private void copyTextToClipboard(String text) {
        ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText("Copied text", text);
        clipboard.setPrimaryClip(clip);

        Toast.makeText(MeetingActivity.this, "Copied to clipboard!", Toast.LENGTH_SHORT).show();
    }

}

步骤5:处理当地参与者媒体

我们需要对以下Views进行单击:

  • 麦克风按钮
  • 网络摄像头按钮
  • 开关相机按钮
  • 离开按钮

添加以下实现:

public class MeetingActivity extends AppCompatActivity {

    private FloatingActionButton btnWebcam, btnMic, btnLeave;
    private ImageButton btnSwitchCameraMode;

  @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);

    //
    btnMic = findViewById(R.id.btnMic);
    btnWebcam = findViewById(R.id.btnWebcam);
    btnLeave = findViewById(R.id.btnLeave);
    btnSwitchCameraMode = findViewById(R.id.btnSwitchCameraMode);

    //...

    // actions
    setActionListeners();
  }

  private void setActionListeners() {
        // Toggle mic
        btnMic.setOnClickListener(view -> toggleMic());

        // Toggle webcam
        btnWebcam.setOnClickListener(view -> toggleWebCam());

        // Leave meeting
        btnLeave.setOnClickListener(view -> {
            // this will make the local participant leave the meeting
            meeting.leave();
        });

        // Switch camera
        btnSwitchCameraMode.setOnClickListener(view -> {
            //a participant can change stream from front/rear camera during the meeting.
            meeting.changeWebcam();
        });

   }
}
private void toggleMic() {
        if (micEnabled) {
            // this will mute the local participant's mic
            meeting.muteMic();
        } else {
            // this will unmute the local participant's mic
            meeting.unmuteMic();
        }
        micEnabled = !micEnabled;
        // change mic icon according to micEnable status
        toggleMicIcon();
    }

    @SuppressLint("ResourceType")
    private void toggleMicIcon() {
        if (micEnabled) {
            btnMic.setImageResource(R.drawable.ic_mic_on);
            btnMic.setColorFilter(Color.WHITE);
            Drawable buttonDrawable = btnMic.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.TRANSPARENT);
            btnMic.setBackground(buttonDrawable);

        } else {
            btnMic.setImageResource(R.drawable.ic_mic_off);
            btnMic.setColorFilter(Color.BLACK);
            Drawable buttonDrawable = btnMic.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.WHITE);
            btnMic.setBackground(buttonDrawable);
        }
    }
private void toggleWebCam() {
        if (webcamEnabled) {
            // this will disable the local participant webcam
            meeting.disableWebcam();
        } else {
            // this will enable the local participant webcam
            meeting.enableWebcam();
        }
        webcamEnabled = !webcamEnabled;
        // change webCam icon according to webcamEnabled status
        toggleWebcamIcon();
    }

    @SuppressLint("ResourceType")
    private void toggleWebcamIcon() {
        if (webcamEnabled) {
            btnWebcam.setImageResource(R.drawable.ic_video_camera);
            btnWebcam.setColorFilter(Color.WHITE);
            Drawable buttonDrawable = btnWebcam.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.TRANSPARENT);
            btnWebcam.setBackground(buttonDrawable);

        } else {
            btnWebcam.setImageResource(R.drawable.ic_video_camera_off);
            btnWebcam.setColorFilter(Color.BLACK);
            Drawable buttonDrawable = btnWebcam.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.WHITE);
            btnWebcam.setBackground(buttonDrawable);
        }
    }

设置本地参与者视图

要设置参与者视图,我们必须实现ParticipantEventListener抽象类的所有方法,并使用Participant类的addEventListener()方法将侦听器添加到Participant类中。 ParticipantEventListener类有两种方法:

  • onStreamEnabled-每当参与者启用麦克风/网络摄像头时,此事件将被触发并返回Stream
  • onStreamDisabled-每当参与者在会议中禁用麦克风/网络摄像头时,此事件将被触发并返回Stream
public class MeetingActivity extends AppCompatActivity {

    private VideoView localView;
    private VideoView participantView;
    private CardView localCard, participantCard;
    private ImageView localParticipantImg;

  @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);

    //... 

    localCard = findViewById(R.id.LocalCard);
    participantCard = findViewById(R.id.ParticipantCard);
    localView = findViewById(R.id.localView);
    participantView = findViewById(R.id.localParticipant);
    localParticipantImg = findViewById(R.id.localParticipant_img);

    //...

    // setup local participant view
    setLocalListeners();
  }

  private void setLocalListeners() {
        meeting.getLocalParticipant().addEventListener(new ParticipantEventListener() {
            @Override
            public void onStreamEnabled(Stream stream) {
                if (stream.getKind().equalsIgnoreCase("video")) {
                    VideoTrack track = (VideoTrack) stream.getTrack();
                    localView.setVisibility(View.VISIBLE);
                    localView.addTrack(track);
                    localView.setZOrderMediaOverlay(true);
                    localCard.bringToFront();
                }
            }

            @Override
            public void onStreamDisabled(Stream stream) {
                if (stream.getKind().equalsIgnoreCase("video")) {
                    localView.removeTrack();
                    localView.setVisibility(View.GONE);
                }
            }
        });
    }
}

设置远程参与者视图

private final ParticipantEventListener participantEventListener = new ParticipantEventListener() {
        // trigger when participant enabled mic/webcam
        @Override
        public void onStreamEnabled(Stream stream) {
            if (stream.getKind().equalsIgnoreCase("video")) {
                localView.setZOrderMediaOverlay(true);
                localCard.bringToFront();
                VideoTrack track = (VideoTrack) stream.getTrack();
                participantView.setVisibility(View.VISIBLE);
                participantView.addTrack(track);
            }
        }

        // trigger when participant disabled mic/webcam
        @Override
        public void onStreamDisabled(Stream stream) {
            if (stream.getKind().equalsIgnoreCase("video")) {
                participantView.removeTrack();
                participantView.setVisibility(View.GONE);
            }
        }
    };

处理会议活动并管理参与者的观点

  • 添加MeetingEventListener进行听力活动,例如会议/左和参与者加入/左。
public class MeetingActivity extends AppCompatActivity {
  @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);
    //...

    // handle meeting events
    meeting.addEventListener(meetingEventListener);
  }

  private final MeetingEventListener meetingEventListener = new MeetingEventListener() {
        @Override
        public void onMeetingJoined() {
            // change mic,webCam icon after meeting successfully joined
            toggleMicIcon();
            toggleWebcamIcon();
        }

        @Override
        public void onMeetingLeft() {
            if (!isDestroyed()) {
                Intent intent = new Intent(MeetingActivity.this, JoinActivity.class);
                startActivity(intent);
                finish();
            }
        }

        @Override
        public void onParticipantJoined(Participant participant) {
            // Display local participant as miniView when other participant joined
            changeLocalParticipantView(true);
            Toast.makeText(MeetingActivity.this, participant.getDisplayName() + " joined",
                    Toast.LENGTH_SHORT).show();
            participant.addEventListener(participantEventListener);
        }

        @Override
        public void onParticipantLeft(Participant participant) {
            // Display local participant as largeView when other participant left
            changeLocalParticipantView(false);
            Toast.makeText(MeetingActivity.this, participant.getDisplayName() + " left",
                    Toast.LENGTH_SHORT).show();
        }
    };
}
  • changeLocalParticipantView(isMiniView: Boolean)函数首先检查本地参与者的视频是否显示为小型视图或大视法。
  • 如果会议只有一名参与者(当地参与者),则将本地参与者显示为大浏览。
  • 当另一名参与者(当地参与者除外)加入时,changeLocalParticipantView(true)被称为。结果,本地参与者被显示为量表,而其他参与者则显示为大浏览。
private void changeLocalParticipantView(boolean isMiniView) {
        if(isMiniView)
        {
            // show localCard as miniView
            localCard.setLayoutParams(new CardView.LayoutParams(300, 430, Gravity.RIGHT | Gravity.BOTTOM));
            ViewGroup.MarginLayoutParams cardViewMarginParams = (ViewGroup.MarginLayoutParams) localCard.getLayoutParams();
            cardViewMarginParams.setMargins(30, 0, 60, 40);
            localCard.requestLayout();
            // set height-width of localParticipant_img
            localParticipantImg.setLayoutParams(new FrameLayout.LayoutParams(150, 150, Gravity.CENTER));
            participantCard.setVisibility(View.VISIBLE);
        }else{
            // show localCard as largeView
            localCard.setLayoutParams(new CardView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
            ViewGroup.MarginLayoutParams cardViewMarginParams = (ViewGroup.MarginLayoutParams) localCard.getLayoutParams();
            cardViewMarginParams.setMargins(30, 5, 30, 30);
            localCard.requestLayout();
            // set height-width of localParticipant_img
            localParticipantImg.setLayoutParams(new FrameLayout.LayoutParams(400, 400, Gravity.CENTER));
            participantCard.setVisibility(View.GONE);
        }
    }

破坏一切

当应用程序关闭并且不再使用时,我们需要发布资源。用以下代码覆盖onDestroy

protected void onDestroy() {
        if(meeting !=null)
        {
            meeting.removeAllListeners();
            meeting.getLocalParticipant().removeAllListeners();
            meeting.leave();
            meeting = null;
        }
        if (participantView != null) {
            participantView.setVisibility(View.GONE);
            participantView.releaseSurfaceViewRenderer();
        }

        if (localView != null) {
            localView.setVisibility(View.GONE);
            localView.releaseSurfaceViewRenderer();
        }

        super.onDestroy();
    }

java.lang.IllegalStateException: This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead.
如果您在运行时遇到此错误,请在theme.xml文件中包含这些行。
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>

应用演示

tadaa!我们的应用已经准备好了。容易,不是吗?
在两个不同的设备上安装并运行该应用程序,并确保它们连接到Internet。

此应用仅支持2位参与者,它管理不超过2个参与者。如果您想处理2个以上的参与者,请结帐我们的组呼叫示例here

结论

  • 在此博客中,我们了解了什么是视频SDK,如何从视频SDK dashboard获得访问令牌,以及如何使用视频SDK创建一个一对一的视频呼叫应用程序。
  • 继续创建高级功能,例如屏幕共享,聊天等。
  • 要查看应用程序的完整实现,请查看此GitHub repository
  • 如果您遇到任何问题或有疑问,请加入我们的Discord community

更多的Android资源