悬浮窗功能
This commit is contained in:
parent
481452e688
commit
d8180a1ff6
@ -59,6 +59,13 @@
|
|||||||
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
|
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
|
||||||
<!-- </intent-filter> -->
|
<!-- </intent-filter> -->
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".activity.ScreenPermissionActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:theme="@style/Theme.Transparent"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:exported="true" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.ScreenRecordService"
|
android:name=".service.ScreenRecordService"
|
||||||
|
|||||||
@ -0,0 +1,73 @@
|
|||||||
|
package com.audio.record.screen.test.activity
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.media.projection.MediaProjectionManager
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.PersistableBundle
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.audio.record.screen.test.base.BaseActivity
|
||||||
|
import com.audio.record.screen.test.databinding.ActivityPlayBinding
|
||||||
|
import com.audio.record.screen.test.service.FloatingWindowBridge
|
||||||
|
import com.audio.record.screen.test.tool.Common
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service悬浮窗中请求权限的透明Activity
|
||||||
|
*/
|
||||||
|
class ScreenPermissionActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var screenCaptureLauncher: ActivityResultLauncher<Intent>
|
||||||
|
|
||||||
|
//录音权限
|
||||||
|
private lateinit var micLauncher: ActivityResultLauncher<String>
|
||||||
|
companion object{
|
||||||
|
|
||||||
|
const val key_with_audio="key_with_audio"
|
||||||
|
}
|
||||||
|
private var withAudio = false
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
Common.setStatusBarTextColor(this, true)
|
||||||
|
withAudio = intent.getBooleanExtra(key_with_audio, false)
|
||||||
|
screenCaptureLauncher = registerForActivityResult(
|
||||||
|
ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode == RESULT_OK && result.data != null) {
|
||||||
|
val data: Intent? = result.data
|
||||||
|
FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
|
||||||
|
}
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
micLauncher = registerForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission()
|
||||||
|
) { isGranted ->
|
||||||
|
if (isGranted) {
|
||||||
|
Common.showLog("mic 权限授予")
|
||||||
|
requestScreenPermission()
|
||||||
|
} else {
|
||||||
|
Common.showLog("mic 权限拒绝")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(withAudio){
|
||||||
|
micLauncher.launch(android.Manifest.permission.RECORD_AUDIO)
|
||||||
|
}else{
|
||||||
|
requestScreenPermission()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun requestScreenPermission() {
|
||||||
|
val mediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||||
|
val intent = mediaProjectionManager.createScreenCaptureIntent()
|
||||||
|
screenCaptureLauncher.launch(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -4,14 +4,14 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
import com.audio.record.screen.test.fragment.MainFragment
|
import com.audio.record.screen.test.fragment.MainFragment
|
||||||
import com.audio.record.screen.test.fragment.RecorderFragment
|
import com.audio.record.screen.test.fragment.RecordMainFragment
|
||||||
|
|
||||||
|
|
||||||
class ViewPager2Adapter(fragmentActivity: FragmentActivity) :
|
class ViewPager2Adapter(fragmentActivity: FragmentActivity) :
|
||||||
FragmentStateAdapter(fragmentActivity) {
|
FragmentStateAdapter(fragmentActivity) {
|
||||||
private val fragments: List<Fragment> = arrayListOf(
|
private val fragments: List<Fragment> = arrayListOf(
|
||||||
MainFragment.newInstance(),
|
MainFragment.newInstance(),
|
||||||
RecorderFragment.newInstance(),
|
RecordMainFragment.newInstance(),
|
||||||
MainFragment.newInstance()
|
MainFragment.newInstance()
|
||||||
)
|
)
|
||||||
override fun getItemCount(): Int = fragments.size
|
override fun getItemCount(): Int = fragments.size
|
||||||
|
|||||||
@ -4,19 +4,16 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.NavDestination
|
|
||||||
import androidx.navigation.fragment.NavHostFragment
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
import com.audio.record.screen.test.R
|
import com.audio.record.screen.test.R
|
||||||
import com.audio.record.screen.test.base.BaseFragment
|
import com.audio.record.screen.test.base.BaseFragment
|
||||||
import com.audio.record.screen.test.databinding.FragmentRecordBinding
|
import com.audio.record.screen.test.databinding.FragmentRecordBinding
|
||||||
import com.audio.record.screen.test.tool.Common
|
|
||||||
|
|
||||||
class RecorderFragment : BaseFragment<FragmentRecordBinding>() {
|
class RecordMainFragment : BaseFragment<FragmentRecordBinding>() {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun newInstance() =
|
fun newInstance() =
|
||||||
RecorderFragment().apply {
|
RecordMainFragment().apply {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10,7 +10,10 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.audio.record.screen.test.R
|
import com.audio.record.screen.test.R
|
||||||
@ -21,16 +24,18 @@ import com.audio.record.screen.test.dialog.DialogPermission
|
|||||||
import com.audio.record.screen.test.service.FloatingCallback
|
import com.audio.record.screen.test.service.FloatingCallback
|
||||||
import com.audio.record.screen.test.service.FloatingWindowBridge
|
import com.audio.record.screen.test.service.FloatingWindowBridge
|
||||||
import com.audio.record.screen.test.tool.Common
|
import com.audio.record.screen.test.tool.Common
|
||||||
|
import com.audio.record.screen.test.tool.ConstValue
|
||||||
import com.audio.record.screen.test.tool.Permission
|
import com.audio.record.screen.test.tool.Permission
|
||||||
import com.audio.record.screen.test.tool.ScreenCaptureHelper
|
import com.audio.record.screen.test.tool.ScreenCaptureHelper
|
||||||
import com.audio.record.screen.test.viewmodel.MainViewModel
|
import com.audio.record.screen.test.viewmodel.MainViewModel
|
||||||
import com.audio.record.screen.test.viewmodel.PreviewViewModel
|
import com.audio.record.screen.test.viewmodel.PreviewViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), FloatingCallback {
|
class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), FloatingCallback {
|
||||||
|
|
||||||
private lateinit var navController: NavController
|
private lateinit var navController: NavController
|
||||||
private val REQUEST_SCREENSHOT = 125
|
|
||||||
private lateinit var viewModel: MainViewModel
|
private lateinit var viewModel: MainViewModel
|
||||||
private lateinit var mediaProjectionManager: MediaProjectionManager
|
private lateinit var mediaProjectionManager: MediaProjectionManager
|
||||||
|
|
||||||
@ -51,6 +56,9 @@ class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), Floati
|
|||||||
//是否带音频录制
|
//是否带音频录制
|
||||||
private var isWithAudio = false
|
private var isWithAudio = false
|
||||||
|
|
||||||
|
|
||||||
|
private var pendingType: Int = -1
|
||||||
|
private val key_type = "check_type"
|
||||||
override fun initBinding(
|
override fun initBinding(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?
|
container: ViewGroup?
|
||||||
@ -115,7 +123,7 @@ class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), Floati
|
|||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
val data: Intent? = result.data
|
val data: Intent? = result.data
|
||||||
FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
|
FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
|
||||||
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
|
|
||||||
} else {
|
} else {
|
||||||
Common.showLog("用户取消了录屏权限授权")
|
Common.showLog("用户取消了录屏权限授权")
|
||||||
}
|
}
|
||||||
@ -125,10 +133,16 @@ class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), Floati
|
|||||||
recorderLauncher =
|
recorderLauncher =
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
if (result.resultCode == Activity.RESULT_OK && result.data != null) {
|
if (result.resultCode == Activity.RESULT_OK && result.data != null) {
|
||||||
val data = result.data
|
val intent = result.data
|
||||||
val resultCode = result.resultCode
|
val resultCode = result.resultCode
|
||||||
FloatingWindowBridge.updateMediaProjection(result.resultCode, data!!)
|
FloatingWindowBridge.updateMediaProjection(result.resultCode, intent!!)
|
||||||
startCountDown()
|
when(pendingType){
|
||||||
|
ConstValue.type_record->{ startCountDown()}
|
||||||
|
ConstValue.type_show_screenshot->{
|
||||||
|
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Common.showLog("录屏授权失败 ")
|
Common.showLog("录屏授权失败 ")
|
||||||
}
|
}
|
||||||
@ -196,10 +210,7 @@ class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), Floati
|
|||||||
|
|
||||||
private fun setScreenshot(boolean: Boolean) {
|
private fun setScreenshot(boolean: Boolean) {
|
||||||
if (boolean) {
|
if (boolean) {
|
||||||
if (FloatingWindowBridge.getMediaProjection() == null) {
|
requestRecordPermission(ConstValue.type_show_screenshot){
|
||||||
val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
|
|
||||||
screenCaptureLauncher.launch(captureIntent)
|
|
||||||
} else {
|
|
||||||
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
|
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_show_screenshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,14 +228,28 @@ class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), Floati
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun startRecorder() {
|
private fun startRecorder() {
|
||||||
if (FloatingWindowBridge.getMediaProjection() == null) {
|
requestRecordPermission( ConstValue.type_record){
|
||||||
val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
|
|
||||||
recorderLauncher.launch(captureIntent)
|
|
||||||
} else {
|
|
||||||
startCountDown()
|
startCountDown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求录屏权限
|
||||||
|
*/
|
||||||
|
private fun requestRecordPermission(type:Int,check:(()->Unit)? = null){
|
||||||
|
// TODO: requestRecordPermission
|
||||||
|
if (FloatingWindowBridge.getMediaProjection() == null) {
|
||||||
|
val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
|
||||||
|
pendingType = type
|
||||||
|
recorderLauncher.launch(captureIntent)
|
||||||
|
}else{
|
||||||
|
check?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun showAudioDialog() {
|
private fun showAudioDialog() {
|
||||||
mAudioDialog = mAudioDialog ?: DialogAudio {
|
mAudioDialog = mAudioDialog ?: DialogAudio {
|
||||||
when (it) {
|
when (it) {
|
||||||
@ -249,7 +274,22 @@ class RecordNormalFragment : BaseFragment<FragmentRecordNormalBinding>(), Floati
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartRecording() {
|
override fun onStartRecording() {
|
||||||
|
Common.showLog(" FragmentRecordNormal 回调 onStartRecording")
|
||||||
|
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||||
navController.navigate(R.id.action_to_recording)
|
navController.navigate(R.id.action_to_recording)
|
||||||
|
Common.showLog(" FragmentRecordNormal 回调 onStartRecording 执行111111111")
|
||||||
|
} else {
|
||||||
|
Common.showLog(" FragmentRecordNormal 回调 onStartRecording 延迟执行")
|
||||||
|
// 延迟执行
|
||||||
|
lifecycleScope.launch {
|
||||||
|
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
Common.showLog(" FragmentRecordNormal 回调 onStartRecording 延迟执行 11111111")
|
||||||
|
navController.navigate(R.id.action_to_recording)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -7,4 +7,6 @@ interface FloatingCallback {
|
|||||||
fun onUpdateRecordTime(time:String){}
|
fun onUpdateRecordTime(time:String){}
|
||||||
|
|
||||||
fun onStopRecord(){}
|
fun onStopRecord(){}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,6 +80,7 @@ object FloatingWindowBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun sendCommand(command: String, countdownValues: Array<String>? = null) {
|
fun sendCommand(command: String, countdownValues: Array<String>? = null) {
|
||||||
|
Common.showLog("-------------command=${command}")
|
||||||
when (command) {
|
when (command) {
|
||||||
COMMEND_hide_camera -> service?.hideFrontCamera()
|
COMMEND_hide_camera -> service?.hideFrontCamera()
|
||||||
COMMEND_show_camera -> service?.showCameraView()
|
COMMEND_show_camera -> service?.showCameraView()
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
package com.audio.record.screen.test.service
|
||||||
|
|
||||||
|
|
||||||
|
enum class RecordState {
|
||||||
|
DEFAULT,
|
||||||
|
RECORDING,
|
||||||
|
PAUSE }
|
||||||
@ -5,7 +5,6 @@ import android.app.Notification
|
|||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.ContentUris
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.PixelFormat
|
import android.graphics.PixelFormat
|
||||||
@ -17,12 +16,11 @@ import android.media.projection.MediaProjectionManager
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.CountDownTimer
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.provider.MediaStore
|
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -30,6 +28,7 @@ import android.view.WindowManager
|
|||||||
import android.view.animation.AccelerateDecelerateInterpolator
|
import android.view.animation.AccelerateDecelerateInterpolator
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
@ -39,21 +38,26 @@ import androidx.camera.lifecycle.ProcessCameraProvider
|
|||||||
import androidx.camera.view.PreviewView
|
import androidx.camera.view.PreviewView
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.view.isVisible
|
||||||
import com.audio.record.screen.test.R
|
import com.audio.record.screen.test.R
|
||||||
import com.audio.record.screen.test.activity.PlayActivity
|
import com.audio.record.screen.test.activity.PlayActivity
|
||||||
|
import com.audio.record.screen.test.activity.ScreenPermissionActivity
|
||||||
import com.audio.record.screen.test.tool.Common
|
import com.audio.record.screen.test.tool.Common
|
||||||
|
import com.audio.record.screen.test.tool.ConstValue
|
||||||
import com.audio.record.screen.test.tool.DraggableViewHelper
|
import com.audio.record.screen.test.tool.DraggableViewHelper
|
||||||
|
import com.audio.record.screen.test.tool.Extend.dpToPx
|
||||||
|
import com.audio.record.screen.test.tool.Extend.pxToDp
|
||||||
import com.audio.record.screen.test.tool.ScreenCaptureHelper
|
import com.audio.record.screen.test.tool.ScreenCaptureHelper
|
||||||
import com.audio.record.screen.test.tool.VideoFileHelper
|
import com.audio.record.screen.test.tool.VideoFileHelper
|
||||||
import com.audio.record.screen.test.view.CountDownFloatingManager
|
import com.audio.record.screen.test.view.CountDownFloatingManager
|
||||||
import java.io.File
|
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
class ScreenRecordService : Service() {
|
class ScreenRecordService : Service() {
|
||||||
|
|
||||||
private var lifecycleOwner: CustomLifecycleOwner? = null
|
private var lifecycleOwner: CustomLifecycleOwner? = null
|
||||||
|
|
||||||
private var isBallViewAdded = false
|
private var isBallViewAdded = false
|
||||||
|
private var isBallExpandViewAdded = false
|
||||||
private var isScreenshotViewAdded = false
|
private var isScreenshotViewAdded = false
|
||||||
private var isWebcamViewAdded = false
|
private var isWebcamViewAdded = false
|
||||||
private var isRecordViewAdded = false
|
private var isRecordViewAdded = false
|
||||||
@ -64,6 +68,17 @@ class ScreenRecordService : Service() {
|
|||||||
|
|
||||||
//悬浮球View
|
//悬浮球View
|
||||||
private var ballView: View? = null
|
private var ballView: View? = null
|
||||||
|
private var imIconVideo: ImageView? = null
|
||||||
|
private var smallLayoutRecording: LinearLayout? = null
|
||||||
|
private var smallTvTime: TextView? = null
|
||||||
|
|
||||||
|
|
||||||
|
//悬浮球展开View
|
||||||
|
private var ballViewExpand: View? = null
|
||||||
|
private var imRecord: ImageView? = null
|
||||||
|
private var tvRecordTime: TextView? = null
|
||||||
|
private var imNoAudioRecord: ImageView? = null
|
||||||
|
private var imScreenshot:ImageView? = null
|
||||||
|
|
||||||
//截屏View
|
//截屏View
|
||||||
private var screenshotView: View? = null
|
private var screenshotView: View? = null
|
||||||
@ -74,9 +89,10 @@ class ScreenRecordService : Service() {
|
|||||||
private var index = 0
|
private var index = 0
|
||||||
|
|
||||||
//录屏成功View
|
//录屏成功View
|
||||||
private var recordView: View? = null
|
private var recordCompleteView: View? = null
|
||||||
|
|
||||||
private val windowManager by lazy { getSystemService(WINDOW_SERVICE) as WindowManager }
|
private val windowManager by lazy { getSystemService(WINDOW_SERVICE) as WindowManager }
|
||||||
|
private lateinit var layoutParamsBallExpand: WindowManager.LayoutParams
|
||||||
private lateinit var layoutParamsBall: WindowManager.LayoutParams
|
private lateinit var layoutParamsBall: WindowManager.LayoutParams
|
||||||
private lateinit var layoutParamsScreenshot: WindowManager.LayoutParams
|
private lateinit var layoutParamsScreenshot: WindowManager.LayoutParams
|
||||||
private lateinit var layoutParamsCameraView: WindowManager.LayoutParams
|
private lateinit var layoutParamsCameraView: WindowManager.LayoutParams
|
||||||
@ -93,17 +109,22 @@ class ScreenRecordService : Service() {
|
|||||||
private var callbackRef: FloatingCallback? = null
|
private var callbackRef: FloatingCallback? = null
|
||||||
|
|
||||||
//截屏
|
//截屏
|
||||||
|
|
||||||
var mIntent: Intent? = null
|
var mIntent: Intent? = null
|
||||||
private var mCode: Int = 0
|
private var mCode: Int = 0
|
||||||
|
|
||||||
private lateinit var mediaRecorder: MediaRecorder
|
private lateinit var mediaRecorder: MediaRecorder
|
||||||
private lateinit var tmpVideoUri: Uri
|
private lateinit var tmpVideoUri: Uri
|
||||||
|
private lateinit var videoName: String
|
||||||
private var tmpVideoPfd: ParcelFileDescriptor? = null
|
private var tmpVideoPfd: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
|
//当前录制是否带音频
|
||||||
private var isWithAudio = false
|
private var isWithAudio = false
|
||||||
|
|
||||||
private val callbacks = mutableListOf<WeakReference<FloatingCallback>>()
|
private val callbacks = mutableListOf<WeakReference<FloatingCallback>>()
|
||||||
|
private var countDownTimer: CountDownTimer? = null
|
||||||
|
|
||||||
|
//录制状态标志 true 录制中
|
||||||
|
private var mRecordingStatus = -1
|
||||||
|
|
||||||
//录制时间监听相关
|
//录制时间监听相关
|
||||||
private var recordingStartTime: Long = 0L // 本次录制开始时间
|
private var recordingStartTime: Long = 0L // 本次录制开始时间
|
||||||
@ -116,7 +137,10 @@ class ScreenRecordService : Service() {
|
|||||||
private var isPause = false
|
private var isPause = false
|
||||||
private lateinit var virtualDisplay: VirtualDisplay
|
private lateinit var virtualDisplay: VirtualDisplay
|
||||||
|
|
||||||
private var mediaProjection:MediaProjection? = null
|
//悬浮球点击进行的动作 type_record_audio/type_record_without_audio/type_screenshot
|
||||||
|
private var ballType: Int? = null
|
||||||
|
|
||||||
|
private var mediaProjection: MediaProjection? = null
|
||||||
|
|
||||||
private val timeUpdateRunnable = object : Runnable {
|
private val timeUpdateRunnable = object : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
@ -125,6 +149,8 @@ class ScreenRecordService : Service() {
|
|||||||
System.currentTimeMillis() - recordingStartTime - totalPausedTime
|
System.currentTimeMillis() - recordingStartTime - totalPausedTime
|
||||||
val elapsed = currentRecordingTimeInMillis / 1000
|
val elapsed = currentRecordingTimeInMillis / 1000
|
||||||
val onRecordingTimeChanged = Common.onRecordingTimeChanged(elapsed)
|
val onRecordingTimeChanged = Common.onRecordingTimeChanged(elapsed)
|
||||||
|
tvRecordTime?.text = onRecordingTimeChanged
|
||||||
|
smallTvTime?.text = onRecordingTimeChanged
|
||||||
callbacks.forEach {
|
callbacks.forEach {
|
||||||
it.get()?.onUpdateRecordTime(onRecordingTimeChanged)
|
it.get()?.onUpdateRecordTime(onRecordingTimeChanged)
|
||||||
}
|
}
|
||||||
@ -180,9 +206,26 @@ class ScreenRecordService : Service() {
|
|||||||
mIntent = m
|
mIntent = m
|
||||||
mCode = code
|
mCode = code
|
||||||
// mediaProjection = mediaProjectionManager.getMediaProjection(code, m)
|
// mediaProjection = mediaProjectionManager.getMediaProjection(code, m)
|
||||||
|
ballType?.let {
|
||||||
|
when (it) {
|
||||||
|
ConstValue.type_record_without_audio -> {
|
||||||
|
isWithAudio = false
|
||||||
|
val arrayOf = arrayOf("3", "2", "1")
|
||||||
|
showCountDownView(arrayOf)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstValue.type_record_audio -> {
|
||||||
|
isWithAudio = true
|
||||||
|
val arrayOf = arrayOf("3", "2", "1")
|
||||||
|
showCountDownView(arrayOf)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstValue.type_screenshot -> {
|
||||||
|
startScreenshot()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ballType = null
|
||||||
}
|
}
|
||||||
fun resetIntent(){
|
|
||||||
mIntent = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -224,6 +267,22 @@ class ScreenRecordService : Service() {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始截屏
|
||||||
|
*/
|
||||||
|
private fun startScreenshot(){
|
||||||
|
// TODO: 开始截屏
|
||||||
|
mIntent?.let { intent->
|
||||||
|
mediaProjectionManager.getMediaProjection(mCode, intent)?.let { mediaProjection->
|
||||||
|
hideScreenshot()
|
||||||
|
ScreenCaptureHelper.startScreenCapture(this, mediaProjection) {
|
||||||
|
//截屏完成
|
||||||
|
showScreenshot()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
fun showCameraView() {
|
fun showCameraView() {
|
||||||
if (frontCameraView == null) {
|
if (frontCameraView == null) {
|
||||||
@ -255,6 +314,10 @@ class ScreenRecordService : Service() {
|
|||||||
hideFrontCamera() // 自动清除
|
hideFrontCamera() // 自动清除
|
||||||
hideBall()
|
hideBall()
|
||||||
hideScreenshot()
|
hideScreenshot()
|
||||||
|
hideBallExpand()
|
||||||
|
hideRecordCompleteView()
|
||||||
|
ballViewExpand = null
|
||||||
|
recordCompleteView = null
|
||||||
frontCameraView = null
|
frontCameraView = null
|
||||||
ballView = null
|
ballView = null
|
||||||
screenshotView = null
|
screenshotView = null
|
||||||
@ -298,9 +361,15 @@ class ScreenRecordService : Service() {
|
|||||||
layoutParamsBall.gravity = Gravity.TOP or Gravity.START
|
layoutParamsBall.gravity = Gravity.TOP or Gravity.START
|
||||||
layoutParamsBall.x = screenWH.first - size - 20
|
layoutParamsBall.x = screenWH.first - size - 20
|
||||||
layoutParamsBall.y = screenWH.second / 2 - size.div(2) // 居中
|
layoutParamsBall.y = screenWH.second / 2 - size.div(2) // 居中
|
||||||
|
smallLayoutRecording = ballView!!.findViewById<LinearLayout>(R.id.layout_recording)
|
||||||
|
imIconVideo = ballView!!.findViewById<ImageView>(R.id.image)
|
||||||
|
smallTvTime = ballView!!.findViewById<TextView>(R.id.tv_small_time)
|
||||||
|
|
||||||
|
|
||||||
DraggableViewHelper.attachToWindow(ballView!!, layoutParamsBall, windowManager, true)
|
DraggableViewHelper.attachToWindow(ballView!!, layoutParamsBall, windowManager, true) {
|
||||||
|
hideBall()
|
||||||
|
showBallExpand()
|
||||||
|
}
|
||||||
Common.showLog("-------layoutParamsBall.x=${layoutParamsBall.x} layoutParamsBall.y=${layoutParamsBall.y}")
|
Common.showLog("-------layoutParamsBall.x=${layoutParamsBall.x} layoutParamsBall.y=${layoutParamsBall.y}")
|
||||||
}
|
}
|
||||||
if (!isBallViewAdded) {
|
if (!isBallViewAdded) {
|
||||||
@ -310,6 +379,116 @@ class ScreenRecordService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun showBallExpand() {
|
||||||
|
if (ballViewExpand == null) {
|
||||||
|
ballViewExpand = LayoutInflater.from(this).inflate(R.layout.floating_ball_expand, null)
|
||||||
|
layoutParamsBallExpand = getLayoutParams()
|
||||||
|
layoutParamsBallExpand.gravity = Gravity.TOP or Gravity.START
|
||||||
|
imRecord = ballViewExpand!!.findViewById<ImageView>(R.id.ic_record)
|
||||||
|
tvRecordTime = ballViewExpand!!.findViewById<TextView>(R.id.tv_record_time)
|
||||||
|
imNoAudioRecord = ballViewExpand!!.findViewById<ImageView>(R.id.ic_without_audio)
|
||||||
|
imScreenshot = ballViewExpand!!.findViewById<ImageView>(R.id.ic_screenshot)
|
||||||
|
|
||||||
|
imRecord?.setOnClickListener {
|
||||||
|
it.isSelected.let { select ->
|
||||||
|
if (!select) {
|
||||||
|
//录制带音频的视频
|
||||||
|
ballType = ConstValue.type_record_audio
|
||||||
|
intentPermission(true)
|
||||||
|
} else {
|
||||||
|
//录制完成
|
||||||
|
stopRecording()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imNoAudioRecord?.setOnClickListener {
|
||||||
|
// TODO:
|
||||||
|
when (mRecordingStatus) {
|
||||||
|
ConstValue.status_recording -> {
|
||||||
|
//暂停录制
|
||||||
|
pauseRecording()
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstValue.status_pause -> {
|
||||||
|
//继续录制
|
||||||
|
resumeRecording()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
// 开始录制没有声音的视频
|
||||||
|
ballType = ConstValue.type_record_without_audio
|
||||||
|
intentPermission(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
imScreenshot?.setOnClickListener {
|
||||||
|
ballType = ConstValue.type_screenshot
|
||||||
|
intentPermission(false)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isBallExpandViewAdded) {
|
||||||
|
val size = Common.dpToPx(36, this)
|
||||||
|
val aWidthPx = Common.dpToPx(55, this)
|
||||||
|
val aHeightPx = Common.dpToPx(250, this)
|
||||||
|
// 转换 B 的宽高为像素
|
||||||
|
val bWidthPx = size
|
||||||
|
val bHeightPx = size
|
||||||
|
|
||||||
|
// 计算 B 的中心点坐标
|
||||||
|
val bCenterX = layoutParamsBall.x + bWidthPx / 2
|
||||||
|
val bCenterY = layoutParamsBall.y + bHeightPx / 2
|
||||||
|
|
||||||
|
// 让 A 的左上角从 B 的中心点出发(居中显示)
|
||||||
|
var aX = bCenterX - aWidthPx / 2
|
||||||
|
var aY = bCenterY - aHeightPx / 2
|
||||||
|
|
||||||
|
val (screenWidth, screenHeight) = Common.getScreenWH(this)
|
||||||
|
// 修正边界:防止 A 悬浮窗超出屏幕边界
|
||||||
|
aX = aX.coerceIn(0, screenWidth - aWidthPx)
|
||||||
|
aY = aY.coerceIn(0, screenHeight - aHeightPx)
|
||||||
|
|
||||||
|
if (aX >= screenWidth/2) {
|
||||||
|
layoutParamsBallExpand.x = aX - 10.dpToPx(this)
|
||||||
|
Common.showLog("--111111111--aX=${aX} layoutParamsBallExpand.x=${ layoutParamsBallExpand.x}")
|
||||||
|
} else {
|
||||||
|
layoutParamsBallExpand.x = aX + 10.dpToPx(this)
|
||||||
|
Common.showLog("---22222222--aX=${aX} layoutParamsBallExpand.x=${ layoutParamsBallExpand.x}")
|
||||||
|
}
|
||||||
|
layoutParamsBallExpand.y = aY
|
||||||
|
windowManager.addView(ballViewExpand, layoutParamsBallExpand)
|
||||||
|
isBallExpandViewAdded = true
|
||||||
|
delayRemove {
|
||||||
|
Common.showLog("-------切换ball显示")
|
||||||
|
hideBallExpand()
|
||||||
|
showBall()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3秒后自动隐藏
|
||||||
|
*/
|
||||||
|
private fun delayRemove(onFinish: (() -> Unit)?) {
|
||||||
|
countDownTimer?.cancel()
|
||||||
|
countDownTimer = object : CountDownTimer(3000L, 1000L) {
|
||||||
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinish() {
|
||||||
|
onFinish?.invoke()
|
||||||
|
countDownTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
countDownTimer?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//显示截屏
|
//显示截屏
|
||||||
fun showScreenshot() {
|
fun showScreenshot() {
|
||||||
if (screenshotView == null) {
|
if (screenshotView == null) {
|
||||||
@ -327,17 +506,7 @@ class ScreenRecordService : Service() {
|
|||||||
windowManager,
|
windowManager,
|
||||||
true
|
true
|
||||||
) {
|
) {
|
||||||
|
startScreenshot()
|
||||||
Common.showLog("--------------callbackRef=${callbackRef}")
|
|
||||||
// callbackRef?.onFloatingButtonClicked(FloatingWindowBridge.CLICK_screenshot)
|
|
||||||
mIntent?.let {
|
|
||||||
mediaProjectionManager.getMediaProjection(mCode, it)?.let {
|
|
||||||
hideScreenshot()
|
|
||||||
ScreenCaptureHelper.startScreenCapture(this, it) {
|
|
||||||
// showScreenshot()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isScreenshotViewAdded) {
|
if (!isScreenshotViewAdded) {
|
||||||
@ -349,16 +518,21 @@ class ScreenRecordService : Service() {
|
|||||||
|
|
||||||
//显示截屏
|
//显示截屏
|
||||||
fun showRecordView() {
|
fun showRecordView() {
|
||||||
if (recordView == null) {
|
if (recordCompleteView == null) {
|
||||||
recordView = LayoutInflater.from(this).inflate(R.layout.floating_record_complete, null)
|
recordCompleteView =
|
||||||
|
LayoutInflater.from(this).inflate(R.layout.floating_record_complete, null)
|
||||||
layoutParamsRecordView = getLayoutParams()
|
layoutParamsRecordView = getLayoutParams()
|
||||||
layoutParamsRecordView.gravity = Gravity.CENTER
|
layoutParamsRecordView.gravity = Gravity.CENTER
|
||||||
val imClose = recordView!!.findViewById<ImageView>(R.id.close).setOnClickListener {
|
val imClose =
|
||||||
hideRecordView()
|
recordCompleteView!!.findViewById<ImageView>(R.id.close).setOnClickListener {
|
||||||
|
hideRecordCompleteView()
|
||||||
}
|
}
|
||||||
recordView!!.findViewById<FrameLayout>(R.id.layout_video).setOnClickListener {
|
recordCompleteView!!.findViewById<FrameLayout>(R.id.layout_video).setOnClickListener {
|
||||||
// TODO: 跳到播放页面
|
// TODO: 跳到播放页面
|
||||||
|
hideRecordCompleteView()
|
||||||
val intent = Intent(this, PlayActivity::class.java).apply {
|
val intent = Intent(this, PlayActivity::class.java).apply {
|
||||||
|
putExtra(PlayActivity.KEY_URI, tmpVideoUri)
|
||||||
|
putExtra(PlayActivity.KEY_name, videoName)
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
}
|
}
|
||||||
this.startActivity(intent)
|
this.startActivity(intent)
|
||||||
@ -366,15 +540,15 @@ class ScreenRecordService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isRecordViewAdded) {
|
if (!isRecordViewAdded) {
|
||||||
|
|
||||||
val videoThumbnail = Common.getVideoThumbnail(tmpVideoPfd!!)
|
val videoThumbnail = Common.getVideoThumbnail(tmpVideoPfd!!)
|
||||||
Common.showLog("--------------videoThumbnail=${videoThumbnail}")
|
Common.showLog("--------------videoThumbnail=${videoThumbnail}")
|
||||||
val thumb = recordView!!.findViewById<ImageView>(R.id.image)
|
val thumb = recordCompleteView!!.findViewById<ImageView>(R.id.image)
|
||||||
|
|
||||||
thumb.setImageBitmap(videoThumbnail)
|
thumb.setImageBitmap(videoThumbnail)
|
||||||
|
windowManager.addView(recordCompleteView, layoutParamsRecordView)
|
||||||
windowManager.addView(recordView, layoutParamsRecordView)
|
|
||||||
isRecordViewAdded = true
|
isRecordViewAdded = true
|
||||||
|
delayRemove {
|
||||||
|
hideRecordCompleteView()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,9 +566,23 @@ class ScreenRecordService : Service() {
|
|||||||
// frontCameraView = null
|
// frontCameraView = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hideBallExpand() {
|
||||||
|
ballViewExpand?.let {
|
||||||
|
try {
|
||||||
|
if (isBallExpandViewAdded) {
|
||||||
|
windowManager.removeView(it)
|
||||||
|
isBallExpandViewAdded = false
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (_: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// frontCameraView = null
|
||||||
|
}
|
||||||
|
|
||||||
//隐藏录制预览View
|
//隐藏录制预览View
|
||||||
fun hideRecordView() {
|
fun hideRecordCompleteView() {
|
||||||
recordView?.let {
|
recordCompleteView?.let {
|
||||||
try {
|
try {
|
||||||
if (isRecordViewAdded) {
|
if (isRecordViewAdded) {
|
||||||
windowManager.removeView(it)
|
windowManager.removeView(it)
|
||||||
@ -451,7 +639,7 @@ class ScreenRecordService : Service() {
|
|||||||
hideCountDown()
|
hideCountDown()
|
||||||
mIntent?.let { intent ->
|
mIntent?.let { intent ->
|
||||||
mediaProjectionManager.getMediaProjection(mCode, intent)
|
mediaProjectionManager.getMediaProjection(mCode, intent)
|
||||||
?.let { media->
|
?.let { media ->
|
||||||
mediaProjection = media
|
mediaProjection = media
|
||||||
startRecording()
|
startRecording()
|
||||||
callbacks.forEach {
|
callbacks.forEach {
|
||||||
@ -516,11 +704,14 @@ class ScreenRecordService : Service() {
|
|||||||
*/
|
*/
|
||||||
fun startRecording() {
|
fun startRecording() {
|
||||||
Common.showLog("-------录屏中.....")
|
Common.showLog("-------录屏中.....")
|
||||||
|
// TODO: 录屏中.
|
||||||
val fullScreenSize = Common.getFullScreenSize(this)
|
val fullScreenSize = Common.getFullScreenSize(this)
|
||||||
val width = VideoFileHelper.alignTo16(fullScreenSize.first)
|
val width = VideoFileHelper.alignTo16(fullScreenSize.first)
|
||||||
val height = VideoFileHelper.alignTo16(fullScreenSize.second)
|
val height = VideoFileHelper.alignTo16(fullScreenSize.second)
|
||||||
initRecorder(width, height)
|
initRecorder(width, height)
|
||||||
mediaRecorder.start()
|
mediaRecorder.start()
|
||||||
|
mRecordingStatus = ConstValue.status_recording
|
||||||
|
updateBall()
|
||||||
recordingStartTime = System.currentTimeMillis()
|
recordingStartTime = System.currentTimeMillis()
|
||||||
totalPausedTime = 0L
|
totalPausedTime = 0L
|
||||||
mRecorderHandler.post(timeUpdateRunnable)
|
mRecorderHandler.post(timeUpdateRunnable)
|
||||||
@ -556,8 +747,6 @@ class ScreenRecordService : Service() {
|
|||||||
}, null
|
}, null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -600,8 +789,11 @@ class ScreenRecordService : Service() {
|
|||||||
* 暂停录制视频
|
* 暂停录制视频
|
||||||
*/
|
*/
|
||||||
fun pauseRecording() {
|
fun pauseRecording() {
|
||||||
|
// TODO: 暂停录制视频
|
||||||
Common.showLog("-------暂停.....")
|
Common.showLog("-------暂停.....")
|
||||||
mediaRecorder.pause()
|
mediaRecorder.pause()
|
||||||
|
mRecordingStatus = ConstValue.status_pause
|
||||||
|
updateBall()
|
||||||
pauseStartTime = System.currentTimeMillis()
|
pauseStartTime = System.currentTimeMillis()
|
||||||
isPause = true
|
isPause = true
|
||||||
}
|
}
|
||||||
@ -610,15 +802,67 @@ class ScreenRecordService : Service() {
|
|||||||
* 继续录制视频
|
* 继续录制视频
|
||||||
*/
|
*/
|
||||||
fun resumeRecording() {
|
fun resumeRecording() {
|
||||||
|
// TODO: 继续录制视频
|
||||||
Common.showLog("------继续.....")
|
Common.showLog("------继续.....")
|
||||||
|
|
||||||
mediaRecorder.resume()
|
mediaRecorder.resume()
|
||||||
|
mRecordingStatus = ConstValue.status_recording
|
||||||
|
updateBall()
|
||||||
totalPausedTime += System.currentTimeMillis() - pauseStartTime
|
totalPausedTime += System.currentTimeMillis() - pauseStartTime
|
||||||
isPause = false
|
isPause = false
|
||||||
mRecorderHandler.post(timeUpdateRunnable)
|
mRecorderHandler.post(timeUpdateRunnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 录制状态更新
|
||||||
|
*/
|
||||||
|
private fun updateBall() {
|
||||||
|
// TODO: 录制状态更新
|
||||||
|
when (mRecordingStatus) {
|
||||||
|
ConstValue.status_recording -> {
|
||||||
|
imNoAudioRecord?.run {
|
||||||
|
isSelected = true
|
||||||
|
isActivated = false
|
||||||
|
|
||||||
|
}
|
||||||
|
imRecord?.isSelected = true
|
||||||
|
tvRecordTime?.isVisible = true
|
||||||
|
|
||||||
|
imIconVideo?.isVisible = false
|
||||||
|
smallLayoutRecording?.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstValue.status_pause -> {
|
||||||
|
imNoAudioRecord?.run {
|
||||||
|
isSelected = false
|
||||||
|
isActivated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
imIconVideo?.isVisible = false
|
||||||
|
smallLayoutRecording?.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstValue.status_complete -> {
|
||||||
|
imNoAudioRecord?.run {
|
||||||
|
isSelected = false
|
||||||
|
isActivated = false
|
||||||
|
}
|
||||||
|
|
||||||
|
imRecord?.isSelected = false
|
||||||
|
tvRecordTime?.isVisible = false
|
||||||
|
|
||||||
|
imIconVideo?.isVisible = true
|
||||||
|
smallLayoutRecording?.isVisible = false
|
||||||
|
hideBallExpand()
|
||||||
|
showBall()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun initRecorder(width: Int, height: Int) {
|
private fun initRecorder(width: Int, height: Int) {
|
||||||
val (Uri, pfd) = VideoFileHelper.createVideoFile(this, Common.folderName)
|
videoName = System.currentTimeMillis().toString()
|
||||||
|
val (Uri, pfd) = VideoFileHelper.createVideoFile(this, Common.folderName, videoName)
|
||||||
tmpVideoUri = Uri
|
tmpVideoUri = Uri
|
||||||
tmpVideoPfd = pfd
|
tmpVideoPfd = pfd
|
||||||
mediaRecorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
mediaRecorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
@ -626,14 +870,18 @@ class ScreenRecordService : Service() {
|
|||||||
} else {
|
} else {
|
||||||
MediaRecorder()
|
MediaRecorder()
|
||||||
}.apply {
|
}.apply {
|
||||||
if (isWithAudio)
|
if (isWithAudio) {
|
||||||
setAudioSource(MediaRecorder.AudioSource.MIC)
|
setAudioSource(MediaRecorder.AudioSource.MIC)
|
||||||
|
}
|
||||||
setVideoSource(MediaRecorder.VideoSource.SURFACE)
|
setVideoSource(MediaRecorder.VideoSource.SURFACE)
|
||||||
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
|
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
|
||||||
setOutputFile(pfd?.fileDescriptor)
|
setOutputFile(pfd?.fileDescriptor)
|
||||||
setVideoSize(width, height)
|
setVideoSize(width, height)
|
||||||
setVideoEncoder(MediaRecorder.VideoEncoder.H264)
|
if (isWithAudio) {
|
||||||
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
|
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
|
||||||
|
}
|
||||||
|
setVideoEncoder(MediaRecorder.VideoEncoder.H264)
|
||||||
|
|
||||||
setVideoEncodingBitRate(8 * 1000 * 1000)
|
setVideoEncodingBitRate(8 * 1000 * 1000)
|
||||||
setVideoFrameRate(30)
|
setVideoFrameRate(30)
|
||||||
prepare()
|
prepare()
|
||||||
@ -641,6 +889,9 @@ class ScreenRecordService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun stopRecording() {
|
fun stopRecording() {
|
||||||
|
// TODO: 录屏完成
|
||||||
|
mRecordingStatus = ConstValue.status_complete
|
||||||
|
updateBall()
|
||||||
Common.showLog("-------录屏完成.....")
|
Common.showLog("-------录屏完成.....")
|
||||||
mRecorderHandler.removeCallbacks(timeUpdateRunnable)
|
mRecorderHandler.removeCallbacks(timeUpdateRunnable)
|
||||||
releaseAll()
|
releaseAll()
|
||||||
@ -669,4 +920,12 @@ class ScreenRecordService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun intentPermission(requestAudio: Boolean) {
|
||||||
|
val intent = Intent(this, ScreenPermissionActivity::class.java)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
intent.putExtra(ScreenPermissionActivity.key_with_audio, requestAudio)
|
||||||
|
this.startActivity(intent)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.audio.record.screen.test.tool
|
||||||
|
|
||||||
|
object ConstValue {
|
||||||
|
|
||||||
|
|
||||||
|
//--------------RecordNormalFragment
|
||||||
|
//开始录制
|
||||||
|
const val type_record = 0
|
||||||
|
|
||||||
|
//显示截屏悬浮球
|
||||||
|
const val type_show_screenshot = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//--------------ScreenRecordService
|
||||||
|
//一次性使用
|
||||||
|
const val type_record_audio = 3
|
||||||
|
const val type_record_without_audio = 4
|
||||||
|
const val type_screenshot = 5
|
||||||
|
|
||||||
|
|
||||||
|
//当前状态
|
||||||
|
const val status_recording = 6 //录制当中
|
||||||
|
const val status_pause = 7 //录制暂停
|
||||||
|
const val status_complete = 8 //录制完成
|
||||||
|
}
|
||||||
@ -6,6 +6,9 @@ import android.media.Image
|
|||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
|
||||||
object Extend {
|
object Extend {
|
||||||
|
|
||||||
@ -44,11 +47,18 @@ object Extend {
|
|||||||
layoutParams = params
|
layoutParams = params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ImageView.setDrawableCompat(@DrawableRes resId: Int) {
|
||||||
|
val drawable = ContextCompat.getDrawable(context, resId)
|
||||||
|
this.setImageDrawable(drawable)
|
||||||
|
}
|
||||||
|
|
||||||
fun Int.dpToPx(context: Context): Int =
|
fun Int.dpToPx(context: Context): Int =
|
||||||
TypedValue.applyDimension(
|
TypedValue.applyDimension(
|
||||||
TypedValue.COMPLEX_UNIT_DIP, this.toFloat(),
|
TypedValue.COMPLEX_UNIT_DIP, this.toFloat(),
|
||||||
context.resources.displayMetrics
|
context.resources.displayMetrics
|
||||||
).toInt()
|
).toInt()
|
||||||
|
fun Context.pxToDp(px: Int): Float {
|
||||||
|
return px / resources.displayMetrics.density
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -68,8 +68,8 @@ object ScreenCaptureHelper {
|
|||||||
if (image != null) {
|
if (image != null) {
|
||||||
val bitmap = imageToBitmap(image)
|
val bitmap = imageToBitmap(image)
|
||||||
saveBitmap(context, bitmap, folderName)
|
saveBitmap(context, bitmap, folderName)
|
||||||
isOK.invoke()
|
|
||||||
showScreenshotPreviewAnimation(context, bitmap,width,height)
|
showScreenshotPreviewAnimation(context, bitmap,width,height)
|
||||||
|
isOK.invoke()
|
||||||
image.close()
|
image.close()
|
||||||
}
|
}
|
||||||
imageReader.close()
|
imageReader.close()
|
||||||
|
|||||||
12
app/src/main/res/drawable/ball_audio_record.xml
Normal file
12
app/src/main/res/drawable/ball_audio_record.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="26dp"
|
||||||
|
android:height="26dp"
|
||||||
|
android:viewportWidth="26"
|
||||||
|
android:viewportHeight="26">
|
||||||
|
<path
|
||||||
|
android:pathData="M1.733,13C1.733,6.778 6.778,1.733 13,1.733C19.222,1.733 24.267,6.778 24.267,13C24.267,19.222 19.222,24.267 13,24.267C6.778,24.267 1.733,19.222 1.733,13ZM0,13C0,20.18 5.82,26 13,26C20.18,26 26,20.18 26,13C26,5.82 20.18,0 13,0C5.82,0 0,5.82 0,13Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M7,13C7,9.686 9.686,7 13,7C16.314,7 19,9.686 19,13C19,16.314 16.314,19 13,19C9.686,19 7,16.314 7,13Z"
|
||||||
|
android:fillColor="#DD2432"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ball_continue_recording.xml
Normal file
9
app/src/main/res/drawable/ball_continue_recording.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="15dp"
|
||||||
|
android:height="19dp"
|
||||||
|
android:viewportWidth="28"
|
||||||
|
android:viewportHeight="32">
|
||||||
|
<path
|
||||||
|
android:pathData="M26.901,14.257C28.261,15.021 28.261,16.979 26.901,17.743L3.731,30.778C2.397,31.528 0.75,30.564 0.75,29.035L0.75,2.965C0.75,1.436 2.397,0.472 3.731,1.222L26.901,14.257Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
||||||
17
app/src/main/res/drawable/ball_home.xml
Normal file
17
app/src/main/res/drawable/ball_home.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="26dp"
|
||||||
|
android:height="26dp"
|
||||||
|
android:viewportWidth="26"
|
||||||
|
android:viewportHeight="26">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0h26v26h-26z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M26,0H0.005V26H26V0Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M20.195,25.962H15.027C14.514,25.962 14.101,25.548 14.101,25.034V16.99C14.101,16.986 14.093,16.975 14.085,16.975H11.657C11.653,16.975 11.642,16.983 11.642,16.99V25.034C11.642,25.548 11.229,25.962 10.715,25.962H5.551C4.003,25.962 2.744,24.701 2.744,23.15V14.976H2.181C1.308,14.976 0.525,14.457 0.177,13.653C-0.166,12.853 -0.004,11.925 0.598,11.291L10.043,1.298C10.808,0.49 11.881,0.026 12.993,0.026C14.104,0.026 15.177,0.49 15.942,1.298L25.383,11.29C25.981,11.924 26.147,12.852 25.804,13.653C25.46,14.453 24.673,14.975 23.8,14.975H23.001V23.15C23.001,24.7 21.743,25.961 20.195,25.961V25.962ZM15.953,24.105H20.195C20.72,24.105 21.148,23.676 21.148,23.15V14.047C21.148,13.533 21.561,13.119 22.075,13.119H23.8C23.9,13.119 24.039,13.069 24.101,12.922C24.163,12.775 24.105,12.639 24.039,12.566L14.59,2.574C14.17,2.13 13.602,1.882 12.989,1.882C12.375,1.882 11.807,2.126 11.387,2.574L1.942,12.567C1.872,12.64 1.815,12.775 1.88,12.922C1.942,13.069 2.081,13.12 2.181,13.12H3.671C4.184,13.12 4.597,13.533 4.597,14.047V23.151C4.597,23.676 5.026,24.106 5.551,24.106H9.793V16.99C9.793,15.958 10.63,15.119 11.661,15.119H14.089C15.119,15.119 15.957,15.958 15.957,16.99V24.106H15.953L15.953,24.105Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
12
app/src/main/res/drawable/ball_pause.xml
Normal file
12
app/src/main/res/drawable/ball_pause.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="15dp"
|
||||||
|
android:height="14dp"
|
||||||
|
android:viewportWidth="29"
|
||||||
|
android:viewportHeight="38">
|
||||||
|
<path
|
||||||
|
android:pathData="M1,0L10,0A1,1 0,0 1,11 1L11,37A1,1 0,0 1,10 38L1,38A1,1 0,0 1,0 37L0,1A1,1 0,0 1,1 0z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19,0L28,0A1,1 0,0 1,29 1L29,37A1,1 0,0 1,28 38L19,38A1,1 0,0 1,18 37L18,1A1,1 0,0 1,19 0z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ball_recording.xml
Normal file
9
app/src/main/res/drawable/ball_recording.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="15dp"
|
||||||
|
android:height="15dp"
|
||||||
|
android:viewportWidth="15"
|
||||||
|
android:viewportHeight="15">
|
||||||
|
<path
|
||||||
|
android:pathData="M3,0L12,0A3,3 0,0 1,15 3L15,12A3,3 0,0 1,12 15L3,15A3,3 0,0 1,0 12L0,3A3,3 0,0 1,3 0z"
|
||||||
|
android:fillColor="#DD2432"/>
|
||||||
|
</vector>
|
||||||
15
app/src/main/res/drawable/ball_screenshot.xml
Normal file
15
app/src/main/res/drawable/ball_screenshot.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="26dp"
|
||||||
|
android:height="26dp"
|
||||||
|
android:viewportWidth="26"
|
||||||
|
android:viewportHeight="26">
|
||||||
|
<path
|
||||||
|
android:pathData="M4.987,0H6.883V19.948H26V21.844H4.987V0Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M21.844,25.169H19.948V6.052H0V4.156H21.844V25.169Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M24.73,2.573L9.298,18.911L7.919,17.61L23.352,1.27L24.73,2.573Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
||||||
23
app/src/main/res/drawable/ball_without_audio_record.xml
Normal file
23
app/src/main/res/drawable/ball_without_audio_record.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="26dp"
|
||||||
|
android:height="25dp"
|
||||||
|
android:viewportWidth="26"
|
||||||
|
android:viewportHeight="25">
|
||||||
|
<path
|
||||||
|
android:pathData="M5.871,10.903C5.871,8.124 8.124,5.871 10.903,5.871C13.682,5.871 15.936,8.124 15.936,10.903C15.936,13.682 13.682,15.936 10.903,15.936C8.124,15.936 5.871,13.682 5.871,10.903Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M15.097,13.419h10.903v10.903h-10.903z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M26,13.419H15.099V24.323H26V13.419Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M21.531,13.787C21.74,14.011 21.857,14.316 21.857,14.634V23.125C21.857,23.787 21.358,24.323 20.743,24.323C20.447,24.323 20.164,24.196 19.955,23.972L17.929,21.792C17.815,21.67 17.66,21.601 17.499,21.601H16.717C15.823,21.601 15.097,20.821 15.097,19.859V17.9C15.097,16.938 15.823,16.159 16.717,16.159H17.499C17.66,16.159 17.815,16.09 17.929,15.967L19.955,13.788C20.39,13.32 21.095,13.319 21.531,13.787ZM25.869,17.079C26.027,17.249 26.027,17.525 25.869,17.696L24.751,18.897L25.869,20.098C26.027,20.268 26.027,20.545 25.869,20.715C25.71,20.885 25.454,20.885 25.295,20.715L24.177,19.514L23.06,20.715C22.901,20.885 22.644,20.885 22.486,20.715C22.327,20.545 22.327,20.268 22.486,20.098L23.604,18.897L22.486,17.696C22.327,17.525 22.327,17.249 22.486,17.079C22.644,16.909 22.901,16.909 23.06,17.079L24.177,18.28L25.295,17.079C25.453,16.909 25.71,16.909 25.869,17.079L25.869,17.079Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<path
|
||||||
|
android:pathData="M10.903,0C16.925,0 21.807,4.882 21.807,10.903C21.807,11.186 21.792,11.465 21.771,11.742H20.313C20.338,11.466 20.353,11.186 20.353,10.903C20.353,5.685 16.122,1.454 10.903,1.454C5.685,1.454 1.454,5.685 1.454,10.903C1.454,16.122 5.685,20.353 10.903,20.353C11.775,20.353 12.618,20.232 13.42,20.011V20.163C13.42,20.177 13.422,20.191 13.422,20.205V21.51C12.613,21.701 11.771,21.807 10.903,21.807C4.882,21.807 0,16.925 0,10.903C0,4.882 4.882,0 10.903,0Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/bg_ball.xml
Normal file
9
app/src/main/res/drawable/bg_ball.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
|
||||||
|
<corners android:radius="25dp"/>
|
||||||
|
|
||||||
|
<solid android:color="@color/color_CC000000"/>
|
||||||
|
|
||||||
|
</shape>
|
||||||
7
app/src/main/res/drawable/recording_bg_oval.xml
Normal file
7
app/src/main/res/drawable/recording_bg_oval.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="@color/color_CC000000"/>
|
||||||
|
|
||||||
|
|
||||||
|
</shape>
|
||||||
7
app/src/main/res/drawable/recording_oval_red.xml
Normal file
7
app/src/main/res/drawable/recording_oval_red.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="@color/test_red"/>
|
||||||
|
|
||||||
|
|
||||||
|
</shape>
|
||||||
5
app/src/main/res/drawable/status_ball_record.xml
Normal file
5
app/src/main/res/drawable/status_ball_record.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/ball_recording" android:state_selected="true" />
|
||||||
|
<item android:drawable="@drawable/ball_audio_record" android:state_selected="false" />
|
||||||
|
</selector>
|
||||||
14
app/src/main/res/drawable/status_ball_record_three.xml
Normal file
14
app/src/main/res/drawable/status_ball_record_three.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- 暂停状态(优先级高) -->
|
||||||
|
<item android:drawable="@drawable/ball_continue_recording"
|
||||||
|
android:state_activated="true" />
|
||||||
|
|
||||||
|
<!-- 录制状态 -->
|
||||||
|
<item android:drawable="@drawable/ball_pause"
|
||||||
|
android:state_selected="true" />
|
||||||
|
|
||||||
|
<!-- 默认状态 -->
|
||||||
|
<item android:drawable="@drawable/ball_without_audio_record" />
|
||||||
|
|
||||||
|
</selector>
|
||||||
@ -9,4 +9,29 @@
|
|||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
android:src="@drawable/global_ball" />
|
android:src="@drawable/global_ball" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_recording"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:background="@drawable/recording_bg_oval"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="7dp"
|
||||||
|
android:layout_height="7dp"
|
||||||
|
android:src="@drawable/recording_oval_red" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_small_time"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:text="00:00:00"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="6sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
60
app/src/main/res/layout/floating_ball_expand.xml
Normal file
60
app/src/main/res/layout/floating_ball_expand.xml
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="55dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_ball"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="25dp"
|
||||||
|
android:paddingBottom="25dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ic_record"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="29dp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:src="@drawable/status_ball_record" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_record_time"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="00:00:00"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ic_without_audio"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="29dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:src="@drawable/status_ball_record_three" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ic_screenshot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="29dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:src="@drawable/ball_screenshot" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ic_home"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="29dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:src="@drawable/ball_home" />
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
@ -6,4 +6,13 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.RecordScreen" parent="Base.Theme.RecordScreen" />
|
<style name="Theme.RecordScreen" parent="Base.Theme.RecordScreen" />
|
||||||
|
|
||||||
|
|
||||||
|
<style name="Theme.Transparent" parent="Theme.AppCompat.NoActionBar">
|
||||||
|
<item name="android:windowBackground">@color/color_transparent</item>
|
||||||
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
|
<item name="android:windowNoTitle">true</item>
|
||||||
|
<item name="android:backgroundDimEnabled">false</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
Reference in New Issue
Block a user