Merge branch 'development' into videocall_popup
* development: (72 commits) Enable iOS AirPlay fix the end call fix timer issue fix timer issue fix disconected stream fix circle video stream Add App Permissions Utils add video call permissions fix drop video call issue circle screen fix bug, and mini screen design video fix bugs fix header from lab fix header from lab result details fix header fix the video call issues Add video call service video fix bugs video fix bugs add loader video fix bugs ...videocall_popup
@ -1,442 +0,0 @@
|
||||
package com.hmg.hmgDr.ui;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel;
|
||||
import com.hmg.hmgDr.Model.GetSessionStatusModel;
|
||||
import com.hmg.hmgDr.Model.SessionStatusModel;
|
||||
import com.hmg.hmgDr.R;
|
||||
import com.opentok.android.Session;
|
||||
import com.opentok.android.Stream;
|
||||
import com.opentok.android.Publisher;
|
||||
import com.opentok.android.PublisherKit;
|
||||
import com.opentok.android.Subscriber;
|
||||
import com.opentok.android.BaseVideoRenderer;
|
||||
import com.opentok.android.OpentokError;
|
||||
import com.opentok.android.SubscriberKit;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import pub.devrel.easypermissions.AfterPermissionGranted;
|
||||
import pub.devrel.easypermissions.AppSettingsDialog;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
|
||||
public class VideoCallActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks,
|
||||
Session.SessionListener,
|
||||
Publisher.PublisherListener,
|
||||
Subscriber.VideoListener, VideoCallContract.VideoCallView {
|
||||
|
||||
private static final String TAG = VideoCallActivity.class.getSimpleName();
|
||||
|
||||
VideoCallContract.VideoCallPresenter videoCallPresenter;
|
||||
|
||||
private static final int RC_SETTINGS_SCREEN_PERM = 123;
|
||||
private static final int RC_VIDEO_APP_PERM = 124;
|
||||
|
||||
|
||||
private Session mSession;
|
||||
private Publisher mPublisher;
|
||||
private Subscriber mSubscriber;
|
||||
|
||||
private Handler mVolHandler, mConnectedHandler;
|
||||
private Runnable mVolRunnable, mConnectedRunnable;
|
||||
|
||||
private FrameLayout mPublisherViewContainer;
|
||||
private RelativeLayout mSubscriberViewContainer;
|
||||
private RelativeLayout controlPanel;
|
||||
|
||||
private String apiKey;
|
||||
private String sessionId;
|
||||
private String token;
|
||||
private String appLang;
|
||||
private String baseUrl;
|
||||
|
||||
private boolean isSwitchCameraClicked;
|
||||
private boolean isCameraClicked;
|
||||
private boolean isSpeckerClicked;
|
||||
private boolean isMicClicked;
|
||||
|
||||
private ImageView mCallBtn;
|
||||
private ImageView mCameraBtn;
|
||||
private ImageView mSwitchCameraBtn;
|
||||
private ImageView mspeckerBtn;
|
||||
private ImageView mMicBtn;
|
||||
|
||||
private ProgressBar progressBar;
|
||||
private CountDownTimer countDownTimer;
|
||||
private TextView progressBarTextView;
|
||||
private RelativeLayout progressBarLayout;
|
||||
|
||||
private boolean isConnected = false;
|
||||
|
||||
private GetSessionStatusModel sessionStatusModel;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(R.style.AppTheme);
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_video_call);
|
||||
Objects.requireNonNull(getSupportActionBar()).hide();
|
||||
initUI();
|
||||
requestPermissions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (mSession == null) {
|
||||
return;
|
||||
}
|
||||
mSession.onPause();
|
||||
|
||||
if (isFinishing()) {
|
||||
disconnectSession();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (mSession == null) {
|
||||
return;
|
||||
}
|
||||
mSession.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
disconnectSession();
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void initUI() {
|
||||
mPublisherViewContainer = (FrameLayout) findViewById(R.id.local_video_view_container);
|
||||
mSubscriberViewContainer = (RelativeLayout) findViewById(R.id.remote_video_view_container);
|
||||
|
||||
apiKey = getIntent().getStringExtra("apiKey");
|
||||
sessionId = getIntent().getStringExtra("sessionId");
|
||||
token = getIntent().getStringExtra("token");
|
||||
appLang = getIntent().getStringExtra("appLang");
|
||||
baseUrl = getIntent().getStringExtra("baseUrl");
|
||||
sessionStatusModel = getIntent().getParcelableExtra("sessionStatusModel");
|
||||
|
||||
controlPanel = findViewById(R.id.control_panel);
|
||||
|
||||
videoCallPresenter = new VideoCallPresenterImpl(this, baseUrl);
|
||||
|
||||
|
||||
mCallBtn = findViewById(R.id.btn_call);
|
||||
mCameraBtn = findViewById(R.id.btn_camera);
|
||||
mSwitchCameraBtn = findViewById(R.id.btn_switch_camera);
|
||||
mspeckerBtn = findViewById(R.id.btn_specker);
|
||||
mMicBtn = findViewById(R.id.btn_mic);
|
||||
|
||||
// progressBarLayout=findViewById(R.id.progressBar);
|
||||
// progressBar=findViewById(R.id.progress_bar);
|
||||
// progressBarTextView=findViewById(R.id.progress_bar_text);
|
||||
// progressBar.setVisibility(View.GONE);
|
||||
|
||||
hiddenButtons();
|
||||
|
||||
checkClientConnected();
|
||||
|
||||
mSubscriberViewContainer.setOnTouchListener((v, event) -> {
|
||||
controlPanel.setVisibility(View.VISIBLE);
|
||||
mVolHandler.removeCallbacks(mVolRunnable);
|
||||
mVolHandler.postDelayed(mVolRunnable, 5 * 1000);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (appLang.equals("ar")) {
|
||||
progressBarLayout.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void checkClientConnected() {
|
||||
mConnectedHandler = new Handler();
|
||||
mConnectedRunnable = () -> {
|
||||
if (!isConnected) {
|
||||
videoCallPresenter.callClintConnected(sessionStatusModel);
|
||||
}
|
||||
};
|
||||
mConnectedHandler.postDelayed(mConnectedRunnable, 30 * 1000);
|
||||
|
||||
}
|
||||
|
||||
private void hiddenButtons() {
|
||||
mVolHandler = new Handler();
|
||||
mVolRunnable = new Runnable() {
|
||||
public void run() {
|
||||
controlPanel.setVisibility(View.GONE);
|
||||
}
|
||||
};
|
||||
mVolHandler.postDelayed(mVolRunnable, 5 * 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsGranted(int requestCode, List<String> perms) {
|
||||
Log.d(TAG, "onPermissionsGranted:" + requestCode + ":" + perms.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsDenied(int requestCode, List<String> perms) {
|
||||
Log.d(TAG, "onPermissionsDenied:" + requestCode + ":" + perms.size());
|
||||
|
||||
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
|
||||
new AppSettingsDialog.Builder(this)
|
||||
.setTitle(getString(R.string.title_settings_dialog))
|
||||
.setRationale(getString(R.string.rationale_ask_again))
|
||||
.setPositiveButton(getString(R.string.setting))
|
||||
.setNegativeButton(getString(R.string.cancel))
|
||||
.setRequestCode(RC_SETTINGS_SCREEN_PERM)
|
||||
.build()
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterPermissionGranted(RC_VIDEO_APP_PERM)
|
||||
private void requestPermissions() {
|
||||
String[] perms = {Manifest.permission.INTERNET, Manifest.permission.CAMERA,};
|
||||
if (EasyPermissions.hasPermissions(this, perms)) {
|
||||
try {
|
||||
mSession = new Session.Builder(this, apiKey, sessionId).build();
|
||||
mSession.setSessionListener(this);
|
||||
mSession.connect(token);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
EasyPermissions.requestPermissions(this, getString(R.string.remaining_ar), RC_VIDEO_APP_PERM, perms);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(Session session) {
|
||||
Log.i(TAG, "Session Connected");
|
||||
|
||||
mPublisher = new Publisher.Builder(this).build();
|
||||
mPublisher.setPublisherListener(this);
|
||||
|
||||
mPublisherViewContainer.addView(mPublisher.getView());
|
||||
|
||||
if (mPublisher.getView() instanceof GLSurfaceView) {
|
||||
((GLSurfaceView) mPublisher.getView()).setZOrderOnTop(true);
|
||||
}
|
||||
|
||||
mSession.publish(mPublisher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(Session session) {
|
||||
Log.d(TAG, "onDisconnected: disconnected from session " + session.getSessionId());
|
||||
|
||||
mSession = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Session session, OpentokError opentokError) {
|
||||
Log.d(TAG, "onError: Error (" + opentokError.getMessage() + ") in session " + session.getSessionId());
|
||||
|
||||
Toast.makeText(this, "Session error. See the logcat please.", Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStreamReceived(Session session, Stream stream) {
|
||||
Log.d(TAG, "onStreamReceived: New stream " + stream.getStreamId() + " in session " + session.getSessionId());
|
||||
if (mSubscriber != null) {
|
||||
isConnected = true;
|
||||
return;
|
||||
}
|
||||
isConnected = true;
|
||||
subscribeToStream(stream);
|
||||
videoCallPresenter.callChangeCallStatus(new ChangeCallStatusRequestModel(3,sessionStatusModel.getDoctorId(), sessionStatusModel.getGeneralid(),token,sessionStatusModel.getVCID()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStreamDropped(Session session, Stream stream) {
|
||||
Log.d(TAG, "onStreamDropped: Stream " + stream.getStreamId() + " dropped from session " + session.getSessionId());
|
||||
|
||||
if (mSubscriber == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSubscriber.getStream().equals(stream)) {
|
||||
mSubscriberViewContainer.removeView(mSubscriber.getView());
|
||||
mSubscriber.destroy();
|
||||
mSubscriber = null;
|
||||
}
|
||||
disconnectSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStreamCreated(PublisherKit publisherKit, Stream stream) {
|
||||
Log.d(TAG, "onStreamCreated: Own stream " + stream.getStreamId() + " created");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStreamDestroyed(PublisherKit publisherKit, Stream stream) {
|
||||
Log.d(TAG, "onStreamDestroyed: Own stream " + stream.getStreamId() + " destroyed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(PublisherKit publisherKit, OpentokError opentokError) {
|
||||
Log.d(TAG, "onError: Error (" + opentokError.getMessage() + ") in publisher");
|
||||
|
||||
Toast.makeText(this, "Session error. See the logcat please.", Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoDataReceived(SubscriberKit subscriberKit) {
|
||||
mSubscriber.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL);
|
||||
mSubscriberViewContainer.addView(mSubscriber.getView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoDisabled(SubscriberKit subscriberKit, String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoEnabled(SubscriberKit subscriberKit, String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoDisableWarning(SubscriberKit subscriberKit) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoDisableWarningLifted(SubscriberKit subscriberKit) {
|
||||
|
||||
}
|
||||
|
||||
private void subscribeToStream(Stream stream) {
|
||||
mSubscriber = new Subscriber.Builder(VideoCallActivity.this, stream).build();
|
||||
mSubscriber.setVideoListener(this);
|
||||
mSession.subscribe(mSubscriber);
|
||||
}
|
||||
|
||||
private void disconnectSession() {
|
||||
if (mSession == null) {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSubscriber != null) {
|
||||
mSubscriberViewContainer.removeView(mSubscriber.getView());
|
||||
mSession.unsubscribe(mSubscriber);
|
||||
mSubscriber.destroy();
|
||||
mSubscriber = null;
|
||||
}
|
||||
|
||||
if (mPublisher != null) {
|
||||
mPublisherViewContainer.removeView(mPublisher.getView());
|
||||
mSession.unpublish(mPublisher);
|
||||
mPublisher.destroy();
|
||||
mPublisher = null;
|
||||
}
|
||||
mSession.disconnect();
|
||||
if (countDownTimer != null) {
|
||||
countDownTimer.cancel();
|
||||
}
|
||||
videoCallPresenter.callChangeCallStatus(new ChangeCallStatusRequestModel(16,sessionStatusModel.getDoctorId(), sessionStatusModel.getGeneralid(),token,sessionStatusModel.getVCID()));
|
||||
finish();
|
||||
}
|
||||
|
||||
public void onSwitchCameraClicked(View view) {
|
||||
if (mPublisher != null) {
|
||||
isSwitchCameraClicked = !isSwitchCameraClicked;
|
||||
mPublisher.cycleCamera();
|
||||
int res = isSwitchCameraClicked ? R.drawable.flip_disapled : R.drawable.flip_enabled;
|
||||
mSwitchCameraBtn.setImageResource(res);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCameraClicked(View view) {
|
||||
if (mPublisher != null) {
|
||||
isCameraClicked = !isCameraClicked;
|
||||
mPublisher.setPublishVideo(!isCameraClicked);
|
||||
int res = isCameraClicked ? R.drawable.video_disanabled : R.drawable.video_enabled;
|
||||
mCameraBtn.setImageResource(res);
|
||||
}
|
||||
}
|
||||
|
||||
public void onSpeckerClicked(View view) {
|
||||
if (mSubscriber != null) {
|
||||
isSpeckerClicked = !isSpeckerClicked;
|
||||
mSubscriber.setSubscribeToAudio(!isSpeckerClicked);
|
||||
int res = isSpeckerClicked ? R.drawable.audio_disabled : R.drawable.audio_enabled;
|
||||
mspeckerBtn.setImageResource(res);
|
||||
}
|
||||
}
|
||||
|
||||
public void onMicClicked(View view) {
|
||||
|
||||
if (mPublisher != null) {
|
||||
isMicClicked = !isMicClicked;
|
||||
mPublisher.setPublishAudio(!isMicClicked);
|
||||
int res = isMicClicked ? R.drawable.mic_disabled : R.drawable.mic_enabled;
|
||||
mMicBtn.setImageResource(res);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCallClicked(View view) {
|
||||
disconnectSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallSuccessful(SessionStatusModel sessionStatusModel) {
|
||||
if (sessionStatusModel.getSessionStatus() == 2 || sessionStatusModel.getSessionStatus() == 3) {
|
||||
Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra("sessionStatusNotRespond", sessionStatusModel);
|
||||
setResult(Activity.RESULT_OK, returnIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallChangeCallStatusSuccessful(SessionStatusModel sessionStatusModel) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.hmg.hmgDr.ui
|
||||
|
||||
import android.content.Intent
|
||||
|
||||
interface VideoCallResponseListener {
|
||||
|
||||
fun onCallFinished(resultCode : Int, intent: Intent? = null)
|
||||
|
||||
fun errorHandle(message: String)
|
||||
|
||||
fun minimizeVideoEvent(isMinimize : Boolean)
|
||||
|
||||
fun onBackHandle(){}
|
||||
}
|
||||
@ -0,0 +1,893 @@
|
||||
package com.hmg.hmgDr.ui.fragment
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Point
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.opengl.GLSurfaceView
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import androidx.annotation.Nullable
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel
|
||||
import com.hmg.hmgDr.Model.GetSessionStatusModel
|
||||
import com.hmg.hmgDr.Model.SessionStatusModel
|
||||
import com.hmg.hmgDr.R
|
||||
import com.hmg.hmgDr.ui.VideoCallContract.VideoCallPresenter
|
||||
import com.hmg.hmgDr.ui.VideoCallContract.VideoCallView
|
||||
import com.hmg.hmgDr.ui.VideoCallPresenterImpl
|
||||
import com.hmg.hmgDr.ui.VideoCallResponseListener
|
||||
import com.hmg.hmgDr.util.DynamicVideoRenderer
|
||||
import com.hmg.hmgDr.util.ThumbnailCircleVideoRenderer
|
||||
import com.opentok.android.*
|
||||
import com.opentok.android.PublisherKit.PublisherListener
|
||||
import pub.devrel.easypermissions.AfterPermissionGranted
|
||||
import pub.devrel.easypermissions.AppSettingsDialog
|
||||
import pub.devrel.easypermissions.EasyPermissions
|
||||
import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks
|
||||
import kotlin.math.ceil
|
||||
|
||||
|
||||
class VideoCallFragment : DialogFragment(), PermissionCallbacks, Session.SessionListener, PublisherListener,
|
||||
SubscriberKit.VideoListener, VideoCallView {
|
||||
|
||||
private var isFullScreen: Boolean = true
|
||||
private var isCircle: Boolean = false
|
||||
private var x_init_cord = 0
|
||||
private var y_init_cord: Int = 0
|
||||
private var x_init_margin: Int = 0
|
||||
private var y_init_margin: Int = 0
|
||||
private val szWindow: Point = Point()
|
||||
private lateinit var mWindowManager: WindowManager
|
||||
private var isLeft = true
|
||||
|
||||
private lateinit var videoCallPresenter: VideoCallPresenter
|
||||
|
||||
private var mSession: Session? = null
|
||||
private var mPublisher: Publisher? = null
|
||||
private var mSubscriber: Subscriber? = null
|
||||
|
||||
private var mVolHandler: Handler? = null
|
||||
private var mConnectedHandler: Handler? = null
|
||||
private var mVolRunnable: Runnable? = null
|
||||
private var mConnectedRunnable: Runnable? = null
|
||||
|
||||
private lateinit var thumbnail_container: FrameLayout
|
||||
private lateinit var mPublisherViewContainer: FrameLayout
|
||||
private lateinit var mPublisherViewIcon: View
|
||||
private lateinit var mSubscriberViewContainer: FrameLayout
|
||||
private lateinit var mSubscriberViewIcon: ImageView
|
||||
private lateinit var controlPanel: ConstraintLayout
|
||||
|
||||
private var apiKey: String? = null
|
||||
private var sessionId: String? = null
|
||||
private var token: String? = null
|
||||
private var appLang: String? = null
|
||||
private var baseUrl: String? = null
|
||||
|
||||
private var isSwitchCameraClicked = false
|
||||
private var isCameraClicked = false
|
||||
private var isSpeckerClicked = false
|
||||
private var isMicClicked = false
|
||||
|
||||
private lateinit var parentView: View
|
||||
private lateinit var videoCallContainer: ConstraintLayout
|
||||
private lateinit var layoutName: RelativeLayout
|
||||
private lateinit var layoutMini: RelativeLayout
|
||||
private lateinit var icMini: ImageButton
|
||||
private lateinit var mCallBtn: ImageView
|
||||
private lateinit var btnMinimize: ImageView
|
||||
private lateinit var mCameraBtn: ImageView
|
||||
private lateinit var mSwitchCameraBtn: ImageView
|
||||
private lateinit var mspeckerBtn: ImageView
|
||||
private lateinit var mMicBtn: ImageView
|
||||
|
||||
private lateinit var patientName: TextView
|
||||
private lateinit var cmTimer: Chronometer
|
||||
private var elapsedTime: Long = 0
|
||||
private var resume = false
|
||||
|
||||
private val progressBar: ProgressBar? = null
|
||||
private val countDownTimer: CountDownTimer? = null
|
||||
private val progressBarTextView: TextView? = null
|
||||
private val progressBarLayout: RelativeLayout? = null
|
||||
|
||||
private var isConnected = false
|
||||
|
||||
private var sessionStatusModel: GetSessionStatusModel? = null
|
||||
private var videoCallResponseListener: VideoCallResponseListener? = null
|
||||
private lateinit var mDetector: GestureDetectorCompat
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
requireActivity().setTheme(R.style.AppTheme)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
dialog?.window?.setLayout(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
|
||||
override fun getTheme(): Int {
|
||||
return R.style.dialogTheme
|
||||
}
|
||||
|
||||
override fun onCreateDialog(@Nullable savedInstanceState: Bundle?): Dialog {
|
||||
val dialog: Dialog = super.onCreateDialog(savedInstanceState)
|
||||
|
||||
// Add back button listener
|
||||
// Add back button listener
|
||||
dialog.setOnKeyListener { _, keyCode, keyEvent ->
|
||||
// getAction to make sure this doesn't double fire
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == KeyEvent.ACTION_UP) {
|
||||
videoCallResponseListener?.onBackHandle()
|
||||
false // Capture onKey
|
||||
} else true
|
||||
// Don't capture
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// This is done in a post() since the dialog must be drawn before locating.
|
||||
requireView().post {
|
||||
val dialogWindow = dialog!!.window
|
||||
|
||||
if (dialog != null && dialogWindow != null) {
|
||||
dialogWindow.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT));
|
||||
}
|
||||
|
||||
// Make the dialog possible to be outside touch
|
||||
dialogWindow!!.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
)
|
||||
dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
|
||||
requireView().invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
fun setCallListener(videoCallResponseListener: VideoCallResponseListener) {
|
||||
this.videoCallResponseListener = videoCallResponseListener
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View {
|
||||
|
||||
parentView = inflater.inflate(R.layout.activity_video_call, container, false)
|
||||
|
||||
// Objects.requireNonNull(requireActivity().actionBar)!!.hide()
|
||||
arguments?.run {
|
||||
apiKey = getString("apiKey")
|
||||
sessionId = getString("sessionId")
|
||||
token = getString("token")
|
||||
appLang = getString("appLang")
|
||||
baseUrl = getString("baseUrl")
|
||||
sessionStatusModel = getParcelable("sessionStatusModel")
|
||||
}
|
||||
initUI(parentView)
|
||||
requestPermissions()
|
||||
|
||||
handleDragDialog()
|
||||
mDetector = GestureDetectorCompat(context, MyGestureListener({ showControlPanelTemporarily() }, { miniCircleDoubleTap() }))
|
||||
|
||||
return parentView
|
||||
}
|
||||
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (mSession == null) {
|
||||
return
|
||||
}
|
||||
mSession!!.onPause()
|
||||
if (requireActivity().isFinishing) {
|
||||
disconnectSession()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (mSession == null) {
|
||||
return
|
||||
}
|
||||
mSession!!.onResume()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
disconnectSession()
|
||||
cmTimer.stop()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun initUI(view: View) {
|
||||
videoCallContainer = view.findViewById(R.id.video_call_ll)
|
||||
layoutName = view.findViewById(R.id.layout_name)
|
||||
layoutMini = view.findViewById(R.id.layout_mini)
|
||||
icMini = view.findViewById(R.id.ic_mini)
|
||||
thumbnail_container = view.findViewById(R.id.thumbnail_container)
|
||||
mPublisherViewContainer = view.findViewById(R.id.local_video_view_container)
|
||||
mPublisherViewIcon = view.findViewById(R.id.local_video_view_icon)
|
||||
mSubscriberViewIcon = view.findViewById(R.id.remote_video_view_icon)
|
||||
mSubscriberViewContainer = view.findViewById(R.id.remote_video_view_container)
|
||||
|
||||
patientName = view.findViewById<TextView>(R.id.patient_name)
|
||||
patientName.text = sessionStatusModel!!.patientName
|
||||
|
||||
cmTimer = view.findViewById<Chronometer>(R.id.cmTimer)
|
||||
cmTimer.format = "mm:ss"
|
||||
cmTimer.onChronometerTickListener = Chronometer.OnChronometerTickListener { arg0: Chronometer? ->
|
||||
val minutes: Long
|
||||
val seconds: Long
|
||||
if (!resume) {
|
||||
minutes = (SystemClock.elapsedRealtime() - cmTimer.base) / 1000 / 60
|
||||
seconds = (SystemClock.elapsedRealtime() - cmTimer.base) / 1000 % 60
|
||||
elapsedTime = SystemClock.elapsedRealtime()
|
||||
} else {
|
||||
minutes = (elapsedTime - cmTimer.base) / 1000 / 60
|
||||
seconds = (elapsedTime - cmTimer.base) / 1000 % 60
|
||||
elapsedTime += 1000
|
||||
}
|
||||
arg0?.text = "$minutes:$seconds"
|
||||
Log.d(VideoCallFragment.TAG, "onChronometerTick: $minutes : $seconds")
|
||||
}
|
||||
|
||||
icMini.setOnClickListener {
|
||||
onMiniCircleClicked()
|
||||
}
|
||||
|
||||
controlPanel = view.findViewById(R.id.control_panel)
|
||||
videoCallPresenter = VideoCallPresenterImpl(this, baseUrl)
|
||||
mCallBtn = view.findViewById<ImageView>(R.id.btn_call)
|
||||
mCallBtn.setOnClickListener {
|
||||
onCallClicked()
|
||||
}
|
||||
btnMinimize = view.findViewById<ImageView>(R.id.btn_minimize)
|
||||
btnMinimize.setOnClickListener {
|
||||
onMinimizedClicked(it)
|
||||
}
|
||||
mCameraBtn = view.findViewById<ImageView>(R.id.btn_camera)
|
||||
mCameraBtn.setOnClickListener {
|
||||
onCameraClicked(it)
|
||||
}
|
||||
mSwitchCameraBtn = view.findViewById<ImageView>(R.id.btn_switch_camera)
|
||||
mSwitchCameraBtn.setOnClickListener {
|
||||
onSwitchCameraClicked(it)
|
||||
}
|
||||
mspeckerBtn = view.findViewById<ImageView>(R.id.btn_specker)
|
||||
mspeckerBtn.setOnClickListener {
|
||||
onSpeckerClicked(it)
|
||||
}
|
||||
mMicBtn = view.findViewById<ImageView>(R.id.btn_mic)
|
||||
mMicBtn.setOnClickListener {
|
||||
onMicClicked(it)
|
||||
}
|
||||
// progressBarLayout=findViewById(R.id.progressBar);
|
||||
// progressBar=findViewById(R.id.progress_bar);
|
||||
// progressBarTextView=findViewById(R.id.progress_bar_text);
|
||||
// progressBar.setVisibility(View.GONE);
|
||||
hiddenButtons()
|
||||
checkClientConnected()
|
||||
|
||||
if (appLang == "ar") {
|
||||
progressBarLayout!!.layoutDirection = View.LAYOUT_DIRECTION_RTL
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkClientConnected() {
|
||||
mConnectedHandler = Handler((Looper.getMainLooper()))
|
||||
mConnectedRunnable = Runnable {
|
||||
if (!isConnected) {
|
||||
videoCallPresenter.callClintConnected(sessionStatusModel)
|
||||
}
|
||||
}
|
||||
mConnectedHandler!!.postDelayed(mConnectedRunnable!!, (55 * 1000).toLong())
|
||||
}
|
||||
|
||||
private fun hiddenButtons() {
|
||||
mVolHandler = Handler()
|
||||
mVolRunnable = Runnable { controlPanel.visibility = View.GONE }
|
||||
mVolHandler!!.postDelayed(mVolRunnable!!, (5 * 1000).toLong())
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
|
||||
}
|
||||
|
||||
override fun onPermissionsGranted(requestCode: Int, perms: List<String?>) {
|
||||
Log.d(TAG, "onPermissionsGranted:" + requestCode + ":" + perms.size)
|
||||
}
|
||||
|
||||
override fun onPermissionsDenied(requestCode: Int, perms: List<String?>) {
|
||||
Log.d(TAG, "onPermissionsDenied:" + requestCode + ":" + perms.size)
|
||||
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
|
||||
AppSettingsDialog.Builder(this)
|
||||
.setTitle(getString(R.string.title_settings_dialog))
|
||||
.setRationale(getString(R.string.rationale_ask_again))
|
||||
.setPositiveButton(getString(R.string.setting))
|
||||
.setNegativeButton(getString(R.string.cancel))
|
||||
.setRequestCode(RC_SETTINGS_SCREEN_PERM)
|
||||
.build()
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
@AfterPermissionGranted(RC_VIDEO_APP_PERM)
|
||||
private fun requestPermissions() {
|
||||
val perms = arrayOf(Manifest.permission.INTERNET, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.MODIFY_AUDIO_SETTINGS, Manifest.permission.CALL_PHONE)
|
||||
if (EasyPermissions.hasPermissions(requireContext(), *perms)) {
|
||||
try {
|
||||
mSession = Session.Builder(context, apiKey, sessionId).build()
|
||||
mSession!!.setSessionListener(this)
|
||||
mSession!!.connect(token)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} else {
|
||||
EasyPermissions.requestPermissions(this, getString(R.string.remaining_ar), RC_VIDEO_APP_PERM, *perms)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConnected(session: Session?) {
|
||||
Log.i(TAG, "Session Connected")
|
||||
mPublisher = Publisher.Builder(requireContext())
|
||||
// .name("publisher")
|
||||
// .renderer(ThumbnailCircleVideoRenderer(requireContext()))
|
||||
.build()
|
||||
mPublisher!!.setPublisherListener(this)
|
||||
if (mPublisher!!.view is GLSurfaceView) {
|
||||
(mPublisher!!.view as GLSurfaceView).setZOrderOnTop(true)
|
||||
}
|
||||
|
||||
mPublisherViewContainer.addView(mPublisher!!.view)
|
||||
mSession!!.publish(mPublisher)
|
||||
|
||||
if (!resume) {
|
||||
cmTimer.base = SystemClock.elapsedRealtime()
|
||||
}
|
||||
cmTimer.start()
|
||||
videoCallResponseListener?.minimizeVideoEvent(true)
|
||||
}
|
||||
|
||||
override fun onDisconnected(session: Session) {
|
||||
Log.d(TAG, "onDisconnected: disconnected from session " + session.sessionId)
|
||||
mSession = null
|
||||
cmTimer.stop()
|
||||
disconnectSession()
|
||||
videoCallResponseListener?.minimizeVideoEvent(false)
|
||||
}
|
||||
|
||||
override fun onError(session: Session, opentokError: OpentokError) {
|
||||
Log.d(TAG, "onError: Error (" + opentokError.message + ") in session " + session.sessionId)
|
||||
|
||||
// videoCallResponseListener?.errorHandle("Error (" + opentokError.message + ") in session ")
|
||||
// dialog?.dismiss()
|
||||
}
|
||||
|
||||
override fun onStreamReceived(session: Session, stream: Stream) {
|
||||
Log.d(TAG, "onStreamReceived: New stream " + stream.streamId + " in session " + session.sessionId)
|
||||
if (mSubscriber != null) {
|
||||
isConnected = true
|
||||
return
|
||||
}
|
||||
isConnected = true
|
||||
subscribeToStream(stream)
|
||||
if (mConnectedHandler != null && mConnectedRunnable != null)
|
||||
mConnectedHandler!!.removeCallbacks(mConnectedRunnable!!)
|
||||
videoCallPresenter.callChangeCallStatus(ChangeCallStatusRequestModel(3, sessionStatusModel!!.doctorId, sessionStatusModel!!.generalid, token, sessionStatusModel!!.vcid))
|
||||
}
|
||||
|
||||
override fun onStreamDropped(session: Session, stream: Stream) {
|
||||
Log.d(TAG, "onStreamDropped: Stream " + stream.streamId + " dropped from session " + session.sessionId)
|
||||
if (mSubscriber == null) {
|
||||
return
|
||||
}
|
||||
if (mSubscriber!!.stream == stream) {
|
||||
mSubscriberViewContainer.removeView(mSubscriber!!.view)
|
||||
mSubscriber!!.destroy()
|
||||
mSubscriber = null
|
||||
}
|
||||
disconnectSession()
|
||||
}
|
||||
|
||||
override fun onStreamCreated(publisherKit: PublisherKit?, stream: Stream) {
|
||||
Log.d(TAG, "onStreamCreated: Own stream " + stream.streamId + " created")
|
||||
}
|
||||
|
||||
override fun onStreamDestroyed(publisherKit: PublisherKit?, stream: Stream) {
|
||||
Log.d(TAG, "onStreamDestroyed: Own stream " + stream.streamId + " destroyed")
|
||||
}
|
||||
|
||||
override fun onError(publisherKit: PublisherKit?, opentokError: OpentokError) {
|
||||
Log.d(VideoCallFragment.TAG, "onError: Error (" + opentokError.message + ") in publisher")
|
||||
// videoCallResponseListener?.errorHandle("Error (" + opentokError.message + ") in publisher")
|
||||
// dialog?.dismiss()
|
||||
}
|
||||
|
||||
override fun onVideoDataReceived(subscriberKit: SubscriberKit?) {
|
||||
mSubscriber!!.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL)
|
||||
(mSubscriber!!.renderer as DynamicVideoRenderer).enableThumbnailCircle(false)
|
||||
mSubscriberViewContainer.addView(mSubscriber!!.view)
|
||||
// switchToThumbnailCircle()
|
||||
}
|
||||
|
||||
fun switchToThumbnailCircle() {
|
||||
thumbnail_container.postDelayed({
|
||||
val view = mSubscriber!!.view
|
||||
if (view.parent != null) {
|
||||
(view.parent as ViewGroup).removeView(view)
|
||||
}
|
||||
if (view is GLSurfaceView) {
|
||||
view.setZOrderOnTop(true)
|
||||
if (mSubscriber!!.renderer is DynamicVideoRenderer) {
|
||||
(mSubscriber!!.renderer as DynamicVideoRenderer).enableThumbnailCircle(true)
|
||||
thumbnail_container.addView(view)
|
||||
}
|
||||
}
|
||||
switchToFullScreenView()
|
||||
}, 4000)
|
||||
}
|
||||
|
||||
fun switchToFullScreenView() {
|
||||
mSubscriberViewContainer.postDelayed({
|
||||
val view = mSubscriber!!.view
|
||||
if (view.parent != null) {
|
||||
(view.parent as ViewGroup).removeView(view)
|
||||
}
|
||||
if (view is GLSurfaceView) {
|
||||
view.setZOrderOnTop(false)
|
||||
if (mSubscriber!!.renderer is DynamicVideoRenderer) {
|
||||
(mSubscriber!!.renderer as DynamicVideoRenderer).enableThumbnailCircle(false)
|
||||
mSubscriberViewContainer.addView(view)
|
||||
}
|
||||
}
|
||||
switchToThumbnailCircle()
|
||||
}, 4000)
|
||||
}
|
||||
|
||||
override fun onVideoDisabled(subscriberKit: SubscriberKit?, s: String?) {}
|
||||
|
||||
override fun onVideoEnabled(subscriberKit: SubscriberKit?, s: String?) {}
|
||||
|
||||
override fun onVideoDisableWarning(subscriberKit: SubscriberKit?) {}
|
||||
|
||||
override fun onVideoDisableWarningLifted(subscriberKit: SubscriberKit?) {}
|
||||
|
||||
private fun subscribeToStream(stream: Stream) {
|
||||
mSubscriber = Subscriber.Builder(requireContext(), stream)
|
||||
.renderer(DynamicVideoRenderer(requireContext()))
|
||||
.build()
|
||||
mSubscriber!!.setVideoListener(this)
|
||||
mSession!!.subscribe(mSubscriber)
|
||||
}
|
||||
|
||||
private fun disconnectSession() {
|
||||
if (mSession == null) {
|
||||
videoCallResponseListener?.onCallFinished(Activity.RESULT_CANCELED)
|
||||
// requireActivity().setResult(Activity.RESULT_CANCELED)
|
||||
dialog?.dismiss()
|
||||
return
|
||||
}
|
||||
|
||||
if (mSubscriber != null) {
|
||||
mSubscriberViewContainer.removeView(mSubscriber!!.view)
|
||||
mSession!!.unsubscribe(mSubscriber)
|
||||
mSubscriber!!.destroy()
|
||||
mSubscriber = null
|
||||
}
|
||||
if (mPublisher != null) {
|
||||
mPublisherViewContainer.removeView(mPublisher!!.view)
|
||||
mSession!!.unpublish(mPublisher)
|
||||
mPublisher!!.destroy()
|
||||
mPublisher = null
|
||||
}
|
||||
mSession!!.disconnect()
|
||||
countDownTimer?.cancel()
|
||||
|
||||
videoCallPresenter.callChangeCallStatus(ChangeCallStatusRequestModel(16, sessionStatusModel!!.doctorId, sessionStatusModel!!.generalid, token, sessionStatusModel!!.vcid))
|
||||
dialog?.dismiss()
|
||||
}
|
||||
|
||||
override fun onCallSuccessful(sessionStatusModel: SessionStatusModel) {
|
||||
if (sessionStatusModel.sessionStatus == 2 || sessionStatusModel.sessionStatus == 3) {
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putExtra("sessionStatusNotRespond", sessionStatusModel)
|
||||
videoCallResponseListener?.onCallFinished(Activity.RESULT_OK, returnIntent)
|
||||
// requireActivity().setResult(Activity.RESULT_OK, returnIntent)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCallChangeCallStatusSuccessful(sessionStatusModel: SessionStatusModel?) {}
|
||||
|
||||
override fun onFailure() {}
|
||||
|
||||
private fun onSwitchCameraClicked(view: View?) {
|
||||
if (mPublisher != null) {
|
||||
isSwitchCameraClicked = !isSwitchCameraClicked
|
||||
mPublisher!!.cycleCamera()
|
||||
val res = if (isSwitchCameraClicked) R.drawable.camera_front else R.drawable.camera_back
|
||||
mSwitchCameraBtn.setImageResource(res)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCallClicked() {
|
||||
disconnectSession()
|
||||
}
|
||||
|
||||
private fun miniCircleDoubleTap() {
|
||||
if (isCircle) {
|
||||
onMiniCircleClicked()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onMiniCircleClicked() {
|
||||
if (isCircle) {
|
||||
dialog?.window?.setLayout(
|
||||
400,
|
||||
600
|
||||
)
|
||||
} else {
|
||||
dialog?.window?.setLayout(
|
||||
300,
|
||||
300
|
||||
)
|
||||
}
|
||||
isCircle = !isCircle
|
||||
|
||||
if (mSubscriber != null) {
|
||||
(mSubscriber!!.renderer as DynamicVideoRenderer).enableThumbnailCircle(isCircle)
|
||||
} else {
|
||||
if (isCircle) {
|
||||
videoCallContainer.background = ContextCompat.getDrawable(requireContext(), R.drawable.circle_shape)
|
||||
mSubscriberViewContainer.background = ContextCompat.getDrawable(requireContext(), R.drawable.circle_shape)
|
||||
} else {
|
||||
videoCallContainer.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.text_color))
|
||||
mSubscriberViewContainer.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.text_color))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (isCircle) {
|
||||
controlPanel.visibility = View.GONE
|
||||
layoutMini.visibility = View.GONE
|
||||
} else {
|
||||
controlPanel.visibility = View.VISIBLE
|
||||
layoutMini.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun onMinimizedClicked(view: View?) {
|
||||
if (isFullScreen) {
|
||||
dialog?.window?.setLayout(
|
||||
400,
|
||||
600
|
||||
)
|
||||
} else {
|
||||
dialog?.window?.setLayout(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
isFullScreen = !isFullScreen
|
||||
|
||||
val res = if (isFullScreen) R.drawable.reducing else R.drawable.expand
|
||||
btnMinimize.setImageResource(res)
|
||||
setViewsVisibility()
|
||||
|
||||
// videoCallResponseListener?.minimizeVideoEvent(!isFullScreen)
|
||||
}
|
||||
|
||||
private fun setViewsVisibility() {
|
||||
val iconSize: Int = context!!.resources.getDimension(R.dimen.video_icon_size).toInt()
|
||||
val iconSizeSmall: Int = context!!.resources.getDimension(R.dimen.video_icon_size_small).toInt()
|
||||
val btnMinimizeLayoutParam: ConstraintLayout.LayoutParams = btnMinimize.layoutParams as ConstraintLayout.LayoutParams
|
||||
val mCallBtnLayoutParam: ConstraintLayout.LayoutParams = mCallBtn.layoutParams as ConstraintLayout.LayoutParams
|
||||
|
||||
val localPreviewMargin: Int = context!!.resources.getDimension(R.dimen.local_preview_margin_top).toInt()
|
||||
val localPreviewWidth: Int = context!!.resources.getDimension(R.dimen.local_preview_width).toInt()
|
||||
val localPreviewHeight: Int = context!!.resources.getDimension(R.dimen.local_preview_height).toInt()
|
||||
// val localPreviewIconSize: Int = context!!.resources.getDimension(R.dimen.local_back_icon_size).toInt()
|
||||
// val localPreviewMarginSmall : Int = context!!.resources.getDimension(R.dimen.local_preview_margin_small).toInt()
|
||||
// val localPreviewWidthSmall : Int = context!!.resources.getDimension(R.dimen.local_preview_width_small).toInt()
|
||||
// val localPreviewHeightSmall : Int = context!!.resources.getDimension(R.dimen.local_preview_height_small).toInt()
|
||||
// val localPreviewIconSmall: Int = context!!.resources.getDimension(R.dimen.local_back_icon_size_small).toInt()
|
||||
// val localPreviewLayoutIconParam : FrameLayout.LayoutParams
|
||||
val localPreviewLayoutParam: RelativeLayout.LayoutParams = mPublisherViewContainer.layoutParams as RelativeLayout.LayoutParams
|
||||
|
||||
val remotePreviewIconSize: Int = context!!.resources.getDimension(R.dimen.remote_back_icon_size).toInt()
|
||||
val remotePreviewIconSizeSmall: Int = context!!.resources.getDimension(R.dimen.remote_back_icon_size_small).toInt()
|
||||
val remotePreviewLayoutParam: FrameLayout.LayoutParams = mSubscriberViewIcon.layoutParams as FrameLayout.LayoutParams
|
||||
|
||||
if (isFullScreen) {
|
||||
layoutName.visibility = View.VISIBLE
|
||||
layoutMini.visibility = View.GONE
|
||||
mCameraBtn.visibility = View.VISIBLE
|
||||
mSwitchCameraBtn.visibility = View.VISIBLE
|
||||
// mspeckerBtn.visibility = View.VISIBLE
|
||||
mMicBtn.visibility = View.VISIBLE
|
||||
|
||||
btnMinimizeLayoutParam.width = iconSize
|
||||
btnMinimizeLayoutParam.height = iconSize
|
||||
mCallBtnLayoutParam.width = iconSize
|
||||
mCallBtnLayoutParam.height = iconSize
|
||||
// localPreviewLayoutIconParam = FrameLayout.LayoutParams(localPreviewIconSize, localPreviewIconSize)
|
||||
//// localPreviewLayoutParam = RelativeLayout.LayoutParams(localPreviewWidth, localPreviewHeight)
|
||||
localPreviewLayoutParam.width = localPreviewWidth
|
||||
localPreviewLayoutParam.height = localPreviewHeight
|
||||
localPreviewLayoutParam.setMargins(0, localPreviewMargin, localPreviewMargin, 0)
|
||||
// remotePreviewLayoutParam = FrameLayout.LayoutParams(remotePreviewIconSize, remotePreviewIconSize)
|
||||
remotePreviewLayoutParam.width = remotePreviewIconSize
|
||||
remotePreviewLayoutParam.height = remotePreviewIconSize
|
||||
} else {
|
||||
layoutName.visibility = View.GONE
|
||||
layoutMini.visibility = View.VISIBLE
|
||||
mCameraBtn.visibility = View.GONE
|
||||
mSwitchCameraBtn.visibility = View.GONE
|
||||
// mspeckerBtn.visibility = View.GONE
|
||||
mMicBtn.visibility = View.GONE
|
||||
// mPublisherViewContainer.visibility = View.GONE
|
||||
// mPublisherViewIcon.visibility = View.GONE
|
||||
|
||||
// layoutParam = ConstraintLayout.LayoutParams(iconSizeSmall, iconSizeSmall)
|
||||
btnMinimizeLayoutParam.width = iconSizeSmall
|
||||
btnMinimizeLayoutParam.height = iconSizeSmall
|
||||
mCallBtnLayoutParam.width = iconSizeSmall
|
||||
mCallBtnLayoutParam.height = iconSizeSmall
|
||||
|
||||
localPreviewLayoutParam.width = 0
|
||||
localPreviewLayoutParam.height = 0
|
||||
localPreviewLayoutParam.setMargins(0, localPreviewMargin / 2, localPreviewMargin / 2, 0)
|
||||
// localPreviewLayoutIconParam = FrameLayout.LayoutParams(localPreviewIconSmall, localPreviewIconSmall)
|
||||
//// localPreviewLayoutParam = RelativeLayout.LayoutParams(localPreviewWidthSmall, localPreviewHeightSmall)
|
||||
// localPreviewLayoutParam.width = localPreviewWidthSmall
|
||||
// localPreviewLayoutParam.height = localPreviewWidthSmall
|
||||
// localPreviewLayoutParam.setMargins(0,localPreviewMarginSmall, localPreviewMarginSmall, 0)
|
||||
// remotePreviewLayoutParam = FrameLayout.LayoutParams(remotePreviewIconSizeSmall, remotePreviewIconSizeSmall)
|
||||
remotePreviewLayoutParam.width = remotePreviewIconSizeSmall
|
||||
remotePreviewLayoutParam.height = remotePreviewIconSizeSmall
|
||||
|
||||
if (isCircle) {
|
||||
controlPanel.visibility = View.GONE
|
||||
layoutMini.visibility = View.GONE
|
||||
} else {
|
||||
controlPanel.visibility = View.VISIBLE
|
||||
layoutMini.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
mPublisherViewContainer.layoutParams = localPreviewLayoutParam
|
||||
// mPublisherViewIcon.layoutParams = localPreviewLayoutIconParam
|
||||
mSubscriberViewIcon.layoutParams = remotePreviewLayoutParam
|
||||
|
||||
btnMinimize.layoutParams = btnMinimizeLayoutParam
|
||||
mCallBtn.layoutParams = mCallBtnLayoutParam
|
||||
}
|
||||
|
||||
private fun onCameraClicked(view: View?) {
|
||||
if (mPublisher != null) {
|
||||
isCameraClicked = !isCameraClicked
|
||||
mPublisher!!.publishVideo = !isCameraClicked
|
||||
val res = if (isCameraClicked) R.drawable.video_disabled else R.drawable.video_enabled
|
||||
mCameraBtn!!.setImageResource(res)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onMicClicked(view: View?) {
|
||||
if (mPublisher != null) {
|
||||
isMicClicked = !isMicClicked
|
||||
mPublisher!!.publishAudio = !isMicClicked
|
||||
val res = if (isMicClicked) R.drawable.mic_disabled else R.drawable.mic_enabled
|
||||
mMicBtn!!.setImageResource(res)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSpeckerClicked(view: View?) {
|
||||
if (mSubscriber != null) {
|
||||
isSpeckerClicked = !isSpeckerClicked
|
||||
mSubscriber!!.subscribeToAudio = !isSpeckerClicked
|
||||
val res = if (isSpeckerClicked) R.drawable.audio_disabled else R.drawable.audio_enabled
|
||||
mspeckerBtn.setImageResource(res)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun handleDragDialog() {
|
||||
mWindowManager = requireActivity().getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||
getWindowManagerDefaultDisplay()
|
||||
|
||||
videoCallContainer.setOnTouchListener(dragListener)
|
||||
mSubscriberViewContainer.setOnTouchListener(dragListener)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private val dragListener: View.OnTouchListener = View.OnTouchListener { _, event ->
|
||||
mDetector.onTouchEvent(event)
|
||||
|
||||
//Get Floating widget view params
|
||||
val layoutParams: WindowManager.LayoutParams = dialog!!.window!!.attributes
|
||||
//get the touch location coordinates
|
||||
val x_cord = event.rawX.toInt()
|
||||
val y_cord = event.rawY.toInt()
|
||||
val x_cord_Destination: Int
|
||||
var y_cord_Destination: Int
|
||||
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
x_init_cord = x_cord
|
||||
y_init_cord = y_cord
|
||||
|
||||
//remember the initial position.
|
||||
x_init_margin = layoutParams.x
|
||||
y_init_margin = layoutParams.y
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
//Get the difference between initial coordinate and current coordinate
|
||||
val x_diff: Int = x_cord - x_init_cord
|
||||
val y_diff: Int = y_cord - y_init_cord
|
||||
|
||||
y_cord_Destination = y_init_margin + y_diff
|
||||
val barHeight: Int = getStatusBarHeight()
|
||||
if (y_cord_Destination < 0) {
|
||||
// y_cord_Destination = 0
|
||||
// y_cord_Destination =
|
||||
// -(szWindow.y - (videoCallContainer.height /*+ barHeight*/))
|
||||
y_cord_Destination = -(szWindow.y / 2)
|
||||
} else if (y_cord_Destination + (videoCallContainer.height + barHeight) > szWindow.y) {
|
||||
// y_cord_Destination = szWindow.y - (videoCallContainer.height + barHeight)
|
||||
y_cord_Destination = (szWindow.y / 2)
|
||||
}
|
||||
layoutParams.y = y_cord_Destination
|
||||
|
||||
//reset position if user drags the floating view
|
||||
resetPosition(x_cord)
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val x_diff_move: Int = x_cord - x_init_cord
|
||||
val y_diff_move: Int = y_cord - y_init_cord
|
||||
x_cord_Destination = x_init_margin + x_diff_move
|
||||
y_cord_Destination = y_init_margin + y_diff_move
|
||||
|
||||
layoutParams.x = x_cord_Destination
|
||||
layoutParams.y = y_cord_Destination
|
||||
|
||||
dialog!!.window!!.attributes = layoutParams
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
private fun showControlPanelTemporarily() {
|
||||
if (!isCircle) {
|
||||
controlPanel.visibility = View.VISIBLE
|
||||
mVolHandler!!.removeCallbacks(mVolRunnable!!)
|
||||
mVolHandler!!.postDelayed(mVolRunnable!!, (5 * 1000).toLong())
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset position of Floating Widget view on dragging */
|
||||
private fun resetPosition(x_cord_now: Int) {
|
||||
if (x_cord_now <= szWindow.x / 2) {
|
||||
isLeft = true
|
||||
moveToLeft(x_cord_now)
|
||||
} else {
|
||||
isLeft = false
|
||||
moveToRight(x_cord_now)
|
||||
}
|
||||
}
|
||||
|
||||
/* Method to move the Floating widget view to Left */
|
||||
private fun moveToLeft(current_x_cord: Int) {
|
||||
|
||||
var mParams: WindowManager.LayoutParams = dialog!!.window!!.attributes
|
||||
|
||||
mParams.x =
|
||||
(szWindow.x - current_x_cord * current_x_cord - videoCallContainer.width).toInt()
|
||||
|
||||
dialog!!.window!!.attributes = mParams
|
||||
val x = szWindow.x - current_x_cord
|
||||
object : CountDownTimer(500, 5) {
|
||||
//get params of Floating Widget view
|
||||
var mParams: WindowManager.LayoutParams = dialog!!.window!!.attributes
|
||||
override fun onTick(t: Long) {
|
||||
val step = (500 - t) / 5
|
||||
// mParams.x = 0 - (current_x_cord * current_x_cord * step).toInt()
|
||||
mParams.x =
|
||||
(szWindow.x - current_x_cord * current_x_cord * step - videoCallContainer.width).toInt()
|
||||
|
||||
dialog!!.window!!.attributes = mParams
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
mParams.x = -(szWindow.x - videoCallContainer.width)
|
||||
|
||||
dialog!!.window!!.attributes = mParams
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
/* Method to move the Floating widget view to Right */
|
||||
private fun moveToRight(current_x_cord: Int) {
|
||||
// var mParams : WindowManager.LayoutParams = dialog!!.window!!.attributes
|
||||
// mParams.x =
|
||||
// (szWindow.x + current_x_cord * current_x_cord - videoCallContainer.width).toInt()
|
||||
//
|
||||
// dialog!!.window!!.attributes = mParams
|
||||
object : CountDownTimer(500, 5) {
|
||||
//get params of Floating Widget view
|
||||
var mParams: WindowManager.LayoutParams = dialog!!.window!!.attributes
|
||||
override fun onTick(t: Long) {
|
||||
val step = (500 - t) / 5
|
||||
mParams.x =
|
||||
(szWindow.x + current_x_cord * current_x_cord * step - videoCallContainer.width).toInt()
|
||||
|
||||
dialog!!.window!!.attributes = mParams
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
mParams.x = szWindow.x - videoCallContainer.width
|
||||
|
||||
dialog!!.window!!.attributes = mParams
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun getWindowManagerDefaultDisplay() {
|
||||
mWindowManager.getDefaultDisplay()
|
||||
.getSize(szWindow)
|
||||
}
|
||||
|
||||
/* return status bar height on basis of device display metrics */
|
||||
private fun getStatusBarHeight(): Int {
|
||||
return ceil(
|
||||
(25 * requireActivity().applicationContext.resources.displayMetrics.density).toDouble()
|
||||
).toInt()
|
||||
}
|
||||
|
||||
private class MyGestureListener(val onTabCall: () -> Unit, val miniCircleDoubleTap: () -> Unit) : GestureDetector.SimpleOnGestureListener() {
|
||||
|
||||
override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
|
||||
onTabCall()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDoubleTap(e: MotionEvent?): Boolean {
|
||||
miniCircleDoubleTap()
|
||||
return super.onDoubleTap(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(args: Bundle) =
|
||||
VideoCallFragment().apply {
|
||||
arguments = args
|
||||
}
|
||||
|
||||
private val TAG = VideoCallFragment::class.java.simpleName
|
||||
|
||||
private const val RC_SETTINGS_SCREEN_PERM = 123
|
||||
private const val RC_VIDEO_APP_PERM = 124
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,379 @@
|
||||
package com.hmg.hmgDr.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.PixelFormat
|
||||
import android.opengl.GLES20
|
||||
import android.opengl.GLSurfaceView
|
||||
import android.opengl.Matrix
|
||||
import android.view.View
|
||||
import com.opentok.android.BaseVideoRenderer
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.FloatBuffer
|
||||
import java.nio.ShortBuffer
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import javax.microedition.khronos.egl.EGLConfig
|
||||
import javax.microedition.khronos.opengles.GL10
|
||||
|
||||
/*
|
||||
* https://nhancv.medium.com/android-how-to-make-a-circular-view-as-a-thumbnail-of-opentok-27992aee15c9
|
||||
* to solve make circle video stream
|
||||
* */
|
||||
|
||||
class DynamicVideoRenderer(private val mContext: Context) : BaseVideoRenderer() {
|
||||
private val mView: GLSurfaceView = GLSurfaceView(mContext)
|
||||
private val mRenderer: MyRenderer
|
||||
|
||||
interface DynamicVideoRendererMetadataListener {
|
||||
fun onMetadataReady(metadata: ByteArray?)
|
||||
}
|
||||
|
||||
fun setDynamicVideoRendererMetadataListener(metadataListener: DynamicVideoRendererMetadataListener?) {
|
||||
mRenderer.metadataListener = metadataListener
|
||||
}
|
||||
|
||||
fun enableThumbnailCircle(enable: Boolean) {
|
||||
mRenderer.requestEnableThumbnailCircle = enable
|
||||
}
|
||||
|
||||
internal class MyRenderer : GLSurfaceView.Renderer {
|
||||
var mTextureIds = IntArray(3)
|
||||
var mScaleMatrix = FloatArray(16)
|
||||
private val mVertexBuffer: FloatBuffer
|
||||
private val mTextureBuffer: FloatBuffer
|
||||
private val mDrawListBuffer: ShortBuffer
|
||||
var requestEnableThumbnailCircle = false
|
||||
var mVideoFitEnabled = true
|
||||
var mVideoDisabled = false
|
||||
private val mVertexIndex = shortArrayOf(0, 1, 2, 0, 2, 3) // order to draw
|
||||
|
||||
// vertices
|
||||
private val vertexShaderCode = """uniform mat4 uMVPMatrix;attribute vec4 aPosition;
|
||||
attribute vec2 aTextureCoord;
|
||||
varying vec2 vTextureCoord;
|
||||
void main() {
|
||||
gl_Position = uMVPMatrix * aPosition;
|
||||
vTextureCoord = aTextureCoord;
|
||||
}
|
||||
"""
|
||||
private val fragmentShaderCode = """precision mediump float;
|
||||
uniform sampler2D Ytex;
|
||||
uniform sampler2D Utex,Vtex;
|
||||
uniform int enableCircle;
|
||||
uniform vec2 radiusDp;
|
||||
varying vec2 vTextureCoord;
|
||||
void main(void) {
|
||||
float nx,ny,r,g,b,y,u,v;
|
||||
mediump vec4 txl,ux,vx; nx=vTextureCoord[0];
|
||||
ny=vTextureCoord[1];
|
||||
y=texture2D(Ytex,vec2(nx,ny)).r;
|
||||
u=texture2D(Utex,vec2(nx,ny)).r;
|
||||
v=texture2D(Vtex,vec2(nx,ny)).r;
|
||||
y=1.1643*(y-0.0625);
|
||||
u=u-0.5;
|
||||
v=v-0.5;
|
||||
r=y+1.5958*v;
|
||||
g=y-0.39173*u-0.81290*v;
|
||||
b=y+2.017*u;
|
||||
if (enableCircle > 0) {
|
||||
float radius = 0.5;
|
||||
vec4 color0 = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 color1 = vec4(r, g, b, 1.0);
|
||||
vec2 st = (gl_FragCoord.xy/radiusDp.xy); float dist = radius - distance(st,vec2(0.5));
|
||||
float t = 1.0;
|
||||
if (dist < 0.0) t = 0.0;
|
||||
gl_FragColor = mix(color0, color1, t);
|
||||
}
|
||||
else {
|
||||
gl_FragColor = vec4(r, g, b, 1.0);
|
||||
}
|
||||
}
|
||||
"""
|
||||
var mFrameLock = ReentrantLock()
|
||||
var mCurrentFrame: Frame? = null
|
||||
private var mProgram = 0
|
||||
private var mTextureWidth = 0
|
||||
private var mTextureHeight = 0
|
||||
private var mViewportWidth = 0
|
||||
private var mViewportHeight = 0
|
||||
override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
|
||||
gl.glClearColor(0f, 0f, 0f, 1f)
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
|
||||
val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
|
||||
vertexShaderCode)
|
||||
val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
|
||||
fragmentShaderCode)
|
||||
mProgram = GLES20.glCreateProgram() // create empty OpenGL ES
|
||||
// Program
|
||||
GLES20.glAttachShader(mProgram, vertexShader) // add the vertex
|
||||
// shader to program
|
||||
GLES20.glAttachShader(mProgram, fragmentShader) // add the fragment
|
||||
// shader to
|
||||
// program
|
||||
GLES20.glLinkProgram(mProgram)
|
||||
val positionHandle = GLES20.glGetAttribLocation(mProgram,
|
||||
"aPosition")
|
||||
val textureHandle = GLES20.glGetAttribLocation(mProgram,
|
||||
"aTextureCoord")
|
||||
GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
|
||||
GLES20.GL_FLOAT, false, COORDS_PER_VERTEX * 4,
|
||||
mVertexBuffer)
|
||||
GLES20.glEnableVertexAttribArray(positionHandle)
|
||||
GLES20.glVertexAttribPointer(textureHandle,
|
||||
TEXTURECOORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
|
||||
TEXTURECOORDS_PER_VERTEX * 4, mTextureBuffer)
|
||||
GLES20.glEnableVertexAttribArray(textureHandle)
|
||||
GLES20.glUseProgram(mProgram)
|
||||
var i = GLES20.glGetUniformLocation(mProgram, "Ytex")
|
||||
GLES20.glUniform1i(i, 0) /* Bind Ytex to texture unit 0 */
|
||||
i = GLES20.glGetUniformLocation(mProgram, "Utex")
|
||||
GLES20.glUniform1i(i, 1) /* Bind Utex to texture unit 1 */
|
||||
i = GLES20.glGetUniformLocation(mProgram, "Vtex")
|
||||
GLES20.glUniform1i(i, 2) /* Bind Vtex to texture unit 2 */
|
||||
val radiusDpLocation = GLES20.glGetUniformLocation(mProgram, "radiusDp")
|
||||
val radiusDp = (Resources.getSystem().displayMetrics.density * THUMBNAIL_SIZE).toInt()
|
||||
GLES20.glUniform2f(radiusDpLocation, radiusDp.toFloat(), radiusDp.toFloat())
|
||||
mTextureWidth = 0
|
||||
mTextureHeight = 0
|
||||
}
|
||||
|
||||
fun enableThumbnailCircle(enable: Boolean) {
|
||||
GLES20.glUseProgram(mProgram)
|
||||
val enableCircleLocation = GLES20.glGetUniformLocation(mProgram, "enableCircle")
|
||||
GLES20.glUniform1i(enableCircleLocation, if (enable) 1 else 0)
|
||||
}
|
||||
|
||||
fun setupTextures(frame: Frame) {
|
||||
if (mTextureIds[0] != 0) {
|
||||
GLES20.glDeleteTextures(3, mTextureIds, 0)
|
||||
}
|
||||
GLES20.glGenTextures(3, mTextureIds, 0)
|
||||
val w = frame.width
|
||||
val h = frame.height
|
||||
val hw = w + 1 shr 1
|
||||
val hh = h + 1 shr 1
|
||||
initializeTexture(GLES20.GL_TEXTURE0, mTextureIds[0], w, h)
|
||||
initializeTexture(GLES20.GL_TEXTURE1, mTextureIds[1], hw, hh)
|
||||
initializeTexture(GLES20.GL_TEXTURE2, mTextureIds[2], hw, hh)
|
||||
mTextureWidth = frame.width
|
||||
mTextureHeight = frame.height
|
||||
}
|
||||
|
||||
fun updateTextures(frame: Frame) {
|
||||
val width = frame.width
|
||||
val height = frame.height
|
||||
val half_width = width + 1 shr 1
|
||||
val half_height = height + 1 shr 1
|
||||
val y_size = width * height
|
||||
val uv_size = half_width * half_height
|
||||
val bb = frame.buffer
|
||||
// If we are reusing this frame, make sure we reset position and
|
||||
// limit
|
||||
bb.clear()
|
||||
if (bb.remaining() == y_size + uv_size * 2) {
|
||||
bb.position(0)
|
||||
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1)
|
||||
GLES20.glPixelStorei(GLES20.GL_PACK_ALIGNMENT, 1)
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[0])
|
||||
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, width,
|
||||
height, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE,
|
||||
bb)
|
||||
bb.position(y_size)
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE1)
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[1])
|
||||
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0,
|
||||
half_width, half_height, GLES20.GL_LUMINANCE,
|
||||
GLES20.GL_UNSIGNED_BYTE, bb)
|
||||
bb.position(y_size + uv_size)
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE2)
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[2])
|
||||
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0,
|
||||
half_width, half_height, GLES20.GL_LUMINANCE,
|
||||
GLES20.GL_UNSIGNED_BYTE, bb)
|
||||
} else {
|
||||
mTextureWidth = 0
|
||||
mTextureHeight = 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
|
||||
GLES20.glViewport(0, 0, width, height)
|
||||
mViewportWidth = width
|
||||
mViewportHeight = height
|
||||
}
|
||||
|
||||
var metadataListener: DynamicVideoRendererMetadataListener? = null
|
||||
override fun onDrawFrame(gl: GL10) {
|
||||
gl.glClearColor(0f, 0f, 0f, 0f)
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
|
||||
mFrameLock.lock()
|
||||
if (mCurrentFrame != null && !mVideoDisabled) {
|
||||
GLES20.glUseProgram(mProgram)
|
||||
if (mTextureWidth != mCurrentFrame!!.width
|
||||
|| mTextureHeight != mCurrentFrame!!.height) {
|
||||
setupTextures(mCurrentFrame!!)
|
||||
}
|
||||
updateTextures(mCurrentFrame!!)
|
||||
Matrix.setIdentityM(mScaleMatrix, 0)
|
||||
var scaleX = 1.0f
|
||||
var scaleY = 1.0f
|
||||
val ratio = (mCurrentFrame!!.width.toFloat()
|
||||
/ mCurrentFrame!!.height)
|
||||
val vratio = mViewportWidth.toFloat() / mViewportHeight
|
||||
if (mVideoFitEnabled) {
|
||||
if (ratio > vratio) {
|
||||
scaleY = vratio / ratio
|
||||
} else {
|
||||
scaleX = ratio / vratio
|
||||
}
|
||||
} else {
|
||||
if (ratio < vratio) {
|
||||
scaleY = vratio / ratio
|
||||
} else {
|
||||
scaleX = ratio / vratio
|
||||
}
|
||||
}
|
||||
Matrix.scaleM(mScaleMatrix, 0,
|
||||
scaleX * if (mCurrentFrame!!.isMirroredX) -1.0f else 1.0f,
|
||||
scaleY, 1f)
|
||||
metadataListener?.onMetadataReady(mCurrentFrame!!.metadata)
|
||||
val mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram,
|
||||
"uMVPMatrix")
|
||||
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false,
|
||||
mScaleMatrix, 0)
|
||||
enableThumbnailCircle(requestEnableThumbnailCircle)
|
||||
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mVertexIndex.size,
|
||||
GLES20.GL_UNSIGNED_SHORT, mDrawListBuffer)
|
||||
} else {
|
||||
//black frame when video is disabled
|
||||
gl.glClearColor(0f, 0f, 0f, 1f)
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
|
||||
}
|
||||
mFrameLock.unlock()
|
||||
}
|
||||
|
||||
fun displayFrame(frame: Frame?) {
|
||||
mFrameLock.lock()
|
||||
if (mCurrentFrame != null) {
|
||||
mCurrentFrame!!.recycle()
|
||||
}
|
||||
mCurrentFrame = frame
|
||||
mFrameLock.unlock()
|
||||
}
|
||||
|
||||
fun disableVideo(b: Boolean) {
|
||||
mFrameLock.lock()
|
||||
mVideoDisabled = b
|
||||
if (mVideoDisabled) {
|
||||
if (mCurrentFrame != null) {
|
||||
mCurrentFrame!!.recycle()
|
||||
}
|
||||
mCurrentFrame = null
|
||||
}
|
||||
mFrameLock.unlock()
|
||||
}
|
||||
|
||||
fun enableVideoFit(enableVideoFit: Boolean) {
|
||||
mVideoFitEnabled = enableVideoFit
|
||||
}
|
||||
|
||||
companion object {
|
||||
// number of coordinates per vertex in this array
|
||||
const val COORDS_PER_VERTEX = 3
|
||||
const val TEXTURECOORDS_PER_VERTEX = 2
|
||||
var mXYZCoords = floatArrayOf(
|
||||
-1.0f, 1.0f, 0.0f, // top left
|
||||
-1.0f, -1.0f, 0.0f, // bottom left
|
||||
1.0f, -1.0f, 0.0f, // bottom right
|
||||
1.0f, 1.0f, 0.0f // top right
|
||||
)
|
||||
var mUVCoords = floatArrayOf(0f, 0f, 0f, 1f, 1f, 1f, 1f, 0f)
|
||||
fun initializeTexture(name: Int, id: Int, width: Int, height: Int) {
|
||||
GLES20.glActiveTexture(name)
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id)
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST.toFloat())
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat())
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE.toFloat())
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE.toFloat())
|
||||
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
|
||||
width, height, 0, GLES20.GL_LUMINANCE,
|
||||
GLES20.GL_UNSIGNED_BYTE, null)
|
||||
}
|
||||
|
||||
fun loadShader(type: Int, shaderCode: String?): Int {
|
||||
val shader = GLES20.glCreateShader(type)
|
||||
GLES20.glShaderSource(shader, shaderCode)
|
||||
GLES20.glCompileShader(shader)
|
||||
return shader
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val bb = ByteBuffer.allocateDirect(mXYZCoords.size * 4)
|
||||
bb.order(ByteOrder.nativeOrder())
|
||||
mVertexBuffer = bb.asFloatBuffer()
|
||||
mVertexBuffer.put(mXYZCoords)
|
||||
mVertexBuffer.position(0)
|
||||
val tb = ByteBuffer.allocateDirect(mUVCoords.size * 4)
|
||||
tb.order(ByteOrder.nativeOrder())
|
||||
mTextureBuffer = tb.asFloatBuffer()
|
||||
mTextureBuffer.put(mUVCoords)
|
||||
mTextureBuffer.position(0)
|
||||
val dlb = ByteBuffer.allocateDirect(mVertexIndex.size * 2)
|
||||
dlb.order(ByteOrder.nativeOrder())
|
||||
mDrawListBuffer = dlb.asShortBuffer()
|
||||
mDrawListBuffer.put(mVertexIndex)
|
||||
mDrawListBuffer.position(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFrame(frame: Frame) {
|
||||
mRenderer.displayFrame(frame)
|
||||
mView.requestRender()
|
||||
}
|
||||
|
||||
override fun setStyle(key: String, value: String) {
|
||||
if (STYLE_VIDEO_SCALE == key) {
|
||||
if (STYLE_VIDEO_FIT == value) {
|
||||
mRenderer.enableVideoFit(true)
|
||||
} else if (STYLE_VIDEO_FILL == value) {
|
||||
mRenderer.enableVideoFit(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onVideoPropertiesChanged(videoEnabled: Boolean) {
|
||||
mRenderer.disableVideo(!videoEnabled)
|
||||
}
|
||||
|
||||
override fun getView(): View {
|
||||
return mView
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
mView.onPause()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
mView.onResume()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val THUMBNAIL_SIZE = 90 //in dp
|
||||
}
|
||||
|
||||
init {
|
||||
mView.setEGLContextClientVersion(2)
|
||||
mView.setEGLConfigChooser(8, 8, 8, 8, 16, 0)
|
||||
mView.holder.setFormat(PixelFormat.TRANSLUCENT)
|
||||
mRenderer = MyRenderer()
|
||||
mView.setRenderer(mRenderer)
|
||||
mView.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,357 @@
|
||||
package com.hmg.hmgDr.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.PixelFormat
|
||||
import android.opengl.GLES20
|
||||
import android.opengl.GLSurfaceView
|
||||
import android.opengl.Matrix
|
||||
import android.view.View
|
||||
import com.opentok.android.BaseVideoRenderer
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.FloatBuffer
|
||||
import java.nio.ShortBuffer
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import javax.microedition.khronos.egl.EGLConfig
|
||||
import javax.microedition.khronos.opengles.GL10
|
||||
|
||||
|
||||
class ThumbnailCircleVideoRenderer(private val mContext: Context) : BaseVideoRenderer() {
|
||||
private val mView: GLSurfaceView = GLSurfaceView(mContext)
|
||||
private val mRenderer: MyRenderer
|
||||
|
||||
interface ThumbnailCircleVideoRendererMetadataListener {
|
||||
fun onMetadataReady(metadata: ByteArray?)
|
||||
}
|
||||
|
||||
fun setThumbnailCircleVideoRendererMetadataListener(metadataListener: ThumbnailCircleVideoRendererMetadataListener?) {
|
||||
mRenderer.metadataListener = metadataListener
|
||||
}
|
||||
|
||||
internal class MyRenderer : GLSurfaceView.Renderer {
|
||||
var mTextureIds = IntArray(3)
|
||||
var mScaleMatrix = FloatArray(16)
|
||||
private val mVertexBuffer: FloatBuffer
|
||||
private val mTextureBuffer: FloatBuffer
|
||||
private val mDrawListBuffer: ShortBuffer
|
||||
var mVideoFitEnabled = true
|
||||
var mVideoDisabled = false
|
||||
private val mVertexIndex = shortArrayOf(0, 1, 2, 0, 2, 3) // order to draw
|
||||
|
||||
// vertices
|
||||
private val vertexShaderCode = """uniform mat4 uMVPMatrix;attribute vec4 aPosition;
|
||||
attribute vec2 aTextureCoord;
|
||||
varying vec2 vTextureCoord;
|
||||
void main() {
|
||||
gl_Position = uMVPMatrix * aPosition;
|
||||
vTextureCoord = aTextureCoord;
|
||||
}
|
||||
"""
|
||||
private val fragmentShaderCode = """precision mediump float;
|
||||
uniform sampler2D Ytex;
|
||||
uniform sampler2D Utex,Vtex;
|
||||
uniform vec2 radiusDp;
|
||||
varying vec2 vTextureCoord;
|
||||
void main(void) {
|
||||
float nx,ny,r,g,b,y,u,v;
|
||||
mediump vec4 txl,ux,vx; nx=vTextureCoord[0];
|
||||
ny=vTextureCoord[1];
|
||||
y=texture2D(Ytex,vec2(nx,ny)).r;
|
||||
u=texture2D(Utex,vec2(nx,ny)).r;
|
||||
v=texture2D(Vtex,vec2(nx,ny)).r;
|
||||
y=1.1643*(y-0.0625);
|
||||
u=u-0.5;
|
||||
v=v-0.5;
|
||||
r=y+1.5958*v;
|
||||
g=y-0.39173*u-0.81290*v;
|
||||
b=y+2.017*u;
|
||||
float radius = 0.5;
|
||||
vec4 color0 = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 color1 = vec4(r, g, b, 1.0);
|
||||
vec2 st = (gl_FragCoord.xy/radiusDp.xy); float dist = radius - distance(st,vec2(0.5));
|
||||
float t = 1.0;
|
||||
if (dist < 0.0) t = 0.0;
|
||||
gl_FragColor = mix(color0, color1, t);
|
||||
}
|
||||
"""
|
||||
var mFrameLock = ReentrantLock()
|
||||
var mCurrentFrame: Frame? = null
|
||||
private var mProgram = 0
|
||||
private var mTextureWidth = 0
|
||||
private var mTextureHeight = 0
|
||||
private var mViewportWidth = 0
|
||||
private var mViewportHeight = 0
|
||||
override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
|
||||
gl.glClearColor(0f, 0f, 0f, 1f)
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
|
||||
val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
|
||||
vertexShaderCode)
|
||||
val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
|
||||
fragmentShaderCode)
|
||||
mProgram = GLES20.glCreateProgram() // create empty OpenGL ES
|
||||
// Program
|
||||
GLES20.glAttachShader(mProgram, vertexShader) // add the vertex
|
||||
// shader to program
|
||||
GLES20.glAttachShader(mProgram, fragmentShader) // add the fragment
|
||||
// shader to
|
||||
// program
|
||||
GLES20.glLinkProgram(mProgram)
|
||||
val positionHandle = GLES20.glGetAttribLocation(mProgram,
|
||||
"aPosition")
|
||||
val textureHandle = GLES20.glGetAttribLocation(mProgram,
|
||||
"aTextureCoord")
|
||||
GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
|
||||
GLES20.GL_FLOAT, false, COORDS_PER_VERTEX * 4,
|
||||
mVertexBuffer)
|
||||
GLES20.glEnableVertexAttribArray(positionHandle)
|
||||
GLES20.glVertexAttribPointer(textureHandle,
|
||||
TEXTURECOORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
|
||||
TEXTURECOORDS_PER_VERTEX * 4, mTextureBuffer)
|
||||
GLES20.glEnableVertexAttribArray(textureHandle)
|
||||
GLES20.glUseProgram(mProgram)
|
||||
var i = GLES20.glGetUniformLocation(mProgram, "Ytex")
|
||||
GLES20.glUniform1i(i, 0) /* Bind Ytex to texture unit 0 */
|
||||
i = GLES20.glGetUniformLocation(mProgram, "Utex")
|
||||
GLES20.glUniform1i(i, 1) /* Bind Utex to texture unit 1 */
|
||||
i = GLES20.glGetUniformLocation(mProgram, "Vtex")
|
||||
GLES20.glUniform1i(i, 2) /* Bind Vtex to texture unit 2 */
|
||||
val radiusDpLocation = GLES20.glGetUniformLocation(mProgram, "radiusDp")
|
||||
val radiusDp = (Resources.getSystem().displayMetrics.density * THUMBNAIL_SIZE).toInt()
|
||||
GLES20.glUniform2f(radiusDpLocation, radiusDp.toFloat(), radiusDp.toFloat())
|
||||
mTextureWidth = 0
|
||||
mTextureHeight = 0
|
||||
}
|
||||
|
||||
fun setupTextures(frame: Frame) {
|
||||
if (mTextureIds[0] != 0) {
|
||||
GLES20.glDeleteTextures(3, mTextureIds, 0)
|
||||
}
|
||||
GLES20.glGenTextures(3, mTextureIds, 0)
|
||||
val w = frame.width
|
||||
val h = frame.height
|
||||
val hw = w + 1 shr 1
|
||||
val hh = h + 1 shr 1
|
||||
initializeTexture(GLES20.GL_TEXTURE0, mTextureIds[0], w, h)
|
||||
initializeTexture(GLES20.GL_TEXTURE1, mTextureIds[1], hw, hh)
|
||||
initializeTexture(GLES20.GL_TEXTURE2, mTextureIds[2], hw, hh)
|
||||
mTextureWidth = frame.width
|
||||
mTextureHeight = frame.height
|
||||
}
|
||||
|
||||
fun updateTextures(frame: Frame) {
|
||||
val width = frame.width
|
||||
val height = frame.height
|
||||
val half_width = width + 1 shr 1
|
||||
val half_height = height + 1 shr 1
|
||||
val y_size = width * height
|
||||
val uv_size = half_width * half_height
|
||||
val bb = frame.buffer
|
||||
// If we are reusing this frame, make sure we reset position and
|
||||
// limit
|
||||
bb.clear()
|
||||
if (bb.remaining() == y_size + uv_size * 2) {
|
||||
bb.position(0)
|
||||
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1)
|
||||
GLES20.glPixelStorei(GLES20.GL_PACK_ALIGNMENT, 1)
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[0])
|
||||
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, width,
|
||||
height, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE,
|
||||
bb)
|
||||
bb.position(y_size)
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE1)
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[1])
|
||||
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0,
|
||||
half_width, half_height, GLES20.GL_LUMINANCE,
|
||||
GLES20.GL_UNSIGNED_BYTE, bb)
|
||||
bb.position(y_size + uv_size)
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE2)
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[2])
|
||||
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0,
|
||||
half_width, half_height, GLES20.GL_LUMINANCE,
|
||||
GLES20.GL_UNSIGNED_BYTE, bb)
|
||||
} else {
|
||||
mTextureWidth = 0
|
||||
mTextureHeight = 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
|
||||
GLES20.glViewport(0, 0, width, height)
|
||||
mViewportWidth = width
|
||||
mViewportHeight = height
|
||||
}
|
||||
|
||||
var metadataListener: ThumbnailCircleVideoRendererMetadataListener? = null
|
||||
override fun onDrawFrame(gl: GL10) {
|
||||
gl.glClearColor(0f, 0f, 0f, 0f)
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
|
||||
mFrameLock.lock()
|
||||
if (mCurrentFrame != null && !mVideoDisabled) {
|
||||
GLES20.glUseProgram(mProgram)
|
||||
if (mTextureWidth != mCurrentFrame!!.width
|
||||
|| mTextureHeight != mCurrentFrame!!.height) {
|
||||
setupTextures(mCurrentFrame!!)
|
||||
}
|
||||
updateTextures(mCurrentFrame!!)
|
||||
Matrix.setIdentityM(mScaleMatrix, 0)
|
||||
var scaleX = 1.0f
|
||||
var scaleY = 1.0f
|
||||
val ratio = (mCurrentFrame!!.width.toFloat()
|
||||
/ mCurrentFrame!!.height)
|
||||
val vratio = mViewportWidth.toFloat() / mViewportHeight
|
||||
if (mVideoFitEnabled) {
|
||||
if (ratio > vratio) {
|
||||
scaleY = vratio / ratio
|
||||
} else {
|
||||
scaleX = ratio / vratio
|
||||
}
|
||||
} else {
|
||||
if (ratio < vratio) {
|
||||
scaleY = vratio / ratio
|
||||
} else {
|
||||
scaleX = ratio / vratio
|
||||
}
|
||||
}
|
||||
Matrix.scaleM(mScaleMatrix, 0,
|
||||
scaleX * if (mCurrentFrame!!.isMirroredX) -1.0f else 1.0f,
|
||||
scaleY, 1f)
|
||||
metadataListener?.onMetadataReady(mCurrentFrame!!.metadata)
|
||||
val mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram,
|
||||
"uMVPMatrix")
|
||||
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false,
|
||||
mScaleMatrix, 0)
|
||||
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mVertexIndex.size,
|
||||
GLES20.GL_UNSIGNED_SHORT, mDrawListBuffer)
|
||||
} else {
|
||||
//black frame when video is disabled
|
||||
gl.glClearColor(0f, 0f, 0f, 1f)
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
|
||||
}
|
||||
mFrameLock.unlock()
|
||||
}
|
||||
|
||||
fun displayFrame(frame: Frame?) {
|
||||
mFrameLock.lock()
|
||||
if (mCurrentFrame != null) {
|
||||
mCurrentFrame!!.recycle()
|
||||
}
|
||||
mCurrentFrame = frame
|
||||
mFrameLock.unlock()
|
||||
}
|
||||
|
||||
fun disableVideo(b: Boolean) {
|
||||
mFrameLock.lock()
|
||||
mVideoDisabled = b
|
||||
if (mVideoDisabled) {
|
||||
if (mCurrentFrame != null) {
|
||||
mCurrentFrame!!.recycle()
|
||||
}
|
||||
mCurrentFrame = null
|
||||
}
|
||||
mFrameLock.unlock()
|
||||
}
|
||||
|
||||
fun enableVideoFit(enableVideoFit: Boolean) {
|
||||
mVideoFitEnabled = enableVideoFit
|
||||
}
|
||||
|
||||
companion object {
|
||||
// number of coordinates per vertex in this array
|
||||
const val COORDS_PER_VERTEX = 3
|
||||
const val TEXTURECOORDS_PER_VERTEX = 2
|
||||
var mXYZCoords = floatArrayOf(
|
||||
-1.0f, 1.0f, 0.0f, // top left
|
||||
-1.0f, -1.0f, 0.0f, // bottom left
|
||||
1.0f, -1.0f, 0.0f, // bottom right
|
||||
1.0f, 1.0f, 0.0f // top right
|
||||
)
|
||||
var mUVCoords = floatArrayOf(0f, 0f, 0f, 1f, 1f, 1f, 1f, 0f)
|
||||
fun initializeTexture(name: Int, id: Int, width: Int, height: Int) {
|
||||
GLES20.glActiveTexture(name)
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id)
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST.toFloat())
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat())
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE.toFloat())
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE.toFloat())
|
||||
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
|
||||
width, height, 0, GLES20.GL_LUMINANCE,
|
||||
GLES20.GL_UNSIGNED_BYTE, null)
|
||||
}
|
||||
|
||||
fun loadShader(type: Int, shaderCode: String?): Int {
|
||||
val shader = GLES20.glCreateShader(type)
|
||||
GLES20.glShaderSource(shader, shaderCode)
|
||||
GLES20.glCompileShader(shader)
|
||||
return shader
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val bb = ByteBuffer.allocateDirect(mXYZCoords.size * 4)
|
||||
bb.order(ByteOrder.nativeOrder())
|
||||
mVertexBuffer = bb.asFloatBuffer()
|
||||
mVertexBuffer.put(mXYZCoords)
|
||||
mVertexBuffer.position(0)
|
||||
val tb = ByteBuffer.allocateDirect(mUVCoords.size * 4)
|
||||
tb.order(ByteOrder.nativeOrder())
|
||||
mTextureBuffer = tb.asFloatBuffer()
|
||||
mTextureBuffer.put(mUVCoords)
|
||||
mTextureBuffer.position(0)
|
||||
val dlb = ByteBuffer.allocateDirect(mVertexIndex.size * 2)
|
||||
dlb.order(ByteOrder.nativeOrder())
|
||||
mDrawListBuffer = dlb.asShortBuffer()
|
||||
mDrawListBuffer.put(mVertexIndex)
|
||||
mDrawListBuffer.position(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFrame(frame: Frame) {
|
||||
mRenderer.displayFrame(frame)
|
||||
mView.requestRender()
|
||||
}
|
||||
|
||||
override fun setStyle(key: String, value: String) {
|
||||
if (STYLE_VIDEO_SCALE == key) {
|
||||
if (STYLE_VIDEO_FIT == value) {
|
||||
mRenderer.enableVideoFit(true)
|
||||
} else if (STYLE_VIDEO_FILL == value) {
|
||||
mRenderer.enableVideoFit(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onVideoPropertiesChanged(videoEnabled: Boolean) {
|
||||
mRenderer.disableVideo(!videoEnabled)
|
||||
}
|
||||
|
||||
override fun getView(): View {
|
||||
return mView
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
mView.onPause()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
mView.onResume()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val THUMBNAIL_SIZE = 90 //in dp
|
||||
}
|
||||
|
||||
init {
|
||||
mView.setEGLContextClientVersion(2)
|
||||
mView.setEGLConfigChooser(8, 8, 8, 8, 16, 0)
|
||||
mView.holder.setFormat(PixelFormat.TRANSLUCENT)
|
||||
mRenderer = MyRenderer()
|
||||
mView.setRenderer(mRenderer)
|
||||
mView.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
|
||||
<solid
|
||||
android:color="@color/remoteBackground"/>
|
||||
|
||||
<stroke android:width="2dp" android:color="@color/text_color" />
|
||||
|
||||
<size
|
||||
android:width="120dp"
|
||||
android:height="120dp"/>
|
||||
</shape>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,13H5v-2h14v2z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/text_color" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/text_color" />
|
||||
<corners android:radius="10dp" />
|
||||
<padding
|
||||
android:bottom="0dp"
|
||||
android:left="0dp"
|
||||
android:right="0dp"
|
||||
android:top="0dp" />
|
||||
</shape>
|
||||
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="1000dp"/>
|
||||
|
||||
<solid android:color="@color/green_dark"/>
|
||||
|
||||
</shape>
|
||||
|
After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.6 KiB |
@ -0,0 +1,4 @@
|
||||
enum PatientType{
|
||||
IN_PATIENT,
|
||||
OUT_PATIENT,
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
class MyReferralPatientRequestModel {
|
||||
int channel;
|
||||
int clinicID;
|
||||
int doctorID;
|
||||
int editedBy;
|
||||
String firstName;
|
||||
String from;
|
||||
String iPAdress;
|
||||
bool isLoginForDoctorApp;
|
||||
int languageID;
|
||||
String lastName;
|
||||
String middleName;
|
||||
int patientID;
|
||||
String patientIdentificationID;
|
||||
String patientMobileNumber;
|
||||
bool patientOutSA;
|
||||
int patientTypeID;
|
||||
int projectID;
|
||||
String sessionID;
|
||||
String stamp;
|
||||
String to;
|
||||
String tokenID;
|
||||
double versionID;
|
||||
String vidaAuthTokenID;
|
||||
|
||||
MyReferralPatientRequestModel(
|
||||
{this.channel,
|
||||
this.clinicID,
|
||||
this.doctorID,
|
||||
this.editedBy,
|
||||
this.firstName,
|
||||
this.from,
|
||||
this.iPAdress,
|
||||
this.isLoginForDoctorApp,
|
||||
this.languageID,
|
||||
this.lastName,
|
||||
this.middleName,
|
||||
this.patientID,
|
||||
this.patientIdentificationID,
|
||||
this.patientMobileNumber,
|
||||
this.patientOutSA,
|
||||
this.patientTypeID,
|
||||
this.projectID,
|
||||
this.sessionID,
|
||||
this.stamp,
|
||||
this.to,
|
||||
this.tokenID,
|
||||
this.versionID,
|
||||
this.vidaAuthTokenID});
|
||||
|
||||
MyReferralPatientRequestModel.fromJson(Map<String, dynamic> json) {
|
||||
channel = json['Channel'];
|
||||
clinicID = json['ClinicID'];
|
||||
doctorID = json['DoctorID'];
|
||||
editedBy = json['EditedBy'];
|
||||
firstName = json['FirstName'];
|
||||
from = json['From'];
|
||||
iPAdress = json['IPAdress'];
|
||||
isLoginForDoctorApp = json['IsLoginForDoctorApp'];
|
||||
languageID = json['LanguageID'];
|
||||
lastName = json['LastName'];
|
||||
middleName = json['MiddleName'];
|
||||
patientID = json['PatientID'];
|
||||
patientIdentificationID = json['PatientIdentificationID'];
|
||||
patientMobileNumber = json['PatientMobileNumber'];
|
||||
patientOutSA = json['PatientOutSA'];
|
||||
patientTypeID = json['PatientTypeID'];
|
||||
projectID = json['ProjectID'];
|
||||
sessionID = json['SessionID'];
|
||||
stamp = json['stamp'];
|
||||
to = json['To'];
|
||||
tokenID = json['TokenID'];
|
||||
versionID = json['VersionID'];
|
||||
vidaAuthTokenID = json['VidaAuthTokenID'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['Channel'] = this.channel;
|
||||
data['ClinicID'] = this.clinicID;
|
||||
data['DoctorID'] = this.doctorID;
|
||||
data['EditedBy'] = this.editedBy;
|
||||
data['FirstName'] = this.firstName;
|
||||
data['From'] = this.from;
|
||||
data['IPAdress'] = this.iPAdress;
|
||||
data['IsLoginForDoctorApp'] = this.isLoginForDoctorApp;
|
||||
data['LanguageID'] = this.languageID;
|
||||
data['LastName'] = this.lastName;
|
||||
data['MiddleName'] = this.middleName;
|
||||
data['PatientID'] = this.patientID;
|
||||
data['PatientIdentificationID'] = this.patientIdentificationID;
|
||||
data['PatientMobileNumber'] = this.patientMobileNumber;
|
||||
data['PatientOutSA'] = this.patientOutSA;
|
||||
data['PatientTypeID'] = this.patientTypeID;
|
||||
data['ProjectID'] = this.projectID;
|
||||
data['SessionID'] = this.sessionID;
|
||||
data['stamp'] = this.stamp;
|
||||
data['To'] = this.to;
|
||||
data['TokenID'] = this.tokenID;
|
||||
data['VersionID'] = this.versionID;
|
||||
data['VidaAuthTokenID'] = this.vidaAuthTokenID;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
class AddReferredRemarksRequestModel {
|
||||
int projectID;
|
||||
int admissionNo;
|
||||
int lineItemNo;
|
||||
String referredDoctorRemarks;
|
||||
int editedBy;
|
||||
int referalStatus;
|
||||
bool isLoginForDoctorApp;
|
||||
String iPAdress;
|
||||
bool patientOutSA;
|
||||
String tokenID;
|
||||
int languageID;
|
||||
double versionID;
|
||||
int channel;
|
||||
String sessionID;
|
||||
int deviceTypeID;
|
||||
|
||||
AddReferredRemarksRequestModel(
|
||||
{this.projectID,
|
||||
this.admissionNo,
|
||||
this.lineItemNo,
|
||||
this.referredDoctorRemarks,
|
||||
this.editedBy,
|
||||
this.referalStatus,
|
||||
this.isLoginForDoctorApp,
|
||||
this.iPAdress,
|
||||
this.patientOutSA,
|
||||
this.tokenID,
|
||||
this.languageID,
|
||||
this.versionID,
|
||||
this.channel,
|
||||
this.sessionID,
|
||||
this.deviceTypeID});
|
||||
|
||||
AddReferredRemarksRequestModel.fromJson(Map<String, dynamic> json) {
|
||||
projectID = json['ProjectID'];
|
||||
admissionNo = json['AdmissionNo'];
|
||||
lineItemNo = json['LineItemNo'];
|
||||
referredDoctorRemarks = json['ReferredDoctorRemarks'];
|
||||
editedBy = json['EditedBy'];
|
||||
referalStatus = json['ReferalStatus'];
|
||||
isLoginForDoctorApp = json['IsLoginForDoctorApp'];
|
||||
iPAdress = json['IPAdress'];
|
||||
patientOutSA = json['PatientOutSA'];
|
||||
tokenID = json['TokenID'];
|
||||
languageID = json['LanguageID'];
|
||||
versionID = json['VersionID'];
|
||||
channel = json['Channel'];
|
||||
sessionID = json['SessionID'];
|
||||
deviceTypeID = json['DeviceTypeID'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ProjectID'] = this.projectID;
|
||||
data['AdmissionNo'] = this.admissionNo;
|
||||
data['LineItemNo'] = this.lineItemNo;
|
||||
data['ReferredDoctorRemarks'] = this.referredDoctorRemarks;
|
||||
data['EditedBy'] = this.editedBy;
|
||||
data['ReferalStatus'] = this.referalStatus;
|
||||
data['IsLoginForDoctorApp'] = this.isLoginForDoctorApp;
|
||||
data['IPAdress'] = this.iPAdress;
|
||||
data['PatientOutSA'] = this.patientOutSA;
|
||||
data['TokenID'] = this.tokenID;
|
||||
data['LanguageID'] = this.languageID;
|
||||
data['VersionID'] = this.versionID;
|
||||
data['Channel'] = this.channel;
|
||||
data['SessionID'] = this.sessionID;
|
||||
data['DeviceTypeID'] = this.deviceTypeID;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NavigationService {
|
||||
final GlobalKey<NavigatorState> navigatorKey =
|
||||
new GlobalKey<NavigatorState>();
|
||||
Future<dynamic> navigateTo(String routeName,{Object arguments}) {
|
||||
return navigatorKey.currentState.pushNamed(routeName,arguments: arguments);
|
||||
}
|
||||
|
||||
Future<dynamic> pushReplacementNamed(String routeName,{Object arguments}) {
|
||||
return navigatorKey.currentState.pushReplacementNamed(routeName,arguments: arguments);
|
||||
}
|
||||
|
||||
|
||||
Future<dynamic> pushNamedAndRemoveUntil(String routeName) {
|
||||
return navigatorKey.currentState.pushNamedAndRemoveUntil(routeName,(asd)=>false);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
import 'package:doctor_app_flutter/config/config.dart';
|
||||
import 'package:doctor_app_flutter/config/shared_pref_kay.dart';
|
||||
import 'package:doctor_app_flutter/core/service/base/base_service.dart';
|
||||
import 'package:doctor_app_flutter/core/service/patient/LiveCarePatientServices.dart';
|
||||
import 'package:doctor_app_flutter/models/doctor/doctor_profile_model.dart';
|
||||
import 'package:doctor_app_flutter/models/livecare/end_call_req.dart';
|
||||
import 'package:doctor_app_flutter/models/livecare/session_status_model.dart';
|
||||
import 'package:doctor_app_flutter/models/livecare/start_call_res.dart';
|
||||
import 'package:doctor_app_flutter/models/patient/patiant_info_model.dart';
|
||||
import 'package:doctor_app_flutter/util/VideoChannel.dart';
|
||||
import 'package:doctor_app_flutter/util/dr_app_toast_msg.dart';
|
||||
import 'package:doctor_app_flutter/widgets/shared/loader/gif_loader_dialog_utils.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import '../../locator.dart';
|
||||
import '../../routes.dart';
|
||||
import 'NavigationService.dart';
|
||||
|
||||
class VideoCallService extends BaseService{
|
||||
|
||||
StartCallRes startCallRes;
|
||||
PatiantInformtion patient;
|
||||
LiveCarePatientServices _liveCarePatientServices = locator<LiveCarePatientServices>();
|
||||
|
||||
openVideo(StartCallRes startModel,PatiantInformtion patientModel,VoidCallback onCallConnected, VoidCallback onCallDisconnected)async{
|
||||
this.startCallRes = startModel;
|
||||
this.patient = patientModel;
|
||||
DoctorProfileModel doctorProfile = await getDoctorProfile(isGetProfile: true);
|
||||
await VideoChannel.openVideoCallScreen(
|
||||
kToken: startCallRes.openTokenID,//"T1==cGFydG5lcl9pZD00NzI0Nzk1NCZzaWc9NGIyZDljOTY3YjFiNWU1YzUzNzFmMjIyNjJmNmEzY2Y5NzZjOTdlYzpzZXNzaW9uX2lkPTFfTVg0ME56STBOemsxTkg1LU1UWXlNekEyTlRRMU9EVXhObjVrVFRoMFlVdFJXaXRYTWpadFZGZHFhSGxZVGpOdE1UVi1mZyZjcmVhdGVfdGltZT0xNjIzMDY1NDk1Jm5vbmNlPTAuMjM2Mjk0NTIwMTkyOTA4OTcmcm9sZT1wdWJsaXNoZXImZXhwaXJlX3RpbWU9MTYyNTY1NzQ5NCZpbml0aWFsX2xheW91dF9jbGFzc19saXN0PQ==",
|
||||
kSessionId:startCallRes.openSessionID,//1_MX40NzI0Nzk1NH5-MTYyMzA2NTQ1ODUxNn5kTTh0YUtRWitXMjZtVFdqaHlYTjNtMTV-fg
|
||||
kApiKey: '46209962',//'47247954'
|
||||
vcId: patient.vcId,
|
||||
patientName: patient.fullName ?? (patient.firstName != null ? "${patient.firstName} ${patient.lastName}" : "-"),
|
||||
tokenID: await sharedPref.getString(TOKEN),
|
||||
generalId: GENERAL_ID,
|
||||
doctorId: doctorProfile.doctorID,
|
||||
onFailure: (String error) {
|
||||
DrAppToastMsg.showErrorToast(error);
|
||||
},onCallConnected: onCallConnected,
|
||||
onCallEnd: () {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
GifLoaderDialogUtils.showMyDialog(locator<NavigationService>().navigatorKey.currentContext);
|
||||
endCall(patient.vcId, false,).then((value) {
|
||||
GifLoaderDialogUtils.hideDialog(locator<NavigationService>().navigatorKey.currentContext);
|
||||
if (hasError) {
|
||||
DrAppToastMsg.showErrorToast(error);
|
||||
}else
|
||||
locator<NavigationService>().navigateTo(PATIENTS_END_Call,arguments: {
|
||||
"patient": patient,
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
},
|
||||
onCallNotRespond: (SessionStatusModel sessionStatusModel) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
GifLoaderDialogUtils.showMyDialog(locator<NavigationService>().navigatorKey.currentContext);
|
||||
endCall(patient.vcId, sessionStatusModel.sessionStatus == 3,).then((value) {
|
||||
GifLoaderDialogUtils.hideDialog(locator<NavigationService>().navigatorKey.currentContext);
|
||||
if (hasError) {
|
||||
DrAppToastMsg.showErrorToast(error);
|
||||
} else {
|
||||
locator<NavigationService>().navigateTo(PATIENTS_END_Call,arguments: {
|
||||
"patient": patient,
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
Future endCall(int vCID, bool isPatient) async {
|
||||
hasError = false;
|
||||
await getDoctorProfile(isGetProfile: true);
|
||||
EndCallReq endCallReq = new EndCallReq();
|
||||
endCallReq.doctorId = doctorProfile.doctorID;
|
||||
endCallReq.generalid = 'Cs2020@2016\$2958';
|
||||
endCallReq.vCID = vCID;
|
||||
endCallReq.isDestroy = isPatient;
|
||||
await _liveCarePatientServices.endCall(endCallReq);
|
||||
if (_liveCarePatientServices.hasError) {
|
||||
error = _liveCarePatientServices.error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
import 'package:doctor_app_flutter/config/config.dart';
|
||||
import 'package:doctor_app_flutter/core/model/patient_muse/PatientSearchRequestModel.dart';
|
||||
import 'package:doctor_app_flutter/core/service/base/base_service.dart';
|
||||
import 'package:doctor_app_flutter/models/patient/patiant_info_model.dart';
|
||||
|
||||
class ScanQrService extends BaseService {
|
||||
List<PatiantInformtion> myInPatientList = List();
|
||||
List<PatiantInformtion> inPatientList = List();
|
||||
|
||||
Future getInPatient(PatientSearchRequestModel requestModel, bool isMyInpatient) async {
|
||||
hasError = false;
|
||||
await getDoctorProfile();
|
||||
|
||||
if (isMyInpatient) {
|
||||
requestModel.doctorID = doctorProfile.doctorID;
|
||||
} else {
|
||||
requestModel.doctorID = 0;
|
||||
}
|
||||
|
||||
await baseAppClient.post(
|
||||
GET_PATIENT_IN_PATIENT_LIST,
|
||||
onSuccess: (dynamic response, int statusCode) {
|
||||
inPatientList.clear();
|
||||
myInPatientList.clear();
|
||||
|
||||
response['List_MyInPatient'].forEach((v) {
|
||||
PatiantInformtion patient = PatiantInformtion.fromJson(v);
|
||||
inPatientList.add(patient);
|
||||
if (patient.doctorId == doctorProfile.doctorID) {
|
||||
myInPatientList.add(patient);
|
||||
}
|
||||
});
|
||||
},
|
||||
onFailure: (String error, int statusCode) {
|
||||
hasError = true;
|
||||
super.error = error;
|
||||
},
|
||||
body: requestModel.toJson(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
import 'package:doctor_app_flutter/config/config.dart';
|
||||
import 'package:doctor_app_flutter/core/service/base/base_service.dart';
|
||||
import 'package:doctor_app_flutter/models/doctor/verify_referral_doctor_remarks.dart';
|
||||
import 'package:doctor_app_flutter/models/patient/my_referral/my_referred_patient_model.dart';
|
||||
import 'package:doctor_app_flutter/models/patient/request_my_referral_patient_model.dart';
|
||||
|
||||
class ReferredPatientService extends BaseService {
|
||||
List<MyReferredPatientModel> _listMyReferredPatientModel = [];
|
||||
|
||||
List<MyReferredPatientModel> get listMyReferredPatientModel =>
|
||||
_listMyReferredPatientModel;
|
||||
|
||||
RequestMyReferralPatientModel _requestMyReferralPatient =
|
||||
RequestMyReferralPatientModel();
|
||||
VerifyReferralDoctorRemarks _verifyreferraldoctorremarks =
|
||||
VerifyReferralDoctorRemarks();
|
||||
|
||||
Future getMyReferredPatient() async {
|
||||
await baseAppClient.post(
|
||||
GET_MY_REFERRED_PATIENT,
|
||||
onSuccess: (dynamic response, int statusCode) {
|
||||
_listMyReferredPatientModel.clear();
|
||||
response['List_MyReferredPatient'].forEach((v) {
|
||||
listMyReferredPatientModel.add(MyReferredPatientModel.fromJson(v));
|
||||
});
|
||||
// print(response['List_MyReferredPatient']);
|
||||
},
|
||||
onFailure: (String error, int statusCode) {
|
||||
hasError = true;
|
||||
super.error = error;
|
||||
},
|
||||
body: _requestMyReferralPatient.toJson(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
import 'package:doctor_app_flutter/config/config.dart';
|
||||
import 'package:doctor_app_flutter/core/service/base/base_service.dart';
|
||||
import 'package:doctor_app_flutter/models/dashboard/dashboard_model.dart';
|
||||
import 'package:doctor_app_flutter/models/dashboard/get_special_clinical_care_List_Respose_Model.dart';
|
||||
import 'package:doctor_app_flutter/models/dashboard/get_special_clinical_care_mapping_List_Respose_Model.dart';
|
||||
|
||||
class SpecialClinicsService extends BaseService {
|
||||
|
||||
|
||||
List<GetSpecialClinicalCareListResponseModel> _specialClinicalCareList = [];
|
||||
List<GetSpecialClinicalCareListResponseModel> get specialClinicalCareList => _specialClinicalCareList;
|
||||
|
||||
List<GetSpecialClinicalCareMappingListResponseModel> _specialClinicalCareMappingList = [];
|
||||
List<GetSpecialClinicalCareMappingListResponseModel> get specialClinicalCareMappingList => _specialClinicalCareMappingList;
|
||||
Future getSpecialClinicalCareList() async {
|
||||
hasError = false;
|
||||
await baseAppClient.post(
|
||||
GET_SPECIAL_CLINICAL_CARE_LIST,
|
||||
onSuccess: (dynamic response, int statusCode) {
|
||||
|
||||
_specialClinicalCareList.clear();
|
||||
response['List_SpecialClinicalCareList'].forEach((v) {
|
||||
_specialClinicalCareList.add(GetSpecialClinicalCareListResponseModel.fromJson(v));
|
||||
});},
|
||||
onFailure: (String error, int statusCode) {
|
||||
hasError = true;
|
||||
super.error = error;
|
||||
},
|
||||
body: {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Future getSpecialClinicalCareMappingList(int clinicId) async {
|
||||
hasError = false;
|
||||
await baseAppClient.post(
|
||||
GET_SPECIAL_CLINICAL_CARE_MAPPING_LIST,
|
||||
onSuccess: (dynamic response, int statusCode) {
|
||||
|
||||
_specialClinicalCareMappingList.clear();
|
||||
response['List_SpecialClinicalCareMappingList'].forEach((v) {
|
||||
_specialClinicalCareMappingList.add(GetSpecialClinicalCareMappingListResponseModel.fromJson(v));
|
||||
});},
|
||||
onFailure: (String error, int statusCode) {
|
||||
hasError = true;
|
||||
super.error = error;
|
||||
},
|
||||
body: {
|
||||
"ClinicID": clinicId,
|
||||
"DoctorID":0,
|
||||
"EditedBy":0
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
import 'package:doctor_app_flutter/core/enum/viewstate.dart';
|
||||
import 'package:doctor_app_flutter/core/service/patient/referred_patient_service.dart';
|
||||
import 'package:doctor_app_flutter/models/patient/my_referral/my_referred_patient_model.dart';
|
||||
|
||||
import '../../locator.dart';
|
||||
import 'base_view_model.dart';
|
||||
|
||||
class ReferredPatientViewModel extends BaseViewModel {
|
||||
ReferredPatientService _referralPatientService =
|
||||
locator<ReferredPatientService>();
|
||||
|
||||
List<MyReferredPatientModel> get listMyReferredPatientModel =>
|
||||
_referralPatientService.listMyReferredPatientModel;
|
||||
|
||||
Future getMyReferredPatient() async {
|
||||
setState(ViewState.Busy);
|
||||
await _referralPatientService.getMyReferredPatient();
|
||||
if (_referralPatientService.hasError) {
|
||||
error = _referralPatientService.error;
|
||||
setState(ViewState.Error);
|
||||
} else
|
||||
setState(ViewState.Idle);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import 'package:doctor_app_flutter/core/enum/viewstate.dart';
|
||||
import 'package:doctor_app_flutter/core/model/patient_muse/PatientSearchRequestModel.dart';
|
||||
import 'package:doctor_app_flutter/core/service/home/scan_qr_service.dart';
|
||||
import 'package:doctor_app_flutter/core/viewModel/base_view_model.dart';
|
||||
import 'package:doctor_app_flutter/locator.dart';
|
||||
import 'package:doctor_app_flutter/models/patient/patiant_info_model.dart';
|
||||
|
||||
class ScanQrViewModel extends BaseViewModel {
|
||||
ScanQrService _scanQrService = locator<ScanQrService>();
|
||||
List<PatiantInformtion> get inPatientList => _scanQrService.inPatientList;
|
||||
|
||||
Future getInPatientList(PatientSearchRequestModel requestModel, {bool isMyInpatient = false}) async {
|
||||
await getDoctorProfile();
|
||||
setState(ViewState.Busy);
|
||||
|
||||
await _scanQrService.getInPatient(requestModel, true);
|
||||
if (_scanQrService.hasError) {
|
||||
error = _scanQrService.error;
|
||||
|
||||
setState(ViewState.ErrorLocal);
|
||||
} else {
|
||||
// setDefaultInPatientList();
|
||||
setState(ViewState.Idle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
class GetSpecialClinicalCareListResponseModel {
|
||||
int projectID;
|
||||
int clinicID;
|
||||
String clinicDescription;
|
||||
String clinicDescriptionN;
|
||||
bool isActive;
|
||||
|
||||
GetSpecialClinicalCareListResponseModel(
|
||||
{this.projectID,
|
||||
this.clinicID,
|
||||
this.clinicDescription,
|
||||
this.clinicDescriptionN,
|
||||
this.isActive});
|
||||
|
||||
GetSpecialClinicalCareListResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
projectID = json['ProjectID'];
|
||||
clinicID = json['ClinicID'];
|
||||
clinicDescription = json['ClinicDescription'];
|
||||
clinicDescriptionN = json['ClinicDescriptionN'];
|
||||
isActive = json['IsActive'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ProjectID'] = this.projectID;
|
||||
data['ClinicID'] = this.clinicID;
|
||||
data['ClinicDescription'] = this.clinicDescription;
|
||||
data['ClinicDescriptionN'] = this.clinicDescriptionN;
|
||||
data['IsActive'] = this.isActive;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
class GetSpecialClinicalCareMappingListResponseModel {
|
||||
int mappingProjectID;
|
||||
int clinicID;
|
||||
int nursingStationID;
|
||||
bool isActive;
|
||||
int projectID;
|
||||
String description;
|
||||
|
||||
GetSpecialClinicalCareMappingListResponseModel(
|
||||
{this.mappingProjectID,
|
||||
this.clinicID,
|
||||
this.nursingStationID,
|
||||
this.isActive,
|
||||
this.projectID,
|
||||
this.description});
|
||||
|
||||
GetSpecialClinicalCareMappingListResponseModel.fromJson(
|
||||
Map<String, dynamic> json) {
|
||||
mappingProjectID = json['MappingProjectID'];
|
||||
clinicID = json['ClinicID'];
|
||||
nursingStationID = json['NursingStationID'];
|
||||
isActive = json['IsActive'];
|
||||
projectID = json['ProjectID'];
|
||||
description = json['Description'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['MappingProjectID'] = this.mappingProjectID;
|
||||
data['ClinicID'] = this.clinicID;
|
||||
data['NursingStationID'] = this.nursingStationID;
|
||||
data['IsActive'] = this.isActive;
|
||||
data['ProjectID'] = this.projectID;
|
||||
data['Description'] = this.description;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -1,56 +1,56 @@
|
||||
class StartCallReq {
|
||||
int vCID;
|
||||
bool isrecall;
|
||||
String tokenID;
|
||||
String generalid;
|
||||
String clincName;
|
||||
int clinicId;
|
||||
String docSpec;
|
||||
String docotrName;
|
||||
int doctorId;
|
||||
String generalid;
|
||||
bool isOutKsa;
|
||||
bool isrecall;
|
||||
String projectName;
|
||||
String docotrName;
|
||||
String clincName;
|
||||
String docSpec;
|
||||
int clinicId;
|
||||
String tokenID;
|
||||
int vCID;
|
||||
|
||||
StartCallReq(
|
||||
{this.vCID,
|
||||
this.isrecall,
|
||||
this.tokenID,
|
||||
this.generalid,
|
||||
this.doctorId,
|
||||
this.isOutKsa,
|
||||
this.projectName,
|
||||
this.docotrName,
|
||||
this.clincName,
|
||||
this.docSpec,
|
||||
this.clinicId});
|
||||
{this.clincName,
|
||||
this.clinicId,
|
||||
this.docSpec,
|
||||
this.docotrName,
|
||||
this.doctorId,
|
||||
this.generalid,
|
||||
this.isOutKsa,
|
||||
this.isrecall,
|
||||
this.projectName,
|
||||
this.tokenID,
|
||||
this.vCID});
|
||||
|
||||
StartCallReq.fromJson(Map<String, dynamic> json) {
|
||||
vCID = json['VC_ID'];
|
||||
isrecall = json['isrecall'];
|
||||
tokenID = json['TokenID'];
|
||||
generalid = json['generalid'];
|
||||
clincName = json['clincName'];
|
||||
clinicId = json['ClinicId'];
|
||||
docSpec = json['Doc_Spec'];
|
||||
docotrName = json['DocotrName'];
|
||||
doctorId = json['DoctorId'];
|
||||
generalid = json['generalid'];
|
||||
isOutKsa = json['IsOutKsa'];
|
||||
isrecall = json['isrecall'];
|
||||
projectName = json['projectName'];
|
||||
docotrName = json['DocotrName'];
|
||||
clincName = json['clincName'];
|
||||
docSpec = json['Doc_Spec'];
|
||||
clinicId = json['ClinicId'];
|
||||
tokenID = json['TokenID'];
|
||||
vCID = json['VC_ID'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['VC_ID'] = this.vCID;
|
||||
data['isrecall'] = this.isrecall;
|
||||
data['TokenID'] = this.tokenID;
|
||||
data['generalid'] = this.generalid;
|
||||
data['clincName'] = this.clincName;
|
||||
data['ClinicId'] = this.clinicId;
|
||||
data['Doc_Spec'] = this.docSpec;
|
||||
data['DocotrName'] = this.docotrName;
|
||||
data['DoctorId'] = this.doctorId;
|
||||
data['generalid'] = this.generalid;
|
||||
data['IsOutKsa'] = this.isOutKsa;
|
||||
data['isrecall'] = this.isrecall;
|
||||
data['projectName'] = this.projectName;
|
||||
data['DocotrName'] = this.docotrName;
|
||||
data['clincName'] = this.clincName;
|
||||
data['Doc_Spec'] = this.docSpec;
|
||||
data['ClinicId'] = this.clinicId;
|
||||
data['TokenID'] = this.tokenID;
|
||||
data['VC_ID'] = this.vCID;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||