1
1
package app.grapheneos.camera
2
2
3
+ import android.annotation.SuppressLint
3
4
import android.content.Intent
4
- import android.graphics.Bitmap
5
5
import android.graphics.ImageDecoder
6
+ import android.graphics.PointF
7
+ import android.util.Log
6
8
import android.view.LayoutInflater
7
9
import android.view.View
8
10
import android.view.ViewGroup
9
11
import android.widget.ImageView
10
12
import androidx.recyclerview.widget.RecyclerView
11
13
import app.grapheneos.camera.capturer.getVideoThumbnail
12
14
import app.grapheneos.camera.databinding.GallerySlideBinding
13
- import app.grapheneos.camera.ui.ZoomableImageView
15
+ import app.grapheneos.camera.ktx.fixOrientationForImage
14
16
import app.grapheneos.camera.ui.activities.InAppGallery
15
17
import app.grapheneos.camera.ui.activities.VideoPlayer
16
18
import app.grapheneos.camera.ui.fragment.GallerySlide
17
19
import app.grapheneos.camera.util.executeIfAlive
20
+ import com.davemorrissey.labs.subscaleview.ImageSource
21
+ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
18
22
import kotlin.math.max
19
23
20
24
class GallerySliderAdapter (
21
25
private val gActivity : InAppGallery ,
22
26
val items : ArrayList <CapturedItem >
23
27
) : RecyclerView.Adapter<GallerySlide>() {
24
28
29
+ companion object {
30
+ private const val TAG = " GallerySliderAdapter"
31
+ }
32
+
25
33
var atLeastOneBindViewHolderCall = false
26
34
27
35
private val layoutInflater: LayoutInflater = LayoutInflater .from(gActivity)
@@ -34,74 +42,147 @@ class GallerySliderAdapter(
34
42
return items[position].hashCode().toLong()
35
43
}
36
44
45
+ @SuppressLint(" ClickableViewAccessibility" )
37
46
override fun onBindViewHolder (holder : GallerySlide , position : Int ) {
38
- val mediaPreview: ZoomableImageView = holder.binding.slidePreview
39
- // Log.d("GallerySliderAdapter", "postiion $position, preview ${System.identityHashCode(mediaPreview)}")
47
+ holder.currentPostion = position
48
+
49
+ val mediaPreview: SubsamplingScaleImageView = holder.binding.slidePreview
40
50
val playButton: ImageView = holder.binding.playButton
41
51
val item = items[position]
42
52
43
- mediaPreview.setGalleryActivity(gActivity)
44
- mediaPreview.disableZooming()
45
- mediaPreview.setOnClickListener(null )
46
- mediaPreview.visibility = View .INVISIBLE
47
- mediaPreview.setImageBitmap(null )
48
-
49
53
val placeholderText = holder.binding.placeholderText.root
50
54
if (atLeastOneBindViewHolderCall) {
51
55
placeholderText.visibility = View .VISIBLE
52
- placeholderText.setText( " …" )
56
+ placeholderText.text = " …"
53
57
}
54
58
atLeastOneBindViewHolderCall = true
55
59
56
60
playButton.visibility = View .GONE
57
61
58
- holder.currentPostion = position
62
+ mediaPreview.isPanEnabled = false
63
+ mediaPreview.isZoomEnabled = false
64
+
65
+ mediaPreview.setExecutor(gActivity.asyncImageLoader)
66
+
67
+ mediaPreview.maxScale = 3f
68
+
69
+ mediaPreview.setDoubleTapZoomScale(1.5F )
70
+ mediaPreview.setDoubleTapZoomDuration(300 )
71
+
72
+ mediaPreview.setOnImageEventListener(object : SubsamplingScaleImageView .OnImageEventListener {
73
+ override fun onReady () {}
74
+
75
+ override fun onImageLoaded () {
76
+ mediaPreview.visibility = View .VISIBLE
77
+ placeholderText.visibility = View .GONE
78
+
79
+ if (item.type == ITEM_TYPE_IMAGE ) {
80
+ mediaPreview.isPanEnabled = true
81
+ mediaPreview.isZoomEnabled = true
59
82
60
- gActivity.asyncImageLoader.executeIfAlive {
61
- val bitmap: Bitmap ? = try {
62
- if (item.type == ITEM_TYPE_VIDEO ) {
63
- getVideoThumbnail(gActivity, item.uri)
83
+ mediaPreview.setOnClickListener {
84
+ gActivity.toggleActionBarState()
85
+ }
64
86
} else {
65
- val source = ImageDecoder .createSource(gActivity.contentResolver, item.uri)
66
- ImageDecoder .decodeBitmap(source, ImageDownscaler )
67
- }
68
- } catch (e: Exception ) { null }
69
-
70
- gActivity.mainExecutor.execute {
71
- if (holder.currentPostion == position) {
72
- if (bitmap != null ) {
73
- placeholderText.visibility = View .GONE
74
- mediaPreview.visibility = View .VISIBLE
75
- mediaPreview.setImageBitmap(bitmap)
76
-
77
- if (item.type == ITEM_TYPE_VIDEO ) {
78
- playButton.visibility = View .VISIBLE
79
- } else if (item.type == ITEM_TYPE_IMAGE ) {
80
- mediaPreview.enableZooming()
81
- }
87
+ playButton.visibility = View .VISIBLE
82
88
83
- mediaPreview.setOnClickListener {
84
- val curItem = getCurrentItem()
85
- if (curItem.type == ITEM_TYPE_VIDEO ) {
86
- val intent = Intent (gActivity, VideoPlayer ::class .java)
87
- intent.putExtra(VideoPlayer .VIDEO_URI , curItem.uri)
88
- intent.putExtra(VideoPlayer .IN_SECURE_MODE , gActivity.isSecureMode)
89
+ mediaPreview.setOnClickListener {
90
+ val curItem = getCurrentItem()
91
+ if (curItem.type == ITEM_TYPE_VIDEO ) {
92
+ val intent = Intent (gActivity, VideoPlayer ::class .java)
93
+ intent.putExtra(VideoPlayer .VIDEO_URI , curItem.uri)
94
+ intent.putExtra(VideoPlayer .IN_SECURE_MODE , gActivity.isSecureMode)
89
95
90
- gActivity.startActivity(intent)
91
- }
96
+ gActivity.startActivity(intent)
92
97
}
93
- } else {
94
- mediaPreview.visibility = View .INVISIBLE
98
+ }
99
+ }
100
+ }
101
+
102
+ override fun onImageLoadError (e : java.lang.Exception ? ) {
103
+ Log .i(TAG , " onImageLoadError: Failed to load image" )
104
+ if (e == null ) {
105
+ Log .d(TAG , " onImageLoadError received null as an exception" )
106
+ } else {
107
+ e.printStackTrace()
108
+ }
95
109
96
- val resId = if (item.type == ITEM_TYPE_IMAGE ) {
97
- R .string.inaccessible_image
98
- } else { R .string.inaccessible_video }
110
+ onErrorLoadingMedia()
111
+ }
99
112
100
- placeholderText.visibility = View .VISIBLE
101
- placeholderText.setText(gActivity.getString(resId, item.dateString))
113
+ override fun onTileLoadError (e : java.lang.Exception ? ) {
114
+ Log .i(TAG , " onTileLoadError: An unexpected error occurred while loading a tile" )
115
+ if (e == null ) {
116
+ Log .d(TAG , " onTileLoadError: Received null as an exception" )
117
+ } else {
118
+ e.printStackTrace()
119
+ }
120
+
121
+ onErrorLoadingMedia()
122
+ }
123
+
124
+ override fun onPreviewLoadError (e : java.lang.Exception ? ) {}
125
+
126
+ override fun onPreviewReleased () {}
127
+
128
+ fun onErrorLoadingMedia () {
129
+ mediaPreview.visibility = View .INVISIBLE
130
+
131
+ val placeholderTextFormat = if (item.type == ITEM_TYPE_VIDEO ) {
132
+ R .string.inaccessible_video
133
+ } else {
134
+ R .string.inaccessible_image
135
+ }
136
+
137
+ placeholderText.visibility = View .VISIBLE
138
+ placeholderText.text = gActivity.getString(placeholderTextFormat, item.dateString)
139
+ }
140
+ })
141
+
142
+ // Ensure that the touch events are being sent to the most recently viewed media
143
+ mediaPreview.setOnTouchListener { v, event ->
144
+ gActivity.gallerySlider.getChildAt(0 ).findViewById<SubsamplingScaleImageView >(R .id.slide_preview).onTouchEvent(event)
145
+ }
146
+
147
+ mediaPreview.setOnStateChangedListener(object : SubsamplingScaleImageView .OnStateChangedListener {
148
+ override fun onScaleChanged (newScale : Float , origin : Int ) {
149
+ gActivity.let {
150
+ if (newScale == mediaPreview.minScale) {
151
+ gActivity.gallerySlider.isUserInputEnabled = true
152
+ it.showActionBar()
153
+ it.vibrateDevice()
154
+ } else {
155
+ gActivity.gallerySlider.isUserInputEnabled = false
156
+ it.hideActionBar()
157
+ }
158
+ }
159
+ }
160
+
161
+ override fun onCenterChanged (newCenter : PointF ? , origin : Int ) {}
162
+
163
+ })
164
+
165
+ if (item.type == ITEM_TYPE_IMAGE ) {
166
+ mediaPreview.fixOrientationForImage(item.uri)
167
+ mediaPreview.setImage(ImageSource .uri(item.uri))
168
+ } else {
169
+ gActivity.asyncImageLoader.executeIfAlive {
170
+ val thumbnailBitmap = getVideoThumbnail(gActivity, item.uri)
171
+
172
+ if (thumbnailBitmap != null ) {
173
+ gActivity.mainExecutor.execute {
174
+ if (holder.currentPostion == position) {
175
+ thumbnailBitmap.let {
176
+ mediaPreview.setImage(ImageSource .bitmap(thumbnailBitmap))
177
+ }
178
+ } else {
179
+ thumbnailBitmap.recycle()
180
+ }
102
181
}
103
182
} else {
104
- bitmap?.recycle()
183
+ mediaPreview.visibility = View .INVISIBLE
184
+ placeholderText.visibility = View .VISIBLE
185
+ placeholderText.text = gActivity.getString(R .string.inaccessible_video, item.dateString)
105
186
}
106
187
}
107
188
}
0 commit comments