zkit-ui 2.0.4 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/android/build.gradle +100 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GaleriaModule.kt +43 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GaleriaView.kt +305 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryLayoutSupport.java +450 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryMediaItem.java +36 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryPagerAdapter.java +140 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GallerySharedElementGeometry.java +25 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GallerySharedElementNames.java +39 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GallerySharedElementState.java +25 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GallerySourceViewRegistry.java +115 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryTransitionSnapshotView.java +60 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryVideoPlayerView.java +338 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryViewerOverlayView.java +636 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryViewerPageView.java +357 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryViewerTransitionController.java +223 -0
- package/android/src/main/res/layout/galeria_player_view.xml +9 -0
- package/dist/all.d.ts +2 -0
- package/dist/all.d.ts.map +1 -1
- package/dist/all.js +24 -22
- package/dist/ui/NativeImagePreview/NativeImagePreview.d.ts +3 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.js +99 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.d.ts +3 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.js +56 -0
- package/dist/ui/NativeImagePreview/context.d.ts +14 -0
- package/dist/ui/NativeImagePreview/context.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/context.js +38 -0
- package/dist/ui/NativeImagePreview/index.d.ts +3 -0
- package/dist/ui/NativeImagePreview/index.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/index.js +5 -0
- package/dist/ui/NativeImagePreview/types.d.ts +66 -0
- package/dist/ui/NativeImagePreview/types.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/types.js +2 -0
- package/dist/ui/NativeImagePreview/utils.d.ts +8 -0
- package/dist/ui/NativeImagePreview/utils.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/utils.js +82 -0
- package/expo-module.config.json +11 -0
- package/ios/GaleriaModule.swift +46 -0
- package/ios/GaleriaView.swift +253 -0
- package/ios/GalleryViewer/GaleriaImageLoader.swift +73 -0
- package/ios/GalleryViewer/GaleriaImageViewInteraction.swift +245 -0
- package/ios/GalleryViewer/GaleriaLayoutSupport.swift +143 -0
- package/ios/GalleryViewer/GaleriaMedia.swift +28 -0
- package/ios/GalleryViewer/GaleriaSourceViewRegistry.swift +80 -0
- package/ios/GalleryViewer/GaleriaVideoPlayerView.swift +437 -0
- package/ios/GalleryViewer/GaleriaViewerConfiguration.swift +51 -0
- package/ios/GalleryViewer/GaleriaViewerController.swift +745 -0
- package/ios/GalleryViewer/GaleriaViewerPageView.swift +623 -0
- package/ios/GalleryViewer/GaleriaViewerTransitionCoordinator.swift +312 -0
- package/package.json +20 -12
- package/src/all.ts +18 -0
- package/src/services/ImagePreviewService/README.md +19 -0
- package/src/ui/NativeImagePreview/NativeImagePreview.tsx +146 -0
- package/src/ui/NativeImagePreview/NativeImagePreview.web.tsx +37 -0
- package/src/ui/NativeImagePreview/README.md +111 -0
- package/src/ui/NativeImagePreview/context.tsx +22 -0
- package/src/ui/NativeImagePreview/index.tsx +18 -0
- package/src/ui/NativeImagePreview/types.ts +92 -0
- package/src/ui/NativeImagePreview/utils.ts +107 -0
- package/zkit-ui.podspec +13 -1
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
package cn.fontree.zkit.ui.preview;
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap;
|
|
4
|
+
import android.graphics.Canvas;
|
|
5
|
+
import android.graphics.Matrix;
|
|
6
|
+
import android.graphics.Rect;
|
|
7
|
+
import android.graphics.RectF;
|
|
8
|
+
import android.graphics.drawable.BitmapDrawable;
|
|
9
|
+
import android.graphics.drawable.Drawable;
|
|
10
|
+
import android.view.View;
|
|
11
|
+
import android.widget.ImageView;
|
|
12
|
+
|
|
13
|
+
import androidx.annotation.NonNull;
|
|
14
|
+
import androidx.annotation.Nullable;
|
|
15
|
+
|
|
16
|
+
import com.github.chrisbanes.photoview.PhotoView;
|
|
17
|
+
|
|
18
|
+
public final class GalleryLayoutSupport {
|
|
19
|
+
private static final int MAX_TRANSITION_BITMAP_SIDE = 2048;
|
|
20
|
+
|
|
21
|
+
private GalleryLayoutSupport() {
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Nullable
|
|
25
|
+
public static GallerySharedElementState captureImageViewState(@Nullable ImageView imageView) {
|
|
26
|
+
return captureImageViewState(imageView, null);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Nullable
|
|
30
|
+
public static GallerySharedElementState captureImageViewState(@Nullable ImageView imageView, @Nullable RectF clippingFrameInWindow) {
|
|
31
|
+
if (imageView == null) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
Drawable drawable = imageView.getDrawable();
|
|
35
|
+
if (drawable == null || imageView.getWidth() <= 0 || imageView.getHeight() <= 0) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
RectF imageBoundsInWindow = viewBoundsOnScreen(imageView);
|
|
40
|
+
RectF clippingFrame = clippingFrameInWindow != null
|
|
41
|
+
? new RectF(clippingFrameInWindow)
|
|
42
|
+
: visibleBoundsOnScreen(imageView);
|
|
43
|
+
if (clippingFrame.isEmpty()) {
|
|
44
|
+
clippingFrame = new RectF(imageBoundsInWindow);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
RectF contentFrameInView = resolveDisplayedContentFrame(imageView, drawable);
|
|
48
|
+
if (contentFrameInView == null || contentFrameInView.isEmpty()) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
Drawable transitionDrawable = transitionDrawableForImageView(imageView);
|
|
53
|
+
if (transitionDrawable == null) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
RectF contentFrameInWindow = new RectF(contentFrameInView);
|
|
58
|
+
int[] location = new int[2];
|
|
59
|
+
imageView.getLocationOnScreen(location);
|
|
60
|
+
contentFrameInWindow.offset(location[0], location[1]);
|
|
61
|
+
|
|
62
|
+
RectF visibleFrameInWindow = new RectF();
|
|
63
|
+
boolean intersects = visibleFrameInWindow.setIntersect(clippingFrame, contentFrameInWindow);
|
|
64
|
+
if (!intersects || visibleFrameInWindow.isEmpty()) {
|
|
65
|
+
visibleFrameInWindow = new RectF(clippingFrame);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
RectF contentFrameInVisibleBounds = new RectF(
|
|
69
|
+
contentFrameInWindow.left - visibleFrameInWindow.left,
|
|
70
|
+
contentFrameInWindow.top - visibleFrameInWindow.top,
|
|
71
|
+
contentFrameInWindow.right - visibleFrameInWindow.left,
|
|
72
|
+
contentFrameInWindow.bottom - visibleFrameInWindow.top
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return new GallerySharedElementState(
|
|
76
|
+
transitionDrawable,
|
|
77
|
+
new GallerySharedElementGeometry(visibleFrameInWindow, contentFrameInVisibleBounds)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Nullable
|
|
82
|
+
public static GallerySharedElementGeometry captureImageViewGeometry(@Nullable ImageView imageView, @Nullable Drawable geometryDrawable) {
|
|
83
|
+
if (imageView == null || imageView.getWidth() <= 0 || imageView.getHeight() <= 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
RectF imageBoundsInWindow = viewBoundsOnScreen(imageView);
|
|
88
|
+
RectF clippingFrame = visibleBoundsOnScreen(imageView);
|
|
89
|
+
if (clippingFrame.isEmpty()) {
|
|
90
|
+
clippingFrame = new RectF(imageBoundsInWindow);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
RectF contentFrameInView = null;
|
|
94
|
+
Drawable drawable = geometryDrawable != null ? geometryDrawable : imageView.getDrawable();
|
|
95
|
+
if (drawable != null) {
|
|
96
|
+
contentFrameInView = resolveDisplayedContentFrame(imageView, drawable);
|
|
97
|
+
}
|
|
98
|
+
if (contentFrameInView == null || contentFrameInView.isEmpty()) {
|
|
99
|
+
contentFrameInView = new RectF(0f, 0f, imageView.getWidth(), imageView.getHeight());
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
RectF contentFrameInWindow = new RectF(contentFrameInView);
|
|
103
|
+
int[] location = new int[2];
|
|
104
|
+
imageView.getLocationOnScreen(location);
|
|
105
|
+
contentFrameInWindow.offset(location[0], location[1]);
|
|
106
|
+
|
|
107
|
+
RectF visibleFrameInWindow = new RectF();
|
|
108
|
+
boolean intersects = visibleFrameInWindow.setIntersect(clippingFrame, contentFrameInWindow);
|
|
109
|
+
if (!intersects || visibleFrameInWindow.isEmpty()) {
|
|
110
|
+
visibleFrameInWindow = new RectF(clippingFrame);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
RectF contentFrameInVisibleBounds = new RectF(
|
|
114
|
+
contentFrameInWindow.left - visibleFrameInWindow.left,
|
|
115
|
+
contentFrameInWindow.top - visibleFrameInWindow.top,
|
|
116
|
+
contentFrameInWindow.right - visibleFrameInWindow.left,
|
|
117
|
+
contentFrameInWindow.bottom - visibleFrameInWindow.top
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return new GallerySharedElementGeometry(visibleFrameInWindow, contentFrameInVisibleBounds);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@NonNull
|
|
124
|
+
public static RectF viewBoundsOnScreen(@NonNull View view) {
|
|
125
|
+
int[] location = new int[2];
|
|
126
|
+
view.getLocationOnScreen(location);
|
|
127
|
+
return new RectF(
|
|
128
|
+
location[0],
|
|
129
|
+
location[1],
|
|
130
|
+
location[0] + view.getWidth(),
|
|
131
|
+
location[1] + view.getHeight()
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@NonNull
|
|
136
|
+
public static RectF visibleBoundsOnScreen(@NonNull View view) {
|
|
137
|
+
Rect localVisibleRect = new Rect();
|
|
138
|
+
if (!view.getLocalVisibleRect(localVisibleRect) || localVisibleRect.isEmpty()) {
|
|
139
|
+
return viewBoundsOnScreen(view);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
int[] location = new int[2];
|
|
143
|
+
view.getLocationOnScreen(location);
|
|
144
|
+
return new RectF(
|
|
145
|
+
location[0] + localVisibleRect.left,
|
|
146
|
+
location[1] + localVisibleRect.top,
|
|
147
|
+
location[0] + localVisibleRect.right,
|
|
148
|
+
location[1] + localVisibleRect.bottom
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@Nullable
|
|
153
|
+
public static Drawable cloneDrawable(@Nullable Drawable drawable, @NonNull View view) {
|
|
154
|
+
if (drawable == null) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
Drawable.ConstantState constantState = drawable.getConstantState();
|
|
158
|
+
if (constantState == null) {
|
|
159
|
+
return drawable.mutate();
|
|
160
|
+
}
|
|
161
|
+
return constantState.newDrawable(view.getResources()).mutate();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@Nullable
|
|
165
|
+
public static Drawable transitionDrawableForImageView(@Nullable ImageView imageView) {
|
|
166
|
+
if (imageView == null) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
Drawable draweeActualDrawable = resolveDraweeActualDrawable(imageView);
|
|
170
|
+
if (draweeActualDrawable != null) {
|
|
171
|
+
Drawable clonedActualDrawable = cloneDrawableFromConstantState(draweeActualDrawable, imageView);
|
|
172
|
+
if (clonedActualDrawable != null) {
|
|
173
|
+
return clonedActualDrawable;
|
|
174
|
+
}
|
|
175
|
+
Drawable bitmapCopy = bitmapDrawableCopy(draweeActualDrawable, imageView);
|
|
176
|
+
if (bitmapCopy != null) {
|
|
177
|
+
return bitmapCopy;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (isDraweeRootDrawable(imageView.getDrawable())) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
return cloneDrawable(imageView.getDrawable(), imageView);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@Nullable
|
|
187
|
+
private static Drawable cloneDrawableFromConstantState(@Nullable Drawable drawable, @NonNull View view) {
|
|
188
|
+
if (drawable == null) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
Drawable.ConstantState constantState = drawable.getConstantState();
|
|
192
|
+
if (constantState == null) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
return constantState.newDrawable(view.getResources()).mutate();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@Nullable
|
|
199
|
+
private static Drawable bitmapDrawableCopy(@NonNull Drawable drawable, @NonNull View view) {
|
|
200
|
+
int intrinsicWidth = drawable.getIntrinsicWidth();
|
|
201
|
+
int intrinsicHeight = drawable.getIntrinsicHeight();
|
|
202
|
+
if (intrinsicWidth <= 0 || intrinsicHeight <= 0) {
|
|
203
|
+
Rect bounds = drawable.getBounds();
|
|
204
|
+
intrinsicWidth = bounds.width();
|
|
205
|
+
intrinsicHeight = bounds.height();
|
|
206
|
+
}
|
|
207
|
+
if (intrinsicWidth <= 0 || intrinsicHeight <= 0) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
float scale = Math.min(
|
|
212
|
+
1f,
|
|
213
|
+
MAX_TRANSITION_BITMAP_SIDE / Math.max((float) intrinsicWidth, (float) intrinsicHeight)
|
|
214
|
+
);
|
|
215
|
+
int bitmapWidth = Math.max(1, Math.round(intrinsicWidth * scale));
|
|
216
|
+
int bitmapHeight = Math.max(1, Math.round(intrinsicHeight * scale));
|
|
217
|
+
|
|
218
|
+
Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
|
|
219
|
+
Canvas canvas = new Canvas(bitmap);
|
|
220
|
+
Rect oldBounds = new Rect(drawable.getBounds());
|
|
221
|
+
drawable.setBounds(0, 0, bitmapWidth, bitmapHeight);
|
|
222
|
+
drawable.draw(canvas);
|
|
223
|
+
drawable.setBounds(oldBounds);
|
|
224
|
+
return new BitmapDrawable(view.getResources(), bitmap);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@NonNull
|
|
228
|
+
public static GallerySharedElementGeometry defaultGeometryFor(@NonNull Drawable drawable, @NonNull RectF containerBounds) {
|
|
229
|
+
RectF visibleFrame = aspectFitRect(drawable, containerBounds);
|
|
230
|
+
return new GallerySharedElementGeometry(
|
|
231
|
+
visibleFrame,
|
|
232
|
+
new RectF(0f, 0f, visibleFrame.width(), visibleFrame.height())
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
@NonNull
|
|
237
|
+
public static GallerySharedElementGeometry defaultGeometryForAspectRatio(float width, float height, @NonNull RectF containerBounds) {
|
|
238
|
+
float safeWidth = Math.max(1f, width);
|
|
239
|
+
float safeHeight = Math.max(1f, height);
|
|
240
|
+
float widthScale = containerBounds.width() / safeWidth;
|
|
241
|
+
float heightScale = containerBounds.height() / safeHeight;
|
|
242
|
+
float scale = Math.min(widthScale, heightScale);
|
|
243
|
+
float targetWidth = safeWidth * scale;
|
|
244
|
+
float targetHeight = safeHeight * scale;
|
|
245
|
+
float left = containerBounds.left + (containerBounds.width() - targetWidth) * 0.5f;
|
|
246
|
+
float top = containerBounds.top + (containerBounds.height() - targetHeight) * 0.5f;
|
|
247
|
+
RectF visibleFrame = new RectF(left, top, left + targetWidth, top + targetHeight);
|
|
248
|
+
return new GallerySharedElementGeometry(
|
|
249
|
+
visibleFrame,
|
|
250
|
+
new RectF(0f, 0f, visibleFrame.width(), visibleFrame.height())
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
@NonNull
|
|
255
|
+
public static RectF aspectFitRect(@NonNull Drawable drawable, @NonNull RectF containerBounds) {
|
|
256
|
+
int intrinsicWidth = Math.max(1, drawable.getIntrinsicWidth());
|
|
257
|
+
int intrinsicHeight = Math.max(1, drawable.getIntrinsicHeight());
|
|
258
|
+
float widthScale = containerBounds.width() / intrinsicWidth;
|
|
259
|
+
float heightScale = containerBounds.height() / intrinsicHeight;
|
|
260
|
+
float scale = Math.min(widthScale, heightScale);
|
|
261
|
+
float width = intrinsicWidth * scale;
|
|
262
|
+
float height = intrinsicHeight * scale;
|
|
263
|
+
float left = containerBounds.left + (containerBounds.width() - width) * 0.5f;
|
|
264
|
+
float top = containerBounds.top + (containerBounds.height() - height) * 0.5f;
|
|
265
|
+
return new RectF(left, top, left + width, top + height);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@Nullable
|
|
269
|
+
private static RectF resolveDisplayedContentFrame(@NonNull ImageView imageView, @NonNull Drawable drawable) {
|
|
270
|
+
RectF viewBounds = new RectF(0f, 0f, imageView.getWidth(), imageView.getHeight());
|
|
271
|
+
if (viewBounds.isEmpty()) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (imageView instanceof PhotoView) {
|
|
276
|
+
RectF displayRect = ((PhotoView) imageView).getDisplayRect();
|
|
277
|
+
if (displayRect != null && !displayRect.isEmpty()) {
|
|
278
|
+
return new RectF(displayRect);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
RectF draweeContentFrame = resolveDraweeActualContentFrame(imageView);
|
|
283
|
+
if (draweeContentFrame != null && !draweeContentFrame.isEmpty()) {
|
|
284
|
+
return draweeContentFrame;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
int intrinsicWidth = drawable.getIntrinsicWidth();
|
|
288
|
+
int intrinsicHeight = drawable.getIntrinsicHeight();
|
|
289
|
+
if (intrinsicWidth <= 0 || intrinsicHeight <= 0) {
|
|
290
|
+
return new RectF(viewBounds);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
ImageView.ScaleType scaleType = imageView.getScaleType();
|
|
294
|
+
if (scaleType == ImageView.ScaleType.MATRIX) {
|
|
295
|
+
RectF contentFrameInView = new RectF(0f, 0f, intrinsicWidth, intrinsicHeight);
|
|
296
|
+
Matrix imageMatrix = new Matrix(imageView.getImageMatrix());
|
|
297
|
+
imageMatrix.mapRect(contentFrameInView);
|
|
298
|
+
return contentFrameInView;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (scaleType == ImageView.ScaleType.FIT_XY) {
|
|
302
|
+
return new RectF(viewBounds);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
float widthScale = viewBounds.width() / intrinsicWidth;
|
|
306
|
+
float heightScale = viewBounds.height() / intrinsicHeight;
|
|
307
|
+
float scale;
|
|
308
|
+
float dx = 0f;
|
|
309
|
+
float dy = 0f;
|
|
310
|
+
|
|
311
|
+
switch (scaleType) {
|
|
312
|
+
case CENTER:
|
|
313
|
+
scale = 1f;
|
|
314
|
+
dx = (viewBounds.width() - intrinsicWidth) * 0.5f;
|
|
315
|
+
dy = (viewBounds.height() - intrinsicHeight) * 0.5f;
|
|
316
|
+
break;
|
|
317
|
+
case CENTER_CROP:
|
|
318
|
+
scale = Math.max(widthScale, heightScale);
|
|
319
|
+
dx = (viewBounds.width() - intrinsicWidth * scale) * 0.5f;
|
|
320
|
+
dy = (viewBounds.height() - intrinsicHeight * scale) * 0.5f;
|
|
321
|
+
break;
|
|
322
|
+
case CENTER_INSIDE:
|
|
323
|
+
scale = Math.min(1f, Math.min(widthScale, heightScale));
|
|
324
|
+
dx = (viewBounds.width() - intrinsicWidth * scale) * 0.5f;
|
|
325
|
+
dy = (viewBounds.height() - intrinsicHeight * scale) * 0.5f;
|
|
326
|
+
break;
|
|
327
|
+
case FIT_START:
|
|
328
|
+
scale = Math.min(widthScale, heightScale);
|
|
329
|
+
break;
|
|
330
|
+
case FIT_END:
|
|
331
|
+
scale = Math.min(widthScale, heightScale);
|
|
332
|
+
dx = viewBounds.width() - intrinsicWidth * scale;
|
|
333
|
+
dy = viewBounds.height() - intrinsicHeight * scale;
|
|
334
|
+
break;
|
|
335
|
+
case FIT_CENTER:
|
|
336
|
+
default:
|
|
337
|
+
scale = Math.min(widthScale, heightScale);
|
|
338
|
+
dx = (viewBounds.width() - intrinsicWidth * scale) * 0.5f;
|
|
339
|
+
dy = (viewBounds.height() - intrinsicHeight * scale) * 0.5f;
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
float width = intrinsicWidth * scale;
|
|
344
|
+
float height = intrinsicHeight * scale;
|
|
345
|
+
return new RectF(dx, dy, dx + width, dy + height);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@Nullable
|
|
349
|
+
private static RectF resolveDraweeActualContentFrame(@NonNull ImageView imageView) {
|
|
350
|
+
try {
|
|
351
|
+
Object hierarchy = imageView.getClass().getMethod("getHierarchy").invoke(imageView);
|
|
352
|
+
if (hierarchy == null) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
RectF bounds = new RectF();
|
|
356
|
+
hierarchy.getClass().getMethod("getActualImageBounds", RectF.class).invoke(hierarchy, bounds);
|
|
357
|
+
if (bounds.width() <= 1f || bounds.height() <= 1f) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
return bounds;
|
|
361
|
+
} catch (Exception ignored) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
@Nullable
|
|
367
|
+
private static Drawable resolveDraweeActualDrawable(@NonNull ImageView imageView) {
|
|
368
|
+
try {
|
|
369
|
+
Object hierarchy = imageView.getClass().getMethod("getHierarchy").invoke(imageView);
|
|
370
|
+
if (hierarchy == null) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
Object hasImage = hierarchy.getClass().getMethod("hasImage").invoke(hierarchy);
|
|
374
|
+
if (!(hasImage instanceof Boolean) || !((Boolean) hasImage)) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
java.lang.reflect.Field actualImageWrapperField = hierarchy.getClass().getDeclaredField("mActualImageWrapper");
|
|
378
|
+
actualImageWrapperField.setAccessible(true);
|
|
379
|
+
Object actualImageWrapper = actualImageWrapperField.get(hierarchy);
|
|
380
|
+
if (actualImageWrapper == null) {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
Object actualDrawable = actualImageWrapper.getClass().getMethod("getDrawable").invoke(actualImageWrapper);
|
|
384
|
+
if (actualDrawable instanceof Drawable) {
|
|
385
|
+
return (Drawable) actualDrawable;
|
|
386
|
+
}
|
|
387
|
+
} catch (Exception ignored) {
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return findPositiveIntrinsicLeafDrawable(
|
|
391
|
+
imageView.getDrawable(),
|
|
392
|
+
new java.util.IdentityHashMap<>(),
|
|
393
|
+
0
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
@Nullable
|
|
398
|
+
private static Drawable findPositiveIntrinsicLeafDrawable(
|
|
399
|
+
@Nullable Drawable drawable,
|
|
400
|
+
@NonNull java.util.IdentityHashMap<Drawable, Boolean> visited,
|
|
401
|
+
int depth
|
|
402
|
+
) {
|
|
403
|
+
if (drawable == null || depth > 12 || visited.containsKey(drawable)) {
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
visited.put(drawable, true);
|
|
407
|
+
|
|
408
|
+
if (!isDraweeWrapperDrawable(drawable)
|
|
409
|
+
&& drawable.getIntrinsicWidth() > 0
|
|
410
|
+
&& drawable.getIntrinsicHeight() > 0) {
|
|
411
|
+
return drawable;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
Drawable current = drawable.getCurrent();
|
|
415
|
+
if (current != null && current != drawable) {
|
|
416
|
+
Drawable found = findPositiveIntrinsicLeafDrawable(current, visited, depth + 1);
|
|
417
|
+
if (found != null) {
|
|
418
|
+
return found;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
Object count = drawable.getClass().getMethod("getNumberOfLayers").invoke(drawable);
|
|
424
|
+
if (count instanceof Integer) {
|
|
425
|
+
for (int i = ((Integer) count) - 1; i >= 0; i--) {
|
|
426
|
+
Object layer = drawable.getClass().getMethod("getDrawable", int.class).invoke(drawable, i);
|
|
427
|
+
if (layer instanceof Drawable) {
|
|
428
|
+
Drawable found = findPositiveIntrinsicLeafDrawable((Drawable) layer, visited, depth + 1);
|
|
429
|
+
if (found != null) {
|
|
430
|
+
return found;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
} catch (Exception ignored) {
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private static boolean isDraweeRootDrawable(@Nullable Drawable drawable) {
|
|
442
|
+
return drawable != null
|
|
443
|
+
&& "com.facebook.drawee.generic.RootDrawable".equals(drawable.getClass().getName());
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
private static boolean isDraweeWrapperDrawable(@NonNull Drawable drawable) {
|
|
447
|
+
String className = drawable.getClass().getName();
|
|
448
|
+
return className.startsWith("com.facebook.drawee.");
|
|
449
|
+
}
|
|
450
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
package cn.fontree.zkit.ui.preview;
|
|
2
|
+
|
|
3
|
+
public class GalleryMediaItem {
|
|
4
|
+
public enum MediaType {
|
|
5
|
+
IMAGE,
|
|
6
|
+
VIDEO
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
private final String id;
|
|
10
|
+
private final MediaType mediaType;
|
|
11
|
+
private final String sourceUrl;
|
|
12
|
+
private final String thumbnailUrl;
|
|
13
|
+
|
|
14
|
+
public GalleryMediaItem(String id, MediaType mediaType, String sourceUrl, String thumbnailUrl) {
|
|
15
|
+
this.id = id;
|
|
16
|
+
this.mediaType = mediaType;
|
|
17
|
+
this.sourceUrl = sourceUrl;
|
|
18
|
+
this.thumbnailUrl = thumbnailUrl;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public String getId() {
|
|
22
|
+
return id;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public MediaType getMediaType() {
|
|
26
|
+
return mediaType;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public String getSourceUrl() {
|
|
30
|
+
return sourceUrl;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public String getThumbnailUrl() {
|
|
34
|
+
return thumbnailUrl;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
package cn.fontree.zkit.ui.preview;
|
|
2
|
+
|
|
3
|
+
import android.view.ViewGroup;
|
|
4
|
+
|
|
5
|
+
import androidx.annotation.NonNull;
|
|
6
|
+
import androidx.annotation.Nullable;
|
|
7
|
+
import androidx.recyclerview.widget.RecyclerView;
|
|
8
|
+
|
|
9
|
+
import java.util.ArrayList;
|
|
10
|
+
import java.util.HashMap;
|
|
11
|
+
import java.util.List;
|
|
12
|
+
import java.util.Map;
|
|
13
|
+
|
|
14
|
+
public final class GalleryPagerAdapter extends RecyclerView.Adapter<GalleryPagerAdapter.PageViewHolder> {
|
|
15
|
+
public interface PageBindCallback {
|
|
16
|
+
void onPageBound(@NonNull GalleryViewerPageView pageView, int position);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private final List<GalleryMediaItem> items = new ArrayList<>();
|
|
20
|
+
private final Map<Integer, GalleryViewerPageView> attachedViews = new HashMap<>();
|
|
21
|
+
@Nullable
|
|
22
|
+
private final GalleryViewerPageView.Listener pageListener;
|
|
23
|
+
@Nullable
|
|
24
|
+
private final PageBindCallback pageBindCallback;
|
|
25
|
+
private int activeIndex = -1;
|
|
26
|
+
|
|
27
|
+
public GalleryPagerAdapter(
|
|
28
|
+
@Nullable GalleryViewerPageView.Listener pageListener,
|
|
29
|
+
@Nullable PageBindCallback pageBindCallback
|
|
30
|
+
) {
|
|
31
|
+
this.pageListener = pageListener;
|
|
32
|
+
this.pageBindCallback = pageBindCallback;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public void setItems(@NonNull List<GalleryMediaItem> list) {
|
|
36
|
+
attachedViews.clear();
|
|
37
|
+
items.clear();
|
|
38
|
+
items.addAll(list);
|
|
39
|
+
notifyDataSetChanged();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public void setActiveIndex(int index) {
|
|
43
|
+
activeIndex = index;
|
|
44
|
+
for (Map.Entry<Integer, GalleryViewerPageView> entry : attachedViews.entrySet()) {
|
|
45
|
+
entry.getValue().setActive(entry.getKey() == index);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public void releaseAll() {
|
|
50
|
+
for (GalleryViewerPageView view : attachedViews.values()) {
|
|
51
|
+
view.release();
|
|
52
|
+
}
|
|
53
|
+
attachedViews.clear();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Nullable
|
|
57
|
+
public GalleryViewerPageView findAttachedView(int position) {
|
|
58
|
+
GalleryViewerPageView pageView = attachedViews.get(position);
|
|
59
|
+
if (pageView == null) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (!pageView.isAttachedToWindow()) {
|
|
63
|
+
attachedViews.remove(position);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return pageView;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Nullable
|
|
70
|
+
public GalleryViewerPageView findActiveView() {
|
|
71
|
+
return attachedViews.get(activeIndex);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@NonNull
|
|
75
|
+
@Override
|
|
76
|
+
public PageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
|
77
|
+
GalleryViewerPageView pageView = new GalleryViewerPageView(parent.getContext());
|
|
78
|
+
pageView.setListener(pageListener);
|
|
79
|
+
return new PageViewHolder(pageView);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@Override
|
|
83
|
+
public void onBindViewHolder(@NonNull PageViewHolder holder, int position) {
|
|
84
|
+
holder.pageView.bind(items.get(position));
|
|
85
|
+
if (pageBindCallback != null) {
|
|
86
|
+
pageBindCallback.onPageBound(holder.pageView, position);
|
|
87
|
+
}
|
|
88
|
+
holder.pageView.setActive(position == activeIndex);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Override
|
|
92
|
+
public void onViewAttachedToWindow(@NonNull PageViewHolder holder) {
|
|
93
|
+
super.onViewAttachedToWindow(holder);
|
|
94
|
+
removeAttachedViewKeys(holder.pageView);
|
|
95
|
+
int position = holder.getBindingAdapterPosition();
|
|
96
|
+
if (position == RecyclerView.NO_POSITION) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
attachedViews.put(position, holder.pageView);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@Override
|
|
103
|
+
public void onViewDetachedFromWindow(@NonNull PageViewHolder holder) {
|
|
104
|
+
super.onViewDetachedFromWindow(holder);
|
|
105
|
+
removeAttachedViewKeys(holder.pageView);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Override
|
|
109
|
+
public void onViewRecycled(@NonNull PageViewHolder holder) {
|
|
110
|
+
super.onViewRecycled(holder);
|
|
111
|
+
removeAttachedViewKeys(holder.pageView);
|
|
112
|
+
holder.pageView.release();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private void removeAttachedViewKeys(@NonNull GalleryViewerPageView pageView) {
|
|
116
|
+
List<Integer> keys = new ArrayList<>();
|
|
117
|
+
for (Map.Entry<Integer, GalleryViewerPageView> entry : attachedViews.entrySet()) {
|
|
118
|
+
if (entry.getValue() == pageView) {
|
|
119
|
+
keys.add(entry.getKey());
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (Integer key : keys) {
|
|
123
|
+
attachedViews.remove(key);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@Override
|
|
128
|
+
public int getItemCount() {
|
|
129
|
+
return items.size();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static final class PageViewHolder extends RecyclerView.ViewHolder {
|
|
133
|
+
final GalleryViewerPageView pageView;
|
|
134
|
+
|
|
135
|
+
PageViewHolder(@NonNull GalleryViewerPageView itemView) {
|
|
136
|
+
super(itemView);
|
|
137
|
+
this.pageView = itemView;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package cn.fontree.zkit.ui.preview;
|
|
2
|
+
|
|
3
|
+
import android.graphics.RectF;
|
|
4
|
+
|
|
5
|
+
import androidx.annotation.NonNull;
|
|
6
|
+
|
|
7
|
+
public final class GallerySharedElementGeometry {
|
|
8
|
+
private final RectF visibleFrameInWindow;
|
|
9
|
+
private final RectF contentFrameInVisibleBounds;
|
|
10
|
+
|
|
11
|
+
public GallerySharedElementGeometry(@NonNull RectF visibleFrameInWindow, @NonNull RectF contentFrameInVisibleBounds) {
|
|
12
|
+
this.visibleFrameInWindow = new RectF(visibleFrameInWindow);
|
|
13
|
+
this.contentFrameInVisibleBounds = new RectF(contentFrameInVisibleBounds);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@NonNull
|
|
17
|
+
public RectF getVisibleFrameInWindow() {
|
|
18
|
+
return new RectF(visibleFrameInWindow);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@NonNull
|
|
22
|
+
public RectF getContentFrameInVisibleBounds() {
|
|
23
|
+
return new RectF(contentFrameInVisibleBounds);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package cn.fontree.zkit.ui.preview;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
|
|
6
|
+
public final class GallerySharedElementNames {
|
|
7
|
+
private static final String PREFIX = "zkit-native-image-preview:media:";
|
|
8
|
+
|
|
9
|
+
private GallerySharedElementNames() {
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@NonNull
|
|
13
|
+
public static String forItem(@NonNull GalleryMediaItem item) {
|
|
14
|
+
return forItem(null, item);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@NonNull
|
|
18
|
+
public static String forItem(@Nullable String galleryId, @NonNull GalleryMediaItem item) {
|
|
19
|
+
return forId(galleryId, item.getId());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@NonNull
|
|
23
|
+
public static String forIndex(int index) {
|
|
24
|
+
return forIndex(null, index);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@NonNull
|
|
28
|
+
public static String forIndex(@Nullable String galleryId, int index) {
|
|
29
|
+
return forId(galleryId, String.valueOf(index));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@NonNull
|
|
33
|
+
private static String forId(@Nullable String galleryId, @NonNull String id) {
|
|
34
|
+
if (galleryId == null || galleryId.isEmpty()) {
|
|
35
|
+
return PREFIX + id;
|
|
36
|
+
}
|
|
37
|
+
return PREFIX + galleryId + ":" + id;
|
|
38
|
+
}
|
|
39
|
+
}
|