及时更新video和image

This commit is contained in:
litingting 2025-06-24 18:33:00 +08:00
parent b98970177e
commit 973e0f1d30
31 changed files with 596 additions and 216 deletions

View File

@ -24,26 +24,14 @@
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:icon="@mipmap/logo"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:roundIcon="@mipmap/logo"
android:supportsRtl="true"
android:theme="@style/Theme.RecordScreen"
tools:targetApi="31">
<activity
android:name=".activity.PlayActivity"
android:screenOrientation="portrait"
android:exported="false" />
<activity
android:name=".activity.ImageViewActivity"
android:screenOrientation="portrait"
android:exported="false" />
<activity
android:name=".activity.PreviewActivity"
android:screenOrientation="portrait"
android:exported="false" />
<activity
android:name=".activity.MainActivity1"
android:name=".activity.WelcomeActivity"
android:screenOrientation="portrait"
android:exported="true" >
<intent-filter>
@ -52,9 +40,28 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.PlayActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".activity.ImageViewActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".activity.PreviewActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".activity.MainActivity1"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".activity.MainActivity"
android:exported="true">
android:exported="false">
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.MAIN" /> -->
@ -66,11 +73,19 @@
<activity
android:name=".activity.ScreenPermissionActivity"
android:excludeFromRecents="true"
android:taskAffinity=""
android:screenOrientation="portrait"
android:theme="@style/Theme.Transparent"
android:exported="true"
android:launchMode="singleTop"
android:exported="true" />
android:screenOrientation="portrait"
android:taskAffinity=""
android:theme="@style/Theme.Transparent" />
<activity
android:name=".activity.ScreenshotAnimActivity"
android:excludeFromRecents="true"
android:exported="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:taskAffinity=""
android:theme="@style/Theme.Transparent" />
<service
android:name=".service.ScreenRecordService"

View File

@ -124,7 +124,7 @@ class MainActivity1 : BaseActivity<ActivityMain1Binding>(), ConnectionListener,F
private fun bingVp() {
val tabIcons =
intArrayOf(R.drawable.tab1_selector, R.drawable.tab2_selector, R.drawable.tab3_selector)
intArrayOf(R.drawable.tab1_selector, R.drawable.tab2_selector)
binding.run {
viewPager2.isUserInputEnabled = false
viewPager2.adapter = ViewPager2Adapter(this@MainActivity1)
@ -155,11 +155,8 @@ class MainActivity1 : BaseActivity<ActivityMain1Binding>(), ConnectionListener,F
override fun onTabReselected(tab: TabLayout.Tab?) {
}
})
}
}
private fun showPermissionDialog() {
@ -213,28 +210,18 @@ class MainActivity1 : BaseActivity<ActivityMain1Binding>(), ConnectionListener,F
this@MainActivity1.moveTaskToBack(true)
}
override fun onServiceConnected() {
viewModel.updateServiceConnectStatus(true)
FloatingWindowBridge.registerCallback(this)
// updateFloatingBtnStatus()
//新开进程的时候检测当前悬浮窗状态刷新当前各ImageView状态
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_sync_view_status)
}
/**
* 新开进程的时候检测当前悬浮窗状态刷新当前各ImageView状态
*/
private fun updateFloatingBtnStatus(){
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_sync_view_status)
}
override fun onResume() {
super.onResume()
//初始进程中在桌面回到Activity的时候更新当前录制状态
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_sync_recording_status)
}
override fun onServiceConnected() {
viewModel.updateServiceConnectStatus(true)
FloatingWindowBridge.registerCallback(this)
//新开进程的时候检测当前悬浮窗状态刷新当前各ImageView状态
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_sync_view_status)
FloatingWindowBridge.sendCommand(FloatingWindowBridge.COMMEND_sync_recording_status)
}
override fun onRefreshViewShow(
webCamShow: Boolean,

View File

@ -0,0 +1,110 @@
package com.audio.record.screen.test.activity
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.media.projection.MediaProjectionManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.PersistableBundle
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import android.widget.TextView
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.audio.record.screen.test.R
import com.audio.record.screen.test.base.BaseActivity
import com.audio.record.screen.test.databinding.ActivityPlayBinding
import com.audio.record.screen.test.service.FloatingCallback
import com.audio.record.screen.test.service.FloatingWindowBridge
import com.audio.record.screen.test.service.ScreenshotAnimExecutor
import com.audio.record.screen.test.tool.Common
import com.audio.record.screen.test.tool.ScreenCaptureHelper
/**
* Service悬浮窗中请求权限的透明Activity
*/
class ScreenshotAnimActivity : AppCompatActivity(), FloatingCallback {
private lateinit var imageView: ImageView
private lateinit var tv: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Common.setStatusBarTextColor(this, true)
setContentView(R.layout.floating_screenshot_anim)
Common.showLog("---ScreenshotAnimActivity--onCreate")
FloatingWindowBridge.registerCallback(this)
imageView = findViewById<ImageView>(R.id.image)
tv = findViewById<TextView>(R.id.tv_content)
}
override fun onRefreshBitmap(bitmap: Bitmap) {
super.onRefreshBitmap(bitmap)
Common.showLog("---ScreenshotAnimActivity--onRefreshBitmap")
tv.isVisible = false
imageView.setImageBitmap(bitmap)
val fullScreenSize = Common.getFullScreenSize(this)
val screenWidth = fullScreenSize.first
val screenHeight = fullScreenSize.second
val scale = 0.25f
val targetX = (screenWidth - screenWidth * scale - 12).toFloat()
val targetY = (screenHeight - screenHeight * scale - 14).toFloat()
// 设置初始位置在中心
imageView.translationX = 0f
imageView.translationY = 0f
imageView.scaleX = 1f
imageView.scaleY = 1f
// 缩放动画
val scaleX = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, scale)
val scaleY = ObjectAnimator.ofFloat(imageView, "scaleY", 1f, scale)
// 位移动画(目标点是右下角)
val translateX = ObjectAnimator.ofFloat(imageView, "translationX", 0f, targetX - screenWidth / 2f)
val translateY = ObjectAnimator.ofFloat(imageView, "translationY", 0f, targetY - screenHeight / 2f)
val animatorSet = AnimatorSet().apply {
playTogether(scaleX, scaleY, translateX, translateY)
duration = 600L
interpolator = DecelerateInterpolator()
addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {
Common.showLog("-----动画开始")
}
override fun onAnimationEnd(animation: Animator) {
Common.showLog("-----动画结束")
Handler(Looper.getMainLooper()).postDelayed({
finish()
}, 2000)
}
override fun onAnimationCancel(animation: Animator) {}
override fun onAnimationRepeat(animation: Animator) {}
})
}
animatorSet.start()
}
override fun onResume() {
super.onResume()
Common.showLog("--ScreenshotAnimActivity---onResume")
}
}

View File

@ -0,0 +1,48 @@
package com.audio.record.screen.test.activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.CountDownTimer
import com.audio.record.screen.test.R
import com.audio.record.screen.test.base.BaseActivity
import com.audio.record.screen.test.databinding.ActivityWelcomeBinding
class WelcomeActivity : BaseActivity<ActivityWelcomeBinding>() {
private val total = 2000L
private var countDownTimer:CountDownTimer? = null
override fun initBinding(): ActivityWelcomeBinding =
ActivityWelcomeBinding.inflate(layoutInflater)
override fun getFullColor(): Boolean = true
override fun onCreateInit() {
countDownTimer =object :CountDownTimer(total, 1000){
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
toMain()
}
}
countDownTimer?.start()
}
private fun toMain(){
startActivity(Intent(this,MainActivity1::class.java))
finish()
}
override fun onInitPadding(): Boolean = false
override fun onDestroy() {
super.onDestroy()
countDownTimer?.cancel()
countDownTimer = null
}
}

View File

@ -32,9 +32,18 @@ class ImageGroupAdapter(context: Context, private var click: (cropRatio: String)
adapter = ImageInfoAdapter(mContext, position == 0) {}.apply {
updateData(item.images)
}
if (this.layoutManager == null) {
layoutManager = GridLayoutManager(mContext, 3)
}
isNestedScrollingEnabled = false
addItemDecoration(GridSpacingItemDecoration(3, 5.dpToPx(mContext), true))
setHasFixedSize(false)
if (this.itemDecorationCount <= 0) {
val gridSpacingItemDecoration =
GridSpacingItemDecoration(3, 5.dpToPx(mContext), true)
addItemDecoration(gridSpacingItemDecoration)
}
}
}
}

View File

@ -7,6 +7,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.audio.record.screen.test.base.BaseAdapter
import com.audio.record.screen.test.data.VideoGroup
import com.audio.record.screen.test.databinding.AdapterVideoDateBinding
import com.audio.record.screen.test.tool.Common
import com.audio.record.screen.test.tool.NoScrollLinearLayoutManager
class VideoGroupAdapter(context: Context, private var click: (cropRatio: String) -> Unit) :
@ -16,7 +17,8 @@ class VideoGroupAdapter(context: Context, private var click: (cropRatio:String)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val itemHolder: VHolder<AdapterVideoDateBinding> = holder as VHolder<AdapterVideoDateBinding>
val itemHolder: VHolder<AdapterVideoDateBinding> =
holder as VHolder<AdapterVideoDateBinding>
val item = data[position]
itemHolder.vb.run {
@ -24,7 +26,8 @@ class VideoGroupAdapter(context: Context, private var click: (cropRatio:String)
tvDate.text = item.date
videoInfoRecycler.run {
adapter = VideoInfoAdapter(mContext){}.apply {
setHasFixedSize(false)
adapter = VideoInfoAdapter(mContext, position == data.size - 1) {}.apply {
updateData(item.videos)
}
layoutManager = NoScrollLinearLayoutManager(mContext)
@ -33,7 +36,6 @@ class VideoGroupAdapter(context: Context, private var click: (cropRatio:String)
}
}
}

View File

@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.audio.record.screen.test.activity.PlayActivity
import com.audio.record.screen.test.base.BaseAdapter
@ -11,7 +12,11 @@ import com.audio.record.screen.test.data.VideoInfo
import com.audio.record.screen.test.databinding.AdapterVideoInfoBinding
import com.audio.record.screen.test.tool.Common
class VideoInfoAdapter(context: Context, private var click: (cropRatio: String) -> Unit) :
class VideoInfoAdapter(
context: Context,
var isLast: Boolean,
private var click: (cropRatio: String) -> Unit
) :
BaseAdapter<VideoInfo, AdapterVideoInfoBinding>(context) {
override fun getViewBinding(parent: ViewGroup?): AdapterVideoInfoBinding {
return AdapterVideoInfoBinding.inflate(LayoutInflater.from(parent!!.context), parent, false)
@ -22,7 +27,9 @@ class VideoInfoAdapter(context: Context, private var click: (cropRatio: String)
holder as VHolder<AdapterVideoInfoBinding>
val item = data[position]
itemHolder.vb.run {
space.isVisible = position == data.size - 1 && isLast
image.setImageBitmap(item.thumbnail)
tvName.text = item.displayName
tvTime.text = Common.formatDuration(item.duration)

View File

@ -11,8 +11,7 @@ class ViewPager2Adapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {
private val fragments: List<Fragment> = arrayListOf(
MainFragment.newInstance(),
RecordMainFragment.newInstance(),
MainFragment.newInstance()
RecordMainFragment.newInstance()
)
override fun getItemCount(): Int = fragments.size

View File

@ -27,7 +27,7 @@ class DialogPermission(private var mClickType: (type: Int) -> Unit) : BottomShee
super.onStart()
val dialog = dialog
if (dialog != null) {
dialog.setCanceledOnTouchOutside(true)
dialog.setCanceledOnTouchOutside(false)
val window = dialog.window
if (window != null) {
window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))

View File

@ -8,12 +8,14 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import com.audio.record.screen.test.adapter.ImageGroupAdapter
import com.audio.record.screen.test.base.BaseFragment
import com.audio.record.screen.test.data.ImageGroup
import com.audio.record.screen.test.databinding.FragmentImageBinding
import com.audio.record.screen.test.tool.Common
import com.audio.record.screen.test.tool.VideoFileHelper
class ImageFragment : BaseFragment<FragmentImageBinding>() {
private lateinit var imageGroupAdapter:ImageGroupAdapter
companion object {
@JvmStatic
@ -30,6 +32,27 @@ class ImageFragment : BaseFragment<FragmentImageBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
imageGroupAdapter = ImageGroupAdapter(requireContext()) {
}
binding.videoRecycler.run {
adapter = imageGroupAdapter
layoutManager = LinearLayoutManager(requireContext())
}
}
override fun onResume() {
super.onResume()
getInfo()?.let {
imageGroupAdapter.updateData(it)
}
}
private fun getInfo():List<ImageGroup>?{
val queryVideoInfoListInFolder =
VideoFileHelper.queryGroupedImagesByDay(requireContext(),Common.imagesFolderDir)
@ -38,23 +61,13 @@ class ImageFragment : BaseFragment<FragmentImageBinding>() {
layoutEmpty.isVisible = true
}
Common.showLog("IMag isEmpty()")
return
return null
} else {
binding.run {
layoutEmpty.isVisible = false
}
}
val imageGroupAdapter = ImageGroupAdapter(requireContext()) {
}
binding.videoRecycler.run {
adapter = imageGroupAdapter.apply {
updateData(queryVideoInfoListInFolder)
}
layoutManager = LinearLayoutManager(requireContext())
}
return queryVideoInfoListInFolder
}

View File

@ -17,6 +17,7 @@ import com.audio.record.screen.test.tool.Common
import com.audio.record.screen.test.tool.VideoFileHelper
class VideoFragment : BaseFragment<FragmentVideoBinding>() {
private lateinit var videoGroupAdapter:VideoGroupAdapter
companion object {
@JvmStatic
@ -33,7 +34,24 @@ class VideoFragment : BaseFragment<FragmentVideoBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
videoGroupAdapter = VideoGroupAdapter(requireContext()) {
}
binding.videoRecycler.run {
adapter = videoGroupAdapter
layoutManager = LinearLayoutManager(requireContext())
}
}
override fun onResume() {
super.onResume()
getInfo()?.let {
videoGroupAdapter.updateData(it)
}
}
private fun getInfo():MutableList<VideoGroup>?{
val queryVideoInfoListInFolder =
VideoFileHelper.queryVideoInfoListInFolder(requireContext(),Common.videosFolderDir)
@ -43,7 +61,7 @@ class VideoFragment : BaseFragment<FragmentVideoBinding>() {
layoutRecent.isVisible = false
}
Common.showLog("queryVideoInfoListInFolder.isEmpty()")
return
return null
} else {
binding.run {
layoutEmpty.isVisible = false
@ -62,21 +80,9 @@ class VideoFragment : BaseFragment<FragmentVideoBinding>() {
val removeRecent = removeRecent(listOf)
if (removeRecent.isEmpty()||removeRecent[0].videos.isEmpty()) {
Common.showLog("removeRecent.isEmpty()")
return
return null
}
val videoGroupAdapter = VideoGroupAdapter(requireContext()) {
}
binding.videoRecycler.run {
adapter = videoGroupAdapter.apply {
updateData(removeRecent)
}
layoutManager = LinearLayoutManager(requireContext())
}
return removeRecent
}

View File

@ -1,5 +1,6 @@
package com.audio.record.screen.test.service
import android.graphics.Bitmap
import androidx.camera.core.processing.SurfaceProcessorNode.In
interface FloatingCallback {
@ -17,6 +18,6 @@ interface FloatingCallback {
fun onRefreshRecordingStatus(status:Int){}
fun onRefreshBitmap(bitmap:Bitmap){}
}

View File

@ -130,8 +130,8 @@ object FloatingWindowBridge {
if (isBound) {
Common.showLog("Service-=======unbindService 1111")
context.unbindService(connection)
service = null
mBinder = null
// service = null
// mBinder = null
isBound = false
}
}

View File

@ -1,7 +0,0 @@
package com.audio.record.screen.test.service
enum class RecordState {
DEFAULT,
RECORDING,
PAUSE }

View File

@ -40,8 +40,10 @@ import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import com.audio.record.screen.test.R
import com.audio.record.screen.test.activity.MainActivity1
import com.audio.record.screen.test.activity.PlayActivity
import com.audio.record.screen.test.activity.ScreenPermissionActivity
import com.audio.record.screen.test.activity.ScreenshotAnimActivity
import com.audio.record.screen.test.tool.Common
import com.audio.record.screen.test.tool.ConstValue
import com.audio.record.screen.test.tool.DraggableViewHelper
@ -161,10 +163,7 @@ class ScreenRecordService : Service() {
inner class FloatingBinder : Binder() {
fun getService() = this@ScreenRecordService
fun setCallback(cb: FloatingCallback) {
callbackRef = cb
}
fun registerCallback(callback: FloatingCallback) {
// 避免重复添加
@ -215,13 +214,14 @@ class ScreenRecordService : Service() {
}
ConstValue.type_record_audio -> {
Common.showLog("Service--- 录制带音频的视频")
isWithAudio = true
val arrayOf = arrayOf("3", "2", "1")
showCountDownView(arrayOf)
}
ConstValue.type_screenshot -> {
startScreenshot()
startScreenshot(isScreenshotViewAdded)
}
}
ballType = null
@ -262,11 +262,12 @@ class ScreenRecordService : Service() {
}
return NotificationCompat.Builder(this, channelId)
.setContentTitle("屏幕录制中")
.setContentText("正在录制屏幕...")
.setContentTitle("")
.setContentText("")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCustomContentView(remoteViews)
.setCustomBigContentView(remoteViews)
.setSmallIcon(R.drawable.test)
.setSmallIcon(R.mipmap.logo)
.build()
}
@ -274,18 +275,26 @@ class ScreenRecordService : Service() {
/**
* 开始截屏
*/
private fun startScreenshot(){
private fun startScreenshot(showView: Boolean) {
// TODO: 开始截屏
mIntent?.let { intent ->
mediaProjectionManager.getMediaProjection(mCode, intent)?.let { mediaProjection ->
hideScreenshot()
ScreenCaptureHelper.startScreenCapture(this, mediaProjection) {
val intent = Intent(this, ScreenshotAnimActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
ScreenCaptureHelper.startScreenCapture(this, mediaProjection) { bit ->
callbacks.forEach {
it.get()?.onRefreshBitmap(bit)
}
//截屏完成
if (showView)
showScreenshot()
}
}
}
}
@SuppressLint("ClickableViewAccessibility")
fun showCameraView() {
if (frontCameraView == null) {
@ -375,7 +384,7 @@ class ScreenRecordService : Service() {
}
Common.showLog("-------layoutParamsBall.x=${layoutParamsBall.x} layoutParamsBall.y=${layoutParamsBall.y}")
}
if (!isBallViewAdded) {
if (!isBallViewAdded&&!isBallExpandViewAdded) {
windowManager.addView(ballView, layoutParamsBall)
isBallViewAdded = true
}
@ -391,6 +400,17 @@ class ScreenRecordService : Service() {
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)
ballViewExpand!!.findViewById<ImageView>(R.id.ic_home).setOnClickListener {
val launchIntent = packageManager.getLaunchIntentForPackage(packageName)
launchIntent?.apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(this)
}
}
imRecord?.setOnClickListener {
it.isSelected.let { select ->
@ -509,7 +529,7 @@ class ScreenRecordService : Service() {
windowManager,
true
) {
startScreenshot()
startScreenshot(true)
}
}
if (!isScreenshotViewAdded) {
@ -633,12 +653,14 @@ class ScreenRecordService : Service() {
}
if (countdownView?.windowToken == null) {
Common.showLog("Service--- 录制带音频的视频 addView")
windowManager.addView(countdownView, layoutParamsCountDown)
val tvCountdown = countdownView!!.findViewById<TextView>(R.id.tv_countdown)
index = 0
countDownHandler = Handler(Looper.getMainLooper())
animateNext(countdownValues, tvCountdown) {
//倒计时结束后,开启录制
Common.showLog("Service--- 倒计时结束")
hideCountDown()
mIntent?.let { intent ->
mediaProjectionManager.getMediaProjection(mCode, intent)
@ -900,7 +922,7 @@ class ScreenRecordService : Service() {
fun getViewStatus() {
callbacks.forEach {
it.get()?.onRefreshViewShow(isWebcamViewAdded,isScreenshotViewAdded,isBallViewAdded)
it.get()?.onRefreshViewShow(isWebcamViewAdded, isScreenshotViewAdded, isBallViewAdded||isBallExpandViewAdded)
}
}
@ -909,6 +931,7 @@ class ScreenRecordService : Service() {
it.get()?.onRefreshRecordingStatus(mRecordingStatus)
}
}
fun stopRecording() {
// TODO: 录屏完成
mRecordingStatus = ConstValue.status_complete

View File

@ -0,0 +1,21 @@
package com.audio.record.screen.test.service
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import com.audio.record.screen.test.activity.ScreenPermissionActivity
import com.audio.record.screen.test.activity.ScreenshotAnimActivity
import com.audio.record.screen.test.tool.Common
object ScreenshotAnimExecutor {
var latestBitmap: Bitmap? = null
fun show(context: Context, bitmap: Bitmap) {
latestBitmap = bitmap
Common.showLog("-----show-------------")
val intent = Intent(context, ScreenshotAnimActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
}
}

View File

@ -1,5 +1,8 @@
package com.audio.record.screen.test.tool
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.ContentValues
import android.content.Context
import android.content.Intent
@ -23,6 +26,7 @@ import android.view.WindowManager
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import com.audio.record.screen.test.R
import com.audio.record.screen.test.service.ScreenshotAnimExecutor
import java.io.File
import java.io.FileOutputStream
@ -37,7 +41,7 @@ object ScreenCaptureHelper {
context: Context,
mediaProjection: MediaProjection,
folderName: String = Common.imagesFolderDir,
isOK:()->Unit
isOK:(bitmap:Bitmap)->Unit
) {
val metrics = Resources.getSystem().displayMetrics
val full = Common.getFullScreenSize(context)
@ -45,11 +49,11 @@ object ScreenCaptureHelper {
val height = full.second
val density = metrics.densityDpi
Common.showLog("startScreenCapture width=${width}, height=${height} density=${density} Thread=${Thread.currentThread().name}")
// Common.showLog("startScreenCapture width=${width}, height=${height} density=${density} Thread=${Thread.currentThread().name}")
val imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2)
mediaProjection.registerCallback(object : MediaProjection.Callback() {
override fun onStop() {
Common.showLog("startScreenCapture MediaProjection 被系统或用户停止")
// Common.showLog("startScreenCapture MediaProjection 被系统或用户停止")
// 这里应该释放 MediaRecorder 和 VirtualDisplay 等
virtualDisplay?.release()
mediaProjection.stop()
@ -67,9 +71,9 @@ object ScreenCaptureHelper {
val image = imageReader.acquireLatestImage()
if (image != null) {
val bitmap = imageToBitmap(image)
isOK.invoke(bitmap)
saveBitmap(context, bitmap, folderName)
showScreenshotPreviewAnimation(context, bitmap,width,height)
isOK.invoke()
image.close()
}
imageReader.close()
@ -165,14 +169,7 @@ object ScreenCaptureHelper {
).apply {
gravity = Gravity.TOP or Gravity.START
}
imageView.post {
Common.showLog("实际宽高 width=${imageView.width}, height=${imageView.height}")
}
Common.showLog("layoutParams layoutParams x=${layoutParams.x}, y=${layoutParams.y}")
windowManager.addView(inflate, layoutParams)
val scale = 0.3f
val targetWidth = (screenWidth * scale).toInt()
val targetHeight = (screenHeight * scale).toInt()
@ -180,19 +177,28 @@ object ScreenCaptureHelper {
val targetX = screenWidth - targetWidth - 20
val navigationBarHeight = Common.getNavigationBarHeight(context)
val statusBarHeight = Common.getStatusBarHeight(context)
Common.showLog("navigationBarHeight=${navigationBarHeight}, statusBarHeight=${statusBarHeight} ")
// Common.showLog("navigationBarHeight=${navigationBarHeight}, statusBarHeight=${statusBarHeight} ")
val targetY = screenHeight - targetHeight - navigationBarHeight - statusBarHeight - 20
Common.showLog("宽高 targetWidth=${targetWidth}, targetHeight=${targetHeight} targetX = ${targetX} targetY = ${targetY}")
// Common.showLog("宽高 targetWidth=${targetWidth}, targetHeight=${targetHeight} targetX = ${targetX} targetY = ${targetY}")
inflate.animate()
.scaleX(scale)
.scaleY(scale)
.setDuration(600)
.setInterpolator(DecelerateInterpolator())
.withEndAction {
val scaleX = ObjectAnimator.ofFloat(inflate, "scaleX", 1f, scale)
val scaleY = ObjectAnimator.ofFloat(inflate, "scaleY", 1f, scale)
val animatorSet = AnimatorSet()
animatorSet.playTogether(scaleX, scaleY)
animatorSet.duration = 50
animatorSet.interpolator = DecelerateInterpolator()
animatorSet.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {
Common.showLog("-----动画开始")
}
override fun onAnimationEnd(animation: Animator) {
// 更新 layoutParams 和结束逻辑...
Common.showLog("-----动画结束")
// 清除动画偏移
inflate.scaleX = 1f
inflate.scaleY = 1f
@ -217,8 +223,11 @@ object ScreenCaptureHelper {
}
}, 3000)
}
.start()
override fun onAnimationCancel(animation: Animator) {}
override fun onAnimationRepeat(animation: Animator) {}
})
animatorSet.start()
inflate.setOnClickListener {
Common.showLog("-----点击小截图")
}
@ -226,4 +235,87 @@ object ScreenCaptureHelper {
}
fun showScreenshotPreviewAnimationNew(context: Context, screenshotBitmap: Bitmap, screenWidth: Int, screenHeight: Int) {
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val inflate = LayoutInflater.from(context).inflate(R.layout.floating_screenshot_anim, null)
val imageView = inflate.findViewById<ImageView>(R.id.image)
imageView.setImageBitmap(screenshotBitmap)
// 初始全屏展示
val layoutParams = WindowManager.LayoutParams(
screenWidth,
screenHeight,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
else
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT
).apply {
gravity = Gravity.TOP or Gravity.START
x = 0
y = 0
}
windowManager.addView(inflate, layoutParams)
// 动画目标位置与缩放比例
val scale = 0.25f
val targetWidth = (screenWidth * scale).toInt()
val targetHeight = (screenHeight * scale).toInt()
val targetX = screenWidth - targetWidth - 32 // 留点边距
val targetY = screenHeight - targetHeight - Common.getNavigationBarHeight(context) - 32
// 设置原始位置(居中)
inflate.pivotX = 0f
inflate.pivotY = 0f
inflate.translationX = 0f
inflate.translationY = 0f
inflate.scaleX = 1f
inflate.scaleY = 1f
// 执行缩放+位移动画
inflate.animate()
.scaleX(scale)
.scaleY(scale)
.translationX(targetX.toFloat())
.translationY(targetY.toFloat())
.setDuration(500)
.setInterpolator(DecelerateInterpolator())
.withStartAction {
Common.showLog("📸 截图动画开始")
}
.withEndAction {
Common.showLog("✅ 截图动画结束:变成小图")
// 动画后清除 scale/translation 状态并更新窗口布局为小图位置
inflate.scaleX = 1f
inflate.scaleY = 1f
inflate.translationX = 0f
inflate.translationY = 0f
layoutParams.width = targetWidth
layoutParams.height = targetHeight
layoutParams.x = targetX
layoutParams.y = targetY
windowManager.updateViewLayout(inflate, layoutParams)
// 延时关闭
inflate.postDelayed({
try {
windowManager.removeView(inflate)
} catch (e: Exception) {
e.printStackTrace()
}
}, 3000)
}
.start()
inflate.setOnClickListener {
Common.showLog("🖼️ 点击了截图缩略图")
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_welcome"
tools:context=".activity.WelcomeActivity">
<ImageView
android:id="@+id/logo"
android:layout_width="157dp"
android:layout_height="157dp"
android:layout_centerInParent="true"
android:src="@drawable/bg_welcome_icon" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/logo"
android:layout_centerHorizontal="true"
android:layout_marginTop="11dp"
android:text="@string/app_name"
android:textColor="@color/black"
android:textSize="18sp" />
</RelativeLayout>

View File

@ -2,7 +2,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_date"

View File

@ -8,9 +8,9 @@
<androidx.cardview.widget.CardView
android:id="@+id/card_img"
android:layout_width="92dp"
android:layout_marginBottom="8dp"
android:layout_marginStart="5dp"
android:layout_height="72dp"
android:layout_marginStart="5dp"
android:layout_marginBottom="8dp"
app:cardCornerRadius="7dp">
@ -61,6 +61,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@string/app_name"
android:visibility="gone"
android:textColor="@color/color_8D8D8D"
android:textSize="11sp" />
@ -69,9 +70,15 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:layout_marginTop="6dp"
android:textColor="@color/color_8D8D8D"
android:textSize="11sp" />
</LinearLayout>
<View
android:id="@+id/space"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_below="@id/card_img" />
</RelativeLayout>

View File

@ -10,98 +10,105 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:text="@string/app_name"
android:textColor="@color/black"
android:id="@+id/title"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_marginStart="5dp"
android:text="@string/Running"
android:textColor="@color/black"
android:id="@+id/text"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<!-- <LinearLayout-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:gravity="center_horizontal"-->
<!-- android:orientation="vertical">-->
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/test" />
<!-- <ImageView-->
<!-- android:layout_width="25dp"-->
<!-- android:layout_height="25dp"-->
<!-- android:src="@drawable/status_ball_record" />-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="录制" />
</LinearLayout>
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="录制" />-->
<!-- </LinearLayout>-->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<!-- <LinearLayout-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:gravity="center_horizontal"-->
<!-- android:orientation="vertical">-->
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/test" />
<!-- <ImageView-->
<!-- android:layout_width="25dp"-->
<!-- android:layout_height="25dp"-->
<!-- android:src="@drawable/status_ball_record_three" />-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="截屏" />
</LinearLayout>
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="截屏" />-->
<!-- </LinearLayout>-->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<!-- <LinearLayout-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:gravity="center_horizontal"-->
<!-- android:orientation="vertical">-->
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/test" />
<!-- <ImageView-->
<!-- android:layout_width="25dp"-->
<!-- android:layout_height="25dp"-->
<!-- android:src="@drawable/ball_screenshot" />-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="工具" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="工具" />-->
<!-- </LinearLayout>-->
<!-- <LinearLayout-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:gravity="center_horizontal"-->
<!-- android:orientation="vertical">-->
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/test" />
<!-- <ImageView-->
<!-- android:layout_width="25dp"-->
<!-- android:layout_height="25dp"-->
<!-- android:src="@drawable/ball_home" />-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="首页" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="首页" />-->
<!-- </LinearLayout>-->
<!-- <LinearLayout-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:gravity="center_horizontal"-->
<!-- android:orientation="vertical">-->
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/test" />
<!-- <ImageView-->
<!-- android:layout_width="25dp"-->
<!-- android:layout_height="25dp"-->
<!-- android:src="@drawable/test" />-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="退出" />
</LinearLayout>
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="退出" />-->
<!-- </LinearLayout>-->
</LinearLayout>

View File

@ -1,12 +1,21 @@
<?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">
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY" />
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/Screenshot_please_wait"
android:textColor="@color/black"
android:textSize="21sp" />
</FrameLayout>

View File

@ -10,7 +10,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="31dp"
android:text="@string/Collection"
android:text="@string/app_name"
android:textColor="@color/black"
android:textSize="39sp"
app:layout_constraintLeft_toLeftOf="parent"

View File

@ -11,7 +11,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="31dp"
android:text="@string/Collection"
android:text="@string/app_name"
android:textColor="@color/black"
android:textSize="39sp"
app:layout_constraintLeft_toLeftOf="parent"

View File

@ -15,6 +15,7 @@
android:text="@string/app_name"
android:textColor="@color/title_color"
android:textSize="28sp"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View File

@ -76,7 +76,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/video_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/layout_recent"
android:layout_marginTop="10dp" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -19,6 +19,8 @@ through the notification panel</string>
<string name="there_s_not">There\'s nothing here.</string>
<string name="Recent">Recent</string>
<string name="New">New</string>
<string name="Running">Running...</string>
<string name="Screenshot_please_wait">Screenshot, please wait...</string>
<string-array name="markArray5">
<item>-100</item>
<item>100</item>