✨
This commit is contained in:
@@ -4,7 +4,8 @@ import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import devs.org.calculator.adapters.ImagePreviewAdapter
|
||||
import devs.org.calculator.callbacks.DialogActionsCallback
|
||||
import devs.org.calculator.databinding.ActivityPreviewBinding
|
||||
@@ -12,6 +13,7 @@ import devs.org.calculator.utils.DialogUtil
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import devs.org.calculator.R
|
||||
|
||||
class PreviewActivity : AppCompatActivity() {
|
||||
|
||||
@@ -39,6 +41,14 @@ class PreviewActivity : AppCompatActivity() {
|
||||
|
||||
setupImagePreview()
|
||||
clickListeners()
|
||||
|
||||
binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private fun setupFileType() {
|
||||
@@ -73,6 +83,25 @@ class PreviewActivity : AppCompatActivity() {
|
||||
val fileName = FileManager.FileName(this).getFileNameFromUri(fileUri).toString()
|
||||
}
|
||||
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (filetype == FileManager.FileType.AUDIO) {
|
||||
(binding.viewPager.adapter as? ImagePreviewAdapter)?.currentMediaPlayer?.pause()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (filetype == FileManager.FileType.AUDIO) {
|
||||
(binding.viewPager.adapter as? ImagePreviewAdapter)?.let { adapter ->
|
||||
adapter.currentMediaPlayer?.release()
|
||||
adapter.currentMediaPlayer = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun clickListeners() {
|
||||
binding.delete.setOnClickListener {
|
||||
val fileUri = FileManager.FileManager().getContentUriImage(this, files[binding.viewPager.currentItem], filetype)
|
||||
@@ -147,4 +176,6 @@ class PreviewActivity : AppCompatActivity() {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -82,11 +82,42 @@ class FileAdapter(
|
||||
}
|
||||
itemView.setOnClickListener {
|
||||
|
||||
val intent = Intent(context, PreviewActivity::class.java).apply {
|
||||
putExtra("type", fileTypes)
|
||||
putExtra("position", position)
|
||||
|
||||
when(fileType){
|
||||
FileManager.FileType.AUDIO -> {
|
||||
// Create an intent to play audio using available audio players
|
||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||
setDataAndType(FileManager.FileManager().getContentUriImage(context, file, fileType), "audio/*")
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(context, "No audio player found!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
FileManager.FileType.DOCUMENT -> {
|
||||
// Create an intent to open the document using available viewers or file managers
|
||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||
setDataAndType(FileManager.FileManager().getContentUriImage(context, file, fileType), "*/*")
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(context, "No suitable app found to open this document!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val intent = Intent(context, PreviewActivity::class.java).apply {
|
||||
putExtra("type", fileTypes)
|
||||
putExtra("position", position)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
context.startActivity(intent)
|
||||
|
||||
|
||||
}
|
||||
itemView.setOnLongClickListener {
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package devs.org.calculator.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.media.MediaPlayer
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.MediaController
|
||||
import android.widget.SeekBar
|
||||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
@@ -13,16 +17,20 @@ import devs.org.calculator.adapters.FileAdapter.FileDiffCallback
|
||||
import devs.org.calculator.databinding.ViewpagerItemsBinding
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import java.io.File
|
||||
import devs.org.calculator.R
|
||||
|
||||
class ImagePreviewAdapter(
|
||||
private val context: Context,
|
||||
private var fileType: FileManager.FileType
|
||||
) : RecyclerView.Adapter<ImagePreviewAdapter.ImageViewHolder>() {
|
||||
|
||||
// Use AsyncListDiffer for managing the list
|
||||
private val differ = AsyncListDiffer(this, FileDiffCallback())
|
||||
var currentMediaPlayer: MediaPlayer? = null
|
||||
var isMediaPlayerPrepared = false
|
||||
var currentViewHolder: ImageViewHolder? = null
|
||||
private var currentPlayingPosition = -1
|
||||
private var isPlaying = false
|
||||
|
||||
// Expose data management through differ
|
||||
var images: List<File>
|
||||
get() = differ.currentList
|
||||
set(value) = differ.submitList(value)
|
||||
@@ -35,15 +43,35 @@ class ImagePreviewAdapter(
|
||||
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
|
||||
val imageUrl = images[position]
|
||||
holder.bind(imageUrl)
|
||||
currentViewHolder = holder
|
||||
|
||||
currentMediaPlayer?.let {
|
||||
if (it.isPlaying) it.pause()
|
||||
it.seekTo(0)
|
||||
}
|
||||
currentMediaPlayer = null
|
||||
isMediaPlayerPrepared = false
|
||||
|
||||
if (currentMediaPlayer?.isPlaying == true) {
|
||||
currentMediaPlayer?.stop()
|
||||
currentMediaPlayer?.release()
|
||||
}
|
||||
currentMediaPlayer = null
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = images.size
|
||||
|
||||
inner class ImageViewHolder(val binding: ViewpagerItemsBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
inner class ImageViewHolder(private val binding: ViewpagerItemsBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
private var mediaPlayer: MediaPlayer? = null
|
||||
private var seekHandler = Handler(Looper.getMainLooper())
|
||||
private var seekRunnable: Runnable? = null
|
||||
|
||||
fun bind(file: File) {
|
||||
when (fileType) {
|
||||
FileManager.FileType.VIDEO -> {
|
||||
binding.imageView.visibility = View.GONE
|
||||
binding.audioBg.visibility = View.GONE
|
||||
binding.videoView.visibility = View.VISIBLE
|
||||
|
||||
val videoUri = Uri.fromFile(file)
|
||||
@@ -73,19 +101,131 @@ class ImagePreviewAdapter(
|
||||
FileManager.FileType.IMAGE -> {
|
||||
binding.imageView.visibility = View.VISIBLE
|
||||
binding.videoView.visibility = View.GONE
|
||||
binding.audioBg.visibility = View.GONE
|
||||
Glide.with(context)
|
||||
.load(file)
|
||||
.into(binding.imageView)
|
||||
}
|
||||
FileManager.FileType.AUDIO -> {
|
||||
// Handle audio if necessary
|
||||
binding.imageView.visibility = View.GONE
|
||||
binding.audioBg.visibility = View.VISIBLE
|
||||
binding.videoView.visibility = View.GONE
|
||||
binding.audioTitle.text = file.name
|
||||
|
||||
setupAudioPlayer(file)
|
||||
setupSeekBar()
|
||||
setupPlaybackControls()
|
||||
}
|
||||
else -> {
|
||||
// Handle other types if necessary
|
||||
binding.imageView.visibility = View.VISIBLE
|
||||
binding.audioBg.visibility = View.GONE
|
||||
binding.videoView.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAudioPlayer(file: File) {
|
||||
mediaPlayer = MediaPlayer().apply {
|
||||
setDataSource(file.absolutePath)
|
||||
setOnPreparedListener { mp ->
|
||||
binding.audioSeekBar.max = mp.duration
|
||||
isMediaPlayerPrepared = true
|
||||
}
|
||||
setOnCompletionListener {
|
||||
// isPlaying = false
|
||||
binding.playPause.setImageResource(R.drawable.play)
|
||||
binding.audioSeekBar.progress = 0
|
||||
seekHandler.removeCallbacks(seekRunnable!!)
|
||||
}
|
||||
prepareAsync()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupSeekBar() {
|
||||
binding.audioSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
mediaPlayer?.seekTo(progress)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
|
||||
})
|
||||
|
||||
seekRunnable = Runnable {
|
||||
mediaPlayer?.let { mp ->
|
||||
if (mp.isPlaying) {
|
||||
binding.audioSeekBar.progress = mp.currentPosition
|
||||
seekHandler.postDelayed(seekRunnable!!, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupPlaybackControls() {
|
||||
binding.playPause.setOnClickListener {
|
||||
if (isPlaying) {
|
||||
pauseAudio()
|
||||
} else {
|
||||
playAudio()
|
||||
}
|
||||
}
|
||||
|
||||
binding.preview.setOnClickListener {
|
||||
mediaPlayer?.let { mp ->
|
||||
val newPosition = mp.currentPosition - 10000
|
||||
mp.seekTo(maxOf(0, newPosition))
|
||||
binding.audioSeekBar.progress = mp.currentPosition
|
||||
}
|
||||
}
|
||||
|
||||
binding.next.setOnClickListener {
|
||||
mediaPlayer?.let { mp ->
|
||||
val newPosition = mp.currentPosition + 10000
|
||||
mp.seekTo(minOf(mp.duration, newPosition))
|
||||
binding.audioSeekBar.progress = mp.currentPosition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun playAudio() {
|
||||
mediaPlayer?.let { mp ->
|
||||
if (currentPlayingPosition != -1 && currentPlayingPosition != adapterPosition) {
|
||||
currentViewHolder?.pauseAudio()
|
||||
}
|
||||
mp.start()
|
||||
isPlaying = true
|
||||
binding.playPause.setImageResource(R.drawable.pause)
|
||||
seekHandler.post(seekRunnable!!)
|
||||
currentPlayingPosition = adapterPosition
|
||||
currentViewHolder = this
|
||||
}
|
||||
}
|
||||
|
||||
private fun pauseAudio() {
|
||||
mediaPlayer?.let { mp ->
|
||||
if (mp.isPlaying) {
|
||||
mp.pause()
|
||||
isPlaying = false
|
||||
binding.playPause.setImageResource(R.drawable.play)
|
||||
seekHandler.removeCallbacks(seekRunnable!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun releaseMediaPlayer() {
|
||||
mediaPlayer?.let { mp ->
|
||||
if (mp.isPlaying) {
|
||||
mp.stop()
|
||||
}
|
||||
mp.release()
|
||||
mediaPlayer = null
|
||||
isPlaying = false
|
||||
seekHandler.removeCallbacks(seekRunnable!!)
|
||||
}
|
||||
}
|
||||
|
||||
private fun playVideoAtPosition(position: Int) {
|
||||
val nextFile = images[position]
|
||||
if (fileType == FileManager.FileType.VIDEO) {
|
||||
@@ -95,5 +235,10 @@ class ImagePreviewAdapter(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewRecycled(holder: ImageViewHolder) {
|
||||
super.onViewRecycled(holder)
|
||||
holder.releaseMediaPlayer()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
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_gravity="center"
|
||||
android:layout_margin="4dp"
|
||||
app:cardCornerRadius="8dp">
|
||||
|
||||
@@ -15,6 +17,7 @@
|
||||
app:zoomage_animateOnReset="true"
|
||||
app:zoomage_autoResetMode="UNDER"
|
||||
app:zoomage_autoCenter="true"
|
||||
android:visibility="gone"
|
||||
app:zoomage_zoomable="true"
|
||||
app:zoomage_translatable="true"
|
||||
app:zoomage_minScale="0.6"
|
||||
@@ -24,6 +27,99 @@
|
||||
<VideoView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/videoView"/>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/audioBg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="20dp"
|
||||
style="@style/Widget.Material3.CardView.Outlined">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="8dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_margin="5dp"
|
||||
android:src="@drawable/music"
|
||||
android:padding="3dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="audio.mp3"
|
||||
android:layout_margin="5dp"
|
||||
android:id="@+id/audioTitle"/>
|
||||
<SeekBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:id="@+id/audioSeekBar"/>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:src="@drawable/previous"
|
||||
android:padding="3dp"
|
||||
android:id="@+id/preview"/>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="40dp"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_height="40dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:src="@drawable/play"
|
||||
android:padding="3dp"
|
||||
android:id="@+id/playPause"/>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:padding="3dp"
|
||||
android:src="@drawable/next"
|
||||
android:id="@+id/next"/>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -5,7 +5,6 @@
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<item name="android:background">@color/black</item>
|
||||
<!-- Secondary brand color -->
|
||||
<item name="colorSecondary">@color/colorSecondary</item>
|
||||
<item name="colorSecondaryVariant">@color/colorSecondary</item>
|
||||
|
||||
Reference in New Issue
Block a user