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,153 @@ 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
+ Log .i(TAG , " onReady was called" )
75
+ }
76
+
77
+ override fun onImageLoaded () {
78
+ Log .i(TAG , " onImageLoaded was called" )
79
+ mediaPreview.visibility = View .VISIBLE
80
+ placeholderText.visibility = View .GONE
81
+
82
+ if (item.type == ITEM_TYPE_IMAGE ) {
83
+ mediaPreview.isPanEnabled = true
84
+ mediaPreview.isZoomEnabled = true
59
85
60
- gActivity.asyncImageLoader.executeIfAlive {
61
- val bitmap: Bitmap ? = try {
62
- if (item.type == ITEM_TYPE_VIDEO ) {
63
- getVideoThumbnail(gActivity, item.uri)
86
+ mediaPreview.setOnClickListener {
87
+ gActivity.toggleActionBarState()
88
+ }
64
89
} 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
- }
90
+ playButton.visibility = View .VISIBLE
82
91
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)
92
+ mediaPreview.setOnClickListener {
93
+ val curItem = getCurrentItem()
94
+ if (curItem.type == ITEM_TYPE_VIDEO ) {
95
+ val intent = Intent (gActivity, VideoPlayer ::class .java)
96
+ intent.putExtra(VideoPlayer .VIDEO_URI , curItem.uri)
97
+ intent.putExtra(VideoPlayer .IN_SECURE_MODE , gActivity.isSecureMode)
89
98
90
- gActivity.startActivity(intent)
91
- }
99
+ gActivity.startActivity(intent)
92
100
}
93
- } else {
94
- mediaPreview.visibility = View .INVISIBLE
101
+ }
102
+ }
103
+ }
104
+
105
+ override fun onImageLoadError (e : java.lang.Exception ? ) {
106
+ Log .i(TAG , " onImageLoadError: Failed to load image" )
107
+ if (e == null ) {
108
+ Log .d(TAG , " onImageLoadError received null as an exception" )
109
+ } else {
110
+ e.printStackTrace()
111
+ }
95
112
96
- val resId = if (item.type == ITEM_TYPE_IMAGE ) {
97
- R .string.inaccessible_image
98
- } else { R .string.inaccessible_video }
113
+ onErrorLoadingMedia()
114
+ }
99
115
100
- placeholderText.visibility = View .VISIBLE
101
- placeholderText.setText(gActivity.getString(resId, item.dateString))
116
+ override fun onTileLoadError (e : java.lang.Exception ? ) {
117
+ Log .i(TAG , " onTileLoadError: An unexpected error occurred while loading a tile" )
118
+ if (e == null ) {
119
+ Log .d(TAG , " onTileLoadError: Received null as an exception" )
120
+ } else {
121
+ e.printStackTrace()
122
+ }
123
+
124
+ onErrorLoadingMedia()
125
+ }
126
+
127
+ override fun onPreviewLoadError (e : java.lang.Exception ? ) {}
128
+
129
+ override fun onPreviewReleased () {}
130
+
131
+ fun onErrorLoadingMedia () {
132
+ mediaPreview.visibility = View .INVISIBLE
133
+
134
+ val placeholderTextFormat = if (item.type == ITEM_TYPE_VIDEO ) {
135
+ R .string.inaccessible_video
136
+ } else {
137
+ R .string.inaccessible_image
138
+ }
139
+
140
+ placeholderText.visibility = View .VISIBLE
141
+ placeholderText.text = gActivity.getString(placeholderTextFormat, item.dateString)
142
+ }
143
+ })
144
+
145
+ // Ensure that the touch events are being sent to the most recently viewed media
146
+ mediaPreview.setOnTouchListener { v, event ->
147
+ gActivity.gallerySlider.getChildAt(0 ).findViewById<SubsamplingScaleImageView >(R .id.slide_preview).onTouchEvent(event)
148
+ }
149
+
150
+ mediaPreview.setOnStateChangedListener(object : SubsamplingScaleImageView .OnStateChangedListener {
151
+ override fun onScaleChanged (newScale : Float , origin : Int ) {
152
+ Log .i(TAG , " onScaleChanged was called $newScale " )
153
+ gActivity.let {
154
+ if (newScale == mediaPreview.minScale) {
155
+ gActivity.gallerySlider.isUserInputEnabled = true
156
+ it.showActionBar()
157
+ it.vibrateDevice()
158
+ } else {
159
+ gActivity.gallerySlider.isUserInputEnabled = false
160
+ it.hideActionBar()
161
+ }
162
+ }
163
+ }
164
+
165
+ override fun onCenterChanged (newCenter : PointF ? , origin : Int ) {
166
+ Log .i(TAG , " onCenterChanged was called" )
167
+ }
168
+
169
+ })
170
+
171
+ if (item.type == ITEM_TYPE_IMAGE ) {
172
+ mediaPreview.fixOrientationForImage(item.uri)
173
+ mediaPreview.setImage(ImageSource .uri(item.uri))
174
+ } else {
175
+ gActivity.asyncImageLoader.executeIfAlive {
176
+ val thumbnailBitmap = getVideoThumbnail(gActivity, item.uri)
177
+
178
+ if (thumbnailBitmap != null ) {
179
+ gActivity.mainExecutor.execute {
180
+ if (holder.currentPostion == position) {
181
+ thumbnailBitmap.let {
182
+ mediaPreview.setImage(ImageSource .bitmap(thumbnailBitmap))
183
+ }
184
+ } else {
185
+ thumbnailBitmap.recycle()
186
+ }
102
187
}
103
188
} else {
104
- bitmap?.recycle()
189
+ mediaPreview.visibility = View .INVISIBLE
190
+ placeholderText.visibility = View .VISIBLE
191
+ placeholderText.text = gActivity.getString(R .string.inaccessible_video, item.dateString)
105
192
}
106
193
}
107
194
}
0 commit comments