zkit-ui 2.0.3 → 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 +21 -13
- 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,25 @@
|
|
|
1
|
+
package cn.fontree.zkit.ui.preview;
|
|
2
|
+
|
|
3
|
+
import android.graphics.drawable.Drawable;
|
|
4
|
+
|
|
5
|
+
import androidx.annotation.NonNull;
|
|
6
|
+
|
|
7
|
+
public final class GallerySharedElementState {
|
|
8
|
+
private final Drawable drawable;
|
|
9
|
+
private final GallerySharedElementGeometry geometry;
|
|
10
|
+
|
|
11
|
+
public GallerySharedElementState(@NonNull Drawable drawable, @NonNull GallerySharedElementGeometry geometry) {
|
|
12
|
+
this.drawable = drawable;
|
|
13
|
+
this.geometry = geometry;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@NonNull
|
|
17
|
+
public Drawable getDrawable() {
|
|
18
|
+
return drawable;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@NonNull
|
|
22
|
+
public GallerySharedElementGeometry getGeometry() {
|
|
23
|
+
return geometry;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
package cn.fontree.zkit.ui.preview;
|
|
2
|
+
|
|
3
|
+
import android.view.View;
|
|
4
|
+
import android.widget.ImageView;
|
|
5
|
+
|
|
6
|
+
import androidx.annotation.NonNull;
|
|
7
|
+
import androidx.annotation.Nullable;
|
|
8
|
+
import androidx.core.view.ViewCompat;
|
|
9
|
+
|
|
10
|
+
import java.lang.ref.WeakReference;
|
|
11
|
+
import java.util.HashMap;
|
|
12
|
+
import java.util.Iterator;
|
|
13
|
+
import java.util.Map;
|
|
14
|
+
|
|
15
|
+
public final class GallerySourceViewRegistry {
|
|
16
|
+
private static final Map<String, WeakReference<ImageView>> SOURCE_VIEWS = new HashMap<>();
|
|
17
|
+
|
|
18
|
+
private GallerySourceViewRegistry() {
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public static synchronized void register(@NonNull String key, @NonNull ImageView imageView) {
|
|
22
|
+
cleanupLocked();
|
|
23
|
+
if (!hasUsableGeometry(imageView)) {
|
|
24
|
+
ImageView existingImageView = resolveMappedViewLocked(key);
|
|
25
|
+
boolean keepExisting = existingImageView != null
|
|
26
|
+
&& existingImageView != imageView
|
|
27
|
+
&& hasUsableGeometry(existingImageView);
|
|
28
|
+
clearMappingsForViewLocked(imageView);
|
|
29
|
+
if (keepExisting) {
|
|
30
|
+
SOURCE_VIEWS.put(key, new WeakReference<>(existingImageView));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
SOURCE_VIEWS.remove(key);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
ImageView previousImageView = resolveMappedViewLocked(key);
|
|
38
|
+
if (!isPreferredSourceView(imageView)
|
|
39
|
+
&& previousImageView != null
|
|
40
|
+
&& previousImageView != imageView
|
|
41
|
+
&& isPreferredSourceView(previousImageView)) {
|
|
42
|
+
clearMappingsForViewLocked(imageView);
|
|
43
|
+
SOURCE_VIEWS.put(key, new WeakReference<>(previousImageView));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
clearMappingsForViewLocked(imageView);
|
|
48
|
+
SOURCE_VIEWS.put(key, new WeakReference<>(imageView));
|
|
49
|
+
ViewCompat.setTransitionName(imageView, key);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public static synchronized void unregisterView(@NonNull ImageView imageView) {
|
|
53
|
+
clearMappingsForViewLocked(imageView);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Nullable
|
|
57
|
+
public static synchronized ImageView find(@NonNull String key) {
|
|
58
|
+
cleanupLocked();
|
|
59
|
+
WeakReference<ImageView> reference = SOURCE_VIEWS.get(key);
|
|
60
|
+
if (reference == null) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
ImageView imageView = reference.get();
|
|
64
|
+
if (imageView == null || !imageView.isAttachedToWindow()) {
|
|
65
|
+
SOURCE_VIEWS.remove(key);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
if (!hasUsableGeometry(imageView)) {
|
|
69
|
+
SOURCE_VIEWS.remove(key);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return imageView;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Nullable
|
|
76
|
+
private static ImageView resolveMappedViewLocked(@NonNull String key) {
|
|
77
|
+
WeakReference<ImageView> reference = SOURCE_VIEWS.get(key);
|
|
78
|
+
return reference != null ? reference.get() : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private static boolean hasUsableGeometry(@Nullable ImageView imageView) {
|
|
82
|
+
return imageView != null
|
|
83
|
+
&& imageView.isAttachedToWindow()
|
|
84
|
+
&& imageView.getWidth() > 0
|
|
85
|
+
&& imageView.getHeight() > 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private static boolean isPreferredSourceView(@Nullable ImageView imageView) {
|
|
89
|
+
return hasUsableGeometry(imageView)
|
|
90
|
+
&& imageView.getVisibility() == View.VISIBLE
|
|
91
|
+
&& imageView.isShown();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private static void clearMappingsForViewLocked(@NonNull ImageView imageView) {
|
|
95
|
+
Iterator<Map.Entry<String, WeakReference<ImageView>>> iterator = SOURCE_VIEWS.entrySet().iterator();
|
|
96
|
+
while (iterator.hasNext()) {
|
|
97
|
+
Map.Entry<String, WeakReference<ImageView>> entry = iterator.next();
|
|
98
|
+
ImageView mappedView = entry.getValue().get();
|
|
99
|
+
if (mappedView == null || mappedView == imageView) {
|
|
100
|
+
iterator.remove();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private static void cleanupLocked() {
|
|
106
|
+
Iterator<Map.Entry<String, WeakReference<ImageView>>> iterator = SOURCE_VIEWS.entrySet().iterator();
|
|
107
|
+
while (iterator.hasNext()) {
|
|
108
|
+
Map.Entry<String, WeakReference<ImageView>> entry = iterator.next();
|
|
109
|
+
ImageView mappedView = entry.getValue().get();
|
|
110
|
+
if (mappedView == null) {
|
|
111
|
+
iterator.remove();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
package cn.fontree.zkit.ui.preview;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.graphics.RectF;
|
|
5
|
+
import android.graphics.drawable.Drawable;
|
|
6
|
+
import android.view.Gravity;
|
|
7
|
+
import android.widget.FrameLayout;
|
|
8
|
+
import android.widget.ImageView;
|
|
9
|
+
|
|
10
|
+
import androidx.annotation.NonNull;
|
|
11
|
+
|
|
12
|
+
public final class GalleryTransitionSnapshotView extends FrameLayout {
|
|
13
|
+
private final ImageView imageView;
|
|
14
|
+
|
|
15
|
+
public GalleryTransitionSnapshotView(@NonNull Context context, @NonNull Drawable drawable) {
|
|
16
|
+
super(context);
|
|
17
|
+
setClipChildren(true);
|
|
18
|
+
setClipToPadding(true);
|
|
19
|
+
imageView = new ImageView(context);
|
|
20
|
+
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
|
|
21
|
+
imageView.setImageDrawable(drawable);
|
|
22
|
+
addView(imageView);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public void applyGeometry(@NonNull GallerySharedElementGeometry geometry) {
|
|
26
|
+
RectF visibleFrame = geometry.getVisibleFrameInWindow();
|
|
27
|
+
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams();
|
|
28
|
+
if (layoutParams == null) {
|
|
29
|
+
layoutParams = new FrameLayout.LayoutParams(
|
|
30
|
+
Math.max(1, Math.round(visibleFrame.width())),
|
|
31
|
+
Math.max(1, Math.round(visibleFrame.height()))
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
layoutParams.width = Math.max(1, Math.round(visibleFrame.width()));
|
|
35
|
+
layoutParams.height = Math.max(1, Math.round(visibleFrame.height()));
|
|
36
|
+
layoutParams.leftMargin = 0;
|
|
37
|
+
layoutParams.topMargin = 0;
|
|
38
|
+
layoutParams.gravity = Gravity.TOP | Gravity.START;
|
|
39
|
+
setLayoutParams(layoutParams);
|
|
40
|
+
setX(visibleFrame.left);
|
|
41
|
+
setY(visibleFrame.top);
|
|
42
|
+
|
|
43
|
+
RectF contentFrame = geometry.getContentFrameInVisibleBounds();
|
|
44
|
+
FrameLayout.LayoutParams imageLayoutParams = (FrameLayout.LayoutParams) imageView.getLayoutParams();
|
|
45
|
+
if (imageLayoutParams == null) {
|
|
46
|
+
imageLayoutParams = new FrameLayout.LayoutParams(
|
|
47
|
+
Math.max(1, Math.round(contentFrame.width())),
|
|
48
|
+
Math.max(1, Math.round(contentFrame.height()))
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
imageLayoutParams.width = Math.max(1, Math.round(contentFrame.width()));
|
|
52
|
+
imageLayoutParams.height = Math.max(1, Math.round(contentFrame.height()));
|
|
53
|
+
imageLayoutParams.leftMargin = 0;
|
|
54
|
+
imageLayoutParams.topMargin = 0;
|
|
55
|
+
imageLayoutParams.gravity = Gravity.TOP | Gravity.START;
|
|
56
|
+
imageView.setLayoutParams(imageLayoutParams);
|
|
57
|
+
imageView.setX(contentFrame.left);
|
|
58
|
+
imageView.setY(contentFrame.top);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
package cn.fontree.zkit.ui.preview;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.content.res.ColorStateList;
|
|
5
|
+
import android.graphics.Color;
|
|
6
|
+
import android.graphics.Rect;
|
|
7
|
+
import android.graphics.drawable.Drawable;
|
|
8
|
+
import android.net.Uri;
|
|
9
|
+
import android.util.TypedValue;
|
|
10
|
+
import android.view.Gravity;
|
|
11
|
+
import android.view.LayoutInflater;
|
|
12
|
+
import android.view.View;
|
|
13
|
+
import android.view.ViewGroup;
|
|
14
|
+
import android.widget.FrameLayout;
|
|
15
|
+
import android.widget.ImageView;
|
|
16
|
+
|
|
17
|
+
import androidx.annotation.NonNull;
|
|
18
|
+
import androidx.annotation.Nullable;
|
|
19
|
+
import androidx.appcompat.content.res.AppCompatResources;
|
|
20
|
+
import androidx.appcompat.widget.AppCompatImageButton;
|
|
21
|
+
import androidx.media3.common.MediaItem;
|
|
22
|
+
import androidx.media3.common.Player;
|
|
23
|
+
import androidx.media3.exoplayer.ExoPlayer;
|
|
24
|
+
import androidx.media3.ui.PlayerControlView;
|
|
25
|
+
import androidx.media3.ui.PlayerView;
|
|
26
|
+
|
|
27
|
+
import com.bumptech.glide.Glide;
|
|
28
|
+
import com.bumptech.glide.load.engine.GlideException;
|
|
29
|
+
import com.bumptech.glide.request.RequestListener;
|
|
30
|
+
import com.bumptech.glide.request.target.Target;
|
|
31
|
+
|
|
32
|
+
public final class GalleryVideoPlayerView extends FrameLayout {
|
|
33
|
+
public interface Listener {
|
|
34
|
+
void onCloseRequested();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private final ImageView posterImageView;
|
|
38
|
+
private final PlayerView playerView;
|
|
39
|
+
private final AppCompatImageButton closeButton;
|
|
40
|
+
@Nullable
|
|
41
|
+
private Runnable contentReadyListener;
|
|
42
|
+
@Nullable
|
|
43
|
+
private GalleryMediaItem item;
|
|
44
|
+
@Nullable
|
|
45
|
+
private ExoPlayer player;
|
|
46
|
+
@Nullable
|
|
47
|
+
private String preparedSourceUrl;
|
|
48
|
+
@Nullable
|
|
49
|
+
private Listener listener;
|
|
50
|
+
private boolean firstFrameReady;
|
|
51
|
+
private boolean active;
|
|
52
|
+
|
|
53
|
+
private final Player.Listener playerListener = new Player.Listener() {
|
|
54
|
+
@Override
|
|
55
|
+
public void onRenderedFirstFrame() {
|
|
56
|
+
firstFrameReady = true;
|
|
57
|
+
if (active) {
|
|
58
|
+
revealPlayer();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
public GalleryVideoPlayerView(@NonNull Context context) {
|
|
64
|
+
super(context);
|
|
65
|
+
setLayoutParams(new LayoutParams(
|
|
66
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
67
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
68
|
+
));
|
|
69
|
+
setBackgroundColor(Color.TRANSPARENT);
|
|
70
|
+
|
|
71
|
+
posterImageView = new ImageView(context);
|
|
72
|
+
posterImageView.setLayoutParams(new LayoutParams(
|
|
73
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
74
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
75
|
+
));
|
|
76
|
+
posterImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
|
77
|
+
posterImageView.setBackgroundColor(Color.TRANSPARENT);
|
|
78
|
+
addView(posterImageView);
|
|
79
|
+
|
|
80
|
+
playerView = (PlayerView) LayoutInflater.from(context)
|
|
81
|
+
.inflate(R.layout.galeria_player_view, this, false);
|
|
82
|
+
playerView.setUseController(true);
|
|
83
|
+
playerView.setControllerAutoShow(true);
|
|
84
|
+
playerView.setControllerHideOnTouch(true);
|
|
85
|
+
playerView.setShutterBackgroundColor(Color.TRANSPARENT);
|
|
86
|
+
playerView.setBackgroundColor(Color.TRANSPARENT);
|
|
87
|
+
playerView.setShowPreviousButton(false);
|
|
88
|
+
playerView.setShowNextButton(false);
|
|
89
|
+
playerView.setShowRewindButton(false);
|
|
90
|
+
playerView.setShowFastForwardButton(false);
|
|
91
|
+
playerView.setShowSubtitleButton(false);
|
|
92
|
+
playerView.setShowVrButton(false);
|
|
93
|
+
playerView.setShowShuffleButton(false);
|
|
94
|
+
playerView.setAlpha(0f);
|
|
95
|
+
playerView.setVisibility(VISIBLE);
|
|
96
|
+
addView(playerView);
|
|
97
|
+
hideControllerView(androidx.media3.ui.R.id.exo_settings);
|
|
98
|
+
hideControllerView(androidx.media3.ui.R.id.exo_overflow_show);
|
|
99
|
+
hideControllerView(androidx.media3.ui.R.id.exo_overflow_hide);
|
|
100
|
+
|
|
101
|
+
closeButton = new AppCompatImageButton(context);
|
|
102
|
+
LayoutParams closeLayoutParams = new LayoutParams(dp(48), dp(48));
|
|
103
|
+
closeLayoutParams.gravity = Gravity.TOP | Gravity.START;
|
|
104
|
+
closeLayoutParams.topMargin = dp(14);
|
|
105
|
+
closeLayoutParams.leftMargin = dp(10);
|
|
106
|
+
closeButton.setLayoutParams(closeLayoutParams);
|
|
107
|
+
closeButton.setImageDrawable(
|
|
108
|
+
AppCompatResources.getDrawable(context, androidx.appcompat.R.drawable.abc_ic_clear_material)
|
|
109
|
+
);
|
|
110
|
+
closeButton.setBackgroundResource(resolveSelectableItemBackgroundBorderless());
|
|
111
|
+
closeButton.setImageTintList(ColorStateList.valueOf(Color.WHITE));
|
|
112
|
+
closeButton.setPadding(dp(12), dp(12), dp(12), dp(12));
|
|
113
|
+
closeButton.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
|
114
|
+
closeButton.setVisibility(GONE);
|
|
115
|
+
closeButton.setOnClickListener(v -> {
|
|
116
|
+
if (listener != null) {
|
|
117
|
+
listener.onCloseRequested();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
addView(closeButton);
|
|
121
|
+
|
|
122
|
+
playerView.setControllerVisibilityListener((PlayerControlView.VisibilityListener) visibility -> {
|
|
123
|
+
if (firstFrameReady) {
|
|
124
|
+
closeButton.setVisibility(visibility == View.VISIBLE ? View.VISIBLE : View.GONE);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public void setListener(@Nullable Listener listener) {
|
|
130
|
+
this.listener = listener;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public void setOnContentReadyListener(@Nullable Runnable contentReadyListener) {
|
|
134
|
+
this.contentReadyListener = contentReadyListener;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public void bind(@NonNull GalleryMediaItem item) {
|
|
138
|
+
this.item = item;
|
|
139
|
+
firstFrameReady = false;
|
|
140
|
+
playerView.animate().cancel();
|
|
141
|
+
playerView.setAlpha(0f);
|
|
142
|
+
playerView.setVisibility(VISIBLE);
|
|
143
|
+
closeButton.setVisibility(GONE);
|
|
144
|
+
posterImageView.animate().cancel();
|
|
145
|
+
posterImageView.setAlpha(1f);
|
|
146
|
+
posterImageView.setVisibility(VISIBLE);
|
|
147
|
+
Glide.with(posterImageView).clear(posterImageView);
|
|
148
|
+
posterImageView.setImageDrawable(null);
|
|
149
|
+
Glide.with(posterImageView)
|
|
150
|
+
.load(item.getThumbnailUrl())
|
|
151
|
+
.dontAnimate()
|
|
152
|
+
.listener(new RequestListener<Drawable>() {
|
|
153
|
+
@Override
|
|
154
|
+
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
|
|
155
|
+
notifyContentReady();
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@Override
|
|
160
|
+
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, com.bumptech.glide.load.DataSource dataSource, boolean isFirstResource) {
|
|
161
|
+
notifyContentReady();
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
.into(posterImageView);
|
|
166
|
+
if (active) {
|
|
167
|
+
ensurePlayerPrepared(item.getSourceUrl());
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public void setActive(boolean active) {
|
|
172
|
+
this.active = active;
|
|
173
|
+
if (item == null) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (active) {
|
|
177
|
+
ensurePlayerPrepared(item.getSourceUrl());
|
|
178
|
+
if (player != null) {
|
|
179
|
+
player.play();
|
|
180
|
+
}
|
|
181
|
+
if (firstFrameReady) {
|
|
182
|
+
revealPlayer();
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
if (player != null) {
|
|
186
|
+
player.pause();
|
|
187
|
+
}
|
|
188
|
+
closeButton.setVisibility(GONE);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public void setMediaHidden(boolean hidden) {
|
|
193
|
+
setAlpha(hidden ? 0f : 1f);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@Nullable
|
|
197
|
+
public GallerySharedElementState sharedElementState() {
|
|
198
|
+
return GalleryLayoutSupport.captureImageViewState(posterImageView);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@Nullable
|
|
202
|
+
public Drawable getPosterDrawable() {
|
|
203
|
+
return posterImageView.getDrawable();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
public void prepareForReturnTransition() {
|
|
207
|
+
playerView.animate().cancel();
|
|
208
|
+
posterImageView.animate().cancel();
|
|
209
|
+
posterImageView.setAlpha(1f);
|
|
210
|
+
posterImageView.setVisibility(VISIBLE);
|
|
211
|
+
playerView.setAlpha(0f);
|
|
212
|
+
playerView.setVisibility(VISIBLE);
|
|
213
|
+
closeButton.setVisibility(GONE);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
public void restoreAfterDismissCancelled() {
|
|
217
|
+
if (!firstFrameReady) {
|
|
218
|
+
prepareForReturnTransition();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
playerView.setVisibility(VISIBLE);
|
|
222
|
+
playerView.setAlpha(1f);
|
|
223
|
+
posterImageView.setVisibility(VISIBLE);
|
|
224
|
+
posterImageView.setAlpha(0f);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
public boolean isTouchOnInteractiveControls(float rawX, float rawY) {
|
|
228
|
+
return isTouchInsideView(playerView.findViewById(androidx.media3.ui.R.id.exo_progress), rawX, rawY, dp(24))
|
|
229
|
+
|| isTouchInsideView(closeButton, rawX, rawY, dp(12));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
public void release() {
|
|
233
|
+
Glide.with(posterImageView.getContext().getApplicationContext()).clear(posterImageView);
|
|
234
|
+
posterImageView.setImageDrawable(null);
|
|
235
|
+
posterImageView.animate().cancel();
|
|
236
|
+
posterImageView.setAlpha(1f);
|
|
237
|
+
posterImageView.setVisibility(VISIBLE);
|
|
238
|
+
closeButton.setVisibility(GONE);
|
|
239
|
+
if (player != null) {
|
|
240
|
+
player.removeListener(playerListener);
|
|
241
|
+
player.release();
|
|
242
|
+
player = null;
|
|
243
|
+
playerView.setPlayer(null);
|
|
244
|
+
}
|
|
245
|
+
playerView.animate().cancel();
|
|
246
|
+
playerView.setAlpha(0f);
|
|
247
|
+
playerView.setVisibility(VISIBLE);
|
|
248
|
+
preparedSourceUrl = null;
|
|
249
|
+
firstFrameReady = false;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private void ensurePlayerPrepared(@NonNull String sourceUrl) {
|
|
253
|
+
if (player == null) {
|
|
254
|
+
player = new ExoPlayer.Builder(getContext()).build();
|
|
255
|
+
player.addListener(playerListener);
|
|
256
|
+
playerView.setPlayer(player);
|
|
257
|
+
}
|
|
258
|
+
if (sourceUrl.equals(preparedSourceUrl)) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
firstFrameReady = false;
|
|
262
|
+
playerView.animate().cancel();
|
|
263
|
+
playerView.setAlpha(0f);
|
|
264
|
+
playerView.setVisibility(VISIBLE);
|
|
265
|
+
closeButton.setVisibility(GONE);
|
|
266
|
+
player.setMediaItem(MediaItem.fromUri(Uri.parse(sourceUrl)));
|
|
267
|
+
player.prepare();
|
|
268
|
+
preparedSourceUrl = sourceUrl;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private void revealPlayer() {
|
|
272
|
+
playerView.setVisibility(VISIBLE);
|
|
273
|
+
playerView.animate().cancel();
|
|
274
|
+
playerView.setAlpha(0f);
|
|
275
|
+
posterImageView.animate().cancel();
|
|
276
|
+
posterImageView.setVisibility(VISIBLE);
|
|
277
|
+
posterImageView.setAlpha(1f);
|
|
278
|
+
playerView.animate()
|
|
279
|
+
.alpha(1f)
|
|
280
|
+
.setDuration(180L)
|
|
281
|
+
.start();
|
|
282
|
+
posterImageView.animate()
|
|
283
|
+
.alpha(0f)
|
|
284
|
+
.setDuration(220L)
|
|
285
|
+
.withEndAction(() -> {
|
|
286
|
+
if (playerView.isControllerFullyVisible()) {
|
|
287
|
+
closeButton.setVisibility(VISIBLE);
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
.start();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private void notifyContentReady() {
|
|
294
|
+
if (contentReadyListener != null) {
|
|
295
|
+
contentReadyListener.run();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private void hideControllerView(int viewId) {
|
|
300
|
+
View target = playerView.findViewById(viewId);
|
|
301
|
+
if (target != null) {
|
|
302
|
+
target.setVisibility(GONE);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private boolean isTouchInsideView(@Nullable View target, float rawX, float rawY, int extraPaddingPx) {
|
|
307
|
+
if (target == null || !target.isShown() || target.getWidth() <= 0 || target.getHeight() <= 0) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
int[] location = new int[2];
|
|
311
|
+
target.getLocationOnScreen(location);
|
|
312
|
+
Rect hitRect = new Rect(
|
|
313
|
+
location[0] - extraPaddingPx,
|
|
314
|
+
location[1] - extraPaddingPx,
|
|
315
|
+
location[0] + target.getWidth() + extraPaddingPx,
|
|
316
|
+
location[1] + target.getHeight() + extraPaddingPx
|
|
317
|
+
);
|
|
318
|
+
return hitRect.contains(Math.round(rawX), Math.round(rawY));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private int resolveSelectableItemBackgroundBorderless() {
|
|
322
|
+
TypedValue outValue = new TypedValue();
|
|
323
|
+
boolean resolved = getContext().getTheme().resolveAttribute(
|
|
324
|
+
android.R.attr.selectableItemBackgroundBorderless,
|
|
325
|
+
outValue,
|
|
326
|
+
true
|
|
327
|
+
);
|
|
328
|
+
return resolved ? outValue.resourceId : 0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private int dp(int value) {
|
|
332
|
+
return (int) TypedValue.applyDimension(
|
|
333
|
+
TypedValue.COMPLEX_UNIT_DIP,
|
|
334
|
+
value,
|
|
335
|
+
getResources().getDisplayMetrics()
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
}
|