Merge branch 'open_photo' into 'master'

Open photo

Closes #321

See merge request pixeldroid/PixelDroid!428
This commit is contained in:
Matthieu 2022-06-09 17:44:01 +00:00
commit 1a518ad492
12 changed files with 294 additions and 122 deletions

View File

@ -13,7 +13,7 @@ jacoco.toolVersion = "0.8.7"
android {
compileSdkVersion 31
compileSdkVersion 32
buildToolsVersion '31.0.0'
compileOptions {
coreLibraryDesugaringEnabled true
@ -27,7 +27,7 @@ android {
defaultConfig {
applicationId "org.pixeldroid.app"
minSdkVersion 23
targetSdkVersion 31
targetSdkVersion 32
versionCode 10
versionName "1.0.beta" + versionCode
@ -106,17 +106,17 @@ dependencies {
/**
* AndroidX dependencies:
*/
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.2'
implementation "androidx.browser:browser:1.4.0"
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.2'
implementation 'androidx.paging:paging-runtime-ktx:3.1.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
@ -133,7 +133,7 @@ dependencies {
// Use the most recent version of CameraX
def cameraX_version = '1.1.0-beta02'
def cameraX_version = '1.1.0-rc01'
implementation "androidx.camera:camera-core:$cameraX_version"
implementation "androidx.camera:camera-camera2:$cameraX_version"
// CameraX Lifecycle library
@ -142,6 +142,8 @@ dependencies {
// CameraX View class
implementation "androidx.camera:camera-view:$cameraX_version"
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
@ -154,7 +156,7 @@ dependencies {
*/
implementation 'com.google.android.material:material:1.5.0'
implementation 'com.google.android.material:material:1.6.1'
//Dagger (dependency injection)
implementation 'com.google.dagger:dagger-android:2.40.5'
@ -163,7 +165,7 @@ dependencies {
kapt 'com.google.dagger:dagger-android-processor:2.40.5'
kapt 'com.google.dagger:dagger-compiler:2.40.5'
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
@ -227,7 +229,7 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0'
androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.2'
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.9.0'
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.9.2'
}

View File

@ -6,7 +6,6 @@ import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.openLinkWithText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition
import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition
@ -24,9 +23,6 @@ import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.rules.Timeout
import org.junit.runner.Description
import org.junit.runner.RunWith
import org.junit.runners.model.Statement
@ -68,22 +64,22 @@ class HomeFeedTest {
@RepeatTest
fun clickingTabOnAlbumShowsNextPhoto() {
//Wait for the feed to load
waitForView(R.id.postPager)
waitForView(R.id.albumPager)
activityScenario.onActivity {
a -> run {
//Pick the second photo
a.findViewById<ViewPager2>(R.id.postPager).currentItem = 2
a.findViewById<ViewPager2>(R.id.albumPager).currentItem = 2
}
}
onView(first(withId(R.id.postPager))).check(matches(isDisplayed()))
onView(first(withId(R.id.albumPager))).check(matches(isDisplayed()))
}
@Test
@RepeatTest
fun tabReClickScrollUp() {
//Wait for the feed to load
waitForView(R.id.postPager)
waitForView(R.id.albumPager)
onView(withId(R.id.list)).perform(scrollToPosition<StatusViewHolder>(4))
@ -97,7 +93,7 @@ class HomeFeedTest {
@RepeatTest
fun hashtag() {
//Wait for the feed to load
waitForView(R.id.postPager)
waitForView(R.id.albumPager)
onView(allOf(withClassName(endsWith("RecyclerView")), not(withId(R.id.material_drawer_recycler_view))))
.perform(

View File

@ -23,9 +23,13 @@
android:theme="@style/AppTheme"
tools:replace="android:allowBackup">
<activity
android:name=".posts.MediaViewerActivity"
android:name=".posts.AlbumActivity"
android:exported="false"
android:theme="@style/AppTheme.ActionBar.Transparent"/>
<activity
android:name=".posts.MediaViewerActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="false"
android:theme="@style/AppTheme.NoActionBar" />
<activity android:name=".postCreation.camera.CameraActivity" />
<activity

View File

@ -0,0 +1,41 @@
package org.pixeldroid.app.posts
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import org.pixeldroid.app.databinding.ActivityAlbumBinding
import org.pixeldroid.app.utils.BaseActivity
import org.pixeldroid.app.utils.api.objects.Attachment
class AlbumActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityAlbumBinding.inflate(layoutInflater)
setContentView(binding.root)
val mediaAttachments = intent.getSerializableExtra("images") as ArrayList<Attachment>
val index = intent.getIntExtra("index", 0)
binding.albumPager.adapter = AlbumViewPagerAdapter(mediaAttachments,
sensitive = false,
opened = true
)
binding.albumPager.currentItem = index
if(mediaAttachments.size == 1){
binding.albumPager.isUserInputEnabled = false
}
else if((mediaAttachments.size) > 1) {
binding.postIndicator.setViewPager(binding.albumPager)
binding.postIndicator.visibility = View.VISIBLE
} else {
binding.postIndicator.visibility = View.GONE
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowTitleEnabled(false)
supportActionBar?.setBackgroundDrawable(null)
window.statusBarColor = ContextCompat.getColor(this,android.R.color.transparent);
}
}

View File

@ -7,6 +7,7 @@ import android.media.AudioManager.STREAM_MUSIC
import android.os.Bundle
import androidx.core.net.toUri
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat.getInsetsController
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.media.AudioAttributesCompat
@ -50,19 +51,19 @@ class MediaViewerActivity : BaseActivity() {
mediaPlayer.setMediaItem(mediaItem)
binding.videoView.mediaControlView?.setOnFullScreenListener{ view, fullscreen ->
val windowInsetsController = ViewCompat.getWindowInsetsController(window.decorView)
val windowInsetsController = getInsetsController(window, window.decorView)
if (!fullscreen) {
// Configure the behavior of the hidden system bars
windowInsetsController?.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// Hide both the status bar and the navigation bar
windowInsetsController?.show(WindowInsetsCompat.Type.systemBars())
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
supportActionBar?.show()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
} else {
// Configure the behavior of the hidden system bars
windowInsetsController?.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// Hide both the status bar and the navigation bar
windowInsetsController?.hide(WindowInsetsCompat.Type.systemBars())
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
requestedOrientation =
if (mediaPlayer.videoSize.height < mediaPlayer.videoSize.width) {

View File

@ -1,29 +1,17 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pixeldroid.app.posts
import android.content.Context
import android.content.Intent
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.GestureDetectorCompat
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
import org.pixeldroid.app.utils.api.objects.Attachment
import kotlin.math.absoluteValue
import kotlin.math.sign
@ -35,13 +23,11 @@ import kotlin.math.sign
* This solution has limitations when using multiple levels of nested scrollable elements
* (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2).
*/
class NestedScrollableHost : ConstraintLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
class NestedScrollableHost(context: Context, attrs: AttributeSet? = null) :
ConstraintLayout(context, attrs) {
private var mDetector: GestureDetectorCompat
private var touchSlop = 0
private var initialX = 0f
private var initialY = 0f
private val parentViewPager: ViewPager2?
get() {
var v: View? = parent as? View
@ -51,12 +37,15 @@ class NestedScrollableHost : ConstraintLayout {
return v as? ViewPager2
}
var doubleTapCallback: ((Boolean) -> Unit)? = null
var images: ArrayList<Attachment> = ArrayList();
var doubleTapCallback: (() -> Unit)? = null
private val child: View? get() = if (childCount > 0) getChildAt(0) else null
init {
touchSlop = ViewConfiguration.get(context).scaledTouchSlop
mDetector = GestureDetectorCompat(context, MyGestureListener())
}
private fun canChildScroll(orientation: Int, delta: Float): Boolean {
@ -69,35 +58,58 @@ class NestedScrollableHost : ConstraintLayout {
}
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
handleInterceptTouchEvent(e)
mDetector.onTouchEvent(e)
return super.onInterceptTouchEvent(e)
}
private fun handleInterceptTouchEvent(e: MotionEvent) {
val orientation = parentViewPager?.orientation ?: return
private inner class MyGestureListener : GestureDetector.SimpleOnGestureListener() {
if (e.action == MotionEvent.ACTION_DOWN) {
initialX = e.x
initialY = e.y
doubleTapCallback?.invoke(true)
}
// Early return if child can't scroll in same direction as parent
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
return
}
override fun onDown(e: MotionEvent): Boolean {
val orientation = parentViewPager?.orientation ?: return true
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
return true
}
if (e.action == MotionEvent.ACTION_DOWN) {
parent.requestDisallowInterceptTouchEvent(true)
} else if (e.action == MotionEvent.ACTION_MOVE) {
val dx = e.x - initialX
val dy = e.y - initialY
return true
}
override fun onDoubleTap(e: MotionEvent?): Boolean {
doubleTapCallback?.invoke()
return super.onDoubleTap(e)
}
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
// Disable opening AlbumActivity if the only image is a video (let the video open directly)
if(images.size == 1 && images.first().type == Attachment.AttachmentType.video){
return super.onSingleTapConfirmed(e)
}
val intent = Intent(context, AlbumActivity::class.java)
intent.putExtra("images", images)
intent.putExtra("index", (child as ViewPager2).currentItem)
context.startActivity(intent)
return super.onSingleTapConfirmed(e)
}
override fun onScroll(
e1: MotionEvent,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
val orientation = parentViewPager?.orientation ?: return true
val dx = e2.x - e1.x
val dy = e2.y - e1.y
val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
// assuming ViewPager2 touch-slop is 2x touch-slop of child
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f/ touchSlopModifier else 1f
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f/touchSlopModifier
if(dx.absoluteValue * .5f > touchSlop || scaledDy > touchSlop) doubleTapCallback?.invoke(false)
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f / touchSlopModifier else 1f
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f / touchSlopModifier
if (scaledDx > touchSlop || scaledDy > touchSlop) {
@ -115,6 +127,7 @@ class NestedScrollableHost : ConstraintLayout {
}
}
}
return super.onScroll(e1, e2, distanceX, distanceY)
}
}

View File

@ -1,27 +1,46 @@
package org.pixeldroid.app.posts
import android.Manifest
import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.graphics.Typeface
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.text.method.LinkMovementMethod
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.recyclerview.widget.RecyclerView
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import androidx.viewbinding.ViewBinding
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.target.CustomViewTarget
import com.bumptech.glide.request.transition.Transition
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.snackbar.Snackbar
import com.karumi.dexter.Dexter
import com.karumi.dexter.listener.PermissionDeniedResponse
import com.karumi.dexter.listener.PermissionGrantedResponse
import com.karumi.dexter.listener.single.BasePermissionListener
import kotlinx.coroutines.launch
import org.pixeldroid.app.R
import org.pixeldroid.app.databinding.AlbumImageViewBinding
import org.pixeldroid.app.databinding.OpenedAlbumBinding
import org.pixeldroid.app.databinding.PostFragmentBinding
import org.pixeldroid.app.posts.MediaViewerActivity.Companion.openActivity
import org.pixeldroid.app.utils.BlurHashDecoder
import org.pixeldroid.app.utils.ImageConverter
import org.pixeldroid.app.utils.api.PixelfedAPI
@ -32,15 +51,8 @@ import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_TAG
import org.pixeldroid.app.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
import org.pixeldroid.app.utils.db.AppDatabase
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
import com.karumi.dexter.Dexter
import com.karumi.dexter.listener.PermissionDeniedResponse
import com.karumi.dexter.listener.PermissionGrantedResponse
import com.karumi.dexter.listener.single.BasePermissionListener
import kotlinx.coroutines.launch
import org.pixeldroid.app.posts.MediaViewerActivity.Companion.VIDEO_DESCRIPTION_TAG
import org.pixeldroid.app.posts.MediaViewerActivity.Companion.VIDEO_URL_TAG
import org.pixeldroid.app.posts.MediaViewerActivity.Companion.openActivity
import retrofit2.HttpException
import java.io.File
import java.io.IOException
import kotlin.math.roundToInt
@ -88,7 +100,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
private fun setupPost(
request: RequestBuilder<Drawable>,
domain: String,
isActivity: Boolean
isActivity: Boolean,
) {
//Setup username as a button that opens the profile
binding.username.apply {
@ -148,9 +160,9 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
binding.postPager.visibility = View.VISIBLE
//Attach the given tabs to the view pager
binding.postPager.adapter = AlbumViewPagerAdapter(status?.media_attachments ?: emptyList(), status?.sensitive)
binding.postPager.adapter = AlbumViewPagerAdapter(status?.media_attachments ?: emptyList(), status?.sensitive, false)
if(status?.media_attachments?.size ?: 0 > 1) {
if((status?.media_attachments?.size ?: 0) > 1) {
binding.postIndicator.setViewPager(binding.postPager)
binding.postIndicator.visibility = View.VISIBLE
} else {
@ -206,7 +218,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
apiHolder: PixelfedAPIHolder,
db: AppDatabase,
lifecycleScope: LifecycleCoroutineScope,
isActivity: Boolean
isActivity: Boolean,
){
//Set the special HTML text
setDescription(apiHolder, lifecycleScope)
@ -459,36 +471,28 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
}
}
//Activate double tap liking
var clicked = false
//Activate tap interactions (double and single)
binding.postPagerHost.doubleTapCallback = {
if(!it) clicked = false
else lifecycleScope.launchWhenCreated {
lifecycleScope.launchWhenCreated {
//Check that the post isn't hidden
if(binding.sensitiveWarning.visibility == View.GONE) {
//Check for double click
if(clicked) {
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
if (binding.liker.isChecked) {
// Button is active, unlike
binding.liker.isChecked = false
unLikePostCall(api)
} else {
// Button is inactive, like
binding.liker.playAnimation()
binding.liker.isChecked = true
binding.likeAnimation.animateView()
likePostCall(api)
}
if (binding.sensitiveWarning.visibility == View.GONE) {
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
if (binding.liker.isChecked) {
// Button is active, unlike
binding.liker.isChecked = false
unLikePostCall(api)
} else {
clicked = true
//Reset clicked to false after 500ms
binding.postPager.handler.postDelayed(fun() { clicked = false }, 500)
// Button is inactive, like
binding.liker.playAnimation()
binding.liker.isChecked = true
binding.likeAnimation.animateView()
likePostCall(api)
}
}
}
}
status?.media_attachments?.let { binding.postPagerHost.images = ArrayList(it) }
}
private fun ImageView.animateView() {
visibility = View.VISIBLE
@ -544,8 +548,8 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
//endregion
private fun showComments(
lifecycleScope: LifecycleCoroutineScope,
isActivity: Boolean
lifecycleScope: LifecycleCoroutineScope,
isActivity: Boolean,
) {
//Show number of comments on the post
if (status?.replies_count == 0) {
@ -584,14 +588,20 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
}
}
private class AlbumViewPagerAdapter(private val media_attachments: List<Attachment>, private var sensitive: Boolean?) :
class AlbumViewPagerAdapter(
private val media_attachments: List<Attachment>, private var sensitive: Boolean?,
private val opened: Boolean, //TODO if opened don't open again, and use PhotoView instead of shite
) :
RecyclerView.Adapter<AlbumViewPagerAdapter.ViewHolder>() {
private var isActionBarHidden: Boolean = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemBinding = AlbumImageViewBinding.inflate(
return if(!opened) ViewHolderClosed(AlbumImageViewBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return ViewHolder(itemBinding)
)) else ViewHolderOpen(OpenedAlbumBinding.inflate(
LayoutInflater.from(parent.context), parent, false
))
}
override fun getItemCount() = media_attachments.size
@ -608,19 +618,52 @@ private class AlbumViewPagerAdapter(private val media_attachments: List<Attachme
}
if (sensitive == false) {
val imageUrl = if(video) preview_url else url
Glide.with(holder.binding.root)
if(opened){
Glide.with(holder.binding.root)
.download(GlideUrl(imageUrl))
.into(object : CustomViewTarget<SubsamplingScaleImageView, File>((holder.image as SubsamplingScaleImageView)) {
override fun onResourceReady(resource: File, t: Transition<in File>?) =
view.setImage(ImageSource.uri(Uri.fromFile(resource)))
override fun onLoadFailed(errorDrawable: Drawable?) {}
override fun onResourceCleared(placeholder: Drawable?) {}
})
(holder.image as SubsamplingScaleImageView).apply {
setMinimumDpi(80)
setDoubleTapZoomDpi(240)
resetScaleAndCenter()
}
holder.image.setOnClickListener {
val windowInsetsController = WindowCompat.getInsetsController((it.context as Activity).window, it)
// Configure the behavior of the hidden system bars
if (isActionBarHidden) {
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// Hide both the status bar and the navigation bar
(it.context as AppCompatActivity).supportActionBar?.show()
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
isActionBarHidden = false
} else {
// Configure the behavior of the hidden system bars
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// Hide both the status bar and the navigation bar
(it.context as AppCompatActivity).supportActionBar?.hide()
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
isActionBarHidden = true
}
}
}
else Glide.with(holder.binding.root)
.asDrawable().fitCenter()
.placeholder(blurhashBitMap)
.load(imageUrl).into(holder.image)
} else {
.load(imageUrl).into(holder.image as ImageView)
} else if(!opened){
Glide.with(holder.binding.root)
.asDrawable().fitCenter()
.load(blurhashBitMap).into(holder.image)
.load(blurhashBitMap).into(holder.image as ImageView)
}
holder.videoPlayButton.visibility = if(video) View.VISIBLE else View.GONE
if(video){
if(video && (opened || media_attachments.size == 1)){
holder.videoPlayButton.setOnClickListener {
openActivity(holder.binding.root.context, url, description)
}
@ -646,9 +689,17 @@ private class AlbumViewPagerAdapter(private val media_attachments: List<Attachme
sensitive = false
notifyDataSetChanged()
}
abstract class ViewHolder(open val binding: ViewBinding) : RecyclerView.ViewHolder(binding.root){
abstract val image: View
abstract val videoPlayButton: ImageView
}
class ViewHolder(val binding: AlbumImageViewBinding) : RecyclerView.ViewHolder(binding.root){
val image: ImageView = binding.imageImageView
val videoPlayButton: ImageView = binding.videoPlayButton
class ViewHolderOpen(override val binding: OpenedAlbumBinding) : ViewHolder(binding) {
override val image: SubsamplingScaleImageView = binding.imageImageView
override val videoPlayButton: ImageView = binding.videoPlayButton
}
class ViewHolderClosed(override val binding: AlbumImageViewBinding) : ViewHolder(binding) {
override val image: ImageView = binding.imageImageView
override val videoPlayButton: ImageView = binding.videoPlayButton
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:background="@android:color/black">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/albumPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:orientation="horizontal" />
<me.relex.circleindicator.CircleIndicator3
android:id="@+id/postIndicator"
android:layout_width="wrap_content"
android:layout_height="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
android:id="@+id/imageImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription" />
<ImageButton
android:id="@+id/videoPlayButton"
android:visibility="gone"
android:background="?attr/selectableItemBackgroundBorderless"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:scaleType="fitCenter"
android:src="@drawable/play_circle_filled"
android:contentDescription="@string/play_video" />
</FrameLayout>

View File

@ -21,6 +21,19 @@
<item name="android:textAppearance">@android:style/TextAppearance.Large</item>
</style>
<style name="AppTheme.ActionBar.Transparent" parent="AppTheme">
<item name="colorPrimary">@android:color/transparent</item>
<item name="colorPrimaryDark">@android:color/transparent</item>
<item name="colorPrimaryVariant">@android:color/transparent</item>
<item name="colorAccent">@android:color/transparent</item>
<item name="colorSecondary">@android:color/transparent</item>
<item name="statusBarForeground">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item>
<item name="statusBarBackground">@android:color/transparent</item>
<item name="android:windowTranslucentStatus">true</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>

View File

@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.6.21'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath 'com.android.tools.build:gradle:7.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

View File

@ -1,4 +1,4 @@
#Sat Sep 18 21:24:34 CEST 2021
#Tue Jun 07 20:42:16 CEST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists