Skip to content

Commit

Permalink
Ask if user wants to download photo again
Browse files Browse the repository at this point in the history
  • Loading branch information
b-lam committed Jun 30, 2020
1 parent 0d8d42b commit 6c79618
Show file tree
Hide file tree
Showing 21 changed files with 177 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,14 @@ import android.content.Intent
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.SafeJobIntentService
import androidx.core.content.FileProvider
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.b_lam.resplash.BuildConfig
import com.b_lam.resplash.data.download.DownloadService
import com.b_lam.resplash.util.NotificationManager
import com.b_lam.resplash.util.error
import com.b_lam.resplash.util.info
import com.b_lam.resplash.util.safeApiCall
import com.b_lam.resplash.util.*
import kotlinx.coroutines.*
import okhttp3.ResponseBody
import okio.buffer
Expand Down Expand Up @@ -141,8 +136,7 @@ class DownloadJobIntentService : SafeJobIntentService(), CoroutineScope by MainS
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
put(MediaStore.Images.Media.SIZE, contentLength())
put(MediaStore.Images.Media.RELATIVE_PATH,
"${Environment.DIRECTORY_PICTURES}${File.separator}$RESPLASH_DIRECTORY")
put(MediaStore.Images.Media.RELATIVE_PATH, RELATIVE_PATH)
put(MediaStore.Images.Media.IS_PENDING, 1)
}

Expand Down Expand Up @@ -187,9 +181,7 @@ class DownloadJobIntentService : SafeJobIntentService(), CoroutineScope by MainS
fileName: String,
onProgress: ((Int) -> Unit)?
): Uri? {
val path = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
RESPLASH_DIRECTORY)
val path = File(LEGACY_PATH)

if (!path.exists()) {
if (!path.mkdirs()) {
Expand Down Expand Up @@ -223,15 +215,13 @@ class DownloadJobIntentService : SafeJobIntentService(), CoroutineScope by MainS
MediaScannerConnection.scanFile(context, arrayOf(file.absolutePath),
arrayOf("image/jpeg"), null)

return FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.fileprovider", file)
return FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, file)
}

companion object {

private const val DOWNLOAD_JOB_ID = 4444

private const val RESPLASH_DIRECTORY = "Resplash"

private const val EXTRA_ACTION = "extra_action"
private const val EXTRA_FILE_NAME = "extra_file_name"
private const val EXTRA_URL = "extra_url"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.b_lam.resplash.ui.photo

import android.animation.Animator
import android.view.View
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
Expand Down Expand Up @@ -36,19 +35,7 @@ class DefaultPhotoViewHolder(parent: View) : RecyclerView.ViewHolder(parent) {
photo_image_view.setOnClickListener { callback.onPhotoClick(photo) }
if (longPressDownload) {
photo_image_view.setOnLongClickListener {
callback.onLongClick(photo)
check_animation_view.isVisible = true
check_animation_view.playAnimation()
check_animation_view.addAnimatorListener(object :
Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {}
override fun onAnimationCancel(animation: Animator?) {}
override fun onAnimationStart(animation: Animator?) {}
override fun onAnimationEnd(animation: Animator?) {
check_animation_view.removeAnimatorListener(this)
check_animation_view.isVisible = false
}
})
callback.onLongClick(photo, check_animation_view)
true
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.b_lam.resplash.ui.photo

import android.animation.Animator
import android.view.View
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.b_lam.resplash.data.photo.model.Photo
import com.b_lam.resplash.util.getPhotoUrl
Expand All @@ -26,19 +24,7 @@ class GridPhotoViewHolder(parent: View) : RecyclerView.ViewHolder(parent) {
photo_image_view.setOnClickListener { callback.onPhotoClick(photo) }
if (longPressDownload) {
photo_image_view.setOnLongClickListener {
callback.onLongClick(photo)
check_animation_view.isVisible = true
check_animation_view.playAnimation()
check_animation_view.addAnimatorListener(object :
Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {}
override fun onAnimationCancel(animation: Animator?) {}
override fun onAnimationStart(animation: Animator?) {}
override fun onAnimationEnd(animation: Animator?) {
check_animation_view.removeAnimatorListener(this)
check_animation_view.isVisible = false
}
})
callback.onLongClick(photo, check_animation_view)
true
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.b_lam.resplash.ui.photo

import android.animation.Animator
import android.view.View
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.b_lam.resplash.data.photo.model.Photo
import com.b_lam.resplash.util.getPhotoUrl
Expand All @@ -26,19 +24,7 @@ class MinimalPhotoViewHolder(parent: View) : RecyclerView.ViewHolder(parent) {
photo_image_view.setOnClickListener { callback.onPhotoClick(photo) }
if (longPressDownload) {
photo_image_view.setOnLongClickListener {
callback.onLongClick(photo)
check_animation_view.isVisible = true
check_animation_view.playAnimation()
check_animation_view.addAnimatorListener(object :
Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {}
override fun onAnimationCancel(animation: Animator?) {}
override fun onAnimationStart(animation: Animator?) {}
override fun onAnimationEnd(animation: Animator?) {
check_animation_view.removeAnimatorListener(this)
check_animation_view.isVisible = false
}
})
callback.onLongClick(photo, check_animation_view)
true
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.airbnb.lottie.LottieAnimationView
import com.b_lam.resplash.R
import com.b_lam.resplash.data.photo.model.Photo
import com.b_lam.resplash.data.user.model.User
Expand Down Expand Up @@ -72,7 +73,7 @@ class PhotoAdapter(

fun onPhotoClick(photo: Photo)
fun onUserClick(user: User)
fun onLongClick(photo: Photo)
fun onLongClick(photo: Photo, animationView: LottieAnimationView)
}

companion object {
Expand Down
40 changes: 33 additions & 7 deletions app/src/main/java/com/b_lam/resplash/ui/photo/PhotoFragment.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.b_lam.resplash.ui.photo

import android.Manifest
import android.animation.Animator
import android.content.Intent
import androidx.core.view.isVisible
import com.airbnb.lottie.LottieAnimationView
import com.b_lam.resplash.R
import com.b_lam.resplash.data.photo.model.Photo
import com.b_lam.resplash.data.user.model.User
Expand Down Expand Up @@ -31,18 +34,41 @@ abstract class PhotoFragment : BaseSwipeRecyclerViewFragment<Photo>() {
}
}

override fun onLongClick(photo: Photo) {
if (requireContext().hasWritePermission()) {
context.toast(R.string.download_started)
DownloadJobIntentService.enqueueDownload(requireActivity().applicationContext,
DownloadJobIntentService.Companion.Action.DOWNLOAD, photo.fileName,
getPhotoUrl(photo, sharedPreferencesRepository.downloadQuality), photo.id)
override fun onLongClick(photo: Photo, animationView: LottieAnimationView) {
if (requireContext().fileExists(photo.fileName)) {
showFileExistsDialog(requireContext()) { downloadPhoto(photo, animationView) }
} else {
requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, requestCode = 0)
downloadPhoto(photo, animationView)
}
}
}

private fun downloadPhoto(photo: Photo, animationView: LottieAnimationView) {
if (requireContext().hasWritePermission()) {
context.toast(R.string.download_started)
animateLongClickDownload(animationView)
DownloadJobIntentService.enqueueDownload(requireActivity().applicationContext,
DownloadJobIntentService.Companion.Action.DOWNLOAD, photo.fileName,
getPhotoUrl(photo, sharedPreferencesRepository.downloadQuality), photo.id)
} else {
requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, requestCode = 0)
}
}

private fun animateLongClickDownload(animationView: LottieAnimationView) {
animationView.isVisible = true
animationView.playAnimation()
animationView.addAnimatorListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {}
override fun onAnimationCancel(animation: Animator?) {}
override fun onAnimationStart(animation: Animator?) {}
override fun onAnimationEnd(animation: Animator?) {
animationView.removeAnimatorListener(this)
animationView.isVisible = false
}
})
}

override val emptyStateTitle: String
get() = getString(R.string.empty_state_title)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,48 @@ class PhotoDetailActivity : BaseActivity(), TagAdapter.ItemEventCallback {
startActivity(Intent(this, LoginActivity::class.java))
}
}
download_button.setOnClickListener { downloadPhoto(photo) }
set_as_wallpaper_button.setOnClickListener { setWallpaper(photo) }
download_button.setOnClickListener {
if (fileExists(photo.fileName)) {
showFileExistsDialog(this) { downloadPhoto(photo) }
} else {
downloadPhoto(photo)
}
}
set_as_wallpaper_button.setOnClickListener {
if (fileExists(photo.fileName)) {
getUriForPhoto(photo.fileName)?.let { uri ->
applyWallpaper(uri)
} ?: run {
downloadWallpaper(photo)
}
} else {
downloadWallpaper(photo)
}
}
set_as_wallpaper_button.show()
}

override fun onTagClick(tag: String) {
Intent(this, SearchActivity::class.java).apply {
putExtra(SearchActivity.EXTRA_SEARCH_QUERY, tag)
startActivity(this)
}
}

private fun setLikeButtonState(likedByUser: Boolean) {
like_button.setImageResource(
if (likedByUser) R.drawable.ic_favorite_filled_24dp
else R.drawable.ic_favorite_border_24dp
)
}

private fun setCollectButtonState(currentUserCollectionIds: List<Int>) {
collect_button.setImageResource(
if (currentUserCollectionIds.isNotEmpty()) R.drawable.ic_bookmark_filled_24dp
else R.drawable.ic_bookmark_border_24dp
)
}

private fun downloadPhoto(photo: Photo) {
if (hasWritePermission()) {
toast(R.string.download_started)
Expand All @@ -218,7 +255,7 @@ class PhotoDetailActivity : BaseActivity(), TagAdapter.ItemEventCallback {
}
}

private fun setWallpaper(photo: Photo) {
private fun downloadWallpaper(photo: Photo) {
if (hasWritePermission()) {
DownloadJobIntentService.enqueueDownload(applicationContext,
DownloadJobIntentService.Companion.Action.WALLPAPER, photo.fileName,
Expand All @@ -233,27 +270,6 @@ class PhotoDetailActivity : BaseActivity(), TagAdapter.ItemEventCallback {
}
}

override fun onTagClick(tag: String) {
Intent(this, SearchActivity::class.java).apply {
putExtra(SearchActivity.EXTRA_SEARCH_QUERY, tag)
startActivity(this)
}
}

private fun setLikeButtonState(likedByUser: Boolean) {
like_button.setImageResource(
if (likedByUser) R.drawable.ic_favorite_filled_24dp
else R.drawable.ic_favorite_border_24dp
)
}

private fun setCollectButtonState(currentUserCollectionIds: List<Int>) {
collect_button.setImageResource(
if (currentUserCollectionIds.isNotEmpty()) R.drawable.ic_bookmark_filled_24dp
else R.drawable.ic_bookmark_border_24dp
)
}

private fun handleDownloadIntent(intent: Intent) {
val action = intent.getSerializableExtra(DownloadJobIntentService.DATA_ACTION)
as? DownloadJobIntentService.Companion.Action
Expand All @@ -263,7 +279,7 @@ class PhotoDetailActivity : BaseActivity(), TagAdapter.ItemEventCallback {
if (success && action == DownloadJobIntentService.Companion.Action.WALLPAPER) {
snackbar?.dismiss()
intent.getParcelableExtra<Uri>(DownloadJobIntentService.DATA_URI)?.let {
setWallpaper(it)
applyWallpaper(it)
}
} else if (success && action == DownloadJobIntentService.Companion.Action.DOWNLOAD) {
toast(R.string.download_complete)
Expand All @@ -275,7 +291,7 @@ class PhotoDetailActivity : BaseActivity(), TagAdapter.ItemEventCallback {
}
}

private fun setWallpaper(uri: Uri) {
private fun applyWallpaper(uri: Uri) {
try {
startActivity(WallpaperManager.getInstance(this).getCropAndSetWallpaperIntent(uri))
} catch (e: IllegalArgumentException) {
Expand Down
67 changes: 67 additions & 0 deletions app/src/main/java/com/b_lam/resplash/util/FileUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.b_lam.resplash.util

import android.content.ContentUris
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import androidx.core.content.FileProvider
import com.b_lam.resplash.BuildConfig
import com.b_lam.resplash.R
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.File

const val RESPLASH_DIRECTORY = "Resplash"

const val FILE_PROVIDER_AUTHORITY = "${BuildConfig.APPLICATION_ID}.fileprovider"

val RELATIVE_PATH = "${Environment.DIRECTORY_PICTURES}${File.separator}$RESPLASH_DIRECTORY"

val LEGACY_PATH = "${Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)}${File.separator}$RESPLASH_DIRECTORY"

fun Context.fileExists(fileName: String): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val projection = arrayOf(MediaStore.Images.Media.DISPLAY_NAME)
val selection = "${MediaStore.Images.Media.RELATIVE_PATH} like ? and " +
"${MediaStore.Images.Media.DISPLAY_NAME} = ?"
val selectionArgs = arrayOf("%$RELATIVE_PATH%", fileName)
contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection,
selectionArgs, null)?.use {
return it.count > 0
} ?: return false
} else {
return File(LEGACY_PATH, fileName).exists()
}
}

fun Context.getUriForPhoto(fileName: String): Uri? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val projection = arrayOf(MediaStore.Images.Media._ID)
val selection = "${MediaStore.Images.Media.RELATIVE_PATH} like ? and " +
"${MediaStore.Images.Media.DISPLAY_NAME} = ?"
val selectionArgs = arrayOf("%$RELATIVE_PATH%", fileName)
contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection,
selectionArgs, null)?.use {
return if (it.moveToFirst()) {
ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
it.getLong(it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)))
} else {
null
}
} ?: return null
} else {
val file = File(LEGACY_PATH, fileName)
return FileProvider.getUriForFile(this, FILE_PROVIDER_AUTHORITY, file)
}
}

fun showFileExistsDialog(context: Context, action: () -> Unit) {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.file_exists_title)
.setMessage(R.string.file_exists_message)
.setPositiveButton(R.string.yes) { _, _ -> action.invoke() }
.setNegativeButton(R.string.no, null)
.show()
}
Loading

0 comments on commit 6c79618

Please sign in to comment.