diff --git a/SampleCode/Android/CHANGELOG.md b/SampleCode/Android/CHANGELOG.md index 64cbf9d5..8dd83a17 100644 --- a/SampleCode/Android/CHANGELOG.md +++ b/SampleCode/Android/CHANGELOG.md @@ -1,5 +1,33 @@ # NEMeetingKit ChangeLog +## v3.17.0 (Oct 31, 2023) + +### New Features + +- 新增屏幕共享服务接口 `NEScreenSharingService` + - 开启屏幕共享 `startScreenShare` + - 停止屏幕共享 `stopScreenShare` + - 添加监听 `addScreenSharingStatusListener` + - 移除监听 `removeScreenSharingStatusListener` + - 屏幕状态变更回调 `onScreenSharingStatusChanged` +- 新增屏幕共享状态变更模型 `NEScreenSharingEvent` + - 当前共享状态 `NEScreenSharingStatus` + - 额外附带参数 `arg` + - 额外附带数据对象 `obj` +- 新增屏幕共享时配置信息类 `NEScreenSharingOptions` + - 开启/关闭音频功能 `enableAudioShare` +- 新增屏幕共享时基本参数类 `NEScreenSharingParams` + - 用户昵称 `displayName` + - 共享码 `sharingCode` +- `NEMeetingKit` 新增获取共享屏幕服务接口 `getScreenSharingService` + +### Compatibility + +* 兼容 `NERoom` 1.21.0 版本 +* 兼容 `NIM` 9.12.0 版本 +* 兼容 `NERtcSDK_Special` 5.5.203 版本 + ## v3.16.1(SEP 8, 2023) + - 适配Android 版本低于8.0无法使用画中画功能,默认以最小化 ## v3.16.0(SEP 6, 2023) diff --git a/SampleCode/Android/README.md b/SampleCode/Android/README.md index 9070fa4c..b0160370 100644 --- a/SampleCode/Android/README.md +++ b/SampleCode/Android/README.md @@ -16,7 +16,7 @@ * Kotlin ``` dependencies { - val meetingkit_version = "3.16.0" + val meetingkit_version = "3.17.0" implementation("com.netease.yunxin.kit.meeting:meeting:$meetingkit_version") } ``` @@ -24,7 +24,7 @@ dependencies { * Groovy ``` dependencies { - def meetingkit_version = "3.16.0" + def meetingkit_version = "3.17.0" implementation "com.netease.yunxin.kit.meeting:meeting:$meetingkit_version" } ``` \ No newline at end of file diff --git a/SampleCode/Android/app/build.gradle b/SampleCode/Android/app/build.gradle index d762863c..72f8191b 100644 --- a/SampleCode/Android/app/build.gradle +++ b/SampleCode/Android/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId "com.netease.yunxin.kit.meeting.sampleapp" minSdkVersion 21 targetSdkVersion 31 - versionCode 31600 - versionName "3.16.0" + versionCode 31700 + versionName "3.17.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -43,7 +43,7 @@ repositories { } dependencies { // meeting - implementation 'com.netease.yunxin.kit.meeting:meeting:3.16.1' + implementation 'com.netease.yunxin.kit.meeting:meeting:3.17.0' implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3' diff --git a/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/data/MeetingDataRepository.java b/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/data/MeetingDataRepository.java index a1056312..fdf5d65f 100644 --- a/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/data/MeetingDataRepository.java +++ b/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/data/MeetingDataRepository.java @@ -5,6 +5,7 @@ package com.netease.yunxin.kit.meeting.sampleapp.data; import android.content.Context; +import android.content.Intent; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.netease.yunxin.kit.meeting.sdk.NECallback; @@ -19,6 +20,9 @@ import com.netease.yunxin.kit.meeting.sdk.NEMeetingStatus; import com.netease.yunxin.kit.meeting.sdk.NEMeetingStatusListener; import com.netease.yunxin.kit.meeting.sdk.NEScheduleMeetingStatusListener; +import com.netease.yunxin.kit.meeting.sdk.NEScreenSharingOptions; +import com.netease.yunxin.kit.meeting.sdk.NEScreenSharingParams; +import com.netease.yunxin.kit.meeting.sdk.NEScreenSharingStatusListener; import com.netease.yunxin.kit.meeting.sdk.NEStartMeetingOptions; import com.netease.yunxin.kit.meeting.sdk.NEStartMeetingParams; import java.util.List; @@ -176,6 +180,25 @@ public void setBeautyFaceValue(int beautyFaceValue) { // NEMeetingKit.getInstance().getSettingsService().setBeautyFaceValue(beautyFaceValue); } + public void startScreenSharing( + Context context, + Intent data, + @NonNull NEScreenSharingParams param, + @Nullable NEScreenSharingOptions opts, + NECallback callback) { + NEMeetingKit.getInstance() + .getScreenSharingService() + .startScreenShare(context, data, param, opts, callback); + } + + public void stopScreenShare(Context context, NECallback callback) { + NEMeetingKit.getInstance().getScreenSharingService().stopScreenShare(callback); + } + + public void addScreenSharingStatusListener(NEScreenSharingStatusListener listener) { + NEMeetingKit.getInstance().getScreenSharingService().addScreenSharingStatusListener(listener); + } + ///////////////////////////////////////////////// /** * SettingsService end * */ ///////////////////////////////////////////////// diff --git a/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/view/HomeFragment.java b/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/view/HomeFragment.java index 8006c732..c6ab6dd3 100644 --- a/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/view/HomeFragment.java +++ b/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/view/HomeFragment.java @@ -117,6 +117,10 @@ private void initListener() { v -> Navigation.findNavController(getView()) .navigate(R.id.action_homeFragment_to_scheduleMeetingFragment)); + binding.btnScreenSharing.setOnClickListener( + v -> + Navigation.findNavController(getView()) + .navigate(R.id.action_homeFragment_to_screenSharingFragment)); binding.btnSetting.setOnClickListener( v -> Navigation.findNavController(getView()) diff --git a/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/view/ScreenSharingFragment.java b/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/view/ScreenSharingFragment.java new file mode 100644 index 00000000..5b092a20 --- /dev/null +++ b/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/view/ScreenSharingFragment.java @@ -0,0 +1,102 @@ +// Copyright (c) 2022 NetEase, Inc. All rights reserved. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +package com.netease.yunxin.kit.meeting.sampleapp.view; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.media.projection.MediaProjectionManager; +import android.text.Editable; +import android.widget.Toast; +import androidx.lifecycle.ViewModelProviders; +import com.netease.yunxin.kit.meeting.sampleapp.base.BaseFragment; +import com.netease.yunxin.kit.meeting.sampleapp.databinding.FragmentScreenShareBinding; +import com.netease.yunxin.kit.meeting.sampleapp.log.LogUtil; +import com.netease.yunxin.kit.meeting.sampleapp.viewmodel.ScreenSharingViewModel; +import com.netease.yunxin.kit.meeting.sdk.NEScreenSharingOptions; +import com.netease.yunxin.kit.meeting.sdk.NEScreenSharingParams; +import com.netease.yunxin.kit.meeting.sdk.NEScreenSharingStatus; +import java.util.Objects; + +public class ScreenSharingFragment extends BaseFragment { + + private static final String TAG = ScreenSharingFragment.class.getSimpleName(); + private ScreenSharingViewModel mViewModel; + private int screenShareRequestCode = 1002; + + @Override + protected FragmentScreenShareBinding getViewBinding() { + return FragmentScreenShareBinding.inflate(getLayoutInflater()); + } + + @Override + protected void initView() { + mViewModel = ViewModelProviders.of(this).get(ScreenSharingViewModel.class); + mViewModel.addScreenSharingStatusListener(); + binding.btnScreenSharing.setOnClickListener( + view -> { + if (Objects.equals( + NEScreenSharingStatus.SCREEN_SHARING_STATUS_IDLE, + mViewModel.screenStatus.getValue())) { + + MediaProjectionManager mediaProjectionManager = + (MediaProjectionManager) + getActivity().getSystemService(Context.MEDIA_PROJECTION_SERVICE); + Intent captureIntent = mediaProjectionManager.createScreenCaptureIntent(); + startActivityForResult(captureIntent, screenShareRequestCode); + } else if (Objects.equals( + NEScreenSharingStatus.SCREEN_SHARING_STATUS_STARTED, + mViewModel.screenStatus.getValue()) + || Objects.equals( + NEScreenSharingStatus.SCREEN_SHARING_STATUS_WAITING, + mViewModel.screenStatus.getValue())) { + mViewModel.stopScreenShare( + (resultCode, resultMsg, resultData) -> { + LogUtil.log(TAG, "stopScreenShare resultCode" + resultCode); + }); + } + }); + mViewModel.observeScreenStatus( + this, + status -> { + // boolean isScreenSharingStarted = Objects.equals(NEScreenSharingStatus.SCREEN_SHARING_STATUS_STARTED, ); + binding.btnScreenSharing.setText(mViewModel.screenStatus.getValue().name()); + }); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + LogUtil.log(TAG, "onActivityResult resultCode" + resultCode + "requestCode" + requestCode); + if (requestCode == screenShareRequestCode && resultCode == Activity.RESULT_OK && data != null) { + + NEScreenSharingParams params = new NEScreenSharingParams(); + Editable editableName = binding.edtScreenSharingName.getText(); + Editable editableCode = binding.edtScreenSharingCode.getText(); + if (editableName != null && editableCode != null) { + params.displayName = editableName.toString(); + params.sharingCode = editableCode.toString(); + } + NEScreenSharingOptions options = new NEScreenSharingOptions(); + options.enableAudioShare = binding.checkboxScreenShareAudio.isChecked(); + mViewModel.startScreenSharing( + data, + params, + options, + (code, msg, resultData) -> { + if (msg != null) { + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show(); + } + }); + } + } + + @Override + protected void initData() {} + + @Override + public void onDestroyView() { + super.onDestroyView(); + } +} diff --git a/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/viewmodel/ScreenSharingViewModel.java b/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/viewmodel/ScreenSharingViewModel.java new file mode 100644 index 00000000..c063dc49 --- /dev/null +++ b/SampleCode/Android/app/src/main/java/com/netease/yunxin/kit/meeting/sampleapp/viewmodel/ScreenSharingViewModel.java @@ -0,0 +1,59 @@ +// Copyright (c) 2022 NetEase, Inc. All rights reserved. +// Use of this source code is governed by a MIT license that can be +// found in the LICENSE file. + +package com.netease.yunxin.kit.meeting.sampleapp.viewmodel; + +import android.app.Application; +import android.content.Context; +import android.content.Intent; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Observer; +import com.netease.yunxin.kit.meeting.sampleapp.data.MeetingDataRepository; +import com.netease.yunxin.kit.meeting.sdk.NECallback; +import com.netease.yunxin.kit.meeting.sdk.NEScreenSharingOptions; +import com.netease.yunxin.kit.meeting.sdk.NEScreenSharingParams; +import com.netease.yunxin.kit.meeting.sdk.NEScreenSharingStatus; + +public class ScreenSharingViewModel extends AndroidViewModel { + private Context context; + + public ScreenSharingViewModel(Application application) { + super(application); + this.context = application.getApplicationContext(); + } + + public MutableLiveData screenStatus = + new MutableLiveData<>(NEScreenSharingStatus.SCREEN_SHARING_STATUS_IDLE); + + private MeetingDataRepository mRepository = MeetingDataRepository.getInstance(); + + public void startScreenSharing( + Intent data, + @NonNull NEScreenSharingParams param, + @Nullable NEScreenSharingOptions opts, + NECallback callback) { + mRepository.startScreenSharing(context, data, param, opts, callback); + } + + public void stopScreenShare(NECallback callback) { + mRepository.stopScreenShare(context, callback); + } + + public void observeScreenStatus(LifecycleOwner owner, Observer observer) { + screenStatus.observe(owner, observer); + } + + public void addScreenSharingStatusListener() { + mRepository.addScreenSharingStatusListener( + event -> { + screenStatus.setValue(event.status); + Toast.makeText(context, "屏幕共享状态变更: " + event.status.name(), Toast.LENGTH_SHORT).show(); + }); + } +} diff --git a/SampleCode/Android/app/src/main/res/drawable/share_screen.png b/SampleCode/Android/app/src/main/res/drawable/share_screen.png new file mode 100644 index 00000000..2e57ccfb Binary files /dev/null and b/SampleCode/Android/app/src/main/res/drawable/share_screen.png differ diff --git a/SampleCode/Android/app/src/main/res/layout/fragment_home.xml b/SampleCode/Android/app/src/main/res/layout/fragment_home.xml index 4df5704a..e52aa180 100644 --- a/SampleCode/Android/app/src/main/res/layout/fragment_home.xml +++ b/SampleCode/Android/app/src/main/res/layout/fragment_home.xml @@ -77,9 +77,25 @@ app:itb_text_size="14sp" app:layout_constraintBottom_toBottomOf="@id/btn_start_meeting" app:layout_constraintLeft_toRightOf="@+id/btn_join_meeting" - app:layout_constraintRight_toRightOf="parent" + app:layout_constraintRight_toLeftOf="@+id/btn_screen_sharing" app:layout_constraintTop_toTopOf="@+id/btn_start_meeting" /> + + + + + + + + + + + +