Merge branch 'open_photo' into 'master'
Open photo Closes #321 See merge request pixeldroid/PixelDroid!428
This commit is contained in:
commit
1a518ad492
|
@ -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'
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue