Merge branch 'ipd-changes-for-vida-plus' of http://34.17.182.140/Haroon6138/doctor_app_flutter into ipd-changes-for-vida-plus

# Conflicts:
#	android/settings.gradle
ipd-changes-for-vida-plus
haroon amjad 1 week ago
commit 912244853b

@ -7,6 +7,15 @@
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
analyzer:
exclude:
- build/**
- android/**
- ios/**
- web/**
- windows/**
- macos/**
- linux/**
include: package:flutter_lints/flutter.yaml
linter:

@ -30,7 +30,7 @@ if (flutterVersionName == null) {
android {
namespace "com.hmg.hmgDr"
compileSdkVersion 35
compileSdkVersion 37
signingConfigs {
release {
@ -52,8 +52,8 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.hmg.hmgDr"
minSdkVersion 24
targetSdkVersion 35
minSdkVersion 28
targetSdkVersion 37
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -67,7 +67,7 @@ android {
debuggable false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
proguardFiles 'proguard-rules.pro'
}
debug {
@ -129,7 +129,7 @@ dependencies {
//openTok
implementation 'com.opentok.android:opentok-android-sdk:2.20.1'
implementation 'com.opentok.android:opentok-android-sdk:2.35.1'
//permissions
// implementation 'pub.devrel:easypermissions:0.4.0'
// implementation project(':easypermissions')

@ -16,7 +16,7 @@ import android.util.Log
import android.widget.Toast
import androidx.annotation.NonNull
import com.google.gson.GsonBuilder
import com.hmg.hmgDr.Service.VideoStreamFloatingWidgetService
//import com.hmg.hmgDr.Service.VideoStreamFloatingWidgetService
import com.hmg.hmgDr.Model.GetSessionStatusModel
import com.hmg.hmgDr.Model.SessionStatusModel
import com.hmg.hmgDr.ui.VideoCallResponseListener
@ -43,7 +43,7 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
private val LAUNCH_VIDEO: Int = 1
private var serviceIntent: Intent? = null
private var videoStreamService: VideoStreamFloatingWidgetService? = null
// private var videoStreamService: VideoStreamFloatingWidgetService? = null
private var bound = false
override fun onCreate(savedInstanceState: Bundle?) {
@ -59,46 +59,46 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
this.result = result
this.call = call
when (call.method) {
"openVideoCall" -> {
val apiKey = call.argument<String>("kApiKey")
val sessionId = call.argument<String>("kSessionId")
val token = call.argument<String>("kToken")
val appLang = call.argument<String>("appLang")
val baseUrl = call.argument<String>("baseUrl")
// Session Status model
val VC_ID = call.argument<Int>("VC_ID")
val tokenID = call.argument<String>("TokenID")
val generalId = call.argument<String>("generalId")
val doctorId = call.argument<Int>("DoctorId")
val patientName = call.argument<String>("patientName")
val isRecording = call.argument<Boolean>("isRecording")
val sessionStatusModel =
GetSessionStatusModel(
VC_ID,
tokenID,
generalId,
doctorId,
patientName,
isRecording!!
)
openVideoCall(apiKey, sessionId, token, appLang, baseUrl, sessionStatusModel)
}
"closeVideoCall" -> {
videoStreamService?.closeVideoCall()
}
else -> {
result.notImplemented()
}
}
// this.result = result
// this.call = call
//
// when (call.method) {
// "openVideoCall" -> {
// val apiKey = call.argument<String>("kApiKey")
// val sessionId = call.argument<String>("kSessionId")
// val token = call.argument<String>("kToken")
// val appLang = call.argument<String>("appLang")
// val baseUrl = call.argument<String>("baseUrl")
//
// // Session Status model
// val VC_ID = call.argument<Int>("VC_ID")
// val tokenID = call.argument<String>("TokenID")
// val generalId = call.argument<String>("generalId")
// val doctorId = call.argument<Int>("DoctorId")
// val patientName = call.argument<String>("patientName")
// val isRecording = call.argument<Boolean>("isRecording")
//
// val sessionStatusModel =
// GetSessionStatusModel(
// VC_ID,
// tokenID,
// generalId,
// doctorId,
// patientName,
// isRecording!!
// )
//
//
// openVideoCall(apiKey, sessionId, token, appLang, baseUrl, sessionStatusModel)
//
// }
// "closeVideoCall" -> {
// videoStreamService?.closeVideoCall()
// }
// else -> {
// result.notImplemented()
// }
// }
}
private fun openVideoCall(
@ -110,24 +110,24 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
sessionStatusModel: GetSessionStatusModel
) {
val arguments = Bundle()
arguments.putString("apiKey", apiKey)
arguments.putString("sessionId", sessionId)
arguments.putString("token", token)
arguments.putString("appLang", appLang)
arguments.putString("baseUrl", baseUrl)
arguments.putParcelable("sessionStatusModel", sessionStatusModel)
// start service
// serviceIntent = Intent(this@MainActivity, VideoStreamContainerService::class.java)
if (videoStreamService == null || videoStreamService?.serviceRunning == false) {
serviceIntent = Intent(this@MainActivity, VideoStreamFloatingWidgetService::class.java)
serviceIntent?.run {
putExtras(arguments)
action = VideoStreamFloatingWidgetService.ACTION_START_CALL
}
checkFloatingWidgetPermission()
}
// val arguments = Bundle()
// arguments.putString("apiKey", apiKey)
// arguments.putString("sessionId", sessionId)
// arguments.putString("token", token)
// arguments.putString("appLang", appLang)
// arguments.putString("baseUrl", baseUrl)
// arguments.putParcelable("sessionStatusModel", sessionStatusModel)
//
// // start service
//// serviceIntent = Intent(this@MainActivity, VideoStreamContainerService::class.java)
// if (videoStreamService == null || videoStreamService?.serviceRunning == false) {
// serviceIntent = Intent(this@MainActivity, VideoStreamFloatingWidgetService::class.java)
// serviceIntent?.run {
// putExtras(arguments)
// action = VideoStreamFloatingWidgetService.ACTION_START_CALL
// }
// checkFloatingWidgetPermission()
// }
}
private fun checkFloatingWidgetPermission() {
@ -212,28 +212,29 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
}
override fun minimizeVideoEvent(isMinimize: Boolean) {
if (isMinimize)
methodChannel.invokeMethod("onCallConnected", null)
else {
methodChannel.invokeMethod("onCallDisconnected", null)
unbindService()
videoStreamService?.serviceRunning = false
videoStreamService = null
}
// if (isMinimize)
// methodChannel.invokeMethod("onCallConnected", null)
// else {
// methodChannel.invokeMethod("onCallDisconnected", null)
// unbindService()
// videoStreamService?.serviceRunning = false
// videoStreamService = null
// }
}
override fun onBackPressed() {
if (videoStreamService != null && videoStreamService?.serviceRunning == true && videoStreamService?.isFullScreen!!) {
videoStreamService!!.onMinimizedClicked()
} else {
super.onBackPressed()
}
super.onBackPressed()
// if (videoStreamService != null && videoStreamService?.serviceRunning == true && videoStreamService?.isFullScreen!!) {
// videoStreamService!!.onMinimizedClicked()
// } else {
// }
}
override fun onPause() {
if (videoStreamService != null && videoStreamService?.serviceRunning == true && videoStreamService?.isFullScreen!!) {
videoStreamService!!.onMinimizedClicked()
}
// if (videoStreamService != null && videoStreamService?.serviceRunning == true && videoStreamService?.isFullScreen!!) {
// videoStreamService!!.onMinimizedClicked()
// }
super.onPause()
}
@ -248,32 +249,32 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler,
// }
private fun bindService() {
serviceIntent?.run {
if (videoStreamService != null && !videoStreamService!!.serviceRunning) {
startService(this)
}
bindService(this, serviceConnection, Context.BIND_AUTO_CREATE)
}
// serviceIntent?.run {
// if (videoStreamService != null && !videoStreamService!!.serviceRunning) {
// startService(this)
// }
// bindService(this, serviceConnection, Context.BIND_AUTO_CREATE)
// }
}
private fun unbindService() {
if (bound) {
videoStreamService?.videoCallResponseListener = null // unregister
unbindService(serviceConnection)
bound = false
}
// if (bound) {
// videoStreamService?.videoCallResponseListener = null // unregister
// unbindService(serviceConnection)
// bound = false
// }
}
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
// val binder: VideoStreamContainerService.VideoStreamBinder =
// service as VideoStreamContainerService.VideoStreamBinder
val binder: VideoStreamFloatingWidgetService.VideoStreamBinder =
service as VideoStreamFloatingWidgetService.VideoStreamBinder
videoStreamService = binder.service
bound = true
videoStreamService!!.videoCallResponseListener = this@MainActivity // register
videoStreamService?.serviceRunning = true
//// val binder: VideoStreamContainerService.VideoStreamBinder =
//// service as VideoStreamContainerService.VideoStreamBinder
// val binder: VideoStreamFloatingWidgetService.VideoStreamBinder =
// service as VideoStreamFloatingWidgetService.VideoStreamBinder
// videoStreamService = binder.service
// bound = true
// videoStreamService!!.videoCallResponseListener = this@MainActivity // register
// videoStreamService?.serviceRunning = true
}
override fun onServiceDisconnected(name: ComponentName?) {

@ -1,91 +1,91 @@
package com.hmg.hmgDr.Service
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.Bundle
import android.os.IBinder
import com.hmg.hmgDr.MainActivity
import com.hmg.hmgDr.ui.VideoCallResponseListener
import com.hmg.hmgDr.ui.fragment.VideoCallFragment
class VideoStreamContainerService : Service(), VideoCallResponseListener {
var videoCallResponseListener: VideoCallResponseListener? = null
var mActivity: MainActivity? = null
set(value) {
field = value
if (field != null) {
setDialogFragment()
}
}
var arguments: Bundle? = null
var serviceRunning: Boolean = false
private val serviceBinder: IBinder = VideoStreamBinder()
private var dialogFragment: VideoCallFragment? = null
override fun onBind(intent: Intent?): IBinder {
return serviceBinder
}
private fun setDialogFragment() {
mActivity?.run {
if (dialogFragment == null) {
val transaction = supportFragmentManager.beginTransaction()
dialogFragment = VideoCallFragment.newInstance(arguments!!)
dialogFragment?.let {
it.setCallListener(this@VideoStreamContainerService)
it.isCancelable = true
if (it.isAdded) {
it.dismiss()
} else {
it.show(transaction, "dialog")
}
}
} else if (!dialogFragment!!.isVisible) {
val transaction = supportFragmentManager.beginTransaction()
dialogFragment!!.show(transaction, "dialog")
} else {
// don't do anything
}
}
}
fun closeVideoCall(){
dialogFragment?.onCallClicked()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null && intent.extras != null) {
arguments = intent.extras
}
// Toast.makeText(this, "Service started by user.", Toast.LENGTH_LONG).show()
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
// Toast.makeText(this, "Service destroyed by user.", Toast.LENGTH_LONG).show()
}
inner class VideoStreamBinder : Binder() {
val service: VideoStreamContainerService
get() = this@VideoStreamContainerService
}
override fun onCallFinished(resultCode: Int, intent: Intent?) {
dialogFragment = null
videoCallResponseListener?.onCallFinished(resultCode, intent)
}
override fun errorHandle(message: String) {
dialogFragment = null
// Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
override fun minimizeVideoEvent(isMinimize: Boolean) {
videoCallResponseListener?.minimizeVideoEvent(isMinimize)
}
}
//package com.hmg.hmgDr.Service
//
//import android.app.Service
//import android.content.Intent
//import android.os.Binder
//import android.os.Bundle
//import android.os.IBinder
//import com.hmg.hmgDr.MainActivity
////import com.hmg.hmgDr.ui.VideoCallResponseListener
////import com.hmg.hmgDr.ui.fragment.VideoCallFragment
//
//class VideoStreamContainerService : Service(), VideoCallResponseListener {
//
//// var videoCallResponseListener: VideoCallResponseListener? = null
//// var mActivity: MainActivity? = null
//// set(value) {
//// field = value
//// if (field != null) {
//// setDialogFragment()
//// }
//// }
//// var arguments: Bundle? = null
//// var serviceRunning: Boolean = false
////
////
//// private val serviceBinder: IBinder = VideoStreamBinder()
//// private var dialogFragment: VideoCallFragment? = null
////
//// override fun onBind(intent: Intent?): IBinder {
//// return serviceBinder
//// }
////
//// private fun setDialogFragment() {
//// mActivity?.run {
//// if (dialogFragment == null) {
//// val transaction = supportFragmentManager.beginTransaction()
//// dialogFragment = VideoCallFragment.newInstance(arguments!!)
//// dialogFragment?.let {
//// it.setCallListener(this@VideoStreamContainerService)
//// it.isCancelable = true
//// if (it.isAdded) {
//// it.dismiss()
//// } else {
//// it.show(transaction, "dialog")
//// }
//// }
//// } else if (!dialogFragment!!.isVisible) {
//// val transaction = supportFragmentManager.beginTransaction()
//// dialogFragment!!.show(transaction, "dialog")
//// } else {
//// // don't do anything
//// }
//// }
//// }
////
//// fun closeVideoCall(){
//// dialogFragment?.onCallClicked()
//// }
////
//// override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
//// if (intent != null && intent.extras != null) {
//// arguments = intent.extras
//// }
////// Toast.makeText(this, "Service started by user.", Toast.LENGTH_LONG).show()
//// return START_STICKY
//// }
////
//// override fun onDestroy() {
//// super.onDestroy()
////// Toast.makeText(this, "Service destroyed by user.", Toast.LENGTH_LONG).show()
//// }
////
//// inner class VideoStreamBinder : Binder() {
//// val service: VideoStreamContainerService
//// get() = this@VideoStreamContainerService
//// }
////
//// override fun onCallFinished(resultCode: Int, intent: Intent?) {
//// dialogFragment = null
//// videoCallResponseListener?.onCallFinished(resultCode, intent)
//// }
////
//// override fun errorHandle(message: String) {
//// dialogFragment = null
////// Toast.makeText(this, message, Toast.LENGTH_LONG).show()
//// }
////
//// override fun minimizeVideoEvent(isMinimize: Boolean) {
//// videoCallResponseListener?.minimizeVideoEvent(isMinimize)
//// }
//}

@ -1,379 +1,379 @@
package com.hmg.hmgDr.util.opentok
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
}
}
//package com.hmg.hmgDr.util.opentok
//
//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
//// }
//}

@ -1,357 +1,357 @@
package com.hmg.hmgDr.util.opentok
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
}
}
//package com.hmg.hmgDr.util.opentok
//
//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
//// }
//}

@ -15,7 +15,7 @@
allprojects {
repositories {
google()
jcenter()
// jcenter()
mavenCentral()
maven { url 'https://developer.huawei.com/repo/' }
// maven { url 'https://tokbox.bintray.com/maven' }
@ -46,6 +46,7 @@ subprojects {
if (namespace == null) {
namespace project.group
}
project.android.compileSdkVersion 37
}
}
}

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip

@ -18,8 +18,8 @@ pluginManagement {
plugins {
id 'dev.flutter.flutter-plugin-loader' version "1.0.0" // apply true
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
id 'com.android.application' version "8.9.0" apply false
id "com.android.application" version '9.0.1' apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
id "com.google.gms.google-services" version "4.4.0" apply false
}

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '13.0'
platform :ios, '15.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 60;
objects = {
/* Begin PBXBuildFile section */
@ -198,7 +198,6 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
CD1E4AEFF4CC6FD150483363 /* [CP] Embed Pods Frameworks */,
6BD71B7017878A134266DEFC /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -238,7 +237,7 @@
);
mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
);
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
@ -303,24 +302,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
6BD71B7017878A134266DEFC /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple/permission_handler_apple_privacy.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/permission_handler_apple_privacy.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@ -347,30 +328,13 @@
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework",
"${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework",
"${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
"${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework",
"${BUILT_PRODUCTS_DIR}/OrderedSet/OrderedSet.framework",
"${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework",
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
"${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework",
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
"${BUILT_PRODUCTS_DIR}/Try/Try.framework",
"${BUILT_PRODUCTS_DIR}/barcode_scan2/barcode_scan2.framework",
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
"${BUILT_PRODUCTS_DIR}/flutter_inappwebview_ios/flutter_inappwebview_ios.framework",
"${BUILT_PRODUCTS_DIR}/flutter_keyboard_visibility/flutter_keyboard_visibility.framework",
"${BUILT_PRODUCTS_DIR}/fluttertoast/fluttertoast.framework",
"${BUILT_PRODUCTS_DIR}/local_auth_ios/local_auth_ios.framework",
"${BUILT_PRODUCTS_DIR}/maps_launcher/maps_launcher.framework",
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
"${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework",
"${BUILT_PRODUCTS_DIR}/speech_to_text/speech_to_text.framework",
"${BUILT_PRODUCTS_DIR}/open_filex/open_filex.framework",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ZoomVideoSDK/CptShare/CptShare.framework/CptShare",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ZoomVideoSDK/Whiteboard/Whiteboard.framework/Whiteboard",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ZoomVideoSDK/ZoomTask/ZoomTask.framework/ZoomTask",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ZoomVideoSDK/ZoomVideoSDK/ZoomVideoSDK.framework/ZoomVideoSDK",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ZoomVideoSDK/zm_annoter_dynamic/zm_annoter_dynamic.framework/zm_annoter_dynamic",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ZoomVideoSDK/zoomcml/zoomcml.framework/zoomcml",
@ -381,30 +345,13 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OrderedSet.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Try.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan2.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_keyboard_visibility.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fluttertoast.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/maps_launcher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/speech_to_text.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/open_filex.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CptShare.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Whiteboard.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZoomTask.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZoomVideoSDK.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/zm_annoter_dynamic.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/zoomcml.framework",
@ -497,7 +444,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -522,7 +469,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -590,7 +537,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -641,7 +588,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -668,7 +615,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -704,7 +651,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -749,7 +696,7 @@
/* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};

@ -5,7 +5,7 @@ import OpenTok
// Copyright © 2020 Cloud. All rights reserved.
@main
@objc class AppDelegate: FlutterAppDelegate ,ICallProtocol {
@objc class AppDelegate: FlutterAppDelegate ,ICallProtocol , FlutterImplicitEngineDelegate {
var result: FlutterResult?
@ -23,9 +23,12 @@ import OpenTok
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
}

@ -48,6 +48,27 @@
<string>Need to upload image</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This permission is not needed by the app, but it is required by an underlying API. If you see this dialog, contact us.</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>

@ -145,7 +145,6 @@ class BaseAppClient {
printer: PrettyPrinter(
methodCount: 2,
errorMethodCount: 5,
lineLength: 1000,
colors: true,
printEmojis: true,
),

@ -385,7 +385,7 @@ const IS_INFECTIOUS_DISEASE_PENDING = 'Services/DoctorApplication.svc/REST/IsInt
const INFECTIOUS_HISTORY = 'Services/DoctorApplication.svc/REST/InterventionHistory';
const UPDATE_INFECTIOUS_STATUS = 'Services/DoctorApplication.svc/REST/UpdateInterventionStatus';
const GET_NURSING_STATIONS = 'Services/DoctorApplication.svc/REST/GetWards';
const GET_ATTACHMENT = 'Services/Patients.svc/REST/GetVidaLabResultAttachment';
const GET_ATTACHMENT = 'Services/DataStreaming.svc/REST/GetVidaLabResultAttachment';
var selectedPatientType = 1;

@ -1291,8 +1291,8 @@ const Map<String, Map<String, String>> localizedValues = {
"ar":"لا يوجد تشخيص سابق"
},
"showAttachment":{
"en":"Show\nAttachment",
"ar":"عرض\nالمرفقات"
"en":"Show Attachment",
"ar":"عرض المرفقات"
},
"selectAnAttachment":{
"en":"Select An Attachment",

@ -39,6 +39,8 @@ class ActivationCodeModel {
data['OTP_SendType'] = otpSendType;
data['generalid'] = this.generalid;
data['LoginDoctorID'] = this.loginDoctorID;
data['NotificationCenterAppGUID'] = "QmNYN21MNHBTM2tkdjU2ZzdBOUhSdWlwT21h";
data['NotificationCenterAppSeviceGUID'] = "d8bd5c76-4b50-4122-81d0-c3aa25db08a2";
return data;
}
}

@ -58,6 +58,8 @@ class AllSpecialLabResultModel {
bool? IsLabTestAttachment; // for the attachment icon in the lab result list
dynamic LabAccessionNo;
dynamic FileHash;
dynamic testDescription;
dynamic testShortDescription;
AllSpecialLabResultModel(
{this.actualDoctorRate,
@ -110,7 +112,10 @@ class AllSpecialLabResultModel {
//this.speciality,
this.status,
this.statusDesc,
this.strOrderDate});
this.strOrderDate,
this.testShortDescription,
this.testDescription,
});
AllSpecialLabResultModel.fromJson(Map<String, dynamic> json) {
actualDoctorRate = json['ActualDoctorRate'];
@ -167,6 +172,8 @@ class AllSpecialLabResultModel {
IsLabTestAttachment = json['IsLabTestAttachment'] ?? false;
LabAccessionNo = json['LabAccessionNo'] ?? json['AccessionNo'];
testDescription = json['Description'] ;
testShortDescription = json['TestShortDescription'];
if (json['FileNameWithExtensionList'] != null) {
FileNameWithExtensionList = <String>[];

@ -21,10 +21,15 @@ class LabResult {
String? verifiedOn;
dynamic verifiedOnDateTime;
bool? IsLabTestAttachment; // for the attachment icon in the lab result list
bool? IsPackage; // for the attachment icon in the lab result list
dynamic LabAccessionNo;
List<String>? FileNameWithExtensionList;
dynamic FileHash;
List<UploadedDocument>? UploadedDocuments;
String? packageName = "";
String? TestShortDescription = "";
String? PackageShortDescription = "";
LabResult(
{this.description,
@ -52,6 +57,8 @@ class LabResult {
this.LabAccessionNo,
this.FileNameWithExtensionList,
this.FileHash,
this.TestShortDescription,
this.PackageShortDescription,
this.UploadedDocuments});
LabResult.fromJson(Map<String, dynamic> json) {
@ -77,8 +84,12 @@ class LabResult {
verifiedOn = json['VerifiedOn'];
verifiedOnDateTime = json['VerifiedOnDateTime'];
IsLabTestAttachment = json['IsLabTestAttachment'] ?? false;
IsPackage = json['IsPackage'] ?? false;
LabAccessionNo = json['LabAccessionNo'] ?? json['AccessionNo'];
packageName = json['PackageName'] ??null ;
TestShortDescription = json['TestShortDescription'] ??null ;
PackageShortDescription = json['PackageShortDescription'] ??null ;
if (json['FileNameWithExtensionList'] != null) {
FileNameWithExtensionList = <String>[];

@ -12,6 +12,10 @@ class PatientLabSpecialResult {
List<UploadedDocument>? UploadedDocuments;
bool? IsLabTestAttachment; // for the attachment icon in the lab result list
dynamic LabAccessionNo;
String? testCode;
String? testDescription;
String? testID;
String? testShortDescription;
PatientLabSpecialResult(
{this.invoiceNo,
@ -21,7 +25,12 @@ class PatientLabSpecialResult {
this.resultDataTxt,
this.FileNameWithExtensionList,
this.UploadedDocuments,
this.IsLabTestAttachment});
this.IsLabTestAttachment,
this.testCode,
this.testDescription,
this.testShortDescription,
this.testID,
});
PatientLabSpecialResult.fromJson(Map<String, dynamic> json) {
SetupID = json['SetupID'];
@ -46,6 +55,11 @@ class PatientLabSpecialResult {
}
IsLabTestAttachment = json['IsLabTestAttachment'] ?? false;
testCode = json['TestCode'];
testDescription = json['Description'];
testID = json['TestID'];
testShortDescription = json['TestShortDescription'];
}
Map<String, dynamic> toJson() {

@ -60,6 +60,7 @@ class LabsService extends BaseService {
List<PatientLabSpecialResult> patientLabSpecialResult = [];
List<LabResult> labResultList = [];
Map<String, List<LabResult>> mappedLabResultList = {};
List<LabOrderResult> labOrdersResultsList = [];
List<LabAttachmentItem> labAttachment = [];
List<LabResultHistory> labOrdersResultHistoryList = [];
@ -125,11 +126,48 @@ class LabsService extends BaseService {
labResultList.add(LabResult.fromJson(lab));
});
}
///sorting the list based on the package name or test code right now if the vida3 add the package name for the doctor application endpoint then it will work cause the elements are going to give the priority to the package name if it is not null or empty otherwise it will give the priority to the test code and then it will sort the list based on the package name first then goes for the test code to sort and enjoy
mappedLabResultList = groupLabResultsByPackageName(labResultList);
}, onFailure: (String error, int statusCode) {
hasError = true;
super.error = error;
}, body: body);
}
Map<String, List<LabResult>> groupLabResultsByPackageName(List<LabResult> labResults) {
Map<String, List<LabResult>> groupedResults = {};
// Group results by package name
for (var labResult in labResults) {
String packageName = labResult.packageName?? labResult.testCode ?? 'Unknown Package';
if (groupedResults.containsKey(packageName)) {
// Package name exists, add to existing list
groupedResults[packageName]!.add(labResult);
} else {
// Package name doesn't exist, create new key with new list
groupedResults[packageName] = [labResult];
}
}
// Sort each list alphabetically by description
groupedResults.forEach((key, list) {
list.sort((a, b) {
String descA = (a.description ?? '').toLowerCase();
String descB = (b.description ?? '').toLowerCase();
return descA.compareTo(descB);
});
});
// Sort the map keys alphabetically and return a new sorted map
var sortedKeys = groupedResults.keys.toList()..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
Map<String, List<LabResult>> sortedGroupedResults = {};
for (var key in sortedKeys) {
sortedGroupedResults[key] = groupedResults[key]!;
}
return sortedGroupedResults;
}
Future getPatientLabOrdersResults({PatientLabOrders? patientLabOrder, required String procedure, required PatiantInformtion patient}) async {
hasError = false;

@ -104,7 +104,7 @@ class PatientSearchViewModel extends BaseViewModel {
String dateTo;
String dateFrom;
if (OutPatientFilterType.Previous == outPatientFilterType) {
selectedFromDate = DateTime(DateTime.now().year, DateTime.now().month - 1, DateTime.now().day);
selectedFromDate = DateTime(DateTime.now().year, DateTime.now().month , DateTime.now().day-2);
selectedToDate = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day - 1);
dateTo = AppDateUtils.convertDateToFormat(selectedToDate!, 'yyyy-MM-dd');
dateFrom = AppDateUtils.convertDateToFormat(selectedFromDate!, 'yyyy-MM-dd');

@ -1,5 +1,5 @@
import 'dart:io';
import 'package:local_auth_darwin/local_auth_darwin.dart';
import 'package:doctor_app_flutter/config/shared_pref_kay.dart';
import 'package:doctor_app_flutter/core/enum/auth_method_types.dart';
import 'package:doctor_app_flutter/core/enum/view_state.dart';
@ -30,7 +30,6 @@ import 'package:flutter/services.dart';
// import 'package:huawei_hmsavailability/huawei_hmsavailability.dart';
// import 'package:local_auth/auth_strings.dart';
import 'package:local_auth/local_auth.dart';
import 'package:local_auth_ios/local_auth_ios.dart';
enum APP_STATUS { LOADING, UNAUTHENTICATED, AUTHENTICATED, UNVERIFIED }
@ -279,15 +278,12 @@ class AuthenticationViewModel extends BaseViewModel {
/// ask user to add his biometric
showIOSAuthMessages() async {
const iosStrings = IOSAuthMessages(cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please Enable Your Touch ID');
final iosStrings = IOSAuthMessages(cancelButton: 'cancel', );
try {
await auth.authenticate(
localizedReason: 'Scan your fingerprint to authenticate',
authMessages: [iosStrings],
options: AuthenticationOptions(
useErrorDialogs: true,
stickyAuth: true,
),
);
} on PlatformException catch (e) {
DrAppToastMsg.showErrorToast(e.toString());

@ -74,6 +74,7 @@ class LabsViewModel extends BaseViewModel {
List<PatientLabSpecialResult> get patientLabSpecialResult => _labsService.patientLabSpecialResult;
List<LabResult> get labResultList => _labsService.labResultList;
Map<String, List<LabResult>> get mappedLabResultList => _labsService.mappedLabResultList;
List<LabResultList> labResultLists = [];

@ -18,6 +18,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:timezone/data/latest_all.dart' as tz;
@ -86,6 +87,7 @@ class MyApp extends StatelessWidget {
locale: projectProvider.appLocal,
localizationsDelegates: [
TranslationBaseDelegate(),
FlutterQuillLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,

@ -26,8 +26,10 @@ class ListOfMyInpatient extends StatelessWidget {
margin: EdgeInsets.symmetric(horizontal: 16.0),
child: patientSearchViewModel.filteredMyInPatientItems.length == 0
? NoData()
: NotificationListener(
child: ListView.builder(
:
NotificationListener(
child:
ListView.builder(
itemCount:
patientSearchViewModel.filteredMyInPatientItems.length,
scrollDirection: Axis.vertical,

@ -80,7 +80,7 @@ class LineChartForDiabetic extends StatelessWidget {
return LineChartData(
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: Colors.white,
getTooltipColor: (LineBarSpot touchedSpot) => Colors.white,
),
touchCallback: (FlTouchEvent event, LineTouchResponse? touchResponse) {},
handleBuiltInTouches: true,

@ -86,7 +86,7 @@ class LineChartCurvedLabHistoryState extends State<LineChartCurvedLabHistory> {
return LineChartData(
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: Colors.white,
getTooltipColor: (LineBarSpot touchedSpot) => Colors.white,
),
touchCallback: (event, touchResponse) {},
handleBuiltInTouches: true,

@ -6,12 +6,10 @@ import 'package:doctor_app_flutter/core/viewModel/project_view_model.dart';
import 'package:doctor_app_flutter/core/model/patient/patiant_info_model.dart';
import 'package:doctor_app_flutter/screens/patients/profile/lab_result/laboratory_result/flow_chart_page.dart';
import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart';
import 'package:doctor_app_flutter/utils/utils.dart';
import 'package:doctor_app_flutter/widgets/shared/app_texts_widget.dart';
import 'package:doctor_app_flutter/widgets/transitions/fade_page.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:open_filex/open_filex.dart';
import 'package:provider/provider.dart';
class LabResultWidget extends StatelessWidget {
@ -24,15 +22,16 @@ class LabResultWidget extends StatelessWidget {
LabResultWidget({Key? key, required this.filterName, required this.patientLabResultList, required this.patientLabOrder, required this.patient, required this.isInpatient, required this.model})
: super(key: key);
late ProjectViewModel projectViewModel;
@override
Widget build(BuildContext context) {
projectViewModel = Provider.of(context);
Provider.of<ProjectViewModel>(context);
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// if (!isInpatient)
@ -41,234 +40,246 @@ class LabResultWidget extends StatelessWidget {
children: [
Row(
children: [
AppText(
filterName,
fontSize: SizeConfig.textMultiplier! * 2.0,
fontWeight: FontWeight.w700,
),
],
),
],
),
SizedBox(
height: 8,
),
Row(
children: [
Expanded(
child: Container(
child: Center(
Visibility(
visible: filterName.isNotEmpty,
child: AppText(
TranslationBase.of(context).description,
color: Colors.black,
fontSize: SizeConfig.textMultiplier! * 2.0,
filterName,
fontSize: SizeConfig.textMultiplier! * 1.6,
fontWeight: FontWeight.w700,
),
),
),
),
Expanded(
child: Container(
child: Center(
child: AppText(
TranslationBase.of(context).value,
color: Colors.black,
fontSize: SizeConfig.textMultiplier! * 2.0,
fontWeight: FontWeight.w700,
),
),
),
),
Expanded(
child: Container(
child: Center(
child: AppText(
TranslationBase.of(context).range,
fontSize: SizeConfig.textMultiplier! * 2.0,
fontWeight: FontWeight.w700,
),
),
),
],
),
Expanded(
child: Container(),
)
],
),
Divider(
color: Colors.grey.shade500,
thickness: 1,
),
SizedBox(
height: 12,
height: 16,
),
ListView.builder(
itemCount: patientLabResultList.length,
scrollDirection: Axis.vertical,
physics: ScrollPhysics(),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Color(0X55F2F2F2),
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
),
child: Row(
SizedBox(height: 8,),
Visibility(
visible: index == 0,
child: Column(
children: [
Expanded(
child: Container(
padding: EdgeInsets.all(10),
child: Center(
child: AppText(
patientLabResultList[index].description!,
textAlign: TextAlign.center,
fontSize: SizeConfig.textMultiplier! * 1.8,
isCopyable: true,
),
),
),
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (patientLabResultList[index].checkResultStatus() != 0)
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red.shade700,
),
child: Icon(
patientLabResultList[index].checkResultStatus() == 1 ? Icons.arrow_upward : Icons.arrow_downward,
color: Colors.white,
size: 16,
),
Visibility(
visible: patientLabResultList[index].PackageShortDescription != null && patientLabResultList[index].PackageShortDescription?.isNotEmpty == true ,
child:Column(
children: [
AppText(
patientLabResultList[index].PackageShortDescription??"",
fontSize: SizeConfig.textMultiplier! * 1.3,
fontWeight: FontWeight.w400,
color: Colors.black87,
isCopyable: true,
),
Expanded(
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 4),
child: Center(
child: AppText(
patientLabResultList[index].resultValue ?? "" + " " + "${patientLabResultList[index].uOM ?? ""}",
textAlign: TextAlign.center,
isCopyable: true,
fontSize: SizeConfig.textMultiplier! * 1.8,
),
),
SizedBox(
height: 8,
),
),
],
),
),
Expanded(
child: Container(
padding: EdgeInsets.all(10),
// color: Colors.white,
child: Center(
child: AppText(
patientLabResultList[index].referanceRange!,
textAlign: TextAlign.center,
fontSize: SizeConfig.textMultiplier! * 1.8,
isCopyable: true,
),
),
),
),
Expanded(
child: Column(
children: [
Visibility(
visible: patientLabResultList[index].IsLabTestAttachment ?? false,
child: InkWell(
onTap: () async {
var labAccessNumber = patientLabResultList[index].LabAccessionNo;
// if (patientLabResultList[index].FileNameWithExtensionList?.isNotEmpty == true || patientLabResultList[index].FileNameWithExtensionList?.length == 1) {
// await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, FileName: patientLabResultList[index].FileNameWithExtensionList?.first);
// model.openPDF();
// return;
// }
//
// if (patientLabResultList[index].UploadedDocuments?.isNotEmpty == true || patientLabResultList[index].UploadedDocuments?.length == 1) {
// await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, uploadedDocument: patientLabResultList[index].UploadedDocuments?.first);
// model.openPDF();
// return;
// }
showUploadedDocumentsBottomSheet(
context,
documents: patientLabResultList[index].UploadedDocuments,
fileName: patientLabResultList[index].FileNameWithExtensionList,
labAccessNumber: labAccessNumber,
projectID: int.parse(patientLabResultList[index].projectID!),
setupID: patientLabResultList[index].setupID);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AppText(
TranslationBase.of(context).showAttachment,
textDecoration: TextDecoration.underline,
color: Colors.red.shade700,
fontSize: SizeConfig.textMultiplier! * 1.2,
fontWeight: FontWeight.w700,
),
],
),
],
) ),
Visibility(
visible: (patientLabResultList.any((lab) => lab.IsLabTestAttachment ?? false)) && (patientLabResultList.any((lab) => lab.IsPackage ?? false)),
child: Center(
child: InkWell(
onTap: () async {
final labWithAttachment = patientLabResultList[index];
var labAccessNumber = labWithAttachment.LabAccessionNo;
showUploadedDocumentsBottomSheet(
context,
documents: labWithAttachment.UploadedDocuments,
fileName: labWithAttachment.FileNameWithExtensionList,
labAccessNumber: labAccessNumber,
projectID: int.parse(labWithAttachment.projectID!),
setupID: labWithAttachment.setupID,
);
},
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 14, horizontal: 24),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(16),
),
),
SizedBox(
height: 8,
),
InkWell(
onTap: () {
Navigator.push(
context,
FadePage(
page: FlowChartPage(
filterName: patientLabResultList[index].description!,
patientLabOrder: patientLabOrder,
patient: patient,
isInpatient: isInpatient,
),
),
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.description_outlined,
color: Colors.red.shade700,
size: 20,
),
SizedBox(width: 8),
AppText(
TranslationBase.of(context).showMoreBtn,
textDecoration: TextDecoration.underline,
TranslationBase.of(context).showAttachment,
color: Colors.red.shade700,
fontSize: SizeConfig.textMultiplier! * 1.4,
fontWeight: FontWeight.w700,
),
],
),
),
],
),
),
),
SizedBox(height: 8,)
],
),
),
// Label (e.g. "Diagnosis:") - bold + underlined, like the screenshot
Row(
children: [
Expanded(
child: AppText(
patientLabResultList[index].description!,
fontSize: SizeConfig.textMultiplier! * 1.9,
color: Colors.black,
),
),
SizedBox(
width: 8,
),
// Value - plain text below the label
AppText(
(patientLabResultList[index].resultValue ?? "") + (patientLabResultList[index].uOM != null ? " ${patientLabResultList[index].uOM}" : ""),
fontSize: SizeConfig.textMultiplier! * 1.8,
fontWeight: FontWeight.w400,
color: Colors.black87,
isCopyable: true,
),
],
),
Visibility(
visible: patientLabResultList[index].TestShortDescription != null ,
child:Column(
children: [
SizedBox(
height: 20,
),
AppText(
patientLabResultList[index].TestShortDescription??"",
fontSize: SizeConfig.textMultiplier! * 1.3,
fontWeight: FontWeight.w400,
color: Colors.black87,
isCopyable: true,
),
SizedBox(
height: 8,
),
],
) ),
SizedBox(
height: 16,
),
Center(
child: InkWell(
onTap: () {
Navigator.push(
context,
FadePage(
page: FlowChartPage(
filterName: patientLabResultList.first.description!,
patientLabOrder: patientLabOrder,
patient: patient,
isInpatient: isInpatient,
),
),
);
},
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 14, horizontal: 24),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.show_chart,
color: Colors.red.shade700,
size: 20,
),
SizedBox(width: 8),
AppText(
TranslationBase.of(context).showMoreBtn,
color: Colors.red.shade700,
fontSize: SizeConfig.textMultiplier! * 1.4,
),
],
),
),
),
),
SizedBox(
height: 16,
),
Visibility(
visible: (patientLabResultList[index].IsLabTestAttachment== true ) && (patientLabResultList[index].IsPackage == false),
child: Center(
child: InkWell(
onTap: () async {
final labWithAttachment = patientLabResultList.firstWhere(
(lab) => lab.IsLabTestAttachment ?? false,
);
var labAccessNumber = labWithAttachment.LabAccessionNo;
showUploadedDocumentsBottomSheet(
context,
documents: labWithAttachment.UploadedDocuments,
fileName: labWithAttachment.FileNameWithExtensionList,
labAccessNumber: labAccessNumber,
projectID: int.parse(labWithAttachment.projectID!),
setupID: labWithAttachment.setupID,
);
},
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 14, horizontal: 24),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.description_outlined,
color: Colors.red.shade700,
size: 20,
),
SizedBox(width: 8),
AppText(
TranslationBase.of(context).showAttachment,
color: Colors.red.shade700,
fontSize: SizeConfig.textMultiplier! * 1.4,
),
],
),
),
),
),
),
],
);
}),
SizedBox(
height: 8,
),
Divider(),
],
),
);
@ -357,13 +368,13 @@ class LabResultWidget extends StatelessWidget {
}
void showUploadedDocumentsBottomSheet(
BuildContext context, {
List<UploadedDocument>? documents,
List<String>? fileName,
String? labAccessNumber,
int? projectID,
String? setupID,
}) {
BuildContext context, {
List<UploadedDocument>? documents,
List<String>? fileName,
String? labAccessNumber,
int? projectID,
String? setupID,
}) {
const double kItemHeight = 66.0; // ListTile with contentPadding: 10
final double screenHeight = MediaQuery.of(context).size.height;
final double maxSheetHeight = screenHeight * 0.5;
@ -422,7 +433,7 @@ class LabResultWidget extends StatelessWidget {
child: const Divider(height: 1, color: Colors.grey),
),
itemBuilder: (context, index) {
final docName = documents?[index]?.fileName ?? fileName?[index];
final docName = documents?[index].fileName ?? fileName?[index];
return ListTile(
contentPadding: const EdgeInsets.all(10),

@ -13,7 +13,8 @@ import 'package:doctor_app_flutter/widgets/shared/app_texts_widget.dart';
import 'package:doctor_app_flutter/widgets/shared/errors/error_message.dart';
import 'package:doctor_app_flutter/widgets/shared/network_base_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:provider/provider.dart';
class LaboratoryResultWidget extends StatefulWidget {
@ -35,6 +36,7 @@ class LaboratoryResultWidget extends StatefulWidget {
class _LaboratoryResultWidgetState extends State<LaboratoryResultWidget> {
bool _isShowMoreGeneral = true;
bool _isShowMore = true;
int expandedIndex = 0;
late ProjectViewModel projectViewModel;
@override
@ -51,15 +53,23 @@ class _LaboratoryResultWidgetState extends State<LaboratoryResultWidget> {
baseViewModel: model,
child: SafeArea(
child: Container(
margin: EdgeInsets.all(15),
margin: EdgeInsets.all(4),
child: Column(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (model.labResultLists.isNotEmpty)
if (model.mappedLabResultList.isNotEmpty)
Container(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(16.0),
),
),
child: Column(
children: [
InkWell(
@ -73,13 +83,13 @@ class _LaboratoryResultWidgetState extends State<LaboratoryResultWidget> {
child: Container(
padding: EdgeInsets.all(10.0),
margin: EdgeInsets.only(left: 5, right: 5),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(5.0),
),
),
// decoration: BoxDecoration(
// shape: BoxShape.rectangle,
// color: Colors.white,
// borderRadius: BorderRadius.all(
// Radius.circular(5.0),
// ),
// ),
child: Row(
children: <Widget>[
Expanded(
@ -107,33 +117,34 @@ class _LaboratoryResultWidgetState extends State<LaboratoryResultWidget> {
AnimatedContainer(
padding: EdgeInsets.all(10.0),
margin: EdgeInsets.only(left: 5, right: 5),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(5.0),
bottomRight: Radius.circular(5.0),
),
),
// decoration: BoxDecoration(
// shape: BoxShape.rectangle,
// color: Colors.white,
// borderRadius: BorderRadius.only(
// bottomLeft: Radius.circular(5.0),
// bottomRight: Radius.circular(5.0),
// ),
// ),
duration: Duration(milliseconds: 7000),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListView.builder(
itemCount: model.labResultLists.length,
scrollDirection: Axis.vertical,
physics: ScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
return LabResultWidget(
patientLabOrder: widget.patientLabOrder!,
filterName: model.labResultLists[index].filterName,
patientLabResultList: model.labResultLists[index].patientLabResultList,
patient: widget.patient!,
isInpatient: widget.isInpatient!,
model: model,
);
}),
// ListView.builder(
// itemCount: model.mappedLabResultList.length,
// scrollDirection: Axis.vertical,
// physics: ScrollPhysics(),
// shrinkWrap: true,
// itemBuilder: (context, index) {
// return LabResultWidget(
// patientLabOrder: widget.patientLabOrder!,
// filterName: model.labResultLists[index].filterName,
// patientLabResultList: model.labResultLists[index].patientLabResultList,
// patient: widget.patient!,
// isInpatient: widget.isInpatient!,
// model: model,
// );
// }),
mappedListDisplay(model)
],
),
),
@ -150,139 +161,85 @@ class _LaboratoryResultWidgetState extends State<LaboratoryResultWidget> {
height: 15,
),
if (widget.details?.isNotEmpty == true)
Column(
children: [
InkWell(
onTap: () {
setState(() {
_isShowMore = !_isShowMore;
});
},
child: Container(
padding: EdgeInsets.all(10.0),
margin: EdgeInsets.only(left: 5, right: 5),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(5.0),
)),
child: Row(
children: <Widget>[
Expanded(
child: Container(
margin: EdgeInsets.only(left: 0, right: 0),
child: AppText(
TranslationBase.of(context).specialResult,
fontSize: SizeConfig.textMultiplier! * 2.3,
bold: false,
))),
Container(
width: 25,
height: 25,
child: Icon(
_isShowMore ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
color: Colors.grey[800],
size: 22,
),
)
],
Container(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(16.0),
)),
child: Column(
children: [
InkWell(
onTap: () {
setState(() {
_isShowMore = !_isShowMore;
});
},
child: Container(
padding: EdgeInsets.all(10.0),
margin: EdgeInsets.only(left: 5, right: 5),
child: Row(
children: <Widget>[
Expanded(
child: Container(
margin: EdgeInsets.only(left: 0, right: 0),
child: AppText(
TranslationBase.of(context).specialResult,
fontSize: SizeConfig.textMultiplier! * 2.3,
bold: false,
))),
Container(
width: 25,
height: 25,
child: Icon(
_isShowMore ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
color: Colors.grey[800],
size: 22,
),
)
],
),
),
),
),
if (_isShowMore)
AnimatedContainer(
padding: EdgeInsets.all(10.0),
margin: EdgeInsets.only(left: 5, right: 5),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(5.0),
bottomRight: Radius.circular(5.0),
)),
duration: Duration(milliseconds: 7000),
child: ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.grey[300],
thickness: 1,
),
itemCount: widget.details?.length ?? 0,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
String? resultData;
try {
resultData = (widget.details?[index])?.resultDataHTML?.toString();
} catch (_) {
resultData = null;
}
final displayData = (resultData == null || resultData.trim().isEmpty)
? TranslationBase.of(context).noDataAvailable
: resultData;
return Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
!Utils.isTextHtml(displayData)
? AppText(displayData)
: Html(data: displayData),
],
),
),
Visibility(
visible: widget.details?[index].IsLabTestAttachment ?? false,
child: InkWell(
onTap: () async {
var labAccessNumber = widget.details?[index].LabAccessionNo;
// if (widget.details?[index].FileNameWithExtensionList?.isNotEmpty == true || widget.details?[index].FileNameWithExtensionList?.length == 1) {
// await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, FileName: widget.details?[index].FileNameWithExtensionList?.first);
// model.openPDF();
// return;
// }
//
// if (widget.details?[index].UploadedDocuments?.isNotEmpty == true || widget.details?[index].UploadedDocuments?.length == 1) {
// await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, uploadedDocument: widget.details?[index].UploadedDocuments?.first);
// model.openPDF();
// return;
// }
if (_isShowMore)
AnimatedContainer(
padding: EdgeInsets.all(10.0),
margin: EdgeInsets.only(left: 5, right: 5),
// decoration: BoxDecoration(
// shape: BoxShape.rectangle,
// color: Colors.white,
// borderRadius: BorderRadius.only(
// bottomLeft: Radius.circular(5.0),
// bottomRight: Radius.circular(5.0),
// )),
duration: Duration(milliseconds: 7000),
child: ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.grey[300],
thickness: 1,
),
itemCount: widget.details?.length ?? 0,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
String? resultData;
try {
resultData = (widget.details?[index])?.resultDataHTML?.toString();
} catch (_) {
resultData = null;
}
final displayData = (resultData == null || resultData.trim().isEmpty)
? TranslationBase.of(context).noDataAvailable
: resultData.trim();
showUploadedDocumentsBottomSheet(
context,
model,
documents: widget.details?[index].UploadedDocuments,
fileName: widget.details?[index].FileNameWithExtensionList,
labAccessNumber: labAccessNumber,
projectID: int.parse(widget.details?[index].ProjectID??"0"),
setupID: widget.details?[index].SetupID);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AppText(
TranslationBase.of(context).showAttachment,
textDecoration: TextDecoration.underline,
color: Colors.red.shade700,
fontSize: SizeConfig.textMultiplier! * 1.2,
fontWeight: FontWeight.w700,
),
],
),
),
),
],
);
},
return buildLabDetailItem(context, index, displayData, model);
},
),
),
),
SizedBox(
height: 10,
),
],
],
),
),
],
),
@ -294,6 +251,83 @@ class _LaboratoryResultWidgetState extends State<LaboratoryResultWidget> {
),
);
}
Widget buildLabDetailItem(BuildContext context, int index, String displayData, LabsViewModel model) {
return Card(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
elevation: 8,
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
!Utils.isTextHtml(displayData)
? AppText(displayData)
: HtmlWidget(displayData),
Visibility(
visible: widget.details?[index].IsLabTestAttachment ?? false,
child: Column(
children: [
SizedBox(height: 8,),
InkWell(
onTap: () async {
var labAccessNumber = widget.details?[index].LabAccessionNo;
// if (widget.details?[index].FileNameWithExtensionList?.isNotEmpty == true || widget.details?[index].FileNameWithExtensionList?.length == 1) {
// await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, FileName: widget.details?[index].FileNameWithExtensionList?.first);
// model.openPDF();
// return;
// }
//
// if (widget.details?[index].UploadedDocuments?.isNotEmpty == true || widget.details?[index].UploadedDocuments?.length == 1) {
// await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, uploadedDocument: widget.details?[index].UploadedDocuments?.first);
// model.openPDF();
// return;
// }
showUploadedDocumentsBottomSheet(
context,
model,
documents: widget.details?[index].UploadedDocuments,
fileName: widget.details?[index].FileNameWithExtensionList,
labAccessNumber: labAccessNumber,
projectID: int.parse(widget.details?[index].ProjectID ?? "0"),
setupID: widget.details?[index].SetupID);
},
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 14, horizontal: 24),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.description_outlined,
color: Colors.red.shade700,
size: 20,
),
SizedBox(width: 8),
AppText(
TranslationBase.of(context).showAttachment,
color: Colors.red.shade700,
fontSize: SizeConfig.textMultiplier! * 1.4,
),
],
),
),
),
],
),
),
],
),
),
);
}
void showUploadedDocumentsBottomSheet(
BuildContext context, LabsViewModel model, {
@ -361,7 +395,7 @@ class _LaboratoryResultWidgetState extends State<LaboratoryResultWidget> {
child: const Divider(height: 1, color: Colors.grey),
),
itemBuilder: (context, index) {
final docName = documents?[index]?.fileName ?? fileName?[index];
final docName = documents?[index].fileName ?? fileName?[index];
return ListTile(
contentPadding: const EdgeInsets.all(10),
@ -403,4 +437,104 @@ class _LaboratoryResultWidgetState extends State<LaboratoryResultWidget> {
},
);
}
mappedListDisplay(LabsViewModel model) {
final packageEntries = model.mappedLabResultList.entries.toList();
return ListView.separated(
separatorBuilder: (_, __) => Padding(padding: EdgeInsets.all(4),
child: Divider(
height: 2,
) ,),
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: packageEntries.length,
itemBuilder: (context, packageIndex) {
final packageName = packageEntries[packageIndex].key;
final labResults = packageEntries[packageIndex].value;
final isExpanded = expandedIndex == packageIndex;
return InkWell(
onTap: () {
setState(() {
if (isExpanded) {
expandedIndex = -1; // Collapse if already expanded
} else
expandedIndex = packageIndex;
});
},
child: Card(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: <Widget>[
Expanded(
child: Container(
margin: EdgeInsets.only(left: 0, right: 0),
child: AppText(
packageName,
fontSize: SizeConfig.textMultiplier! * 1.8,
bold: false,
))),
// Arrow rotates smoothly between down/up instead of snapping
AnimatedRotation(
turns: isExpanded ? 0.5 : 0.0,
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
child: Container(
width: 25,
height: 25,
child: Icon(
Icons.keyboard_arrow_down,
color: Colors.grey[800],
size: 22,
),
),
)
],
),
// Animated expand/collapse of the lab result content
AnimatedSize(
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
alignment: Alignment.topCenter,
child: AnimatedCrossFade(
duration: const Duration(milliseconds: 250),
sizeCurve: Curves.easeInOut,
firstCurve: Curves.easeIn,
secondCurve: Curves.easeIn,
crossFadeState: isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst,
firstChild: const SizedBox(width: double.infinity, height: 0),
secondChild: Column(
mainAxisSize: MainAxisSize.min,
children: [
LabResultWidget(
patientLabOrder: widget.patientLabOrder!,
filterName: "",
patientLabResultList: labResults,
patient: widget.patient!,
isInpatient: widget.isInpatient!,
model: model,
),
],
),
),
),
],
),
),
),
);
},
);
}
}

@ -3,6 +3,7 @@ import 'package:doctor_app_flutter/core/viewModel/PatientMedicalReportViewModel.
import 'package:doctor_app_flutter/core/model/patient/patiant_info_model.dart';
import 'package:doctor_app_flutter/core/viewModel/labs_view_model.dart';
import 'package:doctor_app_flutter/screens/base/base_view.dart';
import 'package:doctor_app_flutter/utils/extenstions_utils.dart';
import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart';
import 'package:doctor_app_flutter/widgets/patients/profile/app_bar/patient-profile-app-bar.dart';
import 'package:doctor_app_flutter/widgets/patients/patient_service_title.dart';
@ -10,8 +11,8 @@ import 'package:doctor_app_flutter/widgets/shared/app_scaffold_widget.dart';
import 'package:doctor_app_flutter/widgets/shared/errors/error_message.dart';
import 'package:doctor_app_flutter/widgets/shared/network_base_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
// import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import '../../../../../config/size_config.dart';
import '../../../../../core/model/labs/lab_result.dart';
@ -52,29 +53,68 @@ class SpecialLabResultDetailsPage extends StatelessWidget {
child: Column(
children: [
(resultData != null && resultData.isNotEmpty)
? Container(
width: double.infinity,
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(8)),
border: Border.fromBorderSide(
BorderSide(
color: Colors.white,
width: 1.0,
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Html(data: resultData ?? ""),
HtmlWidget(resultData ?? ""),
Visibility(
visible: labResult.IsLabTestAttachment ?? false,
child: InkWell(
? Card(
color: Colors.white,
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
elevation: 4,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Visibility(
visible: labResult.testDescription != null && (labResult.testDescription as String).isNotEmpty ,
child:Column(
children: [
SizedBox(
height: 8,
),
AppText(
labResult.testDescription ??"",
fontSize: SizeConfig.textMultiplier! * 1.6,
fontWeight: FontWeight.w700,
color: Colors.black87,
isCopyable: true,
),
SizedBox(
height: 8,
),
],
) ),
Visibility(
visible: labResult.testShortDescription != null && (labResult.testShortDescription as String).isNotEmpty ,
child:Column(
children: [
SizedBox(
height: 8,
),
AppText(
labResult.testShortDescription ??"",
fontSize: SizeConfig.textMultiplier! * 1.3,
fontWeight: FontWeight.w400,
color: Colors.black87,
isCopyable: true,
),
SizedBox(
height: 8,
),
],
) ),
// Html(data: resultData ?? ""),
HtmlWidget(resultData ?? ""),
Visibility(
visible: labResult.IsLabTestAttachment ?? false,
child: Column(
children: [
SizedBox(
height: 12,
),
InkWell(
onTap: () async {
var labAccessNumber = labResult.LabAccessionNo;
// if (patientLabResultList[index].FileNameWithExtensionList?.isNotEmpty == true || patientLabResultList[index].FileNameWithExtensionList?.length == 1) {
@ -89,32 +129,49 @@ class SpecialLabResultDetailsPage extends StatelessWidget {
// return;
// }
showUploadedDocumentsBottomSheet(context,
documents: labResult.UploadedDocuments,
fileName: labResult.FileNameWithExtensionList,
labAccessNumber: labAccessNumber,
projectID: int.parse(labResult.projectID!),
// projectID: 160,
setupID: labResult.setupID,
model: model);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AppText(
TranslationBase
.of(context)
.showAttachment,
textDecoration: TextDecoration.underline,
color: Colors.red.shade700,
fontSize: SizeConfig.textMultiplier! * 1.2,
fontWeight: FontWeight.w700,
showUploadedDocumentsBottomSheet(
context,
documents: labResult.UploadedDocuments,
fileName: labResult.FileNameWithExtensionList,
labAccessNumber: labAccessNumber,
projectID: int.parse(labResult.projectID!),
// projectID: 160,
setupID: labResult.setupID,
model: model,
);
},
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 14, horizontal: 16),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.description_outlined,
color: Colors.red.shade700,
size: 18,
),
SizedBox(width: 6),
AppText(
TranslationBase.of(context).showAttachment,
color: Colors.red.shade700,
fontSize: SizeConfig.textMultiplier! * 1.4,
fontWeight: FontWeight.w400,
),
],
),
),
),
],
),
),
),
],
],
),
),
)
: Container(

@ -10,9 +10,12 @@ import 'package:doctor_app_flutter/widgets/shared/buttons/app_buttons_widget.dar
import 'package:doctor_app_flutter/widgets/shared/loader/gif_loader_dialog_utils.dart';
import 'package:doctor_app_flutter/widgets/shared/text_fields/html_rich_editor.dart';
import 'package:flutter/material.dart';
import 'package:html_editor_enhanced/html_editor.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill_delta_from_html/parser/html_to_delta.dart';
// import 'package:html_editor_plus/editor_plus.dart';
// import 'package:html_editor_enhanced/html_editor.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart';
import '../../../../config/config.dart';
class AddVerifyMedicalReport extends StatefulWidget {
@ -32,7 +35,8 @@ class AddVerifyMedicalReport extends StatefulWidget {
}
class _AddVerifyMedicalReportState extends State<AddVerifyMedicalReport> {
HtmlEditorController _controller = HtmlEditorController();
QuillController _controller = QuillController.basic();
@override
Widget build(BuildContext context) {
@ -41,6 +45,11 @@ class _AddVerifyMedicalReportState extends State<AddVerifyMedicalReport> {
return BaseView<PatientMedicalReportViewModel>(
onModelReady: (model) async {
model.getMedicalReportTemplate();
_controller = htmlToQuillController(widget.medicalReport != null
? widget.medicalNote!
: widget.model!.medicalReportTemplate[0].templateText!.length > 0
? widget.model!.medicalReportTemplate[0].templateText!
: "");
},
builder: (_, model, w) => AppScaffold(
baseViewModel: model,
@ -62,14 +71,10 @@ class _AddVerifyMedicalReportState extends State<AddVerifyMedicalReport> {
children: [
if (model.medicalReportTemplate.length > 0)
HtmlRichEditor(
initialText: (widget.medicalReport != null
? widget.medicalNote!
: widget.model!.medicalReportTemplate[0].templateText!.length > 0
? widget.model!.medicalReportTemplate[0].templateText!
: ""),
controller: _controller,
initialText:"",
hint: "Write the medical report ",
height: MediaQuery.of(context).size.height * 0.75,
controller:_controller
),
],
),
@ -93,7 +98,10 @@ class _AddVerifyMedicalReportState extends State<AddVerifyMedicalReport> {
// disabled: progressNoteController.text.isEmpty,
fontWeight: FontWeight.w700,
onPressed: () async {
txtOfMedicalReport = (await _controller.getText())!;
final converter = QuillDeltaToHtmlConverter(
List.castFrom(_controller.document.toDelta().toJson()),
);
txtOfMedicalReport = converter.convert();
if (txtOfMedicalReport.isNotEmpty) {
GifLoaderDialogUtils.showMyDialog(context);
@ -125,7 +133,10 @@ class _AddVerifyMedicalReportState extends State<AddVerifyMedicalReport> {
color: AppGlobal.appGreenColor,
fontWeight: FontWeight.w700,
onPressed: () async {
txtOfMedicalReport = (await _controller.getText())!;
final converter = QuillDeltaToHtmlConverter(
List.castFrom(_controller.document.toDelta().toJson()),
);
txtOfMedicalReport = converter.convert();
if (txtOfMedicalReport.isNotEmpty) {
GifLoaderDialogUtils.showMyDialog(context);
print("isVidaPlusProject: ${widget.isVidaPlusProject}");
@ -154,6 +165,17 @@ class _AddVerifyMedicalReportState extends State<AddVerifyMedicalReport> {
Permission.microphone,
].request();
}
QuillController htmlToQuillController(String html) {
if(html.isEmpty) {
return QuillController.basic();
}
final delta = HtmlToDelta().convert(html);
return QuillController(
document: Document.fromDelta(delta),
selection: const TextSelection.collapsed(offset: 0),
);
}
}
enum MedicalReportStatus { ADD, VERIFY }

@ -10,7 +10,7 @@ import 'package:doctor_app_flutter/widgets/shared/app_scaffold_widget.dart';
import 'package:doctor_app_flutter/widgets/shared/app_texts_widget.dart';
import 'package:doctor_app_flutter/widgets/shared/errors/error_message.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:provider/provider.dart';
class MedicalReportDetailPage extends StatelessWidget {
@ -74,7 +74,7 @@ class MedicalReportDetailPage extends StatelessWidget {
),
),
),
child: Html(data: medicalReport.reportDataHtml ?? ""),
child: HtmlWidget(medicalReport.reportDataHtml ?? ""),
)
: Container(
child: ErrorMessage(

@ -139,7 +139,7 @@ class LineChartCurvedBloodPressureNew extends StatelessWidget {
return LineChartData(
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: Colors.white,
getTooltipColor: (LineBarSpot touchedSpot) => Colors.white,
),
touchCallback: (touchEvent, LineTouchResponse? touchResponse) {},
handleBuiltInTouches: true,

@ -100,7 +100,7 @@ class LineChartCurved extends StatelessWidget {
return LineChartData(
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: Colors.white,
getTooltipColor: (LineBarSpot touchedSpot) => Colors.white,
),
touchCallback: (event, LineTouchResponse? touchResponse) {},
handleBuiltInTouches: true,

@ -119,7 +119,7 @@ class LineChartCurvedBloodPressure extends StatelessWidget {
return LineChartData(
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: Colors.white,
getTooltipColor: (LineBarSpot touchedSpot) => Colors.white,
),
touchCallback: (event, LineTouchResponse? touchResponse) {},
handleBuiltInTouches: true,

@ -6,8 +6,8 @@ import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../../widgets/shared/app_texts_widget.dart';
import '../../patients/profile/soap_update_vida_plus/assessment/widget/empty_dropdown.dart';
import 'package:material_ui/material_ui.dart' as decoration;
class PharmacyInterventionDialog extends StatefulWidget {
final Function(
@ -281,40 +281,36 @@ class _PharmacyInterventionDialogState
child: EmptyDropDown())
: Expanded(
child: DropdownSearch<NursingStationEntity>(
itemAsString:(item){
return item.description??'';
} ,
items: widget.station?.entityList ?? [],
itemAsString: (item) => item.description ?? '',
items: (filter, loadProps) => widget.station?.entityList ?? [],
compareFn: (a, b) => a.description == b.description, // see notes
popupProps: PopupProps.menu(
showSearchBox: true,
searchDelay: Duration(microseconds: 100),
searchDelay: const Duration(milliseconds: 100),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
border: UnderlineInputBorder(),
hintText: TranslationBase.of(context).search
)
)
decoration: decoration.InputDecoration(
border: decoration.UnderlineInputBorder(),
hintText: TranslationBase.of(context).search,
),
),
),
dropdownButtonProps: DropdownButtonProps(color: AppGlobal.appRedColor,),
dropdownDecoratorProps: DropDownDecoratorProps(
textAlignVertical: TextAlignVertical.center,
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none
suffixProps: DropdownSuffixProps(
dropdownButtonProps: DropdownButtonProps(
iconClosed: Icon(Icons.arrow_drop_down, color: AppGlobal.appRedColor),
iconOpened: Icon(Icons.arrow_drop_up, color: AppGlobal.appRedColor),
),
baseStyle: TextStyle(
fontSize: 14
)
),
selectedItem: nursingStation == null
? widget.station?.entityList?.first
: nursingStation,
onChanged: (newValue) async {
if (newValue != null)
setState(() {
nursingStation = newValue ;
});
},
decoratorProps: DropDownDecoratorProps(
textAlignVertical: TextAlignVertical.center,
baseStyle: TextStyle(fontSize: 14),
decoration: decoration.InputDecoration(border: decoration.InputBorder.none),
),
selectedItem: nursingStation ?? widget.station?.entityList?.firstOrNull,
onSelected: (newValue) {
if (newValue != null) {
setState(() => nursingStation = newValue);
}
},
)
// DropdownButtonHideUnderline(
// child: DropdownButton(

@ -21,6 +21,7 @@ import 'package:flutter/material.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:material_ui/material_ui.dart' as decoration;
Utils helpers = Utils();
@ -424,52 +425,60 @@ class _RescheduleLeaveScreen extends State<RescheduleLeaveScreen> {
model2.coveringDoctors.length > 0
? Expanded(
// add Expanded to have your dropdown button fill remaining space
child: DropdownSearch(
dropdownButtonProps: DropdownButtonProps(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(contentPadding: EdgeInsets.all(0), border: InputBorder.none),
child: DropdownSearch<String>(
items: (filter, loadProps) => model2.coveringDoctors
.map<String>((item) => (projectsProvider.isArabic
? item['doctorNameN']
: item['doctorName'])
.toString())
.toList(),
decoratorProps: const DropDownDecoratorProps(
decoration: decoration.InputDecoration(
contentPadding: EdgeInsets.zero,
border: decoration.InputBorder.none,
),
),
//maxHeight: 300,
items: model2.coveringDoctors.map((item) {
return projectsProvider.isArabic ? item['doctorNameN'] : item['doctorName'];
}).toList(),
// label: "Doctor List",
onChanged: (item) {
model2.coveringDoctors.forEach((newVal) => {if (newVal['doctorName'] == item) doctorID = newVal['DoctorID']});
},
selectedItem: getSelectedDoctor(model2),
onSelected: (item) {
final nameKey = projectsProvider.isArabic ? 'doctorNameN' : 'doctorName';
for (final doctor in model2.coveringDoctors) {
if (doctor[nameKey] == item) {
doctorID = doctor['DoctorID'];
}
}
},
popupProps: PopupProps.bottomSheet(
bottomSheetProps: BottomSheetProps(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
showSearchBox: true,
bottomSheetProps: const BottomSheetProps(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
),
title: Container(
height: 50,
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
title: Container(
height: 50,
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
child: Center(
child: Text(
'',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
child: const Center(
child: Text(
'',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
showSearchBox: true),
),
),
),
)
)
: SizedBox(),
],

@ -2,7 +2,6 @@
import 'dart:async';
import 'dart:convert';
import 'package:doctor_app_flutter/screens/video_call_zoom/video_view.dart';
import 'package:events_emitter/events_emitter.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -142,10 +141,10 @@ class _CallScreenState extends State<CallScreen> {
});
useEffect(() {
eventListener.addEventListener();
EventEmitter emitter = eventListener.eventEmitter;
final sessionJoinListener = emitter.on(EventType.onSessionJoin, (sessionUser) async {
final sessionJoinListener = emitter.addListener(EventType.onSessionJoin, (sessionUser) async {
isInSession.value = true;
zoom.session.getSessionName().then((value) => sessionName.value = value!);
sessionPassword.value = await zoom.session.getSessionPassword();
@ -165,7 +164,7 @@ class _CallScreenState extends State<CallScreen> {
isReceiveSpokenLanguageContentEnabled.value = await zoom.liveTranscriptionHelper.isReceiveSpokenLanguageContentEnabled();
});
final sessionLeaveListener = emitter.on(EventType.onSessionLeave, (data) async {
final sessionLeaveListener = emitter.addListener(EventType.onSessionLeave, (data) async {
isInSession.value = false;
users.value = <ZoomVideoSdkUser>[];
fullScreenUser.value = null;
@ -173,7 +172,7 @@ class _CallScreenState extends State<CallScreen> {
Navigator.pop(context);
});
final sessionNeedPasswordListener = emitter.on(EventType.onSessionNeedPassword, (data) async {
final sessionNeedPasswordListener = emitter.addListener(EventType.onSessionNeedPassword, (data) async {
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
@ -193,7 +192,7 @@ class _CallScreenState extends State<CallScreen> {
);
});
final sessionPasswordWrongListener = emitter.on(EventType.onSessionPasswordWrong, (data) async {
final sessionPasswordWrongListener = emitter.addListener(EventType.onSessionPasswordWrong, (data) async {
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
@ -213,7 +212,7 @@ class _CallScreenState extends State<CallScreen> {
);
});
final userVideoStatusChangedListener = emitter.on(EventType.onUserVideoStatusChanged, (data) async {
final userVideoStatusChangedListener = emitter.addListener(EventType.onUserVideoStatusChanged, (data) async {
data = data as Map;
ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
var userListJson = jsonDecode(data['changedUsers']) as List;
@ -228,7 +227,7 @@ class _CallScreenState extends State<CallScreen> {
videoStatusFlag.value = !videoStatusFlag.value;
});
final userAudioStatusChangedListener = emitter.on(EventType.onUserAudioStatusChanged, (data) async {
final userAudioStatusChangedListener = emitter.addListener(EventType.onUserAudioStatusChanged, (data) async {
data = data as Map;
ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
var userListJson = jsonDecode(data['changedUsers']) as List;
@ -243,7 +242,7 @@ class _CallScreenState extends State<CallScreen> {
audioStatusFlag.value = !audioStatusFlag.value;
});
final userShareStatusChangeListener = emitter.on(EventType.onUserShareStatusChanged, (data) async {
final userShareStatusChangeListener = emitter.addListener(EventType.onUserShareStatusChanged, (data) async {
data = data as Map;
ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
ZoomVideoSdkUser shareUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['user'].toString()));
@ -260,7 +259,7 @@ class _CallScreenState extends State<CallScreen> {
userShareStatusFlag.value = !userShareStatusFlag.value;
});
final userJoinListener = emitter.on(EventType.onUserJoin, (data) async {
final userJoinListener = emitter.addListener(EventType.onUserJoin, (data) async {
if (!isMounted()) return;
data = data as Map;
ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
@ -271,7 +270,7 @@ class _CallScreenState extends State<CallScreen> {
fullScreenUser.value = remoteUserList[1];
});
final userLeaveListener = emitter.on(EventType.onUserLeave, (data) async {
final userLeaveListener = emitter.addListener(EventType.onUserLeave, (data) async {
if (!isMounted()) return;
ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
data = data as Map;
@ -294,7 +293,7 @@ class _CallScreenState extends State<CallScreen> {
users.value = remoteUserList;
});
final userNameChangedListener = emitter.on(EventType.onUserNameChanged, (data) async {
final userNameChangedListener = emitter.addListener(EventType.onUserNameChanged, (data) async {
if (!isMounted()) return;
data = data as Map;
ZoomVideoSdkUser? changedUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['changedUser']));
@ -308,27 +307,27 @@ class _CallScreenState extends State<CallScreen> {
userNameFlag.value = !userNameFlag.value;
});
final commandReceived = emitter.on(EventType.onCommandReceived, (data) async {
final commandReceived = emitter.addListener(EventType.onCommandReceived, (data) async {
data = data as Map;
debugPrint("sender: ${ZoomVideoSdkUser.fromJson(jsonDecode(data['sender']))}, command: ${data['command']}");
});
final chatDeleteMessageNotify = emitter.on(EventType.onChatDeleteMessageNotify, (data) async {
final chatDeleteMessageNotify = emitter.addListener(EventType.onChatDeleteMessageNotify, (data) async {
data = data as Map;
debugPrint("onChatDeleteMessageNotify: messageID: ${data['msgID']}, deleteBy: ${data['type']}");
});
final liveStreamStatusChangeListener = emitter.on(EventType.onLiveStreamStatusChanged, (data) async {
final liveStreamStatusChangeListener = emitter.addListener(EventType.onLiveStreamStatusChanged, (data) async {
data = data as Map;
debugPrint("onLiveStreamStatusChanged: status: ${data['status']}");
});
final liveTranscriptionStatusChangeListener = emitter.on(EventType.onLiveTranscriptionStatus, (data) async {
final liveTranscriptionStatusChangeListener = emitter.addListener(EventType.onLiveTranscriptionStatus, (data) async {
data = data as Map;
debugPrint("onLiveTranscriptionStatus: status: ${data['status']}");
});
final cloudRecordingStatusListener = emitter.on(EventType.onCloudRecordingStatus, (data) async {
final cloudRecordingStatusListener = emitter.addListener(EventType.onCloudRecordingStatus, (data) async {
data = data as Map;
debugPrint("onCloudRecordingStatus: status: ${data['status']}");
ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
@ -380,18 +379,18 @@ class _CallScreenState extends State<CallScreen> {
}
});
final liveTranscriptionMsgInfoReceivedListener = emitter.on(EventType.onLiveTranscriptionMsgInfoReceived, (data) async {
final liveTranscriptionMsgInfoReceivedListener = emitter.addListener(EventType.onLiveTranscriptionMsgInfoReceived, (data) async {
data = data as Map;
ZoomVideoSdkLiveTranscriptionMessageInfo? messageInfo = ZoomVideoSdkLiveTranscriptionMessageInfo.fromJson(jsonDecode(data['messageInfo']));
debugPrint("onLiveTranscriptionMsgInfoReceived: content: ${messageInfo.messageContent}");
});
final inviteByPhoneStatusListener = emitter.on(EventType.onInviteByPhoneStatus, (data) async {
final inviteByPhoneStatusListener = emitter.addListener(EventType.onInviteByPhoneStatus, (data) async {
data = data as Map;
debugPrint("onInviteByPhoneStatus: status: ${data['status']}, reason: ${data['reason']}");
});
final multiCameraStreamStatusChangedListener = emitter.on(EventType.onMultiCameraStreamStatusChanged, (data) async {
final multiCameraStreamStatusChangedListener = emitter.addListener(EventType.onMultiCameraStreamStatusChanged, (data) async {
data = data as Map;
ZoomVideoSdkUser? changedUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['changedUser']));
var status = data['status'];
@ -408,7 +407,7 @@ class _CallScreenState extends State<CallScreen> {
}
});
final requireSystemPermission = emitter.on(EventType.onRequireSystemPermission, (data) async {
final requireSystemPermission = emitter.addListener(EventType.onRequireSystemPermission, (data) async {
data = data as Map;
ZoomVideoSdkUser? changedUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['changedUser']));
var permissionType = data['permissionType'];
@ -446,7 +445,7 @@ class _CallScreenState extends State<CallScreen> {
}
});
final networkStatusChangeListener = emitter.on(EventType.onUserVideoNetworkStatusChanged, (data) async {
final networkStatusChangeListener = emitter.addListener(EventType.onUserVideoNetworkStatusChanged, (data) async {
data = data as Map;
ZoomVideoSdkUser? networkUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['user']));
@ -455,7 +454,7 @@ class _CallScreenState extends State<CallScreen> {
}
});
final eventErrorListener = emitter.on(
final eventErrorListener = emitter.addListener(
EventType.onError,
(data) async {
data = data as Map;
@ -487,24 +486,24 @@ class _CallScreenState extends State<CallScreen> {
},
);
final userRecordingConsentListener = emitter.on(EventType.onUserRecordingConsent, (data) async {
final userRecordingConsentListener = emitter.addListener(EventType.onUserRecordingConsent, (data) async {
data = data as Map;
ZoomVideoSdkUser? user = ZoomVideoSdkUser.fromJson(jsonDecode(data['user']));
debugPrint('userRecordingConsentListener: user= ${user.userName}');
});
final callCRCDeviceStatusListener = emitter.on(EventType.onCallCRCDeviceStatusChanged, (data) async {
final callCRCDeviceStatusListener = emitter.addListener(EventType.onCallCRCDeviceStatusChanged, (data) async {
data = data as Map;
debugPrint('onCallCRCDeviceStatusChanged: status = ${data['status']}');
});
final originalLanguageMsgReceivedListener = emitter.on(EventType.onOriginalLanguageMsgReceived, (data) async {
final originalLanguageMsgReceivedListener = emitter.addListener(EventType.onOriginalLanguageMsgReceived, (data) async {
data = data as Map;
ZoomVideoSdkLiveTranscriptionMessageInfo? messageInfo = ZoomVideoSdkLiveTranscriptionMessageInfo.fromJson(jsonDecode(data['messageInfo']));
debugPrint("onOriginalLanguageMsgReceived: content: ${messageInfo.messageContent}");
});
final chatPrivilegeChangedListener = emitter.on(EventType.onChatPrivilegeChanged, (data) async {
final chatPrivilegeChangedListener = emitter.addListener(EventType.onChatPrivilegeChanged, (data) async {
data = data as Map;
String type = data['privilege'];
debugPrint('chatPrivilegeChangedListener: type= $type');

@ -48,6 +48,7 @@ class SMSOTP {
late ProjectViewModel projectProvider;
String displayTime = '';
bool isClosed = false;
// final TurnstileController _controller = TurnstileController();
// final TurnstileOptions _options = TurnstileOptions(
// size: TurnstileSize.normal,
@ -58,6 +59,7 @@ class SMSOTP {
// );
String? _token;
displayDialog(BuildContext context) async {
double dialogWidth = MediaQuery.of(context).size.width * 0.90;
double dialogInputWidth = (dialogWidth / 4) - (SizeConfig.isWidthLarge ? SizeConfig.getWidthMultiplier(width: dialogWidth) * 4.5 : 20);
@ -76,8 +78,8 @@ class SMSOTP {
child: Container(
color: Colors.white,
child: StatefulBuilder(builder: (context, setState) {
if (displayTime == '') {
startTimer(setState);
if (displayTime == '' && context.mounted) {
startTimer(setState, context);
}
return Center(
@ -246,7 +248,6 @@ class SMSOTP {
)),
),
),
Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
AppText(
TranslationBase.of(context).validationMessage + ' ',
@ -264,34 +265,38 @@ class SMSOTP {
fontSize: SizeConfig.getTextMultiplierBasedOnWidth(width: dialogWidth) * 3.5,
)
]),
SizedBox(height: 10,),
Column(children: [
// CloudflareTurnstile(
// siteKey: '0x4AAAAAAAkmbvEy63pcxBBt',
// baseUrl: BASE_URL,
// options: _options,
// controller: _controller,
// onTokenReceived: (token) {
// setState(() {
// print("Cloudflare token: $token");
// //todo pass this token to checkActivcationCode
// _token = token;
// checkValue();
// });
// },
// // onTokenRecived: (token) {
// // setState(() {
// // print("Cloudflare token: $token");
// // _token = token;
// // });
// // },
// onTokenExpired: () {},
// // mode: TurnstileMode.managed,
// // errorBuilder: (context, error) {
// // return Text(error.message);
// // },
// ),
],),
SizedBox(
height: 10,
),
Column(
children: [
// CloudflareTurnstile(
// siteKey: '0x4AAAAAAAkmbvEy63pcxBBt',
// baseUrl: BASE_URL,
// options: _options,
// controller: _controller,
// onTokenReceived: (token) {
// setState(() {
// print("Cloudflare token: $token");
// //todo pass this token to checkActivcationCode
// _token = token;
// checkValue();
// });
// },
// // onTokenRecived: (token) {
// // setState(() {
// // print("Cloudflare token: $token");
// // _token = token;
// // });
// // },
// onTokenExpired: () {},
// // mode: TurnstileMode.managed,
// // errorBuilder: (context, error) {
// // return Text(error.message);
// // },
// ),
],
),
],
),
),
@ -344,7 +349,8 @@ class SMSOTP {
}
checkValue() async {
if (verifyAccountForm.currentState!.validate() ) { //&& _token !=null removed cloud flare for vida plus
if (verifyAccountForm.currentState!.validate()) {
//&& _token !=null removed cloud flare for vida plus
// if (digit1.text.isNotEmpty && digit2.text.isNotEmpty && digit3.text.isNotEmpty && digit4.text.isNotEmpty) {
onSuccess(digit1.text.toString() + digit2.text.toString() + digit3.text.toString() + digit4.text.toString());
this.isClosed = true;
@ -363,8 +369,11 @@ class SMSOTP {
return minutesString + ":" + secondsString;
}
startTimer(setState) {
startTimer(setState, BuildContext context) {
this.remainingTime--;
if (context.mounted == false) {
return;
}
setState(() {
displayTime = this.getSecondsAsDigitalClock(this.remainingTime);
});
@ -372,7 +381,7 @@ class SMSOTP {
Future.delayed(Duration(seconds: 1), () {
if (this.remainingTime > 0) {
if (isClosed == false) {
startTimer(setState);
startTimer(setState,context);
}
} else {
Navigator.pop(context);

@ -4,11 +4,14 @@ import 'package:doctor_app_flutter/core/viewModel/project_view_model.dart';
import 'package:doctor_app_flutter/icons_app/doctor_app_icons.dart';
import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart';
import 'package:flutter/material.dart';
import 'package:html_editor_enhanced/html_editor.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill_delta_from_html/parser/html_to_delta.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:speech_to_text/speech_recognition_error.dart';
import 'package:speech_to_text/speech_to_text.dart' as stt;
import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart';
import '../speech-text-popup.dart';
@ -19,8 +22,8 @@ class HtmlRichEditor extends StatefulWidget {
final BoxDecoration? decoration;
final bool darkMode;
final bool showBottomToolbar;
final List<Toolbar>? toolbar;
final HtmlEditorController? controller;
final dynamic toolbar;
final QuillController? controller;
HtmlRichEditor({
key,
@ -63,34 +66,68 @@ class _HtmlRichEditorState extends State<HtmlRichEditor> {
return Stack(
children: [
HtmlEditor(
controller: widget.controller!,
htmlToolbarOptions: HtmlToolbarOptions(defaultToolbarButtons: [
StyleButtons(),
FontSettingButtons(),
FontButtons(),
// ColorButtons(),
ListButtons(),
ParagraphButtons(),
// InsertButtons(),
// OtherButtons(),
]),
htmlEditorOptions: HtmlEditorOptions(
hint: widget.hint,
initialText: widget.initialText,
darkMode: widget.darkMode,
// Toolbar
Column(
children: [
QuillSimpleToolbar(
controller: widget.controller!,
config: const QuillSimpleToolbarConfig(
showFontFamily: false,
showSearchButton: false,
showSubscript: false,
showSuperscript: false,
multiRowsDisplay: false, // single scrollable row
),
),
const Divider(height: 1),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: QuillEditor(
controller: widget.controller!,
focusNode: FocusNode(),
scrollController:ScrollController(),
config: const QuillEditorConfig(
placeholder: 'Start writing...',
padding: EdgeInsets.symmetric(vertical: 12),
expands: false,
autoFocus: false,
),
),
),
otherOptions: OtherOptions(
height: widget.height,
decoration: widget.decoration ??
BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(
Radius.circular(30.0),
),
border: Border.all(color: Colors.grey[200]!, width: 0.5),
),
)),
],
),
// HtmlEditor(
// controller: widget.controller!,
// // htmlToolbarOptions: HtmlToolbarOptions(defaultToolbarButtons: [
// // StyleButtons(),
// // FontSettingButtons(),
// // FontButtons(),
// // // ColorButtons(),
// // ListButtons(),
// // ParagraphButtons(),
// // // InsertButtons(),
// // // OtherButtons(),
// // ]),
// // htmlEditorOptions: HtmlEditorOptions(
// // hint: widget.hint,
// // initialText: widget.initialText,
// // darkMode: widget.darkMode,
// // ),
// // otherOptions: OtherOptions(
// // height: widget.height,
// // decoration: widget.decoration ??
// // BoxDecoration(
// // color: Colors.transparent,
// // borderRadius: BorderRadius.all(
// // Radius.circular(30.0),
// // ),
// // border: Border.all(color: Colors.grey[200]!, width: 0.5),
// // ),
// // )
// ),
Positioned(
top: 50, //MediaQuery.of(context).size.height * 0,
right: projectViewModel.isArabic ? MediaQuery.of(context).size.width * 0.75 : 15,
@ -142,10 +179,10 @@ class _HtmlRichEditorState extends State<HtmlRichEditor> {
void resultListener(result) async {
recognizedWord = result.recognizedWords;
event.setValue({"searchText": recognizedWord});
String txt = await widget.controller!.getText();
String txt = _getHtml();
if (result.finalResult == true) {
setState(() {
widget.controller!.setText(txt + recognizedWord);
appendToEnd(widget.controller!, recognizedWord);
SpeechToText.closeAlertDialog(context);
// speech.stop();
});
@ -154,6 +191,23 @@ class _HtmlRichEditorState extends State<HtmlRichEditor> {
}
}
void appendToEnd(QuillController controller, String text) {
final index = controller.document.length - 1; // position before the final '\n'
controller.replaceText(
index,
0,
text,
TextSelection.collapsed(offset: index + text.length),
);
}
String _getHtml() {
final delta = List<Map<String, dynamic>>.from(
widget.controller!.document.toDelta().toJson(),
);
return QuillDeltaToHtmlConverter(delta, ConverterOptions()).convert();
}
Future<void> initSpeechState() async {
bool hasSpeech = await speech.initialize(onError: errorListener, onStatus: statusListener);
print(hasSpeech);

@ -6,10 +6,14 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <maps_launcher/maps_launcher_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) maps_launcher_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MapsLauncherPlugin");
maps_launcher_plugin_register_with_registrar(maps_launcher_registrar);

@ -3,11 +3,13 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
maps_launcher
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
)
set(PLUGIN_BUNDLED_LIBRARIES)

@ -5,28 +5,44 @@
import FlutterMacOS
import Foundation
import audio_session
import connectivity_plus
import file_picker_darwin
import file_selector_macos
import firebase_analytics
import firebase_core
import firebase_messaging
import flutter_inappwebview_macos
import just_audio
import local_auth_darwin
import maps_launcher
import path_provider_foundation
import package_info_plus
import quill_native_bridge_macos
import shared_preferences_foundation
import speech_to_text
import sqflite_darwin
import url_launcher_macos
import video_player_avfoundation
import wakelock_plus
import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin"))
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FirebaseAnalyticsPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin"))
MapsLauncherPlugin.register(with: registry.registrar(forPlugin: "MapsLauncherPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
QuillNativeBridgePlugin.register(with: registry.registrar(forPlugin: "QuillNativeBridgePlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SpeechToTextPlugin.register(with: registry.registrar(forPlugin: "SpeechToTextPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
VideoPlayerPlugin.register(with: registry.registrar(forPlugin: "VideoPlayerPlugin"))
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
}

@ -1,4 +1,4 @@
platform :osx, '10.15'
platform :osx, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

@ -557,7 +557,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
@ -639,7 +639,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
@ -689,7 +689,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;

File diff suppressed because it is too large Load Diff

@ -35,13 +35,13 @@ dependencies:
shared_preferences: ^2.5.3
# imei_plugin: ^1.2.0
# flutter_flexible_toast: ^0.1.4
fluttertoast: ^8.2.12
local_auth: 2.1.8
local_auth_android: ^1.0.48
fluttertoast: ^10.0.0
local_auth: ^3.0.2
local_auth_android: ^2.2.0
http_interceptor: any
# connectivity: ^3.0.6
connectivity_plus: ^6.1.4
connectivity_plus: ^7.3.1
maps_launcher: ^3.0.0+1
url_launcher: ^6.3.2
url_launcher_ios: ^6.3.2
@ -54,7 +54,7 @@ dependencies:
#Icons
eva_icons_flutter: ^4.0.0
font_awesome_flutter: ^11.0.0
dropdown_search: ^5.0.6
dropdown_search: ^8.1.0
flutter_staggered_grid_view: ^0.7.0
expandable: ^5.0.1
@ -62,7 +62,7 @@ dependencies:
# Qr code Scanner
barcode_scan2: ^4.5.1
# permissions
permission_handler: ^11.1.0
permission_handler: ^13.0.2
# device_info: ^2.0.2
# The following adds the Cupertino Icons font to your application.
@ -74,14 +74,14 @@ dependencies:
percent_indicator: ^4.2.3
#Dependency Injection
get_it: ^7.6.4
get_it: ^9.3.0
#chart
fl_chart: ^0.65.0
fl_chart: ^1.2.0
# Firebase
firebase_messaging: ^14.7.5
firebase_analytics : ^10.7.1
firebase_messaging: ^16.7.0
firebase_analytics : ^12.6.0
# cloud_firestore: ^4.15.8
#GIF image
@ -91,17 +91,17 @@ dependencies:
#Autocomplete TextField
autocomplete_textfield: ^2.0.1
date_time_picker: ^2.0.0
flutter_datetime_picker_plus: ^2.1.0
flutter_datetime_picker_plus: ^3.0.1
# Html
html: ^0.15.4
numberpicker: ^2.1.2
# Flutter Html View
flutter_html: ^3.0.0-beta.2
# flutter_html: ^3.0.0-beta.2
sticky_headers: ^0.3.0+2
file_picker: ^8.1.3
file_picker: ^13.1.0
#speech to text
# speech_to_text:
@ -114,20 +114,25 @@ dependencies:
# Html Editor Enhanced
#html_editor_enhanced: any
html_editor_enhanced: ^2.6.0
# html_editor_enhanced: ^2.7.1
# html_editor_plus: ^0.0.7
flutter_quill: ^11.6.0
flutter_quill_extensions: ^11.0.0
flutter_quill_delta_from_html: ^1.5.3
vsc_quill_delta_to_html: ^1.0.5
jhijri_picker: ^1.0.1
jhijri: ^1.0.0
#Network Image
cached_network_image: ^3.4.1
cached_network_image: ^4.0.4
# Badges
badges: ^3.1.2
badges: ^4.0.1
# Hijri
# hijri: ^2.0.3
hijri_picker: ^3.0.0
hijri_picker: ^5.0.0
#flutter_datetime_picker: ^1.5.1
localization: ^2.1.1
@ -136,16 +141,16 @@ dependencies:
flutter_math_fork: ^0.7.2
# cloudflare_turnstile: ^3.0.1
flutter_zoom_videosdk: 1.12.10
dart_jsonwebtoken: ^2.14.0
two_dimensional_scrollables: ^0.3.3
timezone: ^0.10.1
flutter_zoom_videosdk: ^2.5.10
dart_jsonwebtoken: ^3.4.1
two_dimensional_scrollables: ^0.5.5
timezone: ^0.11.1
logger: ^2.6.1
freerasp: ^8.2.1
safe_device: ^1.4.1
path_provider: ^2.0.8
open_filex: ^4.7.0
flutter_widget_from_html: ^0.17.1
flutter_widget_from_html: ^0.17.4
dependency_overrides:
@ -155,6 +160,7 @@ dependency_overrides:
flutter_plugin_android_lifecycle: 2.0.27
#hijri_picker: ^3.0.0
path_provider: ^2.1.5
# permission_handler_android: 13.0.1
dev_dependencies:

@ -7,26 +7,29 @@
#include "generated_plugin_registrant.h"
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <local_auth_windows/local_auth_plugin.h>
#include <maps_launcher/maps_launcher_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <speech_to_text_windows/speech_to_text_windows.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FirebaseCorePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
LocalAuthPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
MapsLauncherPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MapsLauncherPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
SpeechToTextWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SpeechToTextWindows"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

@ -4,15 +4,17 @@
list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
file_selector_windows
firebase_core
flutter_inappwebview_windows
local_auth_windows
maps_launcher
permission_handler_windows
speech_to_text_windows
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
)
set(PLUGIN_BUNDLED_LIBRARIES)

Loading…
Cancel
Save