zkit-ui 2.0.4 → 2.0.6
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/NativeImagePreviewLayoutSupport.java +450 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewMediaItem.java +36 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewModule.kt +43 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewPagerAdapter.java +140 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewSharedElementGeometry.java +25 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewSharedElementNames.java +39 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewSharedElementState.java +25 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewSourceViewRegistry.java +115 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewTransitionSnapshotView.java +60 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewVideoPlayerView.java +338 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewView.kt +305 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewViewerOverlayView.java +636 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewViewerPageView.java +357 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewViewerTransitionController.java +223 -0
- package/android/src/main/res/layout/native_image_preview_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 +4 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.js +150 -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 +68 -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 +83 -0
- package/dist/ui/NativeImagePreview/web/geometry.d.ts +20 -0
- package/dist/ui/NativeImagePreview/web/geometry.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/geometry.js +121 -0
- package/dist/ui/NativeImagePreview/web/media.d.ts +6 -0
- package/dist/ui/NativeImagePreview/web/media.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/media.js +90 -0
- package/dist/ui/NativeImagePreview/web/styles.css +137 -0
- package/dist/ui/NativeImagePreview/web/transition.d.ts +15 -0
- package/dist/ui/NativeImagePreview/web/transition.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/transition.js +77 -0
- package/dist/ui/NativeImagePreview/web/types.d.ts +74 -0
- package/dist/ui/NativeImagePreview/web/types.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/types.js +3 -0
- package/dist/ui/NativeImagePreview/web/viewer.d.ts +104 -0
- package/dist/ui/NativeImagePreview/web/viewer.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/viewer.js +894 -0
- package/expo-module.config.json +11 -0
- package/ios/NativeImagePreview/NativeImagePreviewImageLoader.swift +73 -0
- package/ios/NativeImagePreview/NativeImagePreviewImageViewInteraction.swift +245 -0
- package/ios/NativeImagePreview/NativeImagePreviewLayoutSupport.swift +143 -0
- package/ios/NativeImagePreview/NativeImagePreviewMedia.swift +28 -0
- package/ios/NativeImagePreview/NativeImagePreviewSourceViewRegistry.swift +80 -0
- package/ios/NativeImagePreview/NativeImagePreviewVideoPlayerView.swift +437 -0
- package/ios/NativeImagePreview/NativeImagePreviewViewerConfiguration.swift +51 -0
- package/ios/NativeImagePreview/NativeImagePreviewViewerController.swift +745 -0
- package/ios/NativeImagePreview/NativeImagePreviewViewerPageView.swift +623 -0
- package/ios/NativeImagePreview/NativeImagePreviewViewerTransitionCoordinator.swift +312 -0
- package/ios/NativeImagePreviewModule.swift +46 -0
- package/ios/NativeImagePreviewView.swift +252 -0
- package/package.json +16 -2
- package/src/all.ts +18 -0
- package/src/services/ImagePreviewService/README.md +19 -0
- package/src/types.d.ts +1 -0
- package/src/ui/NativeImagePreview/NativeImagePreview.tsx +146 -0
- package/src/ui/NativeImagePreview/NativeImagePreview.web.tsx +201 -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 +94 -0
- package/src/ui/NativeImagePreview/utils.ts +108 -0
- package/src/ui/NativeImagePreview/web/geometry.ts +134 -0
- package/src/ui/NativeImagePreview/web/media.ts +91 -0
- package/src/ui/NativeImagePreview/web/styles.css +137 -0
- package/src/ui/NativeImagePreview/web/transition.ts +111 -0
- package/src/ui/NativeImagePreview/web/types.ts +88 -0
- package/src/ui/NativeImagePreview/web/viewer.ts +1024 -0
- package/zkit-ui.podspec +13 -1
package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewViewerOverlayView.java
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
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.KeyEvent;
|
|
7
|
+
import android.view.MotionEvent;
|
|
8
|
+
import android.view.View;
|
|
9
|
+
import android.view.ViewConfiguration;
|
|
10
|
+
import android.view.ViewGroup;
|
|
11
|
+
import android.view.VelocityTracker;
|
|
12
|
+
import android.view.ViewParent;
|
|
13
|
+
import android.widget.FrameLayout;
|
|
14
|
+
import android.widget.ImageView;
|
|
15
|
+
|
|
16
|
+
import androidx.annotation.NonNull;
|
|
17
|
+
import androidx.annotation.Nullable;
|
|
18
|
+
import androidx.viewpager2.widget.ViewPager2;
|
|
19
|
+
|
|
20
|
+
import java.util.ArrayList;
|
|
21
|
+
import java.util.List;
|
|
22
|
+
|
|
23
|
+
public final class NativeImagePreviewViewerOverlayView extends FrameLayout implements NativeImagePreviewViewerPageView.Listener {
|
|
24
|
+
public interface Listener {
|
|
25
|
+
void onOverlayDismissed();
|
|
26
|
+
|
|
27
|
+
void onOverlayIndexChange(int index);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private final List<NativeImagePreviewMediaItem> items;
|
|
31
|
+
@Nullable
|
|
32
|
+
private final String previewGroupId;
|
|
33
|
+
@Nullable
|
|
34
|
+
private final Listener listener;
|
|
35
|
+
private final boolean lightTheme;
|
|
36
|
+
private final int touchSlop;
|
|
37
|
+
private final int minFlingVelocity;
|
|
38
|
+
|
|
39
|
+
private View backgroundView;
|
|
40
|
+
private FrameLayout contentView;
|
|
41
|
+
private ViewPager2 viewPager;
|
|
42
|
+
private NativeImagePreviewPagerAdapter pagerAdapter;
|
|
43
|
+
private NativeImagePreviewViewerTransitionController transitionController;
|
|
44
|
+
private int currentIndex;
|
|
45
|
+
private float downX;
|
|
46
|
+
private float downY;
|
|
47
|
+
private boolean dragging;
|
|
48
|
+
private boolean multiTouchLock;
|
|
49
|
+
private boolean needReanchorAfterMultiTouch;
|
|
50
|
+
private float dragStartRawY;
|
|
51
|
+
private float dragStartTranslationY;
|
|
52
|
+
@Nullable
|
|
53
|
+
private VelocityTracker velocityTracker;
|
|
54
|
+
@Nullable
|
|
55
|
+
private View dragTarget;
|
|
56
|
+
@Nullable
|
|
57
|
+
private NativeImagePreviewViewerPageView dragPageView;
|
|
58
|
+
@Nullable
|
|
59
|
+
private ImageView hiddenActiveSourceView;
|
|
60
|
+
private float hiddenActiveSourcePreviousAlpha = 1f;
|
|
61
|
+
private boolean closing;
|
|
62
|
+
private boolean openingTransitionStarted;
|
|
63
|
+
private boolean contentPresented;
|
|
64
|
+
private boolean finished;
|
|
65
|
+
|
|
66
|
+
private final Runnable openTransitionReadyWatcher = new Runnable() {
|
|
67
|
+
@Override
|
|
68
|
+
public void run() {
|
|
69
|
+
if (openingTransitionStarted || closing || finished) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
NativeImagePreviewViewerPageView pageView = resolveCurrentPageView();
|
|
73
|
+
if (pageView == null || getWidth() <= 0 || getHeight() <= 0) {
|
|
74
|
+
postOnAnimation(this);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
startOpenTransition(pageView, pageView.sharedElementState());
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
public NativeImagePreviewViewerOverlayView(
|
|
82
|
+
@NonNull Context context,
|
|
83
|
+
@NonNull List<NativeImagePreviewMediaItem> sourceItems,
|
|
84
|
+
int startIndex,
|
|
85
|
+
boolean lightTheme,
|
|
86
|
+
@Nullable String previewGroupId,
|
|
87
|
+
@Nullable Listener listener
|
|
88
|
+
) {
|
|
89
|
+
super(context);
|
|
90
|
+
this.items = new ArrayList<>(sourceItems);
|
|
91
|
+
this.currentIndex = Math.max(0, Math.min(startIndex, items.size() - 1));
|
|
92
|
+
this.lightTheme = lightTheme;
|
|
93
|
+
this.previewGroupId = previewGroupId;
|
|
94
|
+
this.listener = listener;
|
|
95
|
+
ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
|
|
96
|
+
touchSlop = viewConfiguration.getScaledTouchSlop();
|
|
97
|
+
minFlingVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
|
|
98
|
+
setLayoutParams(new LayoutParams(
|
|
99
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
100
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
101
|
+
));
|
|
102
|
+
setClickable(true);
|
|
103
|
+
setFocusable(true);
|
|
104
|
+
setFocusableInTouchMode(true);
|
|
105
|
+
setupUi();
|
|
106
|
+
post(openTransitionReadyWatcher);
|
|
107
|
+
requestFocus();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public void requestClose() {
|
|
111
|
+
closeViewer();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public void dismissImmediately() {
|
|
115
|
+
finishWithoutAnimation();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@Override
|
|
119
|
+
protected void onDetachedFromWindow() {
|
|
120
|
+
if (!finished) {
|
|
121
|
+
cleanup();
|
|
122
|
+
}
|
|
123
|
+
super.onDetachedFromWindow();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Override
|
|
127
|
+
public boolean dispatchKeyEvent(KeyEvent event) {
|
|
128
|
+
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
|
|
129
|
+
requestClose();
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
return super.dispatchKeyEvent(event);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@Override
|
|
136
|
+
public boolean dispatchTouchEvent(MotionEvent event) {
|
|
137
|
+
if (closing || !contentPresented) {
|
|
138
|
+
super.dispatchTouchEvent(event);
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
int action = event.getActionMasked();
|
|
143
|
+
if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
|
144
|
+
enterMultiTouchLock();
|
|
145
|
+
super.dispatchTouchEvent(event);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
if (action == MotionEvent.ACTION_POINTER_UP) {
|
|
149
|
+
if (multiTouchLock && event.getPointerCount() - 1 <= 1) {
|
|
150
|
+
exitMultiTouchLockAndWaitReanchor();
|
|
151
|
+
}
|
|
152
|
+
super.dispatchTouchEvent(event);
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
if (multiTouchLock && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) {
|
|
156
|
+
clearMultiTouchLock();
|
|
157
|
+
super.dispatchTouchEvent(event);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
if (multiTouchLock) {
|
|
161
|
+
super.dispatchTouchEvent(event);
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
switch (action) {
|
|
166
|
+
case MotionEvent.ACTION_DOWN:
|
|
167
|
+
if (needReanchorAfterMultiTouch) {
|
|
168
|
+
needReanchorAfterMultiTouch = false;
|
|
169
|
+
}
|
|
170
|
+
syncPagerGesturePolicy();
|
|
171
|
+
downX = event.getRawX();
|
|
172
|
+
downY = event.getRawY();
|
|
173
|
+
dragging = false;
|
|
174
|
+
dragTarget = null;
|
|
175
|
+
dragPageView = null;
|
|
176
|
+
ensureVelocityTracker();
|
|
177
|
+
velocityTracker.clear();
|
|
178
|
+
velocityTracker.addMovement(event);
|
|
179
|
+
break;
|
|
180
|
+
case MotionEvent.ACTION_MOVE:
|
|
181
|
+
if (needReanchorAfterMultiTouch) {
|
|
182
|
+
super.dispatchTouchEvent(event);
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
ensureVelocityTracker();
|
|
186
|
+
velocityTracker.addMovement(event);
|
|
187
|
+
if (!dragging) {
|
|
188
|
+
float dx = event.getRawX() - downX;
|
|
189
|
+
float dy = event.getRawY() - downY;
|
|
190
|
+
NativeImagePreviewViewerPageView pageView = resolveCurrentPageView();
|
|
191
|
+
if (pageView != null
|
|
192
|
+
&& Math.abs(dy) > touchSlop
|
|
193
|
+
&& pageView.canBeginVerticalDismiss(dx, dy, event.getRawX(), event.getRawY())) {
|
|
194
|
+
startDragging(pageView, event.getRawY());
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (dragging) {
|
|
198
|
+
applyDragTransform(event.getRawY());
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
syncPagerGesturePolicy();
|
|
202
|
+
break;
|
|
203
|
+
case MotionEvent.ACTION_UP:
|
|
204
|
+
case MotionEvent.ACTION_CANCEL:
|
|
205
|
+
if (needReanchorAfterMultiTouch) {
|
|
206
|
+
if (action == MotionEvent.ACTION_CANCEL) {
|
|
207
|
+
needReanchorAfterMultiTouch = false;
|
|
208
|
+
syncPagerGesturePolicy();
|
|
209
|
+
}
|
|
210
|
+
releaseVelocityTracker();
|
|
211
|
+
super.dispatchTouchEvent(event);
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
if (dragging) {
|
|
215
|
+
ensureVelocityTracker();
|
|
216
|
+
velocityTracker.addMovement(event);
|
|
217
|
+
float velocityY = consumeYVelocity();
|
|
218
|
+
View target = getActiveDragTarget();
|
|
219
|
+
float height = Math.max(1f, getHeight());
|
|
220
|
+
float dismissDistance = height * 0.16f;
|
|
221
|
+
float quickFlingDistance = height * 0.06f;
|
|
222
|
+
float dismissVelocity = Math.max(1300f, minFlingVelocity * 1.6f);
|
|
223
|
+
boolean dismissByDistance = Math.abs(target.getTranslationY()) > dismissDistance;
|
|
224
|
+
boolean dismissByFling = Math.abs(target.getTranslationY()) > quickFlingDistance
|
|
225
|
+
&& Math.abs(velocityY) > dismissVelocity;
|
|
226
|
+
dragging = false;
|
|
227
|
+
syncPagerGesturePolicy();
|
|
228
|
+
releaseVelocityTracker();
|
|
229
|
+
if (dismissByDistance || dismissByFling) {
|
|
230
|
+
closeViewer();
|
|
231
|
+
} else {
|
|
232
|
+
animateBackToCenter();
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
releaseVelocityTracker();
|
|
237
|
+
break;
|
|
238
|
+
default:
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
super.dispatchTouchEvent(event);
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@Override
|
|
247
|
+
public void onDismissRequested(@NonNull NativeImagePreviewViewerPageView pageView) {
|
|
248
|
+
if (pageView == resolveCurrentPageView()) {
|
|
249
|
+
closeViewer();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@Override
|
|
254
|
+
public void onVideoCloseRequested(@NonNull NativeImagePreviewViewerPageView pageView) {
|
|
255
|
+
if (pageView == resolveCurrentPageView()) {
|
|
256
|
+
closeViewer();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private void setupUi() {
|
|
261
|
+
backgroundView = new View(getContext());
|
|
262
|
+
backgroundView.setLayoutParams(new FrameLayout.LayoutParams(
|
|
263
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
264
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
265
|
+
));
|
|
266
|
+
backgroundView.setBackgroundColor(lightTheme ? 0xFFFFFFFF : 0xFF000000);
|
|
267
|
+
backgroundView.setAlpha(0f);
|
|
268
|
+
addView(backgroundView);
|
|
269
|
+
|
|
270
|
+
contentView = new FrameLayout(getContext());
|
|
271
|
+
contentView.setLayoutParams(new FrameLayout.LayoutParams(
|
|
272
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
273
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
274
|
+
));
|
|
275
|
+
contentView.setAlpha(0f);
|
|
276
|
+
|
|
277
|
+
viewPager = new ViewPager2(getContext());
|
|
278
|
+
viewPager.setLayoutParams(new FrameLayout.LayoutParams(
|
|
279
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
280
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
281
|
+
));
|
|
282
|
+
viewPager.setOffscreenPageLimit(1);
|
|
283
|
+
viewPager.setUserInputEnabled(false);
|
|
284
|
+
pagerAdapter = new NativeImagePreviewPagerAdapter(this, this::applySourcePlaceholderIfNeeded);
|
|
285
|
+
viewPager.setAdapter(pagerAdapter);
|
|
286
|
+
pagerAdapter.setItems(items);
|
|
287
|
+
viewPager.setCurrentItem(currentIndex, false);
|
|
288
|
+
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
|
289
|
+
@Override
|
|
290
|
+
public void onPageSelected(int position) {
|
|
291
|
+
currentIndex = position;
|
|
292
|
+
if (contentPresented) {
|
|
293
|
+
pagerAdapter.setActiveIndex(position);
|
|
294
|
+
syncPagerGesturePolicy();
|
|
295
|
+
hideActiveSourceViewForCurrentIndex();
|
|
296
|
+
if (listener != null) {
|
|
297
|
+
listener.onOverlayIndexChange(position);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
contentView.addView(viewPager);
|
|
303
|
+
addView(contentView);
|
|
304
|
+
|
|
305
|
+
transitionController = new NativeImagePreviewViewerTransitionController(this, backgroundView, contentView);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private void startOpenTransition(
|
|
309
|
+
@NonNull NativeImagePreviewViewerPageView pageView,
|
|
310
|
+
@Nullable NativeImagePreviewSharedElementState resolvedPageState
|
|
311
|
+
) {
|
|
312
|
+
if (openingTransitionStarted) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
openingTransitionStarted = true;
|
|
316
|
+
NativeImagePreviewMediaItem item = items.get(currentIndex);
|
|
317
|
+
String anchorKey = NativeImagePreviewSharedElementNames.forItem(previewGroupId, item);
|
|
318
|
+
ImageView sourceView = NativeImagePreviewSourceViewRegistry.find(anchorKey);
|
|
319
|
+
NativeImagePreviewSharedElementState sourceState = NativeImagePreviewLayoutSupport.captureImageViewState(sourceView);
|
|
320
|
+
applySourcePlaceholderIfNeeded(pageView, currentIndex);
|
|
321
|
+
pageView.prepareForOpenTransition();
|
|
322
|
+
pageView.setMediaHidden(true);
|
|
323
|
+
|
|
324
|
+
Drawable fallbackDrawable = sourceState != null ? sourceState.getDrawable() : null;
|
|
325
|
+
RectF overlayBounds = NativeImagePreviewLayoutSupport.viewBoundsOnScreen(this);
|
|
326
|
+
NativeImagePreviewSharedElementState effectivePageState = usableSharedElementState(resolvedPageState)
|
|
327
|
+
? resolvedPageState
|
|
328
|
+
: pageView.sharedElementState();
|
|
329
|
+
NativeImagePreviewSharedElementGeometry targetGeometry = usableSharedElementState(effectivePageState)
|
|
330
|
+
? effectivePageState.getGeometry()
|
|
331
|
+
: defaultOpenGeometryFromSource(sourceState, overlayBounds, pageView, fallbackDrawable);
|
|
332
|
+
|
|
333
|
+
transitionController.performOpenTransition(
|
|
334
|
+
sourceView,
|
|
335
|
+
sourceState,
|
|
336
|
+
targetGeometry,
|
|
337
|
+
() -> finishOpenTransition(pageView)
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private void finishOpenTransition(@NonNull NativeImagePreviewViewerPageView pageView) {
|
|
342
|
+
if (contentPresented) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
pageView.setMediaHidden(false);
|
|
346
|
+
transitionController.completeOpenTransition(() -> {
|
|
347
|
+
contentPresented = true;
|
|
348
|
+
pagerAdapter.setActiveIndex(currentIndex);
|
|
349
|
+
syncPagerGesturePolicy();
|
|
350
|
+
hideActiveSourceViewForCurrentIndex();
|
|
351
|
+
if (listener != null) {
|
|
352
|
+
listener.onOverlayIndexChange(currentIndex);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private void closeViewer() {
|
|
358
|
+
if (closing || finished) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
syncCurrentIndexFromPager();
|
|
362
|
+
closing = true;
|
|
363
|
+
dragging = false;
|
|
364
|
+
viewPager.setUserInputEnabled(false);
|
|
365
|
+
viewPager.requestDisallowInterceptTouchEvent(false);
|
|
366
|
+
releaseVelocityTracker();
|
|
367
|
+
|
|
368
|
+
NativeImagePreviewViewerPageView pageView = resolveCurrentPageView();
|
|
369
|
+
if (pageView == null) {
|
|
370
|
+
finishWithoutAnimation();
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
pageView.prepareForReturnTransition();
|
|
375
|
+
NativeImagePreviewSharedElementState pageState = pageView.sharedElementState();
|
|
376
|
+
pageView.setMediaHidden(true);
|
|
377
|
+
|
|
378
|
+
NativeImagePreviewMediaItem item = items.get(currentIndex);
|
|
379
|
+
String anchorKey = NativeImagePreviewSharedElementNames.forItem(previewGroupId, item);
|
|
380
|
+
ImageView targetView = NativeImagePreviewSourceViewRegistry.find(anchorKey);
|
|
381
|
+
NativeImagePreviewSharedElementState targetState = NativeImagePreviewLayoutSupport.captureImageViewState(targetView);
|
|
382
|
+
NativeImagePreviewSharedElementGeometry targetGeometry = targetState != null
|
|
383
|
+
? targetState.getGeometry()
|
|
384
|
+
: NativeImagePreviewLayoutSupport.captureImageViewGeometry(
|
|
385
|
+
targetView,
|
|
386
|
+
pageState != null ? pageState.getDrawable() : null
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
transitionController.performCloseTransition(
|
|
390
|
+
pageState,
|
|
391
|
+
targetGeometry,
|
|
392
|
+
targetView,
|
|
393
|
+
() -> {
|
|
394
|
+
restoreHiddenActiveSourceView();
|
|
395
|
+
finishWithoutAnimation();
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
@Nullable
|
|
401
|
+
private NativeImagePreviewViewerPageView resolveCurrentPageView() {
|
|
402
|
+
NativeImagePreviewViewerPageView pageView = pagerAdapter.findAttachedView(currentIndex);
|
|
403
|
+
if (pageView != null) {
|
|
404
|
+
return pageView;
|
|
405
|
+
}
|
|
406
|
+
return pagerAdapter.findActiveView();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
@NonNull
|
|
410
|
+
private NativeImagePreviewSharedElementGeometry defaultOpenGeometryFromSource(
|
|
411
|
+
@Nullable NativeImagePreviewSharedElementState sourceState,
|
|
412
|
+
@NonNull RectF overlayBounds,
|
|
413
|
+
@NonNull NativeImagePreviewViewerPageView pageView,
|
|
414
|
+
@Nullable Drawable fallbackDrawable
|
|
415
|
+
) {
|
|
416
|
+
if (sourceState != null) {
|
|
417
|
+
RectF sourceContent = sourceState.getGeometry().getContentFrameInVisibleBounds();
|
|
418
|
+
if (sourceContent.width() > 1f && sourceContent.height() > 1f) {
|
|
419
|
+
return NativeImagePreviewLayoutSupport.defaultGeometryForAspectRatio(
|
|
420
|
+
sourceContent.width(),
|
|
421
|
+
sourceContent.height(),
|
|
422
|
+
overlayBounds
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return pageView.defaultTransitionGeometry(overlayBounds, fallbackDrawable);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
private boolean usableSharedElementState(@Nullable NativeImagePreviewSharedElementState state) {
|
|
430
|
+
if (state == null) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
RectF visibleFrame = state.getGeometry().getVisibleFrameInWindow();
|
|
434
|
+
RectF contentFrame = state.getGeometry().getContentFrameInVisibleBounds();
|
|
435
|
+
return visibleFrame.width() > 1f
|
|
436
|
+
&& visibleFrame.height() > 1f
|
|
437
|
+
&& contentFrame.width() > 1f
|
|
438
|
+
&& contentFrame.height() > 1f;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private void syncCurrentIndexFromPager() {
|
|
442
|
+
if (viewPager == null || items.isEmpty()) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
int pagerIndex = viewPager.getCurrentItem();
|
|
446
|
+
currentIndex = Math.max(0, Math.min(pagerIndex, items.size() - 1));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private void startDragging(@NonNull NativeImagePreviewViewerPageView pageView, float rawY) {
|
|
450
|
+
dragging = true;
|
|
451
|
+
dragPageView = pageView;
|
|
452
|
+
dragPageView.prepareForDismissDrag();
|
|
453
|
+
dragTarget = pageView.getDragTarget();
|
|
454
|
+
dragTarget.animate().cancel();
|
|
455
|
+
backgroundView.animate().cancel();
|
|
456
|
+
dragStartRawY = rawY;
|
|
457
|
+
dragStartTranslationY = dragTarget.getTranslationY();
|
|
458
|
+
viewPager.setUserInputEnabled(false);
|
|
459
|
+
viewPager.requestDisallowInterceptTouchEvent(true);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
private void enterMultiTouchLock() {
|
|
463
|
+
multiTouchLock = true;
|
|
464
|
+
needReanchorAfterMultiTouch = false;
|
|
465
|
+
if (dragging) {
|
|
466
|
+
dragging = false;
|
|
467
|
+
animateBackToCenter();
|
|
468
|
+
}
|
|
469
|
+
dragTarget = null;
|
|
470
|
+
releaseVelocityTracker();
|
|
471
|
+
viewPager.setUserInputEnabled(false);
|
|
472
|
+
viewPager.requestDisallowInterceptTouchEvent(true);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
private void exitMultiTouchLockAndWaitReanchor() {
|
|
476
|
+
multiTouchLock = false;
|
|
477
|
+
needReanchorAfterMultiTouch = true;
|
|
478
|
+
dragging = false;
|
|
479
|
+
dragTarget = null;
|
|
480
|
+
releaseVelocityTracker();
|
|
481
|
+
viewPager.setUserInputEnabled(false);
|
|
482
|
+
viewPager.requestDisallowInterceptTouchEvent(true);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
private void clearMultiTouchLock() {
|
|
486
|
+
multiTouchLock = false;
|
|
487
|
+
needReanchorAfterMultiTouch = false;
|
|
488
|
+
dragging = false;
|
|
489
|
+
dragTarget = null;
|
|
490
|
+
dragPageView = null;
|
|
491
|
+
releaseVelocityTracker();
|
|
492
|
+
syncPagerGesturePolicy();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
private void syncPagerGesturePolicy() {
|
|
496
|
+
NativeImagePreviewViewerPageView pageView = resolveCurrentPageView();
|
|
497
|
+
boolean canPage = items.size() > 1 && pageView != null && pageView.canPageHorizontally();
|
|
498
|
+
viewPager.setUserInputEnabled(canPage);
|
|
499
|
+
viewPager.requestDisallowInterceptTouchEvent(!canPage);
|
|
500
|
+
if (pageView != null) {
|
|
501
|
+
pageView.setAllowParentInterceptOnImageEdge(canPage);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
@NonNull
|
|
506
|
+
private View getActiveDragTarget() {
|
|
507
|
+
if (dragTarget != null) {
|
|
508
|
+
return dragTarget;
|
|
509
|
+
}
|
|
510
|
+
return viewPager;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private void applyDragTransform(float rawY) {
|
|
514
|
+
View target = getActiveDragTarget();
|
|
515
|
+
float dy = dragStartTranslationY + rawY - dragStartRawY;
|
|
516
|
+
float abs = Math.abs(dy);
|
|
517
|
+
float height = Math.max(1, getHeight());
|
|
518
|
+
float progress = Math.min(1f, abs / height);
|
|
519
|
+
float scale = Math.max(0.84f, 1f - progress * 0.16f);
|
|
520
|
+
target.setTranslationY(dy);
|
|
521
|
+
target.setScaleX(scale);
|
|
522
|
+
target.setScaleY(scale);
|
|
523
|
+
backgroundView.setAlpha(1f - Math.min(0.9f, progress * 1.25f));
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
private void animateBackToCenter() {
|
|
527
|
+
View target = getActiveDragTarget();
|
|
528
|
+
NativeImagePreviewViewerPageView restorePageView = dragPageView;
|
|
529
|
+
dragPageView = null;
|
|
530
|
+
target.animate()
|
|
531
|
+
.translationY(0f)
|
|
532
|
+
.scaleX(1f)
|
|
533
|
+
.scaleY(1f)
|
|
534
|
+
.setDuration(200L)
|
|
535
|
+
.withEndAction(() -> {
|
|
536
|
+
backgroundView.setAlpha(1f);
|
|
537
|
+
if (restorePageView != null) {
|
|
538
|
+
restorePageView.restoreAfterDismissCancelled();
|
|
539
|
+
}
|
|
540
|
+
})
|
|
541
|
+
.start();
|
|
542
|
+
backgroundView.animate()
|
|
543
|
+
.alpha(1f)
|
|
544
|
+
.setDuration(200L)
|
|
545
|
+
.start();
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
@Nullable
|
|
549
|
+
private ImageView sourceViewForIndex(int index) {
|
|
550
|
+
if (index < 0 || index >= items.size()) {
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
NativeImagePreviewMediaItem item = items.get(index);
|
|
554
|
+
String anchorKey = NativeImagePreviewSharedElementNames.forItem(previewGroupId, item);
|
|
555
|
+
return NativeImagePreviewSourceViewRegistry.find(anchorKey);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private void applySourcePlaceholderIfNeeded(@NonNull NativeImagePreviewViewerPageView pageView, int index) {
|
|
559
|
+
ImageView sourceView = sourceViewForIndex(index);
|
|
560
|
+
if (sourceView == null) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
Drawable sourceDrawable = NativeImagePreviewLayoutSupport.transitionDrawableForImageView(sourceView);
|
|
564
|
+
pageView.setImagePlaceholderIfEmpty(sourceDrawable);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private void hideActiveSourceViewForCurrentIndex() {
|
|
568
|
+
if (!contentPresented || closing) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
ImageView sourceView = sourceViewForIndex(currentIndex);
|
|
572
|
+
if (hiddenActiveSourceView != sourceView) {
|
|
573
|
+
restoreHiddenActiveSourceView();
|
|
574
|
+
hiddenActiveSourcePreviousAlpha = sourceView != null ? sourceView.getAlpha() : 1f;
|
|
575
|
+
hiddenActiveSourceView = sourceView;
|
|
576
|
+
}
|
|
577
|
+
if (sourceView != null) {
|
|
578
|
+
sourceView.setAlpha(0f);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
private void restoreHiddenActiveSourceView() {
|
|
583
|
+
if (hiddenActiveSourceView != null) {
|
|
584
|
+
hiddenActiveSourceView.setAlpha(hiddenActiveSourcePreviousAlpha);
|
|
585
|
+
}
|
|
586
|
+
hiddenActiveSourceView = null;
|
|
587
|
+
hiddenActiveSourcePreviousAlpha = 1f;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
private void ensureVelocityTracker() {
|
|
591
|
+
if (velocityTracker == null) {
|
|
592
|
+
velocityTracker = VelocityTracker.obtain();
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
private float consumeYVelocity() {
|
|
597
|
+
if (velocityTracker == null) {
|
|
598
|
+
return 0f;
|
|
599
|
+
}
|
|
600
|
+
velocityTracker.computeCurrentVelocity(1000);
|
|
601
|
+
return velocityTracker.getYVelocity();
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
private void releaseVelocityTracker() {
|
|
605
|
+
if (velocityTracker == null) {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
velocityTracker.recycle();
|
|
609
|
+
velocityTracker = null;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
private void finishWithoutAnimation() {
|
|
613
|
+
if (finished) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
finished = true;
|
|
617
|
+
closing = true;
|
|
618
|
+
cleanup();
|
|
619
|
+
ViewParent parent = getParent();
|
|
620
|
+
if (parent instanceof ViewGroup) {
|
|
621
|
+
((ViewGroup) parent).removeView(this);
|
|
622
|
+
}
|
|
623
|
+
if (listener != null) {
|
|
624
|
+
listener.onOverlayDismissed();
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
private void cleanup() {
|
|
629
|
+
restoreHiddenActiveSourceView();
|
|
630
|
+
removeCallbacks(openTransitionReadyWatcher);
|
|
631
|
+
if (pagerAdapter != null) {
|
|
632
|
+
pagerAdapter.releaseAll();
|
|
633
|
+
}
|
|
634
|
+
releaseVelocityTracker();
|
|
635
|
+
}
|
|
636
|
+
}
|