@rafaelaugusto/jolt 0.1.0-beta.16 → 0.1.0-beta.17

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.
Files changed (84) hide show
  1. package/README.md +7 -2
  2. package/dist/bottom-sheet/geometry.d.ts +18 -0
  3. package/dist/bottom-sheet/geometry.d.ts.map +1 -0
  4. package/dist/bottom-sheet/geometry.js +51 -0
  5. package/dist/bottom-sheet/geometry.js.map +1 -0
  6. package/dist/bottom-sheet/index.d.ts +56 -0
  7. package/dist/bottom-sheet/index.d.ts.map +1 -0
  8. package/dist/bottom-sheet/index.js +462 -0
  9. package/dist/bottom-sheet/index.js.map +1 -0
  10. package/dist/bottom-sheet/native.d.ts +11 -0
  11. package/dist/bottom-sheet/native.d.ts.map +1 -0
  12. package/dist/bottom-sheet/native.js +173 -0
  13. package/dist/bottom-sheet/native.js.map +1 -0
  14. package/dist/gesture-native.d.ts +1 -0
  15. package/dist/gesture-native.d.ts.map +1 -1
  16. package/dist/gesture-native.js +6 -0
  17. package/dist/gesture-native.js.map +1 -1
  18. package/dist/gestures/index.d.ts +4 -0
  19. package/dist/gestures/index.d.ts.map +1 -1
  20. package/dist/gestures/index.js +12 -1
  21. package/dist/gestures/index.js.map +1 -1
  22. package/dist/native/index.d.ts +2 -0
  23. package/dist/native/index.d.ts.map +1 -1
  24. package/dist/native/index.js +1 -0
  25. package/dist/native/index.js.map +1 -1
  26. package/dist/native-property.d.ts +2 -0
  27. package/dist/native-property.d.ts.map +1 -1
  28. package/dist/native-property.js +12 -4
  29. package/dist/native-property.js.map +1 -1
  30. package/dist/reorder/handle.d.ts +6 -0
  31. package/dist/reorder/handle.d.ts.map +1 -0
  32. package/dist/reorder/handle.js +10 -0
  33. package/dist/reorder/handle.js.map +1 -0
  34. package/dist/reorder/index.d.ts +10 -0
  35. package/dist/reorder/index.d.ts.map +1 -0
  36. package/dist/reorder/index.js +233 -0
  37. package/dist/reorder/index.js.map +1 -0
  38. package/dist/reorder/list-view.d.ts +5 -0
  39. package/dist/reorder/list-view.d.ts.map +1 -0
  40. package/dist/reorder/list-view.js +238 -0
  41. package/dist/reorder/list-view.js.map +1 -0
  42. package/dist/reorder/model.d.ts +6 -0
  43. package/dist/reorder/model.d.ts.map +1 -0
  44. package/dist/reorder/model.js +30 -0
  45. package/dist/reorder/model.js.map +1 -0
  46. package/dist/reorder/surface.d.ts +6 -0
  47. package/dist/reorder/surface.d.ts.map +1 -0
  48. package/dist/reorder/surface.js +10 -0
  49. package/dist/reorder/surface.js.map +1 -0
  50. package/dist/reorder/types.d.ts +92 -0
  51. package/dist/reorder/types.d.ts.map +1 -0
  52. package/dist/reorder/types.js +2 -0
  53. package/dist/reorder/types.js.map +1 -0
  54. package/dist/scene-shader.d.ts +18 -0
  55. package/dist/scene-shader.d.ts.map +1 -0
  56. package/dist/scene-shader.js +76 -0
  57. package/dist/scene-shader.js.map +1 -0
  58. package/package.json +9 -1
  59. package/platforms/android/java/org/rafaelaugusto/jolt/GestureSession.java +7 -2
  60. package/platforms/android/java/org/rafaelaugusto/jolt/ReorderHandle.java +25 -0
  61. package/platforms/android/java/org/rafaelaugusto/jolt/ReorderSession.java +213 -0
  62. package/platforms/android/java/org/rafaelaugusto/jolt/ReorderSurface.java +27 -0
  63. package/platforms/android/proguard-rules.pro +5 -0
  64. package/platforms/ios/src/JoltGestureSession.h +1 -0
  65. package/platforms/ios/src/JoltGestureSession.m +7 -2
  66. package/platforms/ios/src/JoltReorderSession.h +15 -0
  67. package/platforms/ios/src/JoltReorderSession.m +205 -0
  68. package/platforms/ios/src/module.modulemap +1 -0
  69. package/src/android-plugin.d.ts +34 -0
  70. package/src/bottom-sheet/geometry.ts +84 -0
  71. package/src/bottom-sheet/index.ts +536 -0
  72. package/src/bottom-sheet/native.ts +214 -0
  73. package/src/gesture-native.ts +15 -0
  74. package/src/gestures/index.ts +16 -1
  75. package/src/ios-plugin.d.ts +32 -0
  76. package/src/native/index.ts +6 -0
  77. package/src/native-property.ts +17 -4
  78. package/src/reorder/handle.ts +9 -0
  79. package/src/reorder/index.ts +243 -0
  80. package/src/reorder/list-view.ts +262 -0
  81. package/src/reorder/model.ts +47 -0
  82. package/src/reorder/surface.ts +10 -0
  83. package/src/reorder/types.ts +87 -0
  84. package/src/scene-shader.ts +139 -0
@@ -0,0 +1,92 @@
1
+ import type { View } from '@nativescript/core';
2
+ export type ReorderKey = string | number;
3
+ export type ReorderCancelReason = 'cancelled' | 'unchanged' | 'data-changed' | 'removed' | 'unloaded' | 'disposed' | 'disabled' | 'geometry-changed';
4
+ export interface ReorderMove<K extends ReorderKey = ReorderKey> {
5
+ key: K;
6
+ /** Insert before this identity; null appends. The app owns the mutation. */
7
+ beforeKey: K | null;
8
+ fromIndex: number;
9
+ toIndex: number;
10
+ }
11
+ export interface ReorderOptions<T, K extends ReorderKey = ReorderKey> {
12
+ key(item: T): K;
13
+ /** View id, or a resolver within a realized row. Omit to drag anywhere on the row. */
14
+ handle?: string | ((row: View) => View | undefined);
15
+ onCommit(move: ReorderMove<K>): void;
16
+ onStart?: (key: K) => void;
17
+ onCancel?: (event: {
18
+ key: K;
19
+ reason: ReorderCancelReason;
20
+ }) => void;
21
+ /** Long-press activation in milliseconds; default 350. */
22
+ longPress?: number;
23
+ /** Edge region in DIPs and maximum native autoscroll speed in DIPs/second. */
24
+ edge?: {
25
+ size?: number;
26
+ speed?: number;
27
+ };
28
+ accessibility?: {
29
+ moveUp?: string;
30
+ moveDown?: string;
31
+ };
32
+ }
33
+ export interface Reorder<K extends ReorderKey = ReorderKey> {
34
+ readonly activeKey: K | undefined;
35
+ readonly disposed: boolean;
36
+ /** A keyboard/button alternative. Returns false for a missing key or unchanged order. */
37
+ move(key: K, beforeKey: K | null): boolean;
38
+ moveUp(key: K): boolean;
39
+ moveDown(key: K): boolean;
40
+ cancel(): void;
41
+ /** Call after in-place changes to a plain array or row geometry. */
42
+ refresh(): void;
43
+ pause(): void;
44
+ play(): void;
45
+ dispose(): void;
46
+ }
47
+ export type ReorderNativeEvent = {
48
+ type: 'start';
49
+ index: number;
50
+ } | {
51
+ type: 'commit';
52
+ index: number;
53
+ toIndex: number;
54
+ } | {
55
+ type: 'cancel';
56
+ geometryChanged?: boolean;
57
+ };
58
+ export interface ReorderAdapterDriver {
59
+ cancel(): void;
60
+ setEnabled(enabled: boolean): void;
61
+ setReducedMotion(reduced: boolean): void;
62
+ dispose(): void;
63
+ }
64
+ /**
65
+ * Optional list-plugin integration. Indices refer to the current items until a
66
+ * change invalidates the gesture. Drivers own native input, measured variable
67
+ * heights, snapshot overlays, recycled-cell restoration and edge scrolling.
68
+ * Emit only lifecycle events; do not bridge pointer/display frames to JS.
69
+ */
70
+ export interface ReorderAdapter<T> {
71
+ readonly view: View;
72
+ items(): readonly T[];
73
+ /** Notify synchronously when identities/order/content change. */
74
+ subscribe(changed: () => void): () => void;
75
+ attach(config: {
76
+ handle: ReorderOptions<T>['handle'];
77
+ longPress: number;
78
+ edgeSize: number;
79
+ edgeSpeed: number;
80
+ reducedMotion: boolean;
81
+ accessibility: {
82
+ moveUp: string;
83
+ moveDown: string;
84
+ };
85
+ event(event: ReorderNativeEvent): void;
86
+ /** Returns the moved identity; resolve it again after the app updates data. */
87
+ move(index: number, direction: -1 | 1): ReorderKey | undefined;
88
+ keyAt(index: number): ReorderKey | undefined;
89
+ indexOf(key: ReorderKey): number;
90
+ }): ReorderAdapterDriver;
91
+ }
92
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/reorder/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/reorder/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ /// <reference types="@nativescript/types-ios/lib/ios/objc-x86_64/objc!SceneKit" preserve="true" />
2
+ import { type NativeProperty } from './native-property.js';
3
+ export type SceneUniformType = 'float' | 'float2' | 'float3' | 'float4';
4
+ export type SceneUniformValue<T extends SceneUniformType> = T extends 'float' ? number : T extends 'float2' ? readonly [number, number] : T extends 'float3' ? readonly [number, number, number] : readonly [number, number, number, number];
5
+ export interface SceneUniformOptions<T extends SceneUniformType> {
6
+ type: T;
7
+ /** Finite inclusive limits applied to every component; spring overshoot is clamped. */
8
+ range: readonly [number, number];
9
+ /** One application-defined unit per component. Vectors have no implicit color conversion. */
10
+ units: readonly string[];
11
+ /** Spatial uniforms jump under Reduced Motion; appearance uniforms keep their timing. */
12
+ motion: 'spatial' | 'appearance';
13
+ }
14
+ /** Scene environment multiplier, nonnegative; leaves the environment/background images untouched. */
15
+ export declare const sceneKitEnvironmentIntensity: (scene: SCNScene) => NativeProperty<number>;
16
+ /** Declared Metal shaderModifiers input, initialized by the application before attachment. */
17
+ export declare function sceneKitUniform<T extends SceneUniformType>(target: SCNMaterial | SCNGeometry, name: string, options: SceneUniformOptions<T>): NativeProperty<SceneUniformValue<T>>;
18
+ //# sourceMappingURL=scene-shader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-shader.d.ts","sourceRoot":"","sources":["../src/scene-shader.ts"],"names":[],"mappings":";AACA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAA;AAE7B,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;AACvE,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,gBAAgB,IAAI,CAAC,SAAS,OAAO,GACzE,MAAM,GACN,CAAC,SAAS,QAAQ,GAChB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GACzB,CAAC,SAAS,QAAQ,GAChB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GACjC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AACjD,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,gBAAgB;IAC7D,IAAI,EAAE,CAAC,CAAA;IACP,uFAAuF;IACvF,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,6FAA6F;IAC7F,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,yFAAyF;IACzF,MAAM,EAAE,SAAS,GAAG,YAAY,CAAA;CACjC;AAqBD,qGAAqG;AACrG,eAAO,MAAM,4BAA4B,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,cAAc,CAAC,MAAM,CAcjF,CAAA;AAEJ,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,CAAC,SAAS,gBAAgB,EACxD,MAAM,EAAE,WAAW,GAAG,WAAW,EACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC9B,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAwEtC"}
@@ -0,0 +1,76 @@
1
+ /// <reference types="@nativescript/types-ios/lib/ios/objc-x86_64/objc!SceneKit" preserve="true" />
2
+ import { nativePropertyTarget, registerNativeProperty, } from './native-property.js';
3
+ function runtime() {
4
+ if (typeof JoltSceneShaderProperties === 'undefined')
5
+ throw new Error('Update @rafaelaugusto/jolt-native and rebuild the native app to use SceneKit environment and shader adapters.');
6
+ return JoltSceneShaderProperties;
7
+ }
8
+ function sink(create) {
9
+ const error = new interop.Reference();
10
+ const result = create(error);
11
+ if (!result)
12
+ throw new Error(error.value?.localizedDescription ??
13
+ 'Invalid SceneKit property target or shader schema.');
14
+ return result;
15
+ }
16
+ /** Scene environment multiplier, nonnegative; leaves the environment/background images untouched. */
17
+ export const sceneKitEnvironmentIntensity = registerNativeProperty({
18
+ name: 'jolt.scenekit/environment-intensity',
19
+ units: ['environment illumination multiplier'],
20
+ platforms: ['ios'],
21
+ encode: (value) => [value],
22
+ decode: (values) => values[0],
23
+ validate(value) {
24
+ if (!Number.isFinite(value) || value < 0)
25
+ throw new RangeError('Environment intensity must be finite and nonnegative.');
26
+ },
27
+ interpolate: (a, b, t) => Math.max(0, a + (b - a) * t),
28
+ create: (target) => sink((error) => runtime().environmentError(target, error)),
29
+ });
30
+ /** Declared Metal shaderModifiers input, initialized by the application before attachment. */
31
+ export function sceneKitUniform(target, name, options) {
32
+ if (typeof name !== 'string' ||
33
+ !/^[A-Za-z][A-Za-z0-9_]*$/.test(name) ||
34
+ /^(u_|a_|v_|scn_)/.test(name))
35
+ throw new TypeError('Use a declared custom uniform identifier without a reserved SceneKit prefix or KVC path.');
36
+ if (!options || !['float', 'float2', 'float3', 'float4'].includes(options.type))
37
+ throw new TypeError('Shader uniforms support float, float2, float3 and float4.');
38
+ const count = options.type === 'float' ? 1 : Number(options.type.slice(-1));
39
+ const range = options.range;
40
+ if (!Array.isArray(range) ||
41
+ range.length !== 2 ||
42
+ !range.every((n) => Number.isFinite(n) && Math.abs(n) <= 3.4028234663852886e38) ||
43
+ range[0] >= range[1])
44
+ throw new RangeError('Declare an ordered finite range for the shader components.');
45
+ if (!Array.isArray(options.units) ||
46
+ options.units.length !== count ||
47
+ options.units.some((unit) => typeof unit !== 'string' || !unit.trim()))
48
+ throw new TypeError('Declare one nonempty unit for each shader component.');
49
+ if (!['spatial', 'appearance'].includes(options.motion))
50
+ throw new TypeError('Declare whether the shader motion is spatial or appearance.');
51
+ // Copy the schema: caller mutation cannot change an already attached native sink.
52
+ const [low, high] = range, type = options.type, units = [...options.units];
53
+ const vector = (value) => {
54
+ const values = type === 'float' ? [value] : value;
55
+ if (!Array.isArray(values) ||
56
+ values.length !== count ||
57
+ values.some((n) => typeof n !== 'number' || !Number.isFinite(n) || n < low || n > high))
58
+ throw new RangeError(`${name} needs ${count} finite component(s) in ${low}..${high}.`);
59
+ return [...values];
60
+ };
61
+ return nativePropertyTarget(target, {
62
+ name: `jolt.scenekit/uniform-${name}`,
63
+ units,
64
+ platforms: ['ios'],
65
+ spatial: options.motion === 'spatial',
66
+ encode: vector,
67
+ decode: (values) => (type === 'float' ? values[0] : [...values]),
68
+ interpolate(a, b, t) {
69
+ const first = vector(a), last = vector(b);
70
+ const result = first.map((value, i) => Math.max(low, Math.min(high, value + (last[i] - value) * t)));
71
+ return (type === 'float' ? result[0] : result);
72
+ },
73
+ create: (object) => sink((error) => runtime().uniformNameConfigurationError(object, name, JSON.stringify({ type, low, high }), error)),
74
+ });
75
+ }
76
+ //# sourceMappingURL=scene-shader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-shader.js","sourceRoot":"","sources":["../src/scene-shader.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rafaelaugusto/jolt",
3
- "version": "0.1.0-beta.16",
3
+ "version": "0.1.0-beta.17",
4
4
  "description": "Native animations for NativeScript: presets, springs, keyframes and stagger in one call.",
5
5
  "keywords": [
6
6
  "nativescript",
@@ -41,6 +41,14 @@
41
41
  "./native": {
42
42
  "types": "./dist/native/index.d.ts",
43
43
  "default": "./dist/native/index.js"
44
+ },
45
+ "./bottom-sheet": {
46
+ "types": "./dist/bottom-sheet/index.d.ts",
47
+ "default": "./dist/bottom-sheet/index.js"
48
+ },
49
+ "./reorder": {
50
+ "types": "./dist/reorder/index.d.ts",
51
+ "default": "./dist/reorder/index.js"
44
52
  }
45
53
  },
46
54
  "files": [
@@ -18,7 +18,7 @@ public final class GestureSession {
18
18
  private final GestureDetector taps;
19
19
  private final double density, threshold, failOffset;
20
20
  private final int axis;
21
- private final boolean pinch, rotate, tapEnabled, doubleEnabled;
21
+ private final boolean pinch, rotate, tapEnabled, doubleEnabled, contactEvents;
22
22
  private double minX, maxX, minY, maxY, minScale, maxScale;
23
23
  private double downX, downY, lastX, lastY, lastSpan, lastAngle, velocityX, velocityY;
24
24
  private boolean enabled = true, disposed, active, rejected, fromScroll, contact, ending;
@@ -34,6 +34,7 @@ public final class GestureSession {
34
34
  axis = config.getInt("axis"); threshold = config.getDouble("threshold"); failOffset = config.getDouble("failOffset");
35
35
  pinch = config.getBoolean("pinch"); rotate = config.getBoolean("rotate");
36
36
  tapEnabled = config.getBoolean("tap"); doubleEnabled = config.getBoolean("doubleTap");
37
+ contactEvents = config.optBoolean("contactEvents", false);
37
38
  minX = config.getDouble("minX"); maxX = config.getDouble("maxX"); minY = config.getDouble("minY"); maxY = config.getDouble("maxY");
38
39
  minScale = config.getDouble("minScale"); maxScale = config.getDouble("maxScale");
39
40
  } catch (JSONException error) { throw new IllegalArgumentException(error); }
@@ -46,6 +47,7 @@ public final class GestureSession {
46
47
  if (!doubleEnabled) taps.setOnDoubleTapListener(null);
47
48
  surface.setSession(this);
48
49
  }
50
+ public static boolean supportsContactEvents() { return true; }
49
51
  public boolean isActive() { return active; }
50
52
  boolean intercepts() { return enabled && active && !fromScroll; }
51
53
  boolean accepts(View target) { return enabled && !disposed && target == scroll; }
@@ -73,6 +75,7 @@ public final class GestureSession {
73
75
  fromScroll = bounds.contains((int)event.getX(), (int)event.getY());
74
76
  }
75
77
  rebase(event, -1); downX = lastX; downY = lastY;
78
+ if (contactEvents) emit(6);
76
79
  }
77
80
  if (!contact) return;
78
81
  if (velocity != null) {
@@ -153,15 +156,17 @@ public final class GestureSession {
153
156
  return y.getPosition() > minY + .01 || vy < 0 && !scroll.canScrollVertically(-1);
154
157
  }
155
158
  private void finish(boolean cancelled) {
159
+ boolean hadContact = contact;
156
160
  contact = false;
157
161
  if (velocity != null) { velocity.recycle(); velocity = null; }
158
- if (!active) return;
162
+ if (!active) { if (hadContact && contactEvents) emit(7); return; }
159
163
  active = false;
160
164
  x.releaseInput(x.getPosition(), 0); y.releaseInput(y.getPosition(), 0);
161
165
  scale.releaseInput(scale.getPosition(), 0); rotation.releaseInput(rotation.getPosition(), 0);
162
166
  if (surface.getParent() != null) surface.getParent().requestDisallowInterceptTouchEvent(false);
163
167
  ending = !cancelled;
164
168
  try { emit(cancelled ? 3 : 2); } finally { ending = false; }
169
+ if (hadContact && contactEvents) emit(7);
165
170
  }
166
171
  public void cancel() {
167
172
  if (active) finish(true);
@@ -0,0 +1,25 @@
1
+ package org.rafaelaugusto.jolt;
2
+
3
+ import android.content.Context;
4
+ import android.view.KeyEvent;
5
+ import android.view.MotionEvent;
6
+ import android.widget.Button;
7
+
8
+ /** A focusable button with explicit keyboard move actions. */
9
+ public final class ReorderHandle extends Button {
10
+ public interface MoveListener { void move(int direction); }
11
+ private MoveListener move;
12
+ public ReorderHandle(Context context) { super(context); setFocusable(true); setFocusableInTouchMode(true); }
13
+ public void setMoveListener(MoveListener value) { move = value; }
14
+ @Override public boolean onTouchEvent(MotionEvent event) {
15
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) requestFocus();
16
+ return super.onTouchEvent(event);
17
+ }
18
+ @Override public boolean onKeyDown(int code, KeyEvent event) {
19
+ if (event.isAltPressed() && move != null && (code == KeyEvent.KEYCODE_DPAD_UP || code == KeyEvent.KEYCODE_DPAD_DOWN)) {
20
+ if (event.getRepeatCount() == 0) move.move(code == KeyEvent.KEYCODE_DPAD_UP ? -1 : 1);
21
+ return true;
22
+ }
23
+ return super.onKeyDown(code, event);
24
+ }
25
+ }
@@ -0,0 +1,213 @@
1
+ package org.rafaelaugusto.jolt;
2
+
3
+ import android.animation.Animator;
4
+ import android.animation.AnimatorListenerAdapter;
5
+ import android.animation.ValueAnimator;
6
+ import android.graphics.Bitmap;
7
+ import android.graphics.Canvas;
8
+ import android.graphics.Rect;
9
+ import android.view.Choreographer;
10
+ import android.view.MotionEvent;
11
+ import android.view.View;
12
+ import android.view.ViewConfiguration;
13
+ import android.view.ViewGroup;
14
+ import android.widget.ImageView;
15
+ import android.widget.ListView;
16
+ import org.json.JSONObject;
17
+ import java.util.ArrayList;
18
+ import java.util.Collections;
19
+ import java.util.Set;
20
+ import java.util.WeakHashMap;
21
+
22
+ /** Core ListView input, snapshot, recycling and edge autoscroll stay on the UI thread. */
23
+ public final class ReorderSession implements View.OnTouchListener, Choreographer.FrameCallback, View.OnLayoutChangeListener, View.OnAttachStateChangeListener {
24
+ public interface Listener { void onState(int state, int index, int toIndex); }
25
+ private static final class Cell {
26
+ final float translation, alpha;
27
+ final int index;
28
+ final SpringState spring = new SpringState(0, 0);
29
+ Cell(View view, int index) { this.translation = view.getTranslationY(); this.alpha = view.getAlpha(); this.index = index; }
30
+ }
31
+ final ListView list;
32
+ private final ReorderSurface surface;
33
+ private final Listener listener;
34
+ private final WeakHashMap<View, Cell> cells = new WeakHashMap<>();
35
+ private final Set<View> handles = Collections.newSetFromMap(new WeakHashMap<View, Boolean>());
36
+ private final float density, edgeSize, edgeSpeed, slop;
37
+ private final long longPress;
38
+ private final boolean handlesOnly;
39
+ private final Runnable activate = () -> begin();
40
+ private boolean enabled = true, disposed, active, settling, pending, reduced;
41
+ private int from, target, count, generation, width, height;
42
+ private float downX, downY, pointerX, pointerY, grabOffset, rowHeight, remainder;
43
+ private long previousTime;
44
+ private ImageView overlay;
45
+ private Bitmap bitmap;
46
+ private ValueAnimator drop;
47
+
48
+ public ReorderSession(ReorderSurface surface, ListView list, String options, Listener listener) {
49
+ this.surface = surface; this.list = list; this.listener = listener;
50
+ density = list.getResources().getDisplayMetrics().density;
51
+ slop = ViewConfiguration.get(list.getContext()).getScaledTouchSlop();
52
+ try {
53
+ JSONObject config = new JSONObject(options);
54
+ longPress = config.getLong("longPress"); edgeSize = (float)config.getDouble("edgeSize") * density;
55
+ edgeSpeed = (float)config.getDouble("edgeSpeed") * density;
56
+ reduced = config.getBoolean("reducedMotion"); handlesOnly = config.getBoolean("handles");
57
+ } catch (Exception error) { throw new IllegalArgumentException(error); }
58
+ surface.setSession(this); list.addOnLayoutChangeListener(this); list.addOnAttachStateChangeListener(this);
59
+ }
60
+ public boolean isActive() { return active; }
61
+ public void registerHandle(View view) {
62
+ if (!disposed && view != null) {
63
+ handles.add(view);
64
+ }
65
+ }
66
+ private boolean inHandle(float x, float y) {
67
+ if (!handlesOnly) return true;
68
+ Rect rect = new Rect();
69
+ for (View handle : handles) {
70
+ if (handle == null || !handle.isShown()) continue;
71
+ android.view.ViewParent parent = handle.getParent();
72
+ while (parent != null && parent != list) parent = parent.getParent();
73
+ if (parent != list) continue;
74
+ handle.getDrawingRect(rect); list.offsetDescendantRectToMyCoords(handle, rect);
75
+ if (rect.contains((int)x, (int)y)) return true;
76
+ }
77
+ return false;
78
+ }
79
+ @Override public boolean onTouch(View view, MotionEvent event) {
80
+ if (disposed) return false;
81
+ int action = event.getActionMasked();
82
+ if (action == MotionEvent.ACTION_DOWN) {
83
+ cancel();
84
+ downX = pointerX = event.getX(); downY = pointerY = event.getY();
85
+ if (enabled && list.isEnabled() && inHandle(downX, downY) && list.pointToPosition((int)downX, (int)downY) >= 0) {
86
+ pending = true; list.postDelayed(activate, longPress);
87
+ }
88
+ }
89
+ if (event.getPointerCount() > 1) { boolean handled = active; cancel(); return handled; }
90
+ if (action == MotionEvent.ACTION_MOVE) {
91
+ pointerX = event.getX(); pointerY = event.getY();
92
+ if (pending && Math.hypot(pointerX - downX, pointerY - downY) > slop) clearPending();
93
+ if (active && !settling) update(0);
94
+ }
95
+ boolean owned = active;
96
+ if (action == MotionEvent.ACTION_UP) { clearPending(); if (active && !settling) drop(); }
97
+ else if (action == MotionEvent.ACTION_CANCEL) { clearPending(); if (active) finish(false, 0); }
98
+ if (owned) return true;
99
+ return false;
100
+ }
101
+ private void clearPending() { pending = false; list.removeCallbacks(activate); }
102
+ private void emit(int state, int detail) { if (!disposed && listener != null) listener.onState(state, from, detail); }
103
+ private void begin() {
104
+ if (!pending || disposed || !enabled || !list.isAttachedToWindow()) return;
105
+ clearPending();
106
+ int index = list.pointToPosition((int)pointerX, (int)pointerY);
107
+ View cell = list.getChildAt(index - list.getFirstVisiblePosition());
108
+ if (cell == null || index < 0 || cell.getWidth() <= 0 || cell.getHeight() <= 0 || (long)cell.getWidth() * cell.getHeight() > 8000000) return;
109
+ bitmap = Bitmap.createBitmap(cell.getWidth(), cell.getHeight(), Bitmap.Config.ARGB_8888);
110
+ cell.draw(new Canvas(bitmap));
111
+ overlay = new ImageView(list.getContext()); overlay.setImageBitmap(bitmap);
112
+ overlay.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
113
+ overlay.layout(cell.getLeft(), 0, cell.getRight(), cell.getHeight());
114
+ overlay.setTranslationY(cell.getTop()); overlay.setElevation(12 * density);
115
+ list.getOverlay().add(overlay);
116
+ rowHeight = cell.getHeight() + list.getDividerHeight(); grabOffset = pointerY - cell.getTop();
117
+ width = list.getWidth(); height = list.getHeight(); count = list.getCount();
118
+ from = target = index; active = true; settling = false; remainder = 0; generation++;
119
+ // Stop the ListView's pending click/scroll, retaining this pointer stream.
120
+ MotionEvent cancel = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, pointerX, pointerY, 0);
121
+ list.onTouchEvent(cancel); cancel.recycle();
122
+ if (surface.getParent() != null) surface.getParent().requestDisallowInterceptTouchEvent(true);
123
+ previousTime = System.nanoTime();
124
+ emit(1, from);
125
+ if (!active || disposed) return;
126
+ update(0); Choreographer.getInstance().postFrameCallback(this);
127
+ }
128
+ @Override public void doFrame(long time) {
129
+ if (!active || settling || disposed) return;
130
+ if (!list.isAttachedToWindow() || count != list.getCount() || width != list.getWidth() || height != list.getHeight()) { finish(false, 1); return; }
131
+ double dt = Math.min(.04, Math.max(0, (time - previousTime) / 1e9)); previousTime = time;
132
+ float edge = Math.min(edgeSize, list.getHeight() / 3f), direction = 0;
133
+ if (pointerY < list.getPaddingTop() + edge) direction = -Math.min(1, (list.getPaddingTop() + edge - pointerY) / edge);
134
+ else if (pointerY > list.getHeight() - list.getPaddingBottom() - edge) direction = Math.min(1, (pointerY - list.getHeight() + list.getPaddingBottom() + edge) / edge);
135
+ float distance = (float)(direction * edgeSpeed * dt) + remainder;
136
+ int pixels = (int)distance; remainder = distance - pixels;
137
+ if (pixels != 0) list.scrollListBy(pixels);
138
+ update(dt);
139
+ if (active) Choreographer.getInstance().postFrameCallback(this);
140
+ }
141
+ private void restore(View view, Cell state) { view.setTranslationY(state.translation); view.setAlpha(state.alpha); }
142
+ private void update(double dt) {
143
+ if (!active || settling) return;
144
+ float top = pointerY - grabOffset;
145
+ overlay.setTranslationY(top);
146
+ int first = list.getFirstVisiblePosition(), visible = list.getChildCount(), destination = from;
147
+ for (int offset = 0; offset < visible; offset++) {
148
+ int index = first + offset; if (index == from) continue;
149
+ View view = list.getChildAt(offset);
150
+ float midpoint = view.getTop() + view.getHeight() / 2f - (index > from ? rowHeight : 0);
151
+ int insertion = index - (index > from ? 1 : 0);
152
+ destination = insertion;
153
+ if (top < midpoint) break;
154
+ destination = insertion + 1;
155
+ }
156
+ target = Math.max(0, Math.min(count - 1, destination));
157
+ for (View view : new ArrayList<>(cells.keySet())) {
158
+ int index = list.getPositionForView(view);
159
+ Cell state = cells.get(view);
160
+ if (index < first || index >= first + visible || index != state.index) { restore(view, state); cells.remove(view); }
161
+ }
162
+ for (int offset = 0; offset < visible; offset++) {
163
+ int index = first + offset; View view = list.getChildAt(offset);
164
+ Cell state = cells.get(view);
165
+ if (state == null) { state = new Cell(view, index); cells.put(view, state); }
166
+ float displacement = index > from && index <= target ? -rowHeight : index < from && index >= target ? rowHeight : 0;
167
+ if (reduced) { state.spring.position = displacement; state.spring.velocity = 0; }
168
+ else state.spring.advance(displacement, 360, 34, 1, dt);
169
+ view.setTranslationY(state.translation + (float)state.spring.position);
170
+ view.setAlpha(index == from ? 0 : state.alpha);
171
+ }
172
+ }
173
+ private void drop() {
174
+ if (!active || settling) return;
175
+ settling = true; Choreographer.getInstance().removeFrameCallback(this);
176
+ View destination = list.getChildAt(target - list.getFirstVisiblePosition());
177
+ float end = destination == null ? overlay.getTranslationY() : target > from ? destination.getBottom() - bitmap.getHeight() : destination.getTop();
178
+ if (reduced) { finish(true, 0); return; }
179
+ final int ticket = generation;
180
+ drop = ValueAnimator.ofFloat(overlay.getTranslationY(), end); drop.setDuration(180);
181
+ drop.addUpdateListener(animation -> { if (overlay != null) overlay.setTranslationY((float)animation.getAnimatedValue()); });
182
+ drop.addListener(new AnimatorListenerAdapter() {
183
+ @Override public void onAnimationEnd(Animator animation) { if (active && generation == ticket) finish(true, 0); }
184
+ });
185
+ drop.start();
186
+ }
187
+ private void finish(boolean commit, int detail) {
188
+ if (!active) return;
189
+ active = false; settling = false; generation++;
190
+ Choreographer.getInstance().removeFrameCallback(this);
191
+ if (drop != null) { ValueAnimator previous = drop; drop = null; previous.cancel(); }
192
+ if (overlay != null) { list.getOverlay().remove(overlay); overlay.setImageDrawable(null); overlay = null; }
193
+ if (bitmap != null) { bitmap.recycle(); bitmap = null; }
194
+ for (View view : new ArrayList<>(cells.keySet())) restore(view, cells.get(view));
195
+ cells.clear();
196
+ if (surface.getParent() != null) surface.getParent().requestDisallowInterceptTouchEvent(false);
197
+ emit(commit ? 2 : 3, commit ? target : detail);
198
+ }
199
+ public void cancel() { clearPending(); finish(false, 0); }
200
+ public void setEnabled(boolean value) { enabled = value; if (!value) cancel(); }
201
+ public void setReducedMotion(boolean value) { reduced = value; }
202
+ @Override public void onLayoutChange(View view, int l, int t, int r, int b, int ol, int ot, int or, int ob) {
203
+ if (active && (r - l != width || b - t != height)) finish(false, 1);
204
+ }
205
+ @Override public void onViewDetachedFromWindow(View view) { cancel(); }
206
+ @Override public void onViewAttachedToWindow(View view) {}
207
+ public void dispose() {
208
+ if (disposed) return;
209
+ cancel(); disposed = true; surface.setSession(null);
210
+ list.removeOnLayoutChangeListener(this); list.removeOnAttachStateChangeListener(this);
211
+ handles.clear();
212
+ }
213
+ }
@@ -0,0 +1,27 @@
1
+ package org.rafaelaugusto.jolt;
2
+
3
+ import android.content.Context;
4
+ import android.graphics.Rect;
5
+ import android.view.MotionEvent;
6
+
7
+ /** Capture input above recycled cells without replacing any Core touch listener. */
8
+ public final class ReorderSurface extends org.nativescript.widgets.GridLayout {
9
+ private ReorderSession session;
10
+ public ReorderSurface(Context context) { super(context); }
11
+ void setSession(ReorderSession value) { session = value; }
12
+ @Override public boolean dispatchTouchEvent(MotionEvent event) {
13
+ ReorderSession current = session;
14
+ if (current != null) {
15
+ Rect rect = new Rect(); current.list.getDrawingRect(rect);
16
+ offsetDescendantRectToMyCoords(current.list, rect);
17
+ MotionEvent local = MotionEvent.obtain(event); local.offsetLocation(-rect.left, -rect.top);
18
+ current.onTouch(current.list, local); local.recycle();
19
+ }
20
+ return super.dispatchTouchEvent(event) || current != null;
21
+ }
22
+ @Override public boolean onInterceptTouchEvent(MotionEvent event) {
23
+ return session != null && session.isActive() || super.onInterceptTouchEvent(event);
24
+ }
25
+ @Override public boolean onTouchEvent(MotionEvent event) { return session != null || super.onTouchEvent(event); }
26
+ @Override protected void onDetachedFromWindow() { if (session != null) session.cancel(); super.onDetachedFromWindow(); }
27
+ }
@@ -21,3 +21,8 @@
21
21
  -keep class org.rafaelaugusto.jolt.GradientLayer$* { *; }
22
22
  -keep class org.rafaelaugusto.jolt.ParticleEmitter { *; }
23
23
  -keep class org.rafaelaugusto.jolt.ParticleEmitter$* { *; }
24
+ -keep class org.rafaelaugusto.jolt.ReorderSession { *; }
25
+ -keep class org.rafaelaugusto.jolt.ReorderSession$* { *; }
26
+ -keep class org.rafaelaugusto.jolt.ReorderHandle { *; }
27
+ -keep class org.rafaelaugusto.jolt.ReorderHandle$* { *; }
28
+ -keep class org.rafaelaugusto.jolt.ReorderSurface { *; }
@@ -4,6 +4,7 @@
4
4
  NS_ASSUME_NONNULL_BEGIN
5
5
  @interface JoltGestureSession : NSObject
6
6
  @property(nonatomic, readonly) BOOL active;
7
+ + (BOOL)supportsContactEvents;
7
8
  + (instancetype)attach:(UIView *)surface scroll:(nullable UIScrollView *)scroll
8
9
  x:(JoltLiveSpring *)x y:(JoltLiveSpring *)y scale:(JoltLiveSpring *)scale rotation:(JoltLiveSpring *)rotation
9
10
  options:(NSString *)options onState:(void (^)(NSInteger, double, double))onState;
@@ -25,7 +25,7 @@ static double clamp(double value, double minimum, double maximum) { return MAX(m
25
25
  @property(nonatomic) JoltLiveSpring *x, *y, *scale, *rotation;
26
26
  @property(nonatomic) double minX, maxX, minY, maxY, minScale, maxScale, velocityX, velocityY, scrollPosition;
27
27
  @property(nonatomic, readwrite) BOOL active;
28
- @property(nonatomic) BOOL enabled, disposed, contact, fromScroll, adjusting, ending;
28
+ @property(nonatomic) BOOL enabled, disposed, contactEvents, contact, fromScroll, adjusting, ending;
29
29
  @property(nonatomic, copy) void (^onState)(NSInteger, double, double);
30
30
  - (void)touchStarted:(UITouch *)touch;
31
31
  - (void)finish:(BOOL)cancelled;
@@ -77,6 +77,7 @@ static double clamp(double value, double minimum, double maximum) { return MAX(m
77
77
  @end
78
78
 
79
79
  @implementation JoltGestureSession
80
+ + (BOOL)supportsContactEvents { return YES; }
80
81
  + (instancetype)attach:(UIView *)surface scroll:(UIScrollView *)scroll x:(JoltLiveSpring *)x y:(JoltLiveSpring *)y scale:(JoltLiveSpring *)scale rotation:(JoltLiveSpring *)rotation options:(NSString *)options onState:(void (^)(NSInteger,double,double))onState {
81
82
  NSDictionary *config = [NSJSONSerialization JSONObjectWithData:[options dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
82
83
  JoltGestureSession *s = [self new]; s.surface = surface; s.scroll = scroll; s.x = x; s.y = y; s.scale = scale; s.rotation = rotation;
@@ -91,6 +92,7 @@ static double clamp(double value, double minimum, double maximum) { return MAX(m
91
92
  [s.tap requireGestureRecognizerToFail:s.doubleTap];
92
93
  for (UIGestureRecognizer *g in @[s.pan, s.pinch, s.rotate, s.tap, s.doubleTap]) { g.delegate = s; [surface addGestureRecognizer:g]; }
93
94
  s.pinch.enabled = [config[@"pinch"] boolValue]; s.rotate.enabled = [config[@"rotate"] boolValue];
95
+ s.contactEvents = [config[@"contactEvents"] boolValue];
94
96
  s.tap.enabled = [config[@"tap"] boolValue]; s.doubleTap.enabled = [config[@"doubleTap"] boolValue];
95
97
  // NativeScript's scroll delegate is forwarded, never replaced with a JS per-frame bridge.
96
98
  if (scroll) { s.previousDelegate = scroll.delegate; scroll.delegate = s; }
@@ -116,6 +118,7 @@ static double clamp(double value, double minimum, double maximum) { return MAX(m
116
118
  self.scrollPosition = MAX(-self.scroll.adjustedContentInset.top, self.scroll.contentOffset.y);
117
119
  self.velocityX = self.velocityY = 0;
118
120
  for (JoltLiveSpring *node in @[self.x, self.y, self.scale, self.rotation]) [node releaseInput:node.position velocity:0];
121
+ if (self.contactEvents) [self emit:6];
119
122
  }
120
123
  - (void)emit:(NSInteger)state { if (!self.disposed && self.onState) self.onState(state, self.velocityX, self.velocityY); }
121
124
  - (void)begin {
@@ -178,12 +181,14 @@ static double clamp(double value, double minimum, double maximum) { return MAX(m
178
181
  if ([self.previousDelegate respondsToSelector:_cmd]) [self.previousDelegate scrollViewWillEndDragging:scroll withVelocity:velocity targetContentOffset:target];
179
182
  }
180
183
  - (void)finish:(BOOL)cancelled {
184
+ BOOL hadContact = self.contact;
181
185
  self.contact = NO;
182
- if (!self.active) return;
186
+ if (!self.active) { if (hadContact && self.contactEvents) [self emit:7]; return; }
183
187
  self.active = NO;
184
188
  for (JoltLiveSpring *node in @[self.x, self.y, self.scale, self.rotation]) [node releaseInput:node.position velocity:0];
185
189
  self.ending = !cancelled;
186
190
  @try { [self emit:cancelled ? 3 : 2]; } @finally { self.ending = NO; }
191
+ if (hadContact && self.contactEvents) [self emit:7];
187
192
  }
188
193
  - (void)cancel {
189
194
  if (self.active) [self finish:YES];
@@ -0,0 +1,15 @@
1
+ #import <UIKit/UIKit.h>
2
+
3
+ @interface JoltReorderHandle : UIButton
4
+ @property(nonatomic, copy) void (^onMove)(NSInteger direction);
5
+ @end
6
+
7
+ @interface JoltReorderSession : NSObject
8
+ + (instancetype)attach:(UITableView *)list options:(NSString *)options onState:(void (^)(NSInteger state, NSInteger index, NSInteger toIndex))onState;
9
+ - (void)registerHandle:(UIView *)view;
10
+ - (void)setEnabled:(BOOL)enabled;
11
+ - (void)setReducedMotion:(BOOL)reduced;
12
+ - (void)cancel;
13
+ - (void)dispose;
14
+ @property(nonatomic, readonly) BOOL active;
15
+ @end