From e06bd6a4f8ec595601b64963f211a76b005692cd Mon Sep 17 00:00:00 2001 From: mosazaid Date: Thu, 29 Jul 2021 15:27:47 +0300 Subject: [PATCH] working on videoCall notification --- .../kotlin/com/hmg/hmgDr/AppApplication.kt | 2 +- .../main/kotlin/com/hmg/hmgDr/MainActivity.kt | 42 +- .../VideoStreamFloatingWidgetService.kt | 234 +++++--- .../com/hmg/hmgDr/errorhandler/Action.kt | 7 - .../com/hmg/hmgDr/errorhandler/ActionEntry.kt | 34 -- .../hmg/hmgDr/errorhandler/ErrorHandler.kt | 505 ------------------ .../hmgDr/errorhandler/ExceptionMatcher.kt | 9 - .../com/hmg/hmgDr/errorhandler/Matcher .kt | 5 - .../hmg/hmgDr/errorhandler/MatcherFactory.kt | 11 - .../errorhandler/UnknownErrorCodeException.kt | 3 - .../hmg/hmgDr/errorhandler/retrofit/Range.kt | 45 -- .../retrofit/RetrofitMatcherFactory.kt | 48 -- .../hmg/hmgDr/globalErrorHandler/FileUtil.kt | 2 + .../hmg/hmgDr/model/NotificationVideoModel.kt | 4 +- .../hmgDr/ui/fragment/VideoCallFragment.kt | 2 +- .../kotlin/com/hmg/hmgDr/util/DateUtils.kt | 38 ++ .../com/hmg/hmgDr/util/NotificationUtil.kt | 8 +- .../hmg/hmgDr/util/audio/CustomAudioDevice.kt | 2 +- .../main/res/layout/activity_video_call.xml | 18 +- .../src/main/res/layout/notifi_video_view.xml | 1 + .../res/layout/notifi_video_view_expand.xml | 1 + android/app/src/main/res/values/styles.xml | 2 +- 22 files changed, 246 insertions(+), 777 deletions(-) delete mode 100644 android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/Action.kt delete mode 100644 android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ActionEntry.kt delete mode 100644 android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ErrorHandler.kt delete mode 100644 android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ExceptionMatcher.kt delete mode 100644 android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/Matcher .kt delete mode 100644 android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/MatcherFactory.kt delete mode 100644 android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/UnknownErrorCodeException.kt delete mode 100644 android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/retrofit/Range.kt delete mode 100644 android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/retrofit/RetrofitMatcherFactory.kt create mode 100644 android/app/src/main/kotlin/com/hmg/hmgDr/util/DateUtils.kt diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/AppApplication.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/AppApplication.kt index 8b5994d7..6ebe7374 100644 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/AppApplication.kt +++ b/android/app/src/main/kotlin/com/hmg/hmgDr/AppApplication.kt @@ -7,6 +7,6 @@ class AppApplication : FlutterApplication() { override fun onCreate() { super.onCreate() - LoggingExceptionHandler(this, "ErrorFile") +// LoggingExceptionHandler(this, "ErrorFile") } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/MainActivity.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/MainActivity.kt index 5120cb22..b21d7bd5 100644 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/MainActivity.kt +++ b/android/app/src/main/kotlin/com/hmg/hmgDr/MainActivity.kt @@ -6,6 +6,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection +import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.os.Bundle @@ -15,14 +16,10 @@ 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.model.GetSessionStatusModel import com.hmg.hmgDr.model.SessionStatusModel -import com.hmg.hmgDr.Service.VideoStreamFloatingWidgetService -import com.hmg.hmgDr.globalErrorHandler.FileUtil -import com.hmg.hmgDr.globalErrorHandler.LoggingExceptionHandler import com.hmg.hmgDr.ui.VideoCallResponseListener -import com.hmg.hmgDr.ui.fragment.VideoCallFragment -import com.opentok.android.Session import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodCall @@ -134,15 +131,11 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler, } private fun checkFloatingWidgetPermission() { - requestPermissions() +// requestPermissions() - //Check if the application has draw over other apps permission or not? - //This permission is by default available for API<23. But for API > 23 - //you have to ask for the permission in runtime. - - //Check if the application has draw over other apps permission or not? - //This permission is by default available for API<23. But for API > 23 - //you have to ask for the permission in runtime. +// Check if the application has draw over other apps permission or not? +// This permission is by default available for API<23. But for API > 23 +// you have to ask for the permission in runtime. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) { //If the draw over permission is not available open the settings screen //to grant the permission. @@ -288,7 +281,6 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler, private fun unbindService() { if (bound) { videoStreamService?.videoCallResponseListener = null // unregister -// videoStreamService!!.mActivity = null unbindService(serviceConnection) bound = false } @@ -304,7 +296,6 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler, bound = true videoStreamService!!.videoCallResponseListener = this@MainActivity // register videoStreamService?.serviceRunning = true -// videoStreamService!!.mActivity = this@MainActivity // register } override fun onServiceDisconnected(name: ComponentName?) { @@ -319,8 +310,14 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE ) - if (EasyPermissions.hasPermissions(this, *perms)) { - } else { + var havePermission = false + for (requiredPermission: String in perms) { + val checkVal: Int = checkCallingOrSelfPermission(requiredPermission) + if (checkVal == PackageManager.PERMISSION_GRANTED) { + havePermission = true + } + } + if (!havePermission) { EasyPermissions.requestPermissions( this, getString(R.string.remaining_ar), @@ -328,6 +325,15 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler, *perms ) } + /* if (EasyPermissions.hasPermissions(this, *perms)) { + } else { + EasyPermissions.requestPermissions( + this, + getString(R.string.remaining_ar), + RC_READ_WRITE, + *perms + ) + }*/ } override fun onPermissionsGranted(requestCode: Int, perms: MutableList?) { @@ -356,7 +362,7 @@ class MainActivity : FlutterFragmentActivity(), MethodChannel.MethodCallHandler, EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) } - companion object{ + companion object { private const val RC_READ_WRITE = 1 private const val RC_SETTINGS_SCREEN_PERM = 123 } diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/Service/VideoStreamFloatingWidgetService.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/Service/VideoStreamFloatingWidgetService.kt index fcf86260..2387f6cb 100644 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/Service/VideoStreamFloatingWidgetService.kt +++ b/android/app/src/main/kotlin/com/hmg/hmgDr/Service/VideoStreamFloatingWidgetService.kt @@ -17,16 +17,19 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat import androidx.core.view.GestureDetectorCompat +import com.hmg.hmgDr.MainActivity import com.hmg.hmgDr.R import com.hmg.hmgDr.model.* import com.hmg.hmgDr.ui.VideoCallContract import com.hmg.hmgDr.ui.VideoCallPresenterImpl import com.hmg.hmgDr.ui.VideoCallResponseListener +import com.hmg.hmgDr.util.DateUtils import com.hmg.hmgDr.util.DynamicVideoRenderer import com.hmg.hmgDr.util.NotificationUtil import com.hmg.hmgDr.util.ViewsUtil import com.hmg.hmgDr.util.audio.CustomAudioDevice import com.opentok.android.* +import java.util.* import kotlin.math.ceil @@ -53,8 +56,18 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, const val ACTION_START_CALL = "com.hmg.hmgDr.Service.action.startCall" const val ACTION_MINIMIZE_CALL = "com.hmg.hmgDr.Service.action.minimizeCall" const val ACTION_END_CALL = "com.hmg.hmgDr.Service.action.endCall" + const val ACTION_CHANGE_EXPANDED_COLLAPSED = + "com.hmg.hmgDr.Service.action.changeExpandedCollapsed" + const val NOTIFICATION_IS_EXPANDED = "is_expanded" } + var arguments: Bundle? = null + var serviceRunning: Boolean = false + + private val serviceBinder: IBinder = VideoStreamBinder() + private lateinit var videoCallPresenter: VideoCallContract.VideoCallPresenter + var videoCallResponseListener: VideoCallResponseListener? = null + private lateinit var windowManagerParams: WindowManager.LayoutParams private var mWindowManager: WindowManager? = null private val szWindow = Point() @@ -86,10 +99,8 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, private lateinit var mspeckerBtn: ImageView private lateinit var mMicBtn: ImageView private lateinit var patientName: TextView - private lateinit var cmTimer: Chronometer - - private lateinit var videoCallPresenter: VideoCallContract.VideoCallPresenter - var videoCallResponseListener: VideoCallResponseListener? = null + private lateinit var tvTimer: TextView +// private lateinit var cmTimer: Chronometer private var mSession: Session? = null private var mPublisher: Publisher? = null @@ -101,6 +112,10 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, private var mConnectedRunnable: Runnable? = null private var isConnected = false + private var mCallTimerHandler: Handler? = null + private var mCallTimerRunnable: Runnable? = null + private var mCallFirstTime: Date = Date() + private var sessionStatusModel: GetSessionStatusModel? = null private lateinit var mDetector: GestureDetectorCompat @@ -119,30 +134,15 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, private var formattedCallTime: String = "00:00" private lateinit var notificationData: NotificationVideoModel - private val mVideoPLRatio = 0.0 - private val mVideoBw: Long = 0 - private val mAudioPLRatio = 0.0 - private val mAudioBw: Long = 0 - private val mPrevVideoPacketsLost: Long = 0 - private val mPrevVideoPacketsRcvd: Long = 0 - private val mPrevVideoTimestamp = 0.0 - private val mPrevVideoBytes: Long = 0 - private val mPrevAudioPacketsLost: Long = 0 - private val mPrevAudioPacketsRcvd: Long = 0 - private val mPrevAudioTimestamp = 0.0 - private val mPrevAudioBytes: Long = 0 - private val mStartTestTime: Long = 0 - - private val audioOnly = false - private var resume = false var isFullScreen: Boolean = true private var isCircle: Boolean = false + lateinit var customAudioDevice: CustomAudioDevice - var arguments: Bundle? = null - var serviceRunning: Boolean = false - private val serviceBinder: IBinder = VideoStreamBinder() + // notification's layout + private lateinit var mRemoteViews: RemoteViews + private lateinit var mRemoteViewsExpand: RemoteViews // Notification variables private var mNotificationManagerCompat: NotificationManagerCompat? = null @@ -189,6 +189,26 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, } else if (action == ACTION_MINIMIZE_CALL) { if (!isFullScreen) onMinimizedClicked() + } else if (action == ACTION_CHANGE_EXPANDED_COLLAPSED) { + val isExpanded = intent.extras!!.getBoolean(NOTIFICATION_IS_EXPANDED) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // No use channel importance + if (isExpanded) { + notificationCompatBuilder.priority = Notification.PRIORITY_LOW + } else { + notificationCompatBuilder.priority = Notification.PRIORITY_HIGH + } + } else { + if (isExpanded) { + notificationCompatBuilder.priority = Notification.PRIORITY_LOW + } else { + notificationCompatBuilder.priority = Notification.PRIORITY_HIGH + } + } + mNotificationManagerCompat?.notify( + ONGOING_NOTIFICATION_ID, + notificationCompatBuilder.build() + ) } } @@ -216,9 +236,18 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, } private fun stopService() { +// customAudioDevice.setRendererMute(false) + AudioDeviceManager.getAudioDevice().onPause() + +// cmTimer.stop() + mCallTimerRunnable?.let { + mCallTimerHandler?.removeCallbacks(it) + mCallTimerHandler = null + mCallTimerRunnable = null + } + // because problem that timer still continue longer a bit than service so notification called again mNotificationManagerCompat = null - cmTimer.stop() stopForeground(true) stopSelf() videoCallResponseListener?.minimizeVideoEvent(false) @@ -302,10 +331,10 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, recordContainer.visibility = View.GONE } - +/* cmTimer.format = "mm:ss" cmTimer.onChronometerTickListener = - Chronometer.OnChronometerTickListener { arg0: Chronometer? -> + Chronometer.OnChronometerTickListener { arg0: Chronometer -> // val f: NumberFormat = DecimalFormat("00") // f.format(minutes) val minutes: Long @@ -321,38 +350,14 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, } val format = "%1$02d:%2$02d" // two digits formattedCallTime = String.format(format, minutes, seconds) - arg0?.text = formattedCallTime - - try { - notificationCompatBuilder.contentView.setChronometer( - R.id.notify_timer, - cmTimer.base, - null, - true - ) - notificationCompatBuilder.bigContentView.setChronometer( - R.id.notify_timer, - cmTimer.base, - null, - true - ) - } catch (e: Exception) { - } - - // for change notification timer - if (mNotificationManagerCompat != null) { - val bigTextStyle = setNotificationBigStyle() - notificationCompatBuilder.setStyle(bigTextStyle) - mNotificationManagerCompat?.notify( - ONGOING_NOTIFICATION_ID, - notificationCompatBuilder.build() - ) - } + arg0.text = formattedCallTime } +*/ videoCallPresenter = VideoCallPresenterImpl(this, baseUrl) checkClientConnected() + setCallStartTimer() handleVideoViewHeight() mDetector = GestureDetectorCompat( @@ -363,18 +368,17 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, ) try { // Add a custom audio device before session initialization - - // Add a custom audio device before session initialization - val customAudioDevice = CustomAudioDevice( - this - ) - customAudioDevice.setRendererMute(true) - AudioDeviceManager.setAudioDevice(customAudioDevice) + customAudioDevice = CustomAudioDevice(this) + customAudioDevice.setRendererMute(false) + if (AudioDeviceManager.getAudioDevice() == null) { + AudioDeviceManager.setAudioDevice(customAudioDevice) + } else { + AudioDeviceManager.getAudioDevice().onResume() + } mSession = Session.Builder(this, apiKey, sessionId).build() mSession!!.setSessionListener(this) mSession!!.connect(token) - mSession!!.setReconnectionListener(object : Session.ReconnectionListener { override fun onReconnecting(session: Session?) { @@ -406,7 +410,7 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, mSubscriberViewContainer = view.findViewById(R.id.remote_video_view_container) patientName = view.findViewById(R.id.patient_name) - cmTimer = view.findViewById(R.id.cmTimer) + tvTimer = view.findViewById(R.id.tv_timer) controlPanel = view.findViewById(R.id.control_panel) mCallBtn = view.findViewById(R.id.btn_call) @@ -430,6 +434,45 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, ) } + private fun setCallStartTimer() { + mCallTimerHandler = Handler((Looper.getMainLooper())) + mCallTimerRunnable = Runnable { + formattedCallTime = DateUtils.differentDateTimeBetweenDateAndNow(mCallFirstTime) + tvTimer.text = formattedCallTime + + /* + // comment it because I deleted remoteView + try { + notificationCompatBuilder.contentView.setChronometer( + R.id.notify_timer, + cmTimer.base, + null, + true + ) + notificationCompatBuilder.bigContentView.setChronometer( + R.id.notify_timer, + cmTimer.base, + null, + true + ) + } catch (e: Exception) { + print(e) + }*/ + + // for change notification timer + if (mNotificationManagerCompat != null) { + val bigTextStyle = setNotificationBigStyle() + notificationCompatBuilder.setStyle(bigTextStyle) + mNotificationManagerCompat?.notify( + ONGOING_NOTIFICATION_ID, + notificationCompatBuilder.build() + ) + } + mCallTimerHandler!!.postDelayed(mCallTimerRunnable!!, 1000L) + } + + } + private fun handleVideoViewHeight() { val layoutNameParam: ConstraintLayout.LayoutParams = layoutName.layoutParams as ConstraintLayout.LayoutParams @@ -698,7 +741,7 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, isMicClicked = !isMicClicked mPublisher!!.publishAudio = !isMicClicked val res = if (isMicClicked) R.drawable.mic_disabled else R.drawable.mic_enabled - mMicBtn!!.setImageResource(res) + mMicBtn.setImageResource(res) } } @@ -796,10 +839,10 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, mSession!!.publish(mPublisher) - if (!resume) { - cmTimer.base = SystemClock.elapsedRealtime() - } - cmTimer.start() + // start call timer to show on top + mCallFirstTime = Date() + mCallTimerHandler!!.postDelayed(mCallTimerRunnable!!, 1000L) + videoCallResponseListener?.minimizeVideoEvent(true) } @@ -823,6 +866,7 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, TAG, "onStreamReceived: New stream " + stream.streamId + " in session " + session.sessionId ) + if (mSubscriber != null) { isConnected = true return @@ -1111,24 +1155,57 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, ) } - // notification's layout - val mRemoteViews = RemoteViews(packageName, R.layout.notifi_video_view) + +/* + mRemoteViews = RemoteViews(packageName, R.layout.notifi_video_view) + mRemoteViewsExpand = RemoteViews(packageName, R.layout.notifi_video_view_expand) + + val changeCollapsedIntent: PendingIntent = + Intent(this, VideoStreamFloatingWidgetService::class.java).apply { + action = ACTION_CHANGE_EXPANDED_COLLAPSED + val extras = Bundle() + extras.putBoolean(NOTIFICATION_IS_EXPANDED, false) + putExtras(extras) + } + .let { notificationIntent -> + PendingIntent.getService(this, 0, notificationIntent, 0) + } + val changeExpandedIntent: PendingIntent = + Intent(this, VideoStreamFloatingWidgetService::class.java).apply { + action = ACTION_CHANGE_EXPANDED_COLLAPSED + val extras = Bundle() + extras.putBoolean(NOTIFICATION_IS_EXPANDED, true) + putExtras(extras) + } + .let { notificationIntent -> + PendingIntent.getService(this, 0, notificationIntent, 0) + } + mRemoteViews.setImageViewResource(R.id.iv_icon, R.mipmap.ic_launcher) // notification's title mRemoteViews.setTextViewText(R.id.notify_title, notificationData.mContentTitle) // notification's content mRemoteViews.setTextViewText(R.id.notify_content, notificationData.mContentText) - val mRemoteViewsExpand = RemoteViews(packageName, R.layout.notifi_video_view_expand) + mRemoteViews.setOnClickPendingIntent(R.id.iv_Arrow, changeCollapsedIntent) + mRemoteViews.setChronometer( + R.id.notify_timer, + SystemClock.elapsedRealtime(), + null, + false + ) + mRemoteViewsExpand.setImageViewResource(R.id.iv_icon, R.mipmap.ic_launcher) mRemoteViewsExpand.setTextViewText(R.id.notify_title, notificationData.mContentTitle) mRemoteViewsExpand.setTextViewText(R.id.notify_content, notificationData.mContentText) mRemoteViewsExpand.setOnClickPendingIntent(R.id.btn_end, endCallPendingIntent) + mRemoteViewsExpand.setOnClickPendingIntent(R.id.iv_Arrow, changeExpandedIntent) mRemoteViewsExpand.setChronometer( R.id.notify_timer, SystemClock.elapsedRealtime(), null, false ) +*/ notificationCompatBuilder // BIG_TEXT_STYLE sets title and content for API 16 (4.1 and after). @@ -1144,8 +1221,8 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, R.mipmap.ic_launcher ) ) - .setCustomContentView(mRemoteViews) - .setCustomBigContentView(mRemoteViewsExpand) + /*.setCustomContentView(mRemoteViews) + .setCustomBigContentView(mRemoteViewsExpand)*/ .setContentIntent(pendingIntent) // .setDefaults(NotificationCompat.DEFAULT_ALL) // Set primary color (important for Wear 2.0 Notifications). @@ -1153,11 +1230,15 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, .setCategory(Notification.CATEGORY_SERVICE) .setPriority(notificationData.mPriority) .setVisibility(notificationData.mChannelLockscreenVisibility) -// .addAction(endCallAction) + .addAction(endCallAction) // for version code < O, or for all versions if RemoteViews not used .setUsesChronometer(true) .setWhen(elapsedTime) .setOnlyAlertOnce(true) // This will ensure that updates to an existing notification won't sound/vibrate https://stackoverflow.com/a/52349466/6246772 .setSound(null) + .setOngoing(true) + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .setColorized(true) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) //to show content in lock screen val notification = notificationCompatBuilder .build() @@ -1236,11 +1317,6 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, private val dragListener: View.OnTouchListener = View.OnTouchListener { _, event -> mDetector.onTouchEvent(event) - - if (floatingWidgetView!!.layoutParams is WindowManager.LayoutParams) { - var asd = ""; - } - //Get Floating widget view params val layoutParams: WindowManager.LayoutParams = floatingWidgetView!!.layoutParams as WindowManager.LayoutParams @@ -1382,6 +1458,4 @@ class VideoStreamFloatingWidgetService : Service(), Session.SessionListener, } }.start() } - - } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/Action.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/Action.kt deleted file mode 100644 index 2b1f3dad..00000000 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/Action.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.hmg.hmgDr.errorhandler - -/** - * A functional interface representing an action that gets executed - * upon an error by an [ErrorHandler]. - */ -typealias Action = (Throwable, ErrorHandler) -> Unit diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ActionEntry.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ActionEntry.kt deleted file mode 100644 index 8fc636dd..00000000 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ActionEntry.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.hmg.hmgDr.errorhandler - - -/** - * Container to ease passing around a tuple of two objects. This object provides a sensible - * implementation of equals(), returning true if equals() is true on each of the contained - * objects. - */ -class ActionEntry -/** - * Constructor for an ActionEntry. - * - * @param matcher the matcher object in the ActionEntry - * @param action the action object in the ActionEntry - */( - val matcher: Matcher, - val action: Action -) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || javaClass != other.javaClass) return false - val that = other as ActionEntry - return if (matcher != that.matcher) false else action == that.action - } - - /** - * Compute a hash code using the hash codes of the underlying objects - * - * @return a hashcode of the ActionEntry - */ - override fun hashCode(): Int { - return matcher.hashCode() xor action.hashCode() - } -} diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ErrorHandler.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ErrorHandler.kt deleted file mode 100644 index d504d0f4..00000000 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ErrorHandler.kt +++ /dev/null @@ -1,505 +0,0 @@ -package com.hmg.hmgDr.errorhandler - -import java.util.HashMap -import kotlin.reflect.KClass - -/** - * https://github.com/mgray88/kotlin-error-handler - * An ErrorHandler is responsible for handling an error by executing one or more actions, - * instances of [Action], that are found to match the error. - */ - -class ErrorHandler private constructor() { - - private val errorCodeMap = mutableMapOf, MatcherFactory<*>>() - private val actions = mutableListOf() - private val otherwiseActions= mutableListOf() - private val alwaysActions = mutableListOf() - private var localContext: ThreadLocal = object : ThreadLocal() { - override fun initialValue(): Context { - return Context() - } - } - private var parentErrorHandler: ErrorHandler? = null - - /** - * Create a new ErrorHandler with the given one as parent. - * - * @param parentErrorHandler the parent @{link ErrorHandler} - */ - private constructor(parentErrorHandler: ErrorHandler) : this() { - this.parentErrorHandler = parentErrorHandler - } - - /** - * Register `action` to be executed by [.handle], - * if the thrown error matches the `matcher`. - * - * @param matcher a matcher to match the thrown error - * @param action the associated action - * @return the current `ErrorHandler` instance - to use in command chains - */ - fun on( - matcher: Matcher, - action: Action - ): ErrorHandler { - actions.add(ActionEntry(matcher, action)) - return this - } - - /** - * Kotlin <1.4 lambda compatibility for `[.on(Matcher, Action)]` - */ - fun on( - matcher: (Throwable) -> Boolean, - action: Action - ): ErrorHandler { - return on(object : Matcher { - override fun matches(throwable: Throwable): Boolean { - return matcher(throwable) - } - }, action) - } - - /** - * Register `action` to be executed by [.handle], - * if the thrown error is an instance of `exceptionClass`. - * - * @param exceptionClass the class of the error - * @param action the associated action - * @return the current `ErrorHandler` instance - to use in command chains - */ - fun on( - exceptionClass: KClass, - action: Action - ): ErrorHandler { - actions.add(ActionEntry(ExceptionMatcher(exceptionClass), action)) - return this - } - - /** - * Register `action` to be executed by [.handle], - * if the thrown error is bound (associated) to `errorCode`. - * - * - * See [.bindClass] and [.bind] - * on how to associate arbitrary error codes with actual Throwables via [Matcher]. - * - * @param the error code type - * @param errorCode the error code - * @param action the associated action - * @return the current `ErrorHandler` instance - to use in command chains - */ - fun on( - errorCode: T, - action: Action - ): ErrorHandler { - val matcherFactory: MatcherFactory = - getMatcherFactoryForErrorCode(errorCode) - ?: throw UnknownErrorCodeException(errorCode) - actions.add(ActionEntry(matcherFactory.build(errorCode), action)) - return this - } - - /** - * Register `action` to be executed in case no other *conditional* - * action gets executed. - * - * @param action the action - * @return the current `ErrorHandler` instance - to use in command chains - */ - fun otherwise(action: Action): ErrorHandler { - otherwiseActions.add(action) - return this - } - - /** - * Register `action` to be executed on all errors. - * - * @param action the action - * @return the current `ErrorHandler` instance - to use in command chains - */ - fun always(action: Action): ErrorHandler { - alwaysActions.add(action) - return this - } - - /** - * Skip all following actions registered via an `on` method - * @return the current `ErrorHandler` instance - to use in command chains - */ - fun skipFollowing(): ErrorHandler { - localContext.get().skipFollowing = true - return this - } - - /** - * Skip all actions registered via [.always] - * @return the current `ErrorHandler` instance - to use in command chains - */ - fun skipAlways(): ErrorHandler { - localContext.get().skipAlways = true - return this - } - - /** - * Skip the default matching actions if any - * @return the current `ErrorHandler` instance - to use in command chains - */ - fun skipDefaults(): ErrorHandler { - localContext.get().skipDefaults = true - return this - } - - private fun handle( - error: Throwable, - context: ThreadLocal - ) { - localContext = context - val ctx = localContext.get() - for (actionEntry in actions) { - if (ctx.skipFollowing) break - if (actionEntry.matcher.matches(error)) { - actionEntry.action(error, this) - ctx.handled = true - } - } - if (!ctx.handled && otherwiseActions.isNotEmpty()) { - for (action in otherwiseActions) { - action(error, this) - ctx.handled = true - } - } - if (!ctx.skipAlways) { - for (action in alwaysActions) { - action(error, this) - ctx.handled = true - } - } - if (!ctx.skipDefaults) { - parentErrorHandler?.handle(error, localContext) - } - } - - /** - * Run a custom code block and assign current ErrorHandler instance - * to handle a possible exception throw in 'catch'. - * - * @param closure functional interface containing Exception prone code - */ - fun runHandling(closure: () -> Unit) { - try { - closure() - } catch (throwable: Throwable) { - handle(throwable, localContext) - } - } - - /** - * Handle `error` by executing all matching actions. - * - * @param error the error as a [Throwable] - */ - fun handle(error: Throwable) { - this.handle(error, localContext) - } - - /** - * Bind an `errorCode` to a `Matcher`, using a `MatcherFactory`. - * - * - * - * For example, when we need to catch a network timeout it's better to just write "timeout" - * instead of a train-wreck expression. So we need to bind this "timeout" error code to an actual - * condition that will check the actual error when it occurs to see if its a network timeout or not. - * - * - *
-     * ```
-     * ErrorHandler
-     *     .defaultErrorHandler()
-     *     .bind("timeout") { errorCode ->
-     *         Matcher { throwable ->
-     *             return (throwable is SocketTimeoutException) && throwable.message.contains("Read timed out")
-     *         }
-     *     }
-     *
-     * // ...
-     *
-     * ErrorHandler
-     *     .create()
-     *     .on("timeout") { throwable, handler ->
-     *         showOfflineScreen()
-     *     }
-     * ```
-     * 
- * - * - * @param the error code type - * @param errorCode the errorCode value, can use a primitive for clarity and let it be autoboxed - * @param matcherFactory a factory that given an error code, provides a matcher to match the error against it - * @return the current `ErrorHandler` instance - to use in command chains - */ - fun bind( - errorCode: T, - matcherFactory: MatcherFactory - ): ErrorHandler { - errorCodeMap[ErrorCodeIdentifier(errorCode)] = matcherFactory - return this - } - - /** - * Kotlin <1.4 lambda compatibility for `[.bind(T, MatcherFactory)]` - */ - fun bind( - errorCode: T, - matcherFactory: (T) -> (Throwable) -> Boolean - ): ErrorHandler { - return bind(errorCode, object : MatcherFactory { - override fun build(errorCode: T): Matcher { - val matcher = matcherFactory(errorCode) - return object : Matcher { - override fun matches(throwable: Throwable): Boolean { - return matcher(throwable) - } - } - } - }) - } - - /** - * Bind an `errorCode` `Class` to a `Matcher`, using a `MatcherFactory`. - * - * - * - * For example, when we prefer using plain integers to refer to HTTP errors instead of - * checking the HTTPException status code every time. - * - * - *
-     * ```
-     * ErrorHandler
-     *     .defaultErrorHandler()
-     *     .bindClass(Integer::class) { errorCode ->
-     *         Matcher { throwable ->
-     *             return throwable is HttpException && throwable.code() == errorCode
-     *         }
-     *     }
-     *
-     * // ...
-     *
-     * ErrorHandler
-     *     .create()
-     *     .on(404) { throwable, handler ->
-     *         showResourceNotFoundError()
-     *     }
-     *     .on(500) { throwable, handler ->
-     *         showServerError()
-     *     }
-     * ````
-     * 
- * - * @param the error code type - * @param errorCodeClass the errorCode class - * @param matcherFactory a factory that given an error code, provides a matcher to match the error against it - * @return the current `ErrorHandler` instance - to use in command chains - */ - fun bindClass( - errorCodeClass: KClass, - matcherFactory: MatcherFactory - ): ErrorHandler { - errorCodeMap[ErrorCodeIdentifier(errorCodeClass)] = matcherFactory - return this - } - - /** - * Kotlin <1.4 lambda compatibility for `[.bindClass(KClass, MatcherFactory)]` - */ - fun bindClass( - errorCodeClass: KClass, - matcherFactory: (T) -> (Throwable) -> Boolean - ): ErrorHandler { - return bindClass(errorCodeClass, object : MatcherFactory { - override fun build(errorCode: T): Matcher { - val matcher = matcherFactory(errorCode) - return object : Matcher { - override fun matches(throwable: Throwable): Boolean { - return matcher(throwable) - } - } - } - }) - } - - @Suppress("UNCHECKED_CAST") - protected fun getMatcherFactoryForErrorCode(errorCode: T): MatcherFactory? { - var matcherFactory: MatcherFactory? - matcherFactory = errorCodeMap[ErrorCodeIdentifier(errorCode)] as? MatcherFactory - if (matcherFactory != null) { - return matcherFactory - } - - matcherFactory = errorCodeMap[ErrorCodeIdentifier(errorCode::class)] as? MatcherFactory - if (matcherFactory != null) { - return matcherFactory - } - return if (parentErrorHandler != null) { - parentErrorHandler?.getMatcherFactoryForErrorCode(errorCode) - } else null - } - - /** - * Clear ErrorHandler instance from all its registered Actions and Matchers. - */ - fun clear() { - actions.clear() - errorCodeMap.clear() - otherwiseActions.clear() - alwaysActions.clear() - localContext.get().clear() - } - - private class Context { - private val keys = HashMap() - var handled = false - var skipDefaults = false - var skipFollowing = false - var skipAlways = false - operator fun get(key: Any?): Any? { - return keys[key] - } - - fun put(key: String, value: Any): Any? { - return keys.put(key, value) - } - - fun remove(key: Any?): Any? { - return keys.remove(key) - } - - fun clear() { - keys.clear() - skipDefaults = false - skipFollowing = false - skipAlways = false - } - } - - /** - * Used to identify an error code either by its "literal" value - * or by its Class. - * - * - * When using custom objects as error codes, - * make sure you implement [Object.equals] to allow ErrorHandler - * perform equality comparisons between instances. - */ - private class ErrorCodeIdentifier { - private val errorCode: T? - private val errorCodeClass: KClass? - - internal constructor(errorCode: T) { - this.errorCode = errorCode - this.errorCodeClass = null - } - - internal constructor(errorCodeClass: KClass) { - this.errorCode = null - this.errorCodeClass = errorCodeClass - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || javaClass != other.javaClass) return false - - val that = other as ErrorCodeIdentifier<*> - - if (if (errorCode != null) errorCode != that.errorCode else that.errorCode != null) return false - return if (errorCodeClass != null) errorCodeClass == that.errorCodeClass else that.errorCodeClass == null - } - - override fun hashCode(): Int { - var result = errorCode?.hashCode() ?: 0 - result = 31 * result + (errorCodeClass?.hashCode() ?: 0) - return result - } - } - - companion object { - private var defaultInstance: ErrorHandler? = null - - /** - * Create a new @{link ErrorHandler}, isolated from the default one. - * - * - * In other words, designed to handle all errors by itself without delegating - * to the default error handler. - * - * @return returns a new `ErrorHandler` instance - */ - @JvmStatic - fun createIsolated(): ErrorHandler { - return ErrorHandler() - } - - /** - * Create a new @{link ErrorHandler}, that delegates to the default one, or the - * parent @{link ErrorHandler} passed in - * - * Any default actions, are always executed after the ones registered on this one. - * - * @param parentErrorHandler `ErrorHandler` to use as the parent - * @return returns a new `ErrorHandler` instance - */ - @JvmStatic - fun create(parentErrorHandler: ErrorHandler? = null): ErrorHandler { - return ErrorHandler(parentErrorHandler ?: defaultErrorHandler()) - } - - /** - * Get the default @{link ErrorHandler}, a singleton object - * to which all other instances by default delegate to. - * - * @return the default @{link ErrorHandler} instance - */ - @JvmStatic - @Synchronized - fun defaultErrorHandler(): ErrorHandler { - if (defaultInstance == null) { - defaultInstance = - ErrorHandler() - } - return defaultInstance!! - } - } -} - -/** - * Wrapper around `[.on(KClass, Action)]` to allow action's `Throwable` parameter - * to be typed to the `Throwable` expected - */ -inline fun ErrorHandler.on( - noinline action: (T, ErrorHandler) -> Unit -): ErrorHandler { - return on(T::class) { throwable, errorHandler -> - action(throwable as T, errorHandler) - } -} - -/** - * Lazy `ErrorHandler` initializer which delegates to a parent, or the `defaultErrorHandler` - * if the parent is not supplied. Uses optional lambda function to add actions and bindings to - * the new `ErrorHandler` - * - * @param parentErrorHandler (optional) error handler to delegate default actions to - * @param apply (optional) apply function for adding actions and binding - * @return lazy initialized `ErrorHandler` - */ -inline fun errorHandler( - parentErrorHandler: ErrorHandler? = null, - noinline apply: (ErrorHandler.() -> Unit)? = null -) = lazy { - val eh = ErrorHandler.create(parentErrorHandler) - apply?.invoke(eh) - return@lazy eh -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ExceptionMatcher.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ExceptionMatcher.kt deleted file mode 100644 index 4ab02f8a..00000000 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/ExceptionMatcher.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.hmg.hmgDr.errorhandler - -import kotlin.reflect.KClass - -class ExceptionMatcher(private val errorClass: KClass) : Matcher { - override fun matches(throwable: Throwable): Boolean { - return errorClass.isInstance(throwable) - } -} diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/Matcher .kt b/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/Matcher .kt deleted file mode 100644 index acf8b3c1..00000000 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/Matcher .kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.hmg.hmgDr.errorhandler - -interface Matcher { - fun matches(throwable: Throwable): Boolean -} diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/MatcherFactory.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/MatcherFactory.kt deleted file mode 100644 index af296213..00000000 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/MatcherFactory.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.hmg.hmgDr.errorhandler - -interface MatcherFactory { - /** - * Build a [Matcher] to match the given error code against an error - * - * @param errorCode the error code - * @return a new [Matcher] - */ - fun build(errorCode: T): Matcher -} diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/UnknownErrorCodeException.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/UnknownErrorCodeException.kt deleted file mode 100644 index 46abc572..00000000 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/UnknownErrorCodeException.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.hmg.hmgDr.errorhandler - -class UnknownErrorCodeException(val errorCode: Any) : RuntimeException() diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/retrofit/Range.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/retrofit/Range.kt deleted file mode 100644 index e6abc82e..00000000 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/retrofit/Range.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.hmg.hmgDr.errorhandler.retrofit - -/** - * Range class for HTTP status codes - */ -class Range private constructor(val lowerBound: Int, val upperBound: Int) { - - /** - * Checks if the passed httpStatusCode is contained in given range - * - * @param httpStatusCode the status code to check - * @return true if contains, otherwise false - */ - operator fun contains(httpStatusCode: Int): Boolean { - return httpStatusCode in lowerBound..upperBound - } - - override fun equals(o: Any?): Boolean { - if (this === o) return true - if (o == null || javaClass != o.javaClass) return false - val range = - o as Range - return if (lowerBound != range.lowerBound) false else upperBound == range.upperBound - } - - override fun hashCode(): Int { - var result = lowerBound - result = 31 * result + upperBound - return result - } - - companion object { - /** - * Creates a Range object with lower and upper bound - * @param lowerBound lower limit of Range - * @param upperBound upper limit of Range - * - * @return a Range instance - */ - @JvmStatic - fun of(lowerBound: Int, upperBound: Int): Range { - return Range(lowerBound, upperBound) - } - } -} diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/retrofit/RetrofitMatcherFactory.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/retrofit/RetrofitMatcherFactory.kt deleted file mode 100644 index 062fc30e..00000000 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/errorhandler/retrofit/RetrofitMatcherFactory.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.hmg.hmgDr.errorhandler.retrofit - -import com.hmg.hmgDr.errorhandler.Matcher -import com.hmg.hmgDr.errorhandler.MatcherFactory -import retrofit2.adapter.rxjava.HttpException - -object RetrofitMatcherFactory { - - /** - * Creates a [MatcherFactory] that checks HTTP statuses - * - * @return new MatcherFactory for Retrofit Rx HttpException that works with Integer - */ - @JvmStatic - fun create(): MatcherFactory { - return object : MatcherFactory { - override fun build(errorCode: Int): Matcher { - return object : Matcher { - override fun matches(throwable: Throwable): Boolean { - return throwable is HttpException && - throwable.code() == errorCode - } - } - } - } - } - - /** - * Creates a [MatcherFactory] that checks if HTTP status is in given [Range] - * - * @return new MatcherFactory for Retrofit Rx HttpException that works with Range - */ - @JvmStatic - fun createRange(): MatcherFactory { - return object : MatcherFactory { - override fun build(errorCode: Range): Matcher { - return object : Matcher { - override fun matches(throwable: Throwable): Boolean { - return throwable is HttpException && - errorCode.contains(throwable.code()) - } - } - } - } - } - - -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/globalErrorHandler/FileUtil.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/globalErrorHandler/FileUtil.kt index 13fc2dd7..786ec898 100644 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/globalErrorHandler/FileUtil.kt +++ b/android/app/src/main/kotlin/com/hmg/hmgDr/globalErrorHandler/FileUtil.kt @@ -32,6 +32,8 @@ object FileUtil { bufferedWriter.flush() } catch (e: IOException) { e.printStackTrace() + } catch (e: IllegalStateException) { + e.printStackTrace() } } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/model/NotificationVideoModel.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/model/NotificationVideoModel.kt index 9a0835f3..1d0925a3 100644 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/model/NotificationVideoModel.kt +++ b/android/app/src/main/kotlin/com/hmg/hmgDr/model/NotificationVideoModel.kt @@ -1,5 +1,6 @@ package com.hmg.hmgDr.model +import android.app.Notification import android.app.NotificationManager import android.os.Build import androidx.annotation.RequiresApi @@ -11,7 +12,7 @@ class NotificationVideoModel constructor( mChannelId: String, mChannelName: CharSequence, mChannelDescription: String, - mPriority: Int = NotificationCompat.PRIORITY_DEFAULT, + mPriority: Int = Notification.PRIORITY_MAX, mChannelImportance: Int = NotificationManager.IMPORTANCE_LOW, mChannelEnableVibrate: Boolean = true, mChannelLockscreenVisibility: Int = NotificationCompat.VISIBILITY_PUBLIC, @@ -19,7 +20,6 @@ class NotificationVideoModel constructor( var mBigContentTitle: String = mContentTitle, val mBigText: String = mContentText, var mSummaryText: String - ) : NotificationDataModel( mContentTitle, mContentText, diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/ui/fragment/VideoCallFragment.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/ui/fragment/VideoCallFragment.kt index 010976b9..a5f7811b 100644 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/ui/fragment/VideoCallFragment.kt +++ b/android/app/src/main/kotlin/com/hmg/hmgDr/ui/fragment/VideoCallFragment.kt @@ -294,7 +294,7 @@ class VideoCallFragment : DialogFragment(), PermissionCallbacks, Session.Session recordContainer.visibility = View.GONE } - cmTimer = view.findViewById(R.id.cmTimer) +// cmTimer = view.findViewById(R.id.cmTimer) cmTimer.format = "mm:ss" cmTimer.onChronometerTickListener = Chronometer.OnChronometerTickListener { arg0: Chronometer? -> diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/util/DateUtils.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/util/DateUtils.kt new file mode 100644 index 00000000..dcc2ca5d --- /dev/null +++ b/android/app/src/main/kotlin/com/hmg/hmgDr/util/DateUtils.kt @@ -0,0 +1,38 @@ +package com.hmg.hmgDr.util + +import java.text.SimpleDateFormat +import java.util.* + +object DateUtils { + + var simpleDateFormat: SimpleDateFormat = SimpleDateFormat("hh:mm:ss", Locale.ENGLISH) + + fun differentDateTimeBetweenDateAndNow(firstDate: Date): String { + val now: Date = Calendar.getInstance().time + //1 minute = 60 seconds + //1 hour = 60 x 60 = 3600 + //1 day = 3600 x 24 = 86400 + + var different: Long = now.time - firstDate.time + + val secondsInMilli: Long = 1000 + val minutesInMilli = secondsInMilli * 60 + val hoursInMilli = minutesInMilli * 60 + val daysInMilli = hoursInMilli * 24 + + val elapsedDays = different / daysInMilli + different %= daysInMilli + + val elapsedHours = different / hoursInMilli + different %= hoursInMilli + + val elapsedMinutes = different / minutesInMilli + different %= minutesInMilli + + val elapsedSeconds = different / secondsInMilli + + val format = "%1$02d:%2$02d" // two digits + return String.format(format, elapsedMinutes, elapsedSeconds) + } + +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/util/NotificationUtil.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/util/NotificationUtil.kt index 1c55e1b2..47da457e 100644 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/util/NotificationUtil.kt +++ b/android/app/src/main/kotlin/com/hmg/hmgDr/util/NotificationUtil.kt @@ -9,7 +9,10 @@ import com.hmg.hmgDr.model.NotificationDataModel object NotificationUtil { - fun createNotificationChannel(context: Context, notificationDataModel: NotificationDataModel): String{ + fun createNotificationChannel( + context: Context, + notificationDataModel: NotificationDataModel + ): String { // The id of the channel. val channelId: String = notificationDataModel.mChannelId @@ -21,7 +24,8 @@ object NotificationUtil { val channelDescription: String = notificationDataModel.mChannelDescription val channelImportance: Int = notificationDataModel.mChannelImportance val channelEnableVibrate: Boolean = notificationDataModel.mChannelEnableVibrate - val channelLockscreenVisibility: Int = notificationDataModel.mChannelLockscreenVisibility + val channelLockscreenVisibility: Int = + notificationDataModel.mChannelLockscreenVisibility // Initializes NotificationChannel. val notificationChannel = NotificationChannel(channelId, channelName, channelImportance) diff --git a/android/app/src/main/kotlin/com/hmg/hmgDr/util/audio/CustomAudioDevice.kt b/android/app/src/main/kotlin/com/hmg/hmgDr/util/audio/CustomAudioDevice.kt index ef654844..b9c2796b 100644 --- a/android/app/src/main/kotlin/com/hmg/hmgDr/util/audio/CustomAudioDevice.kt +++ b/android/app/src/main/kotlin/com/hmg/hmgDr/util/audio/CustomAudioDevice.kt @@ -263,7 +263,7 @@ class CustomAudioDevice(context: Context) : BaseAudioDevice() { return false } m_bufferedPlaySamples = 0 - setOutputMode(OutputMode.SpeakerPhone) + outputMode = OutputMode.SpeakerPhone m_shutdownRenderThread = false Thread(m_renderThread).start() return true diff --git a/android/app/src/main/res/layout/activity_video_call.xml b/android/app/src/main/res/layout/activity_video_call.xml index 03c55729..8f49c82b 100644 --- a/android/app/src/main/res/layout/activity_video_call.xml +++ b/android/app/src/main/res/layout/activity_video_call.xml @@ -33,15 +33,25 @@ android:background="@drawable/shape_capsule" android:padding="@dimen/padding_space_small"> - + android:text="00:00" /> + + + + + + + + + + diff --git a/android/app/src/main/res/layout/notifi_video_view.xml b/android/app/src/main/res/layout/notifi_video_view.xml index a2a81be1..a031cce0 100644 --- a/android/app/src/main/res/layout/notifi_video_view.xml +++ b/android/app/src/main/res/layout/notifi_video_view.xml @@ -46,6 +46,7 @@ tools:text="25:45" /> diff --git a/android/app/src/main/res/layout/notifi_video_view_expand.xml b/android/app/src/main/res/layout/notifi_video_view_expand.xml index c06aa9ac..d206eb1c 100644 --- a/android/app/src/main/res/layout/notifi_video_view_expand.xml +++ b/android/app/src/main/res/layout/notifi_video_view_expand.xml @@ -46,6 +46,7 @@ tools:text="25:45" /> diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index dae5749f..e5b2f6d2 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ -