From d3e2fff7d6d75666ee23d484bdebb7c8cd5f30b8 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Fri, 8 May 2026 16:18:34 +0200 Subject: [PATCH 1/7] Mainline dedicated hardware kiosk changes - 2nd Edition Superseeds #160 # Conflicts: # pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt # pretixscan/app/src/main/res/values-de/strings.xml # pretixscan/app/src/main/res/values/strings.xml # pretixscan/libpretixsync-repo --- .../kiosk_portrait_kt0345/avd_print.xml | 161 ++++ .../kiosk_portrait_kt0345/avd_scan.xml | 143 +++ .../kiosk_portrait_kt0345/avd_turn.xml | 89 ++ .../kiosk_portrait_kt0345/print-frame00.svg | 271 ++++++ .../kiosk_portrait_kt0345/print.shapeshifter | 216 +++++ .../kiosk_portrait_kt0345/scan-frame00.svg | 252 +++++ .../kiosk_portrait_kt0345/scan.shapeshifter | 193 ++++ .../kiosk_portrait_kt0345/turn-frame00.svg | 218 +++++ .../kiosk_portrait_kt0345/turn.shapeshifter | 116 +++ pretixscan/app/src/main/AndroidManifest.xml | 8 + .../eu/pretix/pretixscan/droid/AppConfig.kt | 14 + .../droid/hardware/KioskHardware.kt | 33 + .../pretixscan/droid/ui/BaseScanActivity.kt | 858 +++++++++++++++++ .../pretix/pretixscan/droid/ui/GateUtils.kt | 32 + .../pretixscan/droid/ui/KioskActivity.kt | 815 +++++++++++++++++ .../pretixscan/droid/ui/MainActivity.kt | 861 +----------------- .../eu/pretix/pretixscan/droid/ui/Settings.kt | 2 + .../avd_kiosk_portrait_kt0345_print.xml | 161 ++++ .../avd_kiosk_portrait_kt0345_scan.xml | 147 +++ .../avd_kiosk_portrait_kt0345_turn.xml | 89 ++ .../src/main/res/drawable/ic_block_24px.xml | 11 + .../main/res/drawable/ic_cell_tower_24px.xml | 10 + .../src/main/res/drawable/ic_circle_24dp.xml | 4 + .../main/res/drawable/ic_error_stop_24px.xml | 10 + .../main/res/drawable/ic_ethernet_24px.xml | 10 + .../src/main/res/drawable/ic_wifi_24px.xml | 10 + .../app/src/main/res/drawable/ic_wifi_off.xml | 10 + .../src/main/res/drawable/ic_wifi_unknown.xml | 10 + .../src/main/res/layout/activity_kiosk.xml | 427 +++++++++ .../app/src/main/res/values-de/strings.xml | 10 + pretixscan/app/src/main/res/values/color.xml | 6 + .../app/src/main/res/values/strings.xml | 10 + pretixscan/app/src/main/res/values/styles.xml | 11 + pretixscan/app/src/main/res/values/themes.xml | 7 + .../app/src/main/res/xml/preferences.xml | 7 +- pretixscan/libpretixnfc-repo | 2 +- pretixscan/libpretixui-repo | 2 +- 37 files changed, 4410 insertions(+), 826 deletions(-) create mode 100644 img/animations/kiosk_portrait_kt0345/avd_print.xml create mode 100644 img/animations/kiosk_portrait_kt0345/avd_scan.xml create mode 100644 img/animations/kiosk_portrait_kt0345/avd_turn.xml create mode 100644 img/animations/kiosk_portrait_kt0345/print-frame00.svg create mode 100644 img/animations/kiosk_portrait_kt0345/print.shapeshifter create mode 100644 img/animations/kiosk_portrait_kt0345/scan-frame00.svg create mode 100644 img/animations/kiosk_portrait_kt0345/scan.shapeshifter create mode 100644 img/animations/kiosk_portrait_kt0345/turn-frame00.svg create mode 100644 img/animations/kiosk_portrait_kt0345/turn.shapeshifter create mode 100644 pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/hardware/KioskHardware.kt create mode 100644 pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt create mode 100644 pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/GateUtils.kt create mode 100644 pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt create mode 100644 pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_print.xml create mode 100644 pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_scan.xml create mode 100644 pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_turn.xml create mode 100644 pretixscan/app/src/main/res/drawable/ic_block_24px.xml create mode 100644 pretixscan/app/src/main/res/drawable/ic_cell_tower_24px.xml create mode 100644 pretixscan/app/src/main/res/drawable/ic_circle_24dp.xml create mode 100644 pretixscan/app/src/main/res/drawable/ic_error_stop_24px.xml create mode 100644 pretixscan/app/src/main/res/drawable/ic_ethernet_24px.xml create mode 100644 pretixscan/app/src/main/res/drawable/ic_wifi_24px.xml create mode 100644 pretixscan/app/src/main/res/drawable/ic_wifi_off.xml create mode 100644 pretixscan/app/src/main/res/drawable/ic_wifi_unknown.xml create mode 100644 pretixscan/app/src/main/res/layout/activity_kiosk.xml create mode 100644 pretixscan/app/src/main/res/values/themes.xml diff --git a/img/animations/kiosk_portrait_kt0345/avd_print.xml b/img/animations/kiosk_portrait_kt0345/avd_print.xml new file mode 100644 index 00000000..e8dff339 --- /dev/null +++ b/img/animations/kiosk_portrait_kt0345/avd_print.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/animations/kiosk_portrait_kt0345/avd_scan.xml b/img/animations/kiosk_portrait_kt0345/avd_scan.xml new file mode 100644 index 00000000..a551266b --- /dev/null +++ b/img/animations/kiosk_portrait_kt0345/avd_scan.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/animations/kiosk_portrait_kt0345/avd_turn.xml b/img/animations/kiosk_portrait_kt0345/avd_turn.xml new file mode 100644 index 00000000..b2cd99dc --- /dev/null +++ b/img/animations/kiosk_portrait_kt0345/avd_turn.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/animations/kiosk_portrait_kt0345/print-frame00.svg b/img/animations/kiosk_portrait_kt0345/print-frame00.svg new file mode 100644 index 00000000..f347074c --- /dev/null +++ b/img/animations/kiosk_portrait_kt0345/print-frame00.svg @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/animations/kiosk_portrait_kt0345/print.shapeshifter b/img/animations/kiosk_portrait_kt0345/print.shapeshifter new file mode 100644 index 00000000..49c47a44 --- /dev/null +++ b/img/animations/kiosk_portrait_kt0345/print.shapeshifter @@ -0,0 +1,216 @@ +{ + "version": 1, + "layers": { + "vectorLayer": { + "id": "8025926", + "name": "vector", + "type": "vector", + "width": 158, + "height": 190, + "children": [ + { + "id": "8025164", + "name": "path_1", + "type": "path", + "pathData": "M 77.77 6.368 L 15.671 34.767 L 15.671 144.298 L 77.77 186 Z M 78.342 6.368 L 78.342 186 L 122.005 159.255 L 122.005 15.641 Z", + "fillColor": "#dcdcdc", + "strokeColor": "#000000", + "strokeWidth": 2.24624, + "strokeLinecap": "round", + "strokeLinejoin": "round", + "strokeMiterLimit": 13.3, + "fillType": "evenOdd" + }, + { + "id": "8025180", + "name": "path_2", + "type": "path", + "pathData": "M 87.35 119.153 L 114.868 110.302 L 114.868 115.26 L 87.35 124.996 Z", + "strokeColor": "#000000", + "strokeWidth": 2.962, + "strokeLinecap": "round", + "strokeLinejoin": "round", + "strokeMiterLimit": 13.3 + }, + { + "id": "8025232", + "name": "path_4", + "type": "path", + "pathData": "M 75.151 -81.778 L 96.516 -81.778 C 99.09 -81.778 101.163 -79.54 101.163 -76.76 L 101.163 -38.355 C 101.163 -35.575 99.09 -33.337 96.516 -33.337 L 75.151 -33.337 C 72.576 -33.337 70.504 -35.575 70.504 -38.355 L 70.504 -76.76 C 70.504 -79.54 72.576 -81.778 75.151 -81.778 Z", + "fillColor": "#ffffff", + "strokeColor": "#000000", + "strokeWidth": 3.21934, + "strokeLinecap": "round", + "strokeLinejoin": "round", + "strokeMiterLimit": 13.3 + }, + { + "id": "8025262", + "name": "path_5", + "type": "path", + "pathData": "M 88.458 -74.156 C 88.458 -73.68 88.336 -73.213 88.106 -72.801 C 87.876 -72.389 87.544 -72.047 87.145 -71.809 C 86.746 -71.571 86.294 -71.446 85.833 -71.446 C 85.137 -71.446 84.469 -71.731 83.978 -72.24 C 83.486 -72.748 83.209 -73.438 83.209 -74.156 C 83.209 -74.875 83.486 -75.565 83.978 -76.073 C 84.469 -76.581 85.137 -76.867 85.833 -76.867 C 86.294 -76.867 86.746 -76.742 87.145 -76.504 C 87.544 -76.266 87.876 -75.924 88.106 -75.512 C 88.336 -75.1 88.458 -74.632 88.458 -74.156 Z", + "fillColor": "#ffffff", + "strokeColor": "#000000", + "strokeWidth": 3.21934, + "strokeLinecap": "round", + "strokeLinejoin": "round", + "strokeMiterLimit": 13.3 + }, + { + "id": "8025367", + "name": "path_6", + "type": "path", + "pathData": "M 77.12 -44.988 L 79.628 -57.025 L 83.923 -57.025 L 86.432 -44.988 L 84.169 -44.988 L 83.727 -47.165 L 79.825 -47.165 L 79.382 -44.988 Z M 81.382 -55.007 L 80.251 -49.289 L 83.3 -49.289 L 82.169 -55.007 Z M 94.005 -51.077 L 94.005 -47.395 Q 94.038 -46.97 94.137 -46.793 Q 94.235 -46.634 94.546 -46.563 L 94.481 -44.775 Q 93.645 -44.775 93.137 -44.899 Q 92.645 -45.023 92.137 -45.395 Q 90.956 -44.775 89.727 -44.775 Q 87.186 -44.775 87.186 -47.678 Q 87.186 -49.095 87.891 -49.679 Q 88.596 -50.281 90.055 -50.387 L 91.809 -50.529 L 91.809 -51.077 Q 91.809 -51.626 91.579 -51.821 Q 91.35 -52.033 90.841 -52.033 L 87.759 -51.892 L 87.694 -53.538 Q 89.448 -54.051 91.022 -54.051 Q 92.612 -54.051 93.301 -53.343 Q 94.005 -52.635 94.005 -51.077 Z M 90.317 -48.741 Q 89.399 -48.652 89.399 -47.678 Q 89.399 -46.705 90.202 -46.705 Q 90.825 -46.705 91.563 -46.917 L 91.809 -46.988 L 91.809 -48.882 Z", + "fillColor": "#000", + "strokeWidth": 1 + }, + { + "id": "8025580", + "name": "path_8", + "type": "path", + "pathData": "M 98.44 122.278 L 115.631 116.106 C 115.743 116.068 117.28 115.84 117.408 115.863 L 149.827 122.055 C 150.009 122.088 150.689 122.506 150.582 122.558 L 133.53 131.047 C 133.389 131.115 132.392 131.223 132.202 131.175 L 98.981 123.621 C 98.854 123.587 98.3 122.329 98.44 122.278 Z", + "fillColor": "#ffffff", + "strokeWidth": 1 + }, + { + "id": "8025767", + "name": "path_9", + "type": "path", + "pathData": "M 98.325 121.464 C 95.827 122.348 95.599 123.563 97.928 124.155 L 126.537 131.421 C 129.766 132.242 134.572 131.911 137.05 130.695 L 151.087 123.81 C 153.11 122.817 152.106 121.542 148.982 120.968 L 120.76 115.79 C 118.418 115.36 114.843 115.616 112.73 116.363 Z M 100.404 121.96 L 114.819 116.768 C 115.906 116.377 117.67 116.251 118.876 116.479 L 147.16 121.816 C 148.754 122.118 149.203 122.74 148.154 123.242 L 134.048 129.979 C 132.784 130.583 130.498 130.742 128.862 130.339 L 100.257 123.302 C 99.054 123.006 99.132 122.419 100.404 121.96 Z M 108.868 119.483 C 107.34 120.037 107.261 120.796 108.851 121.152 C 110.469 121.514 113.1 121.301 114.629 120.719 C 116.134 120.147 116.095 119.396 114.478 119.062 C 112.886 118.734 110.373 118.936 108.868 119.483 Z M 111.025 119.946 C 111.461 119.784 112.065 119.747 112.427 119.824 C 112.79 119.901 112.826 120.075 112.39 120.239 C 111.952 120.403 111.342 120.44 110.978 120.361 C 110.616 120.283 110.587 120.107 111.025 119.946 Z M 119.393 124.407 L 126.983 127.328 L 128.583 126.619 L 127.135 126.08 L 129.824 124.904 L 131.877 125.157 L 133.391 124.484 L 122.368 123.188 Z M 122.129 124.244 L 122.674 124.016 L 127.851 124.662 L 125.746 125.568 Z M 129.304 121.709 C 128.624 121.994 128.029 122.323 127.518 122.7 L 128.82 122.947 L 130.724 122.072 C 130.942 121.98 131.154 121.924 131.361 121.903 C 131.559 121.883 131.799 121.9 132.081 121.957 L 132.505 122.044 L 131.484 122.549 C 130.906 122.832 130.618 123.073 130.622 123.272 C 130.617 123.469 130.986 123.646 131.732 123.805 C 133.28 124.133 134.619 124.051 135.721 123.561 C 136.25 123.325 136.598 123.063 136.767 122.773 C 137.181 122.718 137.525 122.651 137.799 122.573 C 138.079 122.492 138.393 122.373 138.741 122.218 L 137.351 121.917 C 137.183 121.967 137.037 121.992 136.911 121.994 C 136.776 121.992 136.589 121.974 136.351 121.935 L 133.477 121.363 C 132.679 121.204 131.954 121.152 131.298 121.206 C 130.649 121.258 129.986 121.423 129.304 121.709 Z M 133.788 122.303 L 135.284 122.608 L 135.183 122.688 C 134.98 122.849 134.746 122.987 134.478 123.106 C 134.132 123.257 133.699 123.28 133.184 123.173 C 132.67 123.066 132.59 122.922 132.941 122.741 Z", + "fillColor": "#000", + "strokeWidth": 1 + }, + { + "id": "8025883", + "name": "path_11", + "type": "path", + "pathData": "M 79.151 63.108 L 79.374 121.801 L 86.139 123.879 L 86.057 118.221 L 116.938 107.888 L 115.317 76.851 Z", + "fillColor": "#dcdcdc", + "strokeWidth": 1, + "fillType": "evenOdd" + }, + { + "id": "8025910", + "name": "path_12", + "type": "path", + "pathData": "M 77.77 6.368 L 15.671 34.767 L 15.671 144.298 L 77.77 186 Z M 78.342 6.368 L 78.342 186 Z", + "fillColor": "#dcdcdc", + "strokeColor": "#000000", + "strokeWidth": 2.24624, + "strokeLinecap": "round", + "strokeLinejoin": "round", + "strokeMiterLimit": 13.3, + "fillType": "evenOdd" + }, + { + "id": "8025924", + "name": "path_13", + "type": "path", + "pathData": "M 87.35 124.996 L 87.35 119.153 L 114.868 110.302 L 114.868 112.781", + "strokeColor": "#000000", + "strokeWidth": 2.962, + "strokeLinecap": "round", + "strokeLinejoin": "round" + } + ] + }, + "hiddenLayerIds": [] + }, + "timeline": { + "animation": { + "id": "8025107", + "name": "anim", + "duration": 4000, + "blocks": [ + { + "id": "8025928", + "layerId": "8025580", + "propertyName": "pathData", + "startTime": 0, + "endTime": 2314, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "path", + "fromValue": "M 62.888 114.089 L 81.478 109.223 C 81.589 109.194 81.771 109.189 81.885 109.211 L 113.002 115.168 C 113.17 115.2 113.218 115.255 113.11 115.293 L 94.027 121.704 C 93.881 121.756 92.865 121.869 92.693 121.824 L 62.833 114.204 C 62.722 114.176 62.746 114.124 62.888 114.089 Z", + "toValue": "M 98.44 122.278 L 115.631 116.106 C 115.743 116.068 117.28 115.84 117.408 115.863 L 149.827 122.055 C 150.009 122.088 150.689 122.506 150.582 122.558 L 133.53 131.047 C 133.389 131.115 132.392 131.223 132.202 131.175 L 98.981 123.621 C 98.854 123.587 98.3 122.329 98.44 122.278 Z" + }, + { + "id": "8025942", + "layerId": "8025580", + "propertyName": "pathData", + "startTime": 3673, + "endTime": 3745, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "path", + "fromValue": "M 98.44 122.278 L 115.631 116.106 C 115.743 116.068 117.28 115.84 117.408 115.863 L 149.827 122.055 C 150.009 122.088 150.689 122.506 150.582 122.558 L 133.53 131.047 C 133.389 131.115 132.392 131.223 132.202 131.175 L 98.981 123.621 C 98.854 123.587 98.3 122.329 98.44 122.278 Z", + "toValue": "M 62.888 114.089 L 81.478 109.223 C 81.589 109.194 81.771 109.189 81.885 109.211 L 113.002 115.168 C 113.17 115.2 113.218 115.255 113.11 115.293 L 94.027 121.704 C 93.881 121.756 92.865 121.869 92.693 121.824 L 62.833 114.204 C 62.722 114.176 62.746 114.124 62.888 114.089 Z" + }, + { + "id": "8027271", + "layerId": "8025767", + "propertyName": "pathData", + "startTime": 0, + "endTime": 2314, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "path", + "fromValue": "M 63.763 113.162 C 61.258 113.782 60.786 114.719 62.829 115.238 L 88.332 121.716 C 91.26 122.46 95.896 122.323 98.457 121.417 L 112.842 116.323 C 114.898 115.596 114.124 114.57 111.255 114.045 L 85.682 109.352 C 83.587 108.967 80.229 109.076 78.133 109.596 Z M 65.592 113.597 L 79.993 109.957 C 81.073 109.684 82.732 109.629 83.811 109.833 L 109.413 114.661 C 110.875 114.937 111.211 115.435 110.146 115.801 L 95.719 120.766 C 94.416 121.214 92.216 121.278 90.731 120.913 L 65.177 114.634 C 64.119 114.373 64.313 113.92 65.592 113.597 Z M 73.954 111.894 C 72.426 112.284 72.203 112.868 73.613 113.182 C 75.05 113.501 77.545 113.403 79.08 112.991 C 80.59 112.587 80.692 112.004 79.249 111.708 C 77.832 111.417 75.457 111.511 73.954 111.894 Z M 75.87 112.304 C 76.307 112.19 76.876 112.177 77.198 112.245 C 77.521 112.314 77.522 112.449 77.085 112.565 C 76.645 112.68 76.07 112.694 75.747 112.623 C 75.425 112.555 75.431 112.418 75.87 112.304 Z M 82.852 115.98 L 89.477 118.474 L 91.112 117.954 L 89.838 117.494 L 92.576 116.638 L 94.469 116.889 L 96.009 116.399 L 85.864 115.104 Z M 85.451 115.922 L 86.004 115.758 L 90.758 116.395 L 88.618 117.054 Z M 92.629 114.125 C 91.942 114.329 91.327 114.572 90.783 114.853 L 91.964 115.079 L 93.9 114.445 C 94.12 114.378 94.329 114.339 94.527 114.329 C 94.716 114.318 94.939 114.338 95.194 114.39 L 95.578 114.468 L 94.535 114.836 C 93.944 115.042 93.632 115.223 93.603 115.378 C 93.565 115.532 93.882 115.681 94.559 115.825 C 95.962 116.121 97.238 116.091 98.357 115.735 C 98.893 115.564 99.263 115.368 99.469 115.146 C 99.868 115.112 100.202 115.069 100.473 115.015 C 100.749 114.958 101.064 114.873 101.416 114.761 L 100.154 114.492 C 99.989 114.525 99.847 114.542 99.729 114.54 C 99.602 114.535 99.429 114.515 99.211 114.48 L 96.602 113.961 C 95.88 113.819 95.208 113.761 94.583 113.786 C 93.966 113.809 93.316 113.921 92.629 114.125 Z M 96.741 114.702 L 98.099 114.977 L 97.991 115.037 C 97.774 115.159 97.531 115.261 97.26 115.346 C 96.909 115.457 96.498 115.464 96.03 115.367 C 95.564 115.27 95.513 115.156 95.872 115.024 Z", + "toValue": "M 98.325 121.464 C 95.827 122.348 95.599 123.563 97.928 124.155 L 126.537 131.421 C 129.766 132.242 134.572 131.911 137.05 130.695 L 151.087 123.81 C 153.11 122.817 152.106 121.542 148.982 120.968 L 120.76 115.79 C 118.418 115.36 114.843 115.616 112.73 116.363 Z M 100.404 121.96 L 114.819 116.768 C 115.906 116.377 117.67 116.251 118.876 116.479 L 147.16 121.816 C 148.754 122.118 149.203 122.74 148.154 123.242 L 134.048 129.979 C 132.784 130.583 130.498 130.742 128.862 130.339 L 100.257 123.302 C 99.054 123.006 99.132 122.419 100.404 121.96 Z M 108.868 119.483 C 107.34 120.037 107.261 120.796 108.851 121.152 C 110.469 121.514 113.1 121.301 114.629 120.719 C 116.134 120.147 116.095 119.396 114.478 119.062 C 112.886 118.734 110.373 118.936 108.868 119.483 Z M 111.025 119.946 C 111.461 119.784 112.065 119.747 112.427 119.824 C 112.79 119.901 112.826 120.075 112.39 120.239 C 111.952 120.403 111.342 120.44 110.978 120.361 C 110.616 120.283 110.587 120.107 111.025 119.946 Z M 119.393 124.407 L 126.983 127.328 L 128.583 126.619 L 127.135 126.08 L 129.824 124.904 L 131.877 125.157 L 133.391 124.484 L 122.368 123.188 Z M 122.129 124.244 L 122.674 124.016 L 127.851 124.662 L 125.746 125.568 Z M 129.304 121.709 C 128.624 121.994 128.029 122.323 127.518 122.7 L 128.82 122.947 L 130.724 122.072 C 130.942 121.98 131.154 121.924 131.361 121.903 C 131.559 121.883 131.799 121.9 132.081 121.957 L 132.505 122.044 L 131.484 122.549 C 130.906 122.832 130.618 123.073 130.622 123.272 C 130.617 123.469 130.986 123.646 131.732 123.805 C 133.28 124.133 134.619 124.051 135.721 123.561 C 136.25 123.325 136.598 123.063 136.767 122.773 C 137.181 122.718 137.525 122.651 137.799 122.573 C 138.079 122.492 138.393 122.373 138.741 122.218 L 137.351 121.917 C 137.183 121.967 137.037 121.992 136.911 121.994 C 136.776 121.992 136.589 121.974 136.351 121.935 L 133.477 121.363 C 132.679 121.204 131.954 121.152 131.298 121.206 C 130.649 121.258 129.986 121.423 129.304 121.709 Z M 133.788 122.303 L 135.284 122.608 L 135.183 122.688 C 134.98 122.849 134.746 122.987 134.478 123.106 C 134.132 123.257 133.699 123.28 133.184 123.173 C 132.67 123.066 132.59 122.922 132.941 122.741 Z" + }, + { + "id": "8028005", + "layerId": "8025767", + "propertyName": "pathData", + "startTime": 3673, + "endTime": 3745, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "path", + "fromValue": "M 98.325 121.464 C 95.827 122.348 95.599 123.563 97.928 124.155 L 126.537 131.421 C 129.766 132.242 134.572 131.911 137.05 130.695 L 151.087 123.81 C 153.11 122.817 152.106 121.542 148.982 120.968 L 120.76 115.79 C 118.418 115.36 114.843 115.616 112.73 116.363 Z M 100.404 121.96 L 114.819 116.768 C 115.906 116.377 117.67 116.251 118.876 116.479 L 147.16 121.816 C 148.754 122.118 149.203 122.74 148.154 123.242 L 134.048 129.979 C 132.784 130.583 130.498 130.742 128.862 130.339 L 100.257 123.302 C 99.054 123.006 99.132 122.419 100.404 121.96 Z M 108.868 119.483 C 107.34 120.037 107.261 120.796 108.851 121.152 C 110.469 121.514 113.1 121.301 114.629 120.719 C 116.134 120.147 116.095 119.396 114.478 119.062 C 112.886 118.734 110.373 118.936 108.868 119.483 Z M 111.025 119.946 C 111.461 119.784 112.065 119.747 112.427 119.824 C 112.79 119.901 112.826 120.075 112.39 120.239 C 111.952 120.403 111.342 120.44 110.978 120.361 C 110.616 120.283 110.587 120.107 111.025 119.946 Z M 119.393 124.407 L 126.983 127.328 L 128.583 126.619 L 127.135 126.08 L 129.824 124.904 L 131.877 125.157 L 133.391 124.484 L 122.368 123.188 Z M 122.129 124.244 L 122.674 124.016 L 127.851 124.662 L 125.746 125.568 Z M 129.304 121.709 C 128.624 121.994 128.029 122.323 127.518 122.7 L 128.82 122.947 L 130.724 122.072 C 130.942 121.98 131.154 121.924 131.361 121.903 C 131.559 121.883 131.799 121.9 132.081 121.957 L 132.505 122.044 L 131.484 122.549 C 130.906 122.832 130.618 123.073 130.622 123.272 C 130.617 123.469 130.986 123.646 131.732 123.805 C 133.28 124.133 134.619 124.051 135.721 123.561 C 136.25 123.325 136.598 123.063 136.767 122.773 C 137.181 122.718 137.525 122.651 137.799 122.573 C 138.079 122.492 138.393 122.373 138.741 122.218 L 137.351 121.917 C 137.183 121.967 137.037 121.992 136.911 121.994 C 136.776 121.992 136.589 121.974 136.351 121.935 L 133.477 121.363 C 132.679 121.204 131.954 121.152 131.298 121.206 C 130.649 121.258 129.986 121.423 129.304 121.709 Z M 133.788 122.303 L 135.284 122.608 L 135.183 122.688 C 134.98 122.849 134.746 122.987 134.478 123.106 C 134.132 123.257 133.699 123.28 133.184 123.173 C 132.67 123.066 132.59 122.922 132.941 122.741 Z", + "toValue": "M 63.763 113.162 C 61.258 113.782 60.786 114.719 62.829 115.238 L 88.332 121.716 C 91.26 122.46 95.896 122.323 98.457 121.417 L 112.842 116.323 C 114.898 115.596 114.124 114.57 111.255 114.045 L 85.682 109.352 C 83.587 108.967 80.229 109.076 78.133 109.596 Z M 65.592 113.597 L 79.993 109.957 C 81.073 109.684 82.732 109.629 83.811 109.833 L 109.413 114.661 C 110.875 114.937 111.211 115.435 110.146 115.801 L 95.719 120.766 C 94.416 121.214 92.216 121.278 90.731 120.913 L 65.177 114.634 C 64.119 114.373 64.313 113.92 65.592 113.597 Z M 73.954 111.894 C 72.426 112.284 72.203 112.868 73.613 113.182 C 75.05 113.501 77.545 113.403 79.08 112.991 C 80.59 112.587 80.692 112.004 79.249 111.708 C 77.832 111.417 75.457 111.511 73.954 111.894 Z M 75.87 112.304 C 76.307 112.19 76.876 112.177 77.198 112.245 C 77.521 112.314 77.522 112.449 77.085 112.565 C 76.645 112.68 76.07 112.694 75.747 112.623 C 75.425 112.555 75.431 112.418 75.87 112.304 Z M 82.852 115.98 L 89.477 118.474 L 91.112 117.954 L 89.838 117.494 L 92.576 116.638 L 94.469 116.889 L 96.009 116.399 L 85.864 115.104 Z M 85.451 115.922 L 86.004 115.758 L 90.758 116.395 L 88.618 117.054 Z M 92.629 114.125 C 91.942 114.329 91.327 114.572 90.783 114.853 L 91.964 115.079 L 93.9 114.445 C 94.12 114.378 94.329 114.339 94.527 114.329 C 94.716 114.318 94.939 114.338 95.194 114.39 L 95.578 114.468 L 94.535 114.836 C 93.944 115.042 93.632 115.223 93.603 115.378 C 93.565 115.532 93.882 115.681 94.559 115.825 C 95.962 116.121 97.238 116.091 98.357 115.735 C 98.893 115.564 99.263 115.368 99.469 115.146 C 99.868 115.112 100.202 115.069 100.473 115.015 C 100.749 114.958 101.064 114.873 101.416 114.761 L 100.154 114.492 C 99.989 114.525 99.847 114.542 99.729 114.54 C 99.602 114.535 99.429 114.515 99.211 114.48 L 96.602 113.961 C 95.88 113.819 95.208 113.761 94.583 113.786 C 93.966 113.809 93.316 113.921 92.629 114.125 Z M 96.741 114.702 L 98.099 114.977 L 97.991 115.037 C 97.774 115.159 97.531 115.261 97.26 115.346 C 96.909 115.457 96.498 115.464 96.03 115.367 C 95.564 115.27 95.513 115.156 95.872 115.024 Z" + }, + { + "id": "8169304", + "layerId": "8025580", + "propertyName": "fillAlpha", + "startTime": 3258, + "endTime": 3673, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "number", + "fromValue": 1, + "toValue": 0 + }, + { + "id": "8174432", + "layerId": "8025767", + "propertyName": "fillAlpha", + "startTime": 3258, + "endTime": 3673, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "number", + "fromValue": 1, + "toValue": 0 + }, + { + "id": "8175258", + "layerId": "8025580", + "propertyName": "fillAlpha", + "startTime": 3745, + "endTime": 4000, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "number", + "fromValue": 0, + "toValue": 1 + }, + { + "id": "8177631", + "layerId": "8025767", + "propertyName": "fillAlpha", + "startTime": 3745, + "endTime": 4000, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "number", + "fromValue": 0, + "toValue": 1 + } + ] + } + } +} \ No newline at end of file diff --git a/img/animations/kiosk_portrait_kt0345/scan-frame00.svg b/img/animations/kiosk_portrait_kt0345/scan-frame00.svg new file mode 100644 index 00000000..af1a409e --- /dev/null +++ b/img/animations/kiosk_portrait_kt0345/scan-frame00.svg @@ -0,0 +1,252 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/animations/kiosk_portrait_kt0345/scan.shapeshifter b/img/animations/kiosk_portrait_kt0345/scan.shapeshifter new file mode 100644 index 00000000..a57d8d08 --- /dev/null +++ b/img/animations/kiosk_portrait_kt0345/scan.shapeshifter @@ -0,0 +1,193 @@ +{ + "version": 1, + "layers": { + "vectorLayer": { + "id": "93750", + "name": "vector", + "type": "vector", + "width": 158, + "height": 190, + "children": [ + { + "id": "92557", + "name": "path", + "type": "path", + "pathData": "M 20.851 176.521 L 20.851 189.877 L 35.059 189.877 L 35.059 172.941", + "fillColor": "#ececec", + "strokeColor": "#000000", + "strokeWidth": 1.24593, + "strokeLinecap": "round", + "strokeLinejoin": "round" + }, + { + "id": "92571", + "name": "path_1", + "type": "path", + "pathData": "M 84.298 159.133 C 86.558 158.528 89.469 155.65 88.963 151.954 C 88.045 145.252 73.226 20.176 73.226 20.176 C 73.226 20.176 72.315 14.383 65.882 13.731", + "fillColor": "#c2c2c2", + "strokeColor": "#000000", + "strokeWidth": 1.24593, + "strokeLinecap": "round", + "strokeLinejoin": "round" + }, + { + "id": "92593", + "name": "path_2", + "type": "path", + "pathData": "M 47.466 169.296 L 84.298 159.133 C 84.298 159.133 87.705 157.348 86.935 150.778 C 86.164 144.207 71.684 21.799 71.684 21.799 C 71.684 21.799 71.766 14.173 64.424 13.484 C 57.083 12.794 48.282 12.47 48.282 12.47 L 25.609 12.794 L 9.551 14.253", + "fillColor": "#7f7f7f", + "strokeColor": "#000000", + "strokeWidth": 1.24593, + "strokeLinecap": "round", + "strokeLinejoin": "round" + }, + { + "id": "92621", + "name": "path_3", + "type": "path", + "pathData": "M 9.551 14.253 L 29.799 15.457 C 29.799 15.457 34.388 15.515 35.478 20.906 C 36.568 26.298 52.571 161.667 52.571 161.667 C 52.571 161.667 52.743 167.174 47.466 169.296 C 42.189 171.419 20.76 176.503 18.327 177.154 C 16.02 177.773 13.369 177.535 11.272 175.893 C 9.524 174.524 8.576 171.649 8.576 170.042 C 8.576 168.703 6.253 70.085 6.253 19.645 C 6.253 18.857 6.98 14.1 9.551 14.253 Z", + "fillColor": "#c2c2c2", + "strokeColor": "#000000", + "strokeWidth": 1.24593, + "strokeLinecap": "round", + "strokeLinejoin": "round" + }, + { + "id": "92637", + "name": "path_4", + "type": "path", + "pathData": "M 44.216 29.906 L 65.803 28.369 L 73.338 89.812 L 51.76 93.016 Z", + "fillColor": "#cecece", + "strokeColor": "#000000", + "strokeWidth": 1.24593, + "strokeLinecap": "round", + "strokeLinejoin": "round" + }, + { + "id": "92653", + "name": "path_5", + "type": "path", + "pathData": "M 83.406 148.912 L 77.877 100.44 L 49.214 105.067 L 55.096 156.05 Z", + "strokeColor": "#000000", + "strokeWidth": 1.24593, + "strokeLinecap": "round", + "strokeLinejoin": "round" + }, + { + "id": "92709", + "name": "path_6", + "type": "path", + "pathData": "M 77.877 100.44 L 73.009 109.474 L 77.98 147.021 L 83.406 148.912 M 77.98 147.021 L 54.726 152.849 M 73.009 109.474 L 50.175 113.394 M 64.505 105.33 L 59.8 106.101 L 58.705 109.183 L 63.491 108.413 Z", + "strokeColor": "#000000", + "strokeWidth": 1.24593, + "strokeLinecap": "round", + "strokeLinejoin": "round" + }, + { + "id": "92725", + "name": "path_7", + "type": "path", + "pathData": "M 32.326 13.497 L 32.326 1.06 L 17.868 1.06 L 17.868 13.497 Z", + "fillColor": "#ececec", + "strokeColor": "#000000", + "strokeWidth": 1.24593, + "strokeLinecap": "round", + "strokeLinejoin": "round" + }, + { + "id": "93732", + "name": "path_8", + "type": "path", + "pathData": "M 122.176 168.678 C 125.73 170.773 128.962 169.421 128.962 165.423 L 128.962 103.198 C 128.962 99.199 125.73 97.847 122.176 99.942 L 104.833 110.168 C 103.244 111.105 102.038 113.727 102.038 116.07 L 102.038 152.55 C 102.038 154.894 103.244 157.515 104.833 158.452 Z M 122.176 167.412 L 104.833 157.562 C 103.538 156.827 102.552 154.717 102.552 152.796 L 102.552 149.324 L 127.519 159.02 L 127.519 164.733 C 127.519 167.895 125.017 169.026 122.176 167.412 Z M 117.161 160.359 C 117.435 160.493 117.66 160.342 117.66 160.02 C 117.66 159.697 117.435 159.331 117.161 159.202 L 107.732 154.797 C 107.547 154.71 107.4 154.852 107.4 155.113 C 107.4 155.375 107.547 155.659 107.732 155.748 Z M 127.519 157.638 L 102.552 148.484 L 102.552 120.135 L 127.519 110.982 Z M 121.6 147.307 C 121.879 147.37 122.107 147.177 122.107 146.875 L 122.107 139.227 C 122.106 138.925 121.879 138.664 121.6 138.643 L 115.342 138.173 C 115.123 138.156 114.947 138.357 114.947 138.622 L 114.947 145.329 C 114.947 145.457 114.988 145.588 115.062 145.694 C 115.136 145.801 115.237 145.874 115.342 145.897 Z M 113.796 145.549 C 114.003 145.596 114.173 145.423 114.173 145.163 L 114.173 144.214 C 114.174 144.089 114.133 143.961 114.062 143.86 C 113.991 143.758 113.895 143.69 113.796 143.672 L 110.308 143.018 C 110.222 143.001 110.14 143.032 110.08 143.102 C 110.02 143.172 109.986 143.275 109.986 143.39 C 109.986 143.629 110.129 143.851 110.308 143.889 L 113.423 144.532 L 113.423 145.001 C 113.423 145.125 113.462 145.251 113.532 145.353 C 113.601 145.457 113.697 145.527 113.796 145.549 Z M 109.045 144.479 C 109.127 144.498 109.206 144.471 109.264 144.404 C 109.322 144.337 109.355 144.236 109.355 144.123 L 109.355 142.416 C 109.355 142.303 109.322 142.189 109.264 142.099 C 109.206 142.009 109.127 141.951 109.045 141.938 L 105.379 141.318 L 105.379 134.7 C 105.379 134.597 105.351 134.498 105.303 134.424 C 105.255 134.351 105.19 134.31 105.122 134.31 C 105.053 134.31 104.989 134.351 104.941 134.423 C 104.894 134.495 104.867 134.593 104.867 134.695 L 104.867 141.616 C 104.867 141.828 104.98 142.022 105.122 142.048 L 108.741 142.727 L 108.741 143.569 L 107.275 143.266 C 107.2 143.251 107.129 143.279 107.076 143.345 C 107.024 143.41 106.994 143.506 106.994 143.613 C 106.994 143.836 107.12 144.046 107.275 144.081 Z M 121.104 146.121 L 115.744 145.015 L 115.744 139.177 L 121.104 139.68 Z M 119.662 144.777 C 119.922 144.826 120.134 144.63 120.135 144.338 L 120.135 141.171 C 120.135 141.031 120.085 140.891 119.995 140.782 C 119.907 140.673 119.787 140.605 119.662 140.59 L 116.988 140.289 C 116.754 140.263 116.566 140.463 116.566 140.736 L 116.566 143.702 C 116.567 143.975 116.755 144.231 116.988 144.274 Z M 119.198 143.653 L 117.415 143.352 L 117.415 141.342 L 119.198 141.577 Z M 112.339 142.489 C 112.535 142.521 112.695 142.344 112.695 142.092 C 112.695 141.97 112.657 141.848 112.591 141.751 C 112.524 141.656 112.433 141.594 112.339 141.58 L 110.308 141.274 C 110.222 141.262 110.14 141.296 110.08 141.368 C 110.02 141.44 109.986 141.545 109.986 141.66 C 109.986 141.898 110.13 142.116 110.308 142.146 Z M 113.796 142.271 C 114.003 142.305 114.173 142.12 114.173 141.86 L 114.173 137.608 C 114.174 137.483 114.133 137.361 114.062 137.268 C 113.991 137.176 113.895 137.12 113.796 137.115 L 112.339 137.033 C 112.246 137.028 112.156 137.07 112.091 137.152 C 112.026 137.233 111.989 137.345 111.989 137.465 C 111.989 137.714 112.146 137.927 112.339 137.942 L 113.423 138.023 L 113.423 141.747 C 113.423 141.871 113.462 141.995 113.532 142.093 C 113.601 142.192 113.697 142.256 113.796 142.271 Z M 109.045 141.09 C 109.127 141.103 109.206 141.069 109.264 140.998 C 109.322 140.927 109.355 140.823 109.355 140.71 L 109.355 138.149 C 109.355 138.036 109.322 137.925 109.264 137.841 C 109.206 137.756 109.127 137.706 109.045 137.7 L 107.275 137.567 C 107.12 137.556 106.995 137.727 106.994 137.95 L 106.994 140.377 C 106.994 140.6 107.12 140.801 107.275 140.824 Z M 106.172 140.658 C 106.321 140.681 106.442 140.52 106.442 140.299 L 106.442 136.706 L 107.561 136.769 C 107.638 136.773 107.711 136.734 107.765 136.66 C 107.819 136.585 107.85 136.482 107.85 136.372 C 107.85 136.145 107.72 135.955 107.561 135.95 L 106.172 135.897 C 106.023 135.891 105.904 136.063 105.903 136.281 L 105.903 140.224 C 105.903 140.328 105.932 140.433 105.982 140.515 C 106.032 140.596 106.1 140.648 106.172 140.658 Z M 108.741 140.203 L 107.561 140.048 L 107.561 138.409 L 108.741 138.519 Z M 110.308 140.403 C 110.487 140.427 110.634 140.251 110.635 140.008 L 110.635 139.569 L 111.643 139.683 C 111.734 139.693 111.822 139.656 111.887 139.579 C 111.952 139.501 111.989 139.39 111.989 139.271 C 111.989 139.15 111.952 139.032 111.887 138.941 C 111.822 138.85 111.735 138.795 111.643 138.787 L 110.635 138.692 L 110.635 137.376 C 110.635 137.26 110.601 137.146 110.539 137.06 C 110.478 136.974 110.394 136.923 110.308 136.918 C 110.222 136.914 110.14 136.955 110.08 137.033 C 110.02 137.111 109.986 137.219 109.986 137.334 L 109.986 139.929 C 109.986 140.168 110.13 140.38 110.308 140.403 Z M 121.6 137.559 C 121.734 137.567 121.862 137.517 121.958 137.42 C 122.053 137.323 122.107 137.186 122.107 137.041 L 122.107 133.764 C 122.107 133.619 122.053 133.481 121.958 133.38 C 121.862 133.279 121.734 133.224 121.6 133.227 C 121.468 133.23 121.341 133.289 121.248 133.391 C 121.155 133.494 121.104 133.631 121.104 133.773 L 121.104 136.994 C 121.104 137.136 121.155 137.276 121.248 137.381 C 121.341 137.488 121.468 137.552 121.6 137.559 Z M 119.662 137.45 C 119.787 137.457 119.907 137.409 119.996 137.315 C 120.085 137.22 120.135 137.089 120.135 136.948 C 120.134 136.657 119.922 136.413 119.662 136.403 L 116.566 136.287 L 116.566 135.298 L 117.851 135.323 C 118.093 135.327 118.291 135.102 118.292 134.821 C 118.292 134.686 118.246 134.556 118.162 134.46 C 118.079 134.363 117.967 134.31 117.851 134.31 L 116.152 134.31 C 116.043 134.31 115.939 134.361 115.862 134.452 C 115.786 134.544 115.744 134.668 115.744 134.797 L 115.744 136.742 C 115.744 136.872 115.786 136.998 115.862 137.094 C 115.939 137.189 116.043 137.247 116.152 137.253 Z M 109.045 136.853 C 109.127 136.858 109.206 136.817 109.264 136.74 C 109.322 136.663 109.355 136.556 109.355 136.443 L 109.355 133.879 C 109.355 133.766 109.322 133.658 109.264 133.578 C 109.206 133.5 109.127 133.457 109.045 133.458 L 107.275 133.492 C 107.12 133.496 106.994 133.678 106.994 133.901 C 106.994 134.009 107.024 134.111 107.076 134.188 C 107.129 134.263 107.201 134.306 107.275 134.306 L 108.741 134.306 L 108.741 136.414 C 108.741 136.526 108.773 136.634 108.829 136.716 C 108.886 136.799 108.964 136.848 109.045 136.853 Z M 113.796 135.714 C 114.003 135.72 114.173 135.514 114.173 135.254 L 114.173 131.951 C 114.174 131.826 114.133 131.708 114.062 131.623 C 113.991 131.539 113.895 131.495 113.796 131.5 C 113.696 131.506 113.601 131.56 113.531 131.652 C 113.462 131.742 113.423 131.863 113.423 131.987 L 113.423 135.24 C 113.423 135.363 113.462 135.482 113.532 135.571 C 113.601 135.66 113.697 135.712 113.796 135.714 Z M 112.339 135.673 C 112.535 135.678 112.695 135.478 112.695 135.226 L 112.695 133.394 C 112.695 133.141 112.535 132.941 112.339 132.947 C 112.146 132.953 111.99 133.158 111.989 133.407 L 111.989 135.213 C 111.989 135.461 112.146 135.668 112.339 135.673 Z M 110.308 135.616 C 110.394 135.619 110.477 135.575 110.538 135.495 C 110.6 135.414 110.635 135.304 110.635 135.187 L 110.635 132.555 L 112.339 132.491 C 112.535 132.483 112.695 132.273 112.695 132.02 C 112.695 131.768 112.536 131.571 112.339 131.582 L 110.308 131.696 C 110.222 131.701 110.14 131.752 110.08 131.837 C 110.02 131.921 109.986 132.033 109.986 132.148 L 109.986 135.175 C 109.986 135.413 110.129 135.611 110.308 135.616 Z M 106.172 135.103 C 106.32 135.106 106.442 134.93 106.442 134.71 L 106.442 132.712 L 108.44 132.637 C 108.519 132.634 108.595 132.588 108.652 132.507 C 108.709 132.426 108.741 132.317 108.741 132.206 C 108.741 132.094 108.709 131.989 108.652 131.912 C 108.596 131.837 108.519 131.797 108.44 131.801 L 106.172 131.929 C 106.1 131.933 106.032 131.979 105.982 132.056 C 105.931 132.132 105.903 132.235 105.903 132.339 L 105.903 134.705 C 105.904 134.809 105.932 134.91 105.982 134.985 C 106.032 135.059 106.1 135.103 106.172 135.103 Z M 119.662 134.31 C 119.922 134.31 120.134 134.074 120.135 133.783 L 120.135 132.199 L 121.6 132.143 C 121.879 132.133 122.106 131.881 122.107 131.579 C 122.107 131.434 122.053 131.298 121.958 131.2 C 121.862 131.103 121.734 131.053 121.6 131.06 L 119.662 131.17 C 119.404 131.185 119.198 131.428 119.198 131.715 L 119.198 133.792 C 119.199 134.078 119.405 134.31 119.662 134.31 Z M 117.851 132.284 C 118.093 132.275 118.291 132.04 118.292 131.758 C 118.292 131.476 118.094 131.258 117.851 131.272 L 115.342 131.413 C 115.236 131.419 115.136 131.475 115.062 131.57 C 114.988 131.664 114.946 131.787 114.947 131.915 C 114.947 132.179 115.123 132.387 115.342 132.379 Z M 121.6 129.977 C 121.879 129.957 122.107 129.695 122.107 129.393 L 122.107 121.745 C 122.107 121.443 121.879 121.25 121.6 121.313 L 115.342 122.723 C 115.237 122.746 115.136 122.82 115.062 122.926 C 114.988 123.032 114.947 123.163 114.947 123.29 L 114.947 129.998 C 114.947 130.125 114.988 130.243 115.062 130.328 C 115.136 130.412 115.237 130.455 115.342 130.447 Z M 113.796 130.564 C 114.003 130.547 114.173 130.324 114.173 130.063 L 114.173 123.457 C 114.173 123.197 114.003 123.024 113.796 123.07 C 113.697 123.093 113.601 123.164 113.532 123.266 C 113.462 123.369 113.423 123.496 113.423 123.619 L 113.423 129.661 L 112.339 129.763 C 112.246 129.772 112.156 129.828 112.091 129.919 C 112.026 130.01 111.989 130.128 111.989 130.248 C 111.989 130.497 112.146 130.687 112.339 130.672 Z M 110.308 130.825 C 110.394 130.819 110.477 130.766 110.538 130.68 C 110.6 130.593 110.635 130.479 110.635 130.362 L 110.635 128.608 C 110.635 128.491 110.601 128.384 110.539 128.31 C 110.478 128.235 110.394 128.2 110.308 128.211 C 110.222 128.223 110.14 128.279 110.08 128.369 C 110.02 128.457 109.986 128.572 109.986 128.687 L 109.986 130.417 C 109.986 130.655 110.129 130.838 110.308 130.825 Z M 109.045 130.92 C 109.127 130.914 109.206 130.863 109.264 130.779 C 109.322 130.695 109.355 130.583 109.355 130.47 L 109.355 124.497 C 109.355 124.384 109.322 124.283 109.264 124.216 C 109.206 124.149 109.127 124.122 109.045 124.141 L 105.122 125.024 C 104.98 125.056 104.867 125.254 104.867 125.466 L 104.867 130.85 C 104.867 131.062 104.98 131.226 105.122 131.214 Z M 121.104 128.941 L 115.744 129.444 L 115.744 123.605 L 121.104 122.499 Z M 108.741 130.101 L 105.379 130.417 L 105.379 125.745 L 108.741 125.051 Z M 119.662 128.029 C 119.787 128.016 119.907 127.946 119.995 127.838 C 120.085 127.729 120.135 127.589 120.135 127.448 L 120.135 124.282 C 120.135 124.142 120.085 124.017 119.995 123.935 C 119.907 123.853 119.787 123.82 119.662 123.843 L 116.988 124.345 C 116.754 124.389 116.566 124.645 116.566 124.918 L 116.566 127.884 C 116.566 128.157 116.754 128.357 116.988 128.33 Z M 111.643 128.932 C 111.734 128.922 111.822 128.865 111.887 128.773 C 111.952 128.681 111.989 128.562 111.989 128.442 L 111.989 125.734 C 111.989 125.615 111.952 125.507 111.887 125.434 C 111.822 125.362 111.734 125.331 111.643 125.348 C 111.454 125.384 111.302 125.611 111.302 125.856 L 111.302 128.526 C 111.302 128.772 111.454 128.953 111.643 128.932 Z M 107.85 129.36 C 108.012 129.341 108.143 129.141 108.143 128.912 L 108.143 126.42 C 108.143 126.191 108.012 126.03 107.85 126.06 L 106.172 126.375 C 106.1 126.388 106.032 126.443 105.982 126.526 C 105.931 126.609 105.904 126.715 105.903 126.819 L 105.903 129.185 C 105.904 129.29 105.932 129.386 105.982 129.454 C 106.032 129.523 106.1 129.557 106.172 129.548 Z M 119.198 127.043 L 117.415 127.277 L 117.415 125.269 L 119.198 124.968 Z M 107.561 128.572 L 106.442 128.719 L 106.442 127.123 L 107.561 126.934 Z M 110.308 126.47 C 110.394 126.456 110.478 126.396 110.539 126.303 C 110.601 126.21 110.635 126.092 110.635 125.976 L 110.635 124.66 L 112.339 124.308 C 112.536 124.267 112.695 124.029 112.695 123.776 C 112.695 123.524 112.535 123.355 112.339 123.399 L 110.308 123.856 C 110.129 123.896 109.986 124.123 109.986 124.361 L 109.986 126.092 C 109.986 126.206 110.02 126.31 110.08 126.381 C 110.14 126.453 110.222 126.485 110.308 126.47 Z M 127.519 109.6 L 102.552 119.296 L 102.552 115.825 C 102.552 113.903 103.538 111.792 104.833 111.057 L 122.176 101.208 C 125.017 99.594 127.519 100.725 127.519 103.887 Z M 111.989 113.046 C 112.734 112.716 113.364 111.633 113.364 110.655 C 113.364 109.677 112.734 109.196 111.989 109.579 C 111.265 109.951 110.688 111.011 110.688 111.934 C 110.688 112.86 111.266 113.365 111.989 113.046 Z M 111.989 112.001 C 111.694 112.138 111.463 111.945 111.463 111.563 C 111.463 111.181 111.691 110.771 111.989 110.624 C 112.288 110.477 112.526 110.666 112.526 111.056 C 112.526 111.445 112.288 111.862 111.989 112.001 Z", + "fillColor": "#000", + "strokeWidth": 1 + }, + { + "id": "93748", + "name": "path_9", + "type": "path", + "pathData": "M 48.645 128.586 L 85.332 121.986 L 63.998 106.871 L 59.253 107.642 Z", + "fillColor": "#ffff00", + "fillAlpha": 0.5, + "strokeWidth": 1 + } + ] + }, + "hiddenLayerIds": [] + }, + "timeline": { + "animation": { + "id": "92543", + "name": "anim", + "duration": 3000, + "blocks": [ + { + "id": "93752", + "layerId": "93748", + "propertyName": "pathData", + "startTime": 0, + "endTime": 1500, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "path", + "fromValue": "M 54.667 124.179 L 72.039 121.048 L 63.998 106.871 L 59.253 107.642 Z", + "toValue": "M 111.517 162.443 L 121.106 138.814 L 63.998 106.871 L 59.253 107.642 Z" + }, + { + "id": "94171", + "layerId": "93732", + "propertyName": "pathData", + "startTime": 0, + "endTime": 1500, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "path", + "fromValue": "M 148.07 180.798 C 151.224 180.798 153.778 177.982 153.759 174.53 L 153.458 121.293 C 153.439 117.904 150.88 115.145 147.76 115.145 L 124.753 115.145 C 121.632 115.145 119.089 117.904 119.089 121.293 L 119.089 174.53 C 119.089 177.982 121.66 180.798 124.814 180.798 Z M 148.064 179.577 L 124.813 179.577 C 122.262 179.577 120.203 177.321 120.203 174.53 L 120.202 169.491 L 152.618 169.491 L 152.646 174.53 C 152.661 177.321 150.614 179.577 148.064 179.577 Z M 142.879 175.14 C 143.186 175.14 143.435 174.866 143.433 174.53 C 143.432 174.193 143.182 173.921 142.874 173.921 L 129.971 173.921 C 129.662 173.921 129.414 174.193 129.414 174.53 C 129.415 174.866 129.665 175.14 129.972 175.14 Z M 152.61 168.273 L 120.202 168.273 L 120.194 127.453 L 152.387 127.453 Z M 147.428 160.359 C 147.692 160.359 147.906 160.125 147.905 159.835 L 147.871 152.5 C 147.869 152.211 147.653 151.977 147.389 151.977 L 140.676 151.977 C 140.411 151.977 140.197 152.211 140.197 152.5 L 140.222 159.835 C 140.224 159.973 140.275 160.107 140.365 160.205 C 140.455 160.303 140.578 160.359 140.706 160.359 Z M 138.784 160.359 C 139.05 160.358 139.264 160.123 139.263 159.835 L 139.259 158.781 C 139.259 158.642 139.209 158.509 139.118 158.41 C 139.027 158.311 138.905 158.256 138.778 158.256 L 133.978 158.256 C 133.851 158.256 133.729 158.311 133.639 158.409 C 133.549 158.509 133.5 158.642 133.5 158.781 C 133.501 159.07 133.715 159.305 133.981 159.305 L 138.301 159.305 L 138.302 159.835 C 138.303 159.973 138.353 160.107 138.444 160.205 C 138.535 160.303 138.657 160.359 138.784 160.359 Z M 132.063 160.359 C 132.19 160.359 132.312 160.303 132.401 160.205 C 132.492 160.107 132.542 159.973 132.542 159.835 L 132.537 157.737 C 132.537 157.598 132.486 157.465 132.395 157.366 C 132.306 157.268 132.182 157.214 132.055 157.214 L 125.817 157.214 L 125.807 148.319 C 125.807 148.18 125.756 148.046 125.667 147.948 C 125.577 147.85 125.455 147.796 125.328 147.795 C 125.201 147.795 125.079 147.85 124.989 147.948 C 124.899 148.046 124.848 148.18 124.849 148.319 L 124.858 157.737 C 124.858 158.026 125.073 158.261 125.339 158.261 L 131.579 158.261 L 131.58 159.31 L 129.18 159.31 C 129.052 159.31 128.93 159.365 128.841 159.463 C 128.75 159.562 128.7 159.695 128.701 159.835 C 128.701 160.123 128.916 160.358 129.181 160.359 Z M 146.943 159.31 L 141.181 159.31 L 141.158 153.024 L 146.914 153.024 Z M 145.497 158.261 C 145.763 158.261 145.977 158.026 145.976 157.737 L 145.962 154.594 C 145.961 154.455 145.91 154.322 145.819 154.224 C 145.729 154.126 145.607 154.07 145.479 154.07 L 142.602 154.07 C 142.474 154.07 142.353 154.126 142.263 154.224 C 142.173 154.322 142.123 154.455 142.124 154.594 L 142.136 157.737 C 142.137 158.026 142.353 158.261 142.618 158.261 Z M 145.013 157.214 L 143.094 157.214 L 143.085 155.118 L 145.005 155.118 Z M 136.855 157.208 C 137.119 157.208 137.333 156.973 137.333 156.684 C 137.332 156.545 137.282 156.412 137.191 156.313 C 137.102 156.215 136.979 156.16 136.852 156.16 L 133.973 156.16 C 133.846 156.16 133.724 156.215 133.634 156.313 C 133.544 156.412 133.495 156.545 133.495 156.684 C 133.496 156.973 133.711 157.208 133.976 157.208 Z M 138.773 156.689 C 139.037 156.689 139.251 156.455 139.25 156.166 L 139.235 151.449 C 139.235 151.31 139.183 151.177 139.094 151.079 C 139.003 150.981 138.881 150.926 138.754 150.926 L 136.837 150.926 C 136.71 150.926 136.588 150.981 136.498 151.079 C 136.409 151.177 136.359 151.31 136.36 151.449 C 136.36 151.738 136.575 151.971 136.84 151.972 L 138.278 151.972 L 138.291 156.166 C 138.292 156.305 138.342 156.438 138.433 156.536 C 138.523 156.635 138.646 156.689 138.773 156.689 Z M 132.054 156.166 C 132.181 156.166 132.303 156.111 132.393 156.012 C 132.483 155.914 132.534 155.781 132.532 155.641 L 132.526 152.5 C 132.525 152.211 132.309 151.977 132.045 151.977 L 129.167 151.977 C 128.903 151.977 128.688 152.211 128.688 152.5 L 128.694 155.641 C 128.694 155.781 128.745 155.913 128.835 156.011 C 128.925 156.109 129.047 156.165 129.175 156.166 Z M 127.256 156.166 C 127.521 156.165 127.735 155.931 127.734 155.641 L 127.728 150.932 L 129.646 150.932 C 129.773 150.932 129.895 150.876 129.984 150.778 C 130.074 150.68 130.124 150.546 130.124 150.408 C 130.123 150.12 129.908 149.885 129.643 149.885 L 127.248 149.885 C 127.12 149.885 126.998 149.941 126.909 150.038 C 126.818 150.136 126.768 150.269 126.768 150.408 L 126.774 155.641 C 126.774 155.781 126.826 155.914 126.915 156.012 C 127.006 156.111 127.128 156.166 127.256 156.166 Z M 131.571 155.118 L 129.653 155.118 L 129.649 153.024 L 131.568 153.024 Z M 133.971 155.112 C 134.235 155.112 134.449 154.878 134.449 154.589 L 134.448 154.065 L 135.886 154.065 C 136.013 154.065 136.136 154.01 136.225 153.912 C 136.316 153.813 136.365 153.681 136.365 153.541 C 136.365 153.403 136.314 153.269 136.224 153.171 C 136.133 153.073 136.011 153.017 135.884 153.017 L 134.445 153.017 L 134.442 151.448 C 134.44 151.31 134.39 151.176 134.299 151.078 C 134.21 150.98 134.088 150.926 133.961 150.926 C 133.833 150.926 133.711 150.981 133.622 151.079 C 133.532 151.177 133.482 151.31 133.482 151.448 L 133.487 153.541 L 133.489 154.589 C 133.491 154.878 133.706 155.112 133.971 155.112 Z M 147.384 150.932 C 147.511 150.932 147.633 150.876 147.722 150.778 C 147.812 150.68 147.862 150.546 147.861 150.408 L 147.846 147.273 C 147.846 147.135 147.794 147.001 147.705 146.903 C 147.614 146.805 147.492 146.75 147.365 146.75 C 147.238 146.75 147.115 146.806 147.026 146.904 C 146.937 147.001 146.887 147.135 146.887 147.273 L 146.903 150.408 C 146.903 150.546 146.953 150.68 147.044 150.778 C 147.134 150.876 147.256 150.931 147.384 150.932 Z M 145.466 150.932 C 145.593 150.932 145.715 150.876 145.804 150.778 C 145.894 150.68 145.944 150.546 145.943 150.408 C 145.942 150.118 145.726 149.885 145.462 149.885 L 142.107 149.885 L 142.103 148.84 L 143.541 148.84 C 143.804 148.84 144.018 148.606 144.017 148.319 C 144.017 148.18 143.966 148.046 143.876 147.948 C 143.785 147.85 143.663 147.795 143.536 147.795 L 141.619 147.795 C 141.492 147.795 141.37 147.85 141.28 147.948 C 141.191 148.046 141.141 148.18 141.142 148.319 L 141.149 150.408 C 141.149 150.546 141.201 150.68 141.291 150.778 C 141.381 150.876 141.503 150.932 141.631 150.932 Z M 132.042 150.932 C 132.17 150.932 132.292 150.876 132.381 150.778 C 132.472 150.68 132.522 150.546 132.521 150.408 L 132.515 147.267 C 132.513 147.128 132.463 146.996 132.372 146.898 C 132.283 146.8 132.161 146.745 132.034 146.745 L 129.158 146.745 C 128.895 146.745 128.681 146.98 128.681 147.267 C 128.681 147.405 128.731 147.539 128.822 147.637 C 128.911 147.735 129.034 147.79 129.161 147.791 L 131.558 147.791 L 131.563 150.408 C 131.563 150.546 131.613 150.679 131.704 150.777 C 131.793 150.875 131.915 150.931 132.042 150.932 Z M 138.749 149.363 C 139.013 149.361 139.228 149.128 139.226 148.84 L 139.215 145.185 C 139.214 145.046 139.163 144.913 139.074 144.815 C 138.983 144.718 138.861 144.662 138.734 144.662 C 138.607 144.662 138.486 144.716 138.395 144.815 C 138.306 144.913 138.256 145.046 138.256 145.185 L 138.268 148.84 C 138.268 148.978 138.319 149.112 138.41 149.209 C 138.5 149.307 138.622 149.363 138.749 149.363 Z M 136.832 149.363 C 137.097 149.361 137.309 149.128 137.309 148.84 L 137.303 146.75 C 137.302 146.462 137.088 146.229 136.823 146.229 C 136.559 146.229 136.346 146.462 136.346 146.75 L 136.352 148.84 C 136.353 149.128 136.568 149.361 136.832 149.363 Z M 133.957 149.363 C 134.084 149.363 134.205 149.307 134.294 149.209 C 134.385 149.112 134.435 148.978 134.434 148.84 L 134.427 145.705 L 136.822 145.705 C 137.085 145.705 137.299 145.472 137.298 145.185 C 137.298 144.897 137.083 144.662 136.818 144.662 L 133.944 144.662 C 133.818 144.662 133.696 144.718 133.607 144.815 C 133.517 144.913 133.467 145.046 133.467 145.185 L 133.476 148.84 C 133.477 149.128 133.692 149.361 133.957 149.363 Z M 127.246 148.84 C 127.511 148.84 127.724 148.606 127.724 148.319 L 127.72 145.705 L 131.073 145.705 C 131.2 145.706 131.322 145.651 131.411 145.553 C 131.501 145.456 131.551 145.322 131.551 145.185 C 131.551 145.045 131.501 144.913 131.41 144.815 C 131.321 144.716 131.199 144.662 131.071 144.662 L 127.24 144.662 C 127.113 144.662 126.991 144.716 126.901 144.815 C 126.812 144.913 126.762 145.045 126.762 145.185 L 126.765 148.319 C 126.765 148.457 126.817 148.589 126.906 148.688 C 126.997 148.786 127.119 148.84 127.246 148.84 Z M 145.452 147.795 C 145.716 147.795 145.93 147.562 145.929 147.273 L 145.923 145.705 L 147.36 145.705 C 147.624 145.706 147.837 145.472 147.836 145.185 C 147.836 145.046 147.784 144.913 147.695 144.815 C 147.604 144.716 147.482 144.662 147.355 144.662 L 145.439 144.662 C 145.312 144.662 145.19 144.718 145.1 144.815 C 145.011 144.913 144.962 145.046 144.962 145.185 L 144.971 147.273 C 144.973 147.562 145.188 147.795 145.452 147.795 Z M 143.529 145.705 C 143.792 145.705 144.005 145.472 144.005 145.185 C 144.003 144.897 143.788 144.662 143.523 144.662 L 140.651 144.662 C 140.523 144.662 140.401 144.716 140.312 144.815 C 140.222 144.913 140.172 145.046 140.172 145.185 C 140.173 145.472 140.389 145.706 140.653 145.705 Z M 147.35 143.618 C 147.614 143.618 147.828 143.385 147.827 143.097 L 147.792 135.803 C 147.791 135.515 147.575 135.283 147.311 135.283 L 140.617 135.283 C 140.353 135.283 140.139 135.515 140.141 135.803 L 140.166 143.097 C 140.166 143.235 140.217 143.368 140.307 143.466 C 140.398 143.564 140.52 143.618 140.647 143.618 Z M 138.731 143.618 C 138.995 143.618 139.209 143.385 139.207 143.097 L 139.183 135.803 C 139.182 135.516 138.968 135.283 138.704 135.283 C 138.578 135.283 138.455 135.337 138.366 135.436 C 138.277 135.533 138.226 135.665 138.228 135.803 L 138.248 142.575 L 136.812 142.575 C 136.686 142.575 136.564 142.63 136.474 142.728 C 136.385 142.826 136.335 142.958 136.336 143.097 C 136.337 143.385 136.551 143.618 136.816 143.618 Z M 133.942 143.618 C 134.069 143.618 134.191 143.564 134.28 143.466 C 134.37 143.368 134.42 143.235 134.42 143.097 L 134.415 141.011 C 134.415 140.873 134.364 140.74 134.274 140.642 C 134.183 140.544 134.061 140.49 133.934 140.49 C 133.808 140.49 133.686 140.545 133.597 140.642 C 133.507 140.74 133.457 140.873 133.458 141.011 L 133.463 143.097 C 133.463 143.385 133.678 143.618 133.942 143.618 Z M 132.026 143.618 C 132.153 143.618 132.275 143.564 132.365 143.466 C 132.455 143.368 132.506 143.235 132.505 143.097 L 132.489 135.803 C 132.488 135.665 132.438 135.533 132.347 135.434 C 132.258 135.337 132.136 135.283 132.008 135.283 L 125.315 135.283 C 125.051 135.283 124.837 135.515 124.837 135.803 L 124.844 143.097 C 124.844 143.385 125.059 143.618 125.324 143.618 Z M 146.866 142.575 L 141.12 142.575 L 141.099 136.324 L 146.838 136.324 Z M 131.546 142.575 L 125.801 142.575 L 125.794 136.324 L 131.534 136.324 Z M 145.425 141.533 C 145.552 141.533 145.673 141.477 145.763 141.38 C 145.852 141.282 145.903 141.15 145.903 141.011 L 145.889 137.885 C 145.887 137.747 145.837 137.614 145.746 137.517 C 145.656 137.419 145.534 137.365 145.408 137.365 L 142.537 137.365 C 142.411 137.365 142.289 137.419 142.2 137.517 C 142.11 137.614 142.06 137.747 142.061 137.885 L 142.073 141.011 C 142.074 141.15 142.124 141.282 142.215 141.38 C 142.304 141.477 142.427 141.533 142.554 141.533 Z M 135.852 141.533 C 135.979 141.533 136.1 141.477 136.19 141.38 C 136.28 141.282 136.33 141.15 136.33 141.011 L 136.321 137.885 C 136.321 137.747 136.269 137.614 136.18 137.517 C 136.089 137.419 135.968 137.365 135.841 137.365 C 135.576 137.365 135.364 137.598 135.364 137.885 L 135.372 141.011 C 135.374 141.298 135.588 141.531 135.852 141.533 Z M 130.108 141.533 C 130.372 141.533 130.586 141.3 130.585 141.011 L 130.58 137.885 C 130.579 137.598 130.365 137.365 130.1 137.365 L 127.231 137.365 C 127.104 137.365 126.982 137.419 126.893 137.517 C 126.803 137.614 126.753 137.747 126.753 137.885 L 126.756 141.011 C 126.756 141.15 126.807 141.282 126.898 141.38 C 126.987 141.477 127.109 141.533 127.236 141.533 Z M 144.943 140.49 L 143.029 140.49 L 143.02 138.407 L 144.934 138.407 Z M 129.628 140.49 L 127.712 140.49 L 127.71 138.407 L 129.624 138.407 Z M 133.929 138.407 C 134.056 138.407 134.178 138.351 134.268 138.254 C 134.357 138.156 134.408 138.024 134.408 137.885 L 134.403 136.324 L 136.794 136.324 C 137.059 136.324 137.272 136.091 137.27 135.803 C 137.269 135.516 137.055 135.283 136.792 135.283 L 133.921 135.283 C 133.658 135.283 133.445 135.516 133.445 135.803 L 133.45 137.885 C 133.45 138.024 133.501 138.156 133.592 138.254 C 133.681 138.351 133.803 138.407 133.929 138.407 Z M 152.381 126.252 L 120.194 126.252 L 120.193 121.292 C 120.193 118.552 122.23 116.341 124.754 116.341 L 147.765 116.341 C 150.289 116.341 152.338 118.552 152.353 121.292 Z M 136.279 123.282 C 137.285 123.282 138.108 122.384 138.105 121.292 C 138.102 120.201 137.273 119.305 136.268 119.305 C 135.263 119.305 134.439 120.203 134.442 121.292 C 134.445 122.384 135.274 123.282 136.279 123.282 Z M 136.277 122.083 C 135.869 122.083 135.547 121.735 135.546 121.292 C 135.545 120.849 135.861 120.502 136.272 120.502 C 136.68 120.502 137 120.85 137.001 121.292 C 137.002 121.735 136.683 122.083 136.277 122.083 Z", + "toValue": "M 95.735 174.051 C 96.216 176.927 98.994 177.791 101.887 175.76 L 146.919 144.158 C 149.812 142.128 149.945 139.221 147.404 137.791 L 133.359 129.888 C 131.917 129.076 129.18 129.587 127.19 130.984 L 96.207 152.727 C 94.216 154.124 92.806 156.524 93.078 158.157 Z M 96.687 173.384 L 93.821 157.636 C 93.581 156.321 94.708 154.383 96.334 153.241 L 99.273 151.179 L 105.794 171.775 L 101.626 174.7 C 99.32 176.319 97.103 175.671 96.687 173.384 Z M 99.171 166.875 C 99.226 167.106 99.473 167.151 99.721 166.977 C 99.97 166.802 100.124 166.475 100.067 166.247 L 97.957 157.968 C 97.914 157.796 97.704 157.78 97.49 157.93 C 97.276 158.08 97.136 158.342 97.177 158.515 Z M 106.802 171.068 L 99.983 150.68 L 123.981 133.839 L 140.836 147.183 Z M 111.517 162.443 C 111.607 162.638 111.863 162.668 112.09 162.509 L 117.842 158.472 C 118.069 158.313 118.162 158.037 118.052 157.857 L 115.463 153.645 C 115.369 153.493 115.125 153.486 114.918 153.632 L 109.658 157.323 C 109.559 157.393 109.478 157.491 109.433 157.596 C 109.388 157.702 109.384 157.806 109.421 157.885 Z M 108.879 156.707 C 108.952 156.866 109.178 156.879 109.382 156.736 L 110.13 156.211 C 110.229 156.143 110.308 156.046 110.351 155.944 C 110.394 155.841 110.398 155.742 110.361 155.667 L 109.026 152.978 C 108.992 152.911 108.923 152.871 108.834 152.871 C 108.744 152.869 108.642 152.905 108.55 152.97 C 108.357 153.105 108.255 153.329 108.323 153.471 L 109.485 155.902 L 109.114 156.162 C 109.017 156.23 108.936 156.326 108.893 156.428 C 108.849 156.53 108.843 156.631 108.879 156.707 Z M 107.148 152.946 C 107.179 153.012 107.244 153.05 107.33 153.052 C 107.417 153.055 107.518 153.018 107.61 152.955 L 108.994 151.984 C 109.085 151.918 109.16 151.832 109.201 151.739 C 109.242 151.648 109.247 151.561 109.213 151.496 L 107.683 148.525 L 113.186 144.663 C 113.272 144.603 113.339 144.525 113.375 144.445 C 113.41 144.367 113.409 144.293 113.373 144.241 C 113.337 144.19 113.268 144.165 113.183 144.17 C 113.097 144.176 113.001 144.214 112.916 144.274 L 107.144 148.325 C 106.966 148.449 106.87 148.645 106.93 148.763 L 108.404 151.735 L 107.719 152.216 L 107.146 151.018 C 107.116 150.956 107.053 150.921 106.97 150.922 C 106.887 150.921 106.79 150.956 106.702 151.019 C 106.518 151.148 106.418 151.357 106.477 151.487 Z M 112.168 161.523 L 110.32 157.655 L 114.875 154.458 L 117.04 158.104 Z M 112.481 159.946 C 112.571 160.129 112.824 160.152 113.047 159.997 L 115.455 158.307 C 115.562 158.231 115.644 158.126 115.685 158.013 C 115.725 157.9 115.721 157.79 115.673 157.705 L 114.618 155.866 C 114.573 155.788 114.489 155.741 114.386 155.736 C 114.282 155.731 114.167 155.769 114.065 155.841 L 111.762 157.457 C 111.55 157.606 111.446 157.863 111.531 158.033 Z M 113.111 159.062 L 112.46 157.798 L 114.012 156.709 L 114.698 157.948 Z M 110.534 154.05 C 110.612 154.198 110.837 154.205 111.037 154.065 C 111.134 153.997 111.211 153.903 111.253 153.806 C 111.294 153.707 111.297 153.612 111.259 153.541 L 110.431 151.992 C 110.395 151.926 110.325 151.888 110.234 151.888 C 110.143 151.887 110.041 151.924 109.948 151.988 C 109.755 152.124 109.656 152.346 109.728 152.485 Z M 111.466 154.891 C 111.547 155.045 111.779 155.054 111.984 154.91 L 115.333 152.56 C 115.432 152.491 115.508 152.396 115.546 152.298 C 115.585 152.199 115.582 152.104 115.537 152.034 L 114.879 151 C 114.837 150.934 114.758 150.896 114.661 150.896 C 114.563 150.896 114.454 150.935 114.359 151.002 C 114.161 151.141 114.069 151.367 114.156 151.508 L 114.634 152.288 L 111.688 154.355 C 111.59 154.424 111.512 154.518 111.471 154.619 C 111.429 154.719 111.427 154.817 111.466 154.891 Z M 109.902 151.013 C 109.936 151.077 110.005 151.113 110.094 151.112 C 110.184 151.113 110.287 151.077 110.378 151.012 L 112.454 149.555 C 112.645 149.42 112.735 149.208 112.657 149.08 L 111.828 147.732 C 111.755 147.612 111.546 147.62 111.363 147.748 L 109.365 149.15 C 109.277 149.211 109.205 149.295 109.165 149.381 C 109.125 149.467 109.121 149.549 109.152 149.61 Z M 108.677 148.718 C 108.741 148.839 108.942 148.834 109.124 148.706 L 112.091 146.624 L 112.636 147.479 C 112.672 147.537 112.743 147.568 112.833 147.564 C 112.923 147.561 113.023 147.524 113.113 147.461 C 113.299 147.33 113.386 147.127 113.308 147.008 L 112.616 145.954 C 112.58 145.9 112.511 145.871 112.424 145.876 C 112.338 145.88 112.24 145.917 112.154 145.977 L 108.887 148.271 C 108.8 148.331 108.729 148.413 108.69 148.497 C 108.65 148.581 108.645 148.66 108.677 148.718 Z M 110.46 150.293 L 109.947 149.367 L 111.292 148.423 L 111.83 149.331 Z M 111.133 151.499 C 111.209 151.636 111.429 151.638 111.624 151.501 L 111.977 151.254 L 112.41 152.009 C 112.45 152.077 112.525 152.115 112.62 152.116 C 112.715 152.117 112.822 152.08 112.918 152.013 C 113.014 151.946 113.09 151.854 113.129 151.76 C 113.168 151.664 113.167 151.573 113.127 151.505 L 112.683 150.758 L 113.742 150.015 C 113.836 149.949 113.91 149.861 113.948 149.769 C 113.985 149.677 113.984 149.59 113.943 149.527 C 113.903 149.465 113.827 149.43 113.734 149.432 C 113.64 149.433 113.536 149.471 113.442 149.536 L 112.045 150.517 L 111.346 151.007 C 111.153 151.143 111.058 151.362 111.133 151.499 Z M 118.869 157.284 C 118.923 157.37 119.018 157.422 119.134 157.429 C 119.25 157.435 119.377 157.396 119.486 157.319 L 121.95 155.59 C 122.059 155.514 122.14 155.407 122.175 155.295 C 122.21 155.182 122.196 155.072 122.136 154.991 C 122.077 154.909 121.977 154.861 121.859 154.857 C 121.741 154.853 121.615 154.894 121.507 154.97 L 119.072 156.678 C 118.964 156.754 118.881 156.86 118.844 156.973 C 118.805 157.087 118.815 157.198 118.869 157.284 Z M 118.068 156.024 C 118.12 156.107 118.212 156.155 118.325 156.161 C 118.437 156.166 118.56 156.127 118.667 156.053 C 118.889 155.897 118.977 155.633 118.866 155.464 L 117.52 153.416 L 118.288 152.877 L 118.865 153.727 C 118.974 153.886 119.236 153.892 119.454 153.741 C 119.558 153.668 119.636 153.568 119.672 153.462 C 119.708 153.357 119.699 153.255 119.646 153.179 L 118.863 152.064 C 118.813 151.992 118.724 151.951 118.617 151.95 C 118.51 151.949 118.392 151.989 118.292 152.06 L 116.775 153.125 C 116.674 153.195 116.595 153.293 116.557 153.395 C 116.519 153.498 116.525 153.598 116.572 153.672 Z M 113.346 148.596 C 113.384 148.657 113.458 148.69 113.551 148.688 C 113.643 148.685 113.747 148.648 113.839 148.583 L 115.918 147.124 C 116.009 147.059 116.081 146.975 116.116 146.887 C 116.15 146.8 116.146 146.719 116.104 146.661 L 115.177 145.382 C 115.094 145.268 114.879 145.281 114.695 145.41 C 114.606 145.471 114.537 145.553 114.502 145.636 C 114.467 145.718 114.469 145.796 114.508 145.851 L 115.261 146.924 L 113.544 148.128 C 113.453 148.191 113.382 148.276 113.344 148.364 C 113.307 148.451 113.307 148.535 113.346 148.596 Z M 116.642 151.258 C 116.738 151.402 116.983 151.402 117.188 151.258 L 119.789 149.432 C 119.888 149.364 119.962 149.271 119.996 149.175 C 120.029 149.08 120.021 148.989 119.97 148.923 C 119.919 148.858 119.832 148.822 119.727 148.824 C 119.622 148.827 119.509 148.867 119.411 148.935 L 116.837 150.742 C 116.74 150.81 116.664 150.903 116.628 151 C 116.591 151.097 116.596 151.189 116.642 151.258 Z M 115.962 150.24 C 116.055 150.378 116.292 150.377 116.494 150.235 L 117.949 149.214 C 118.15 149.073 118.232 148.85 118.134 148.716 C 118.036 148.584 117.797 148.59 117.598 148.729 L 116.157 149.74 C 115.958 149.879 115.871 150.103 115.962 150.24 Z M 114.993 148.79 C 115.035 148.853 115.112 148.886 115.207 148.884 C 115.304 148.882 115.41 148.844 115.503 148.779 L 117.622 147.292 L 118.496 148.462 C 118.597 148.594 118.84 148.588 119.041 148.448 C 119.242 148.307 119.322 148.085 119.221 147.954 L 118.154 146.572 C 118.109 146.513 118.027 146.482 117.93 146.487 C 117.832 146.491 117.725 146.531 117.633 146.596 L 115.187 148.312 C 114.994 148.448 114.907 148.661 114.993 148.79 Z M 113.272 145.493 C 113.349 145.606 113.558 145.594 113.74 145.467 L 115.389 144.309 L 116.467 145.75 C 116.51 145.807 116.586 145.836 116.68 145.831 C 116.775 145.826 116.879 145.788 116.97 145.724 C 117.061 145.66 117.131 145.576 117.165 145.491 C 117.198 145.406 117.193 145.327 117.149 145.271 L 115.898 143.651 C 115.858 143.598 115.785 143.573 115.696 143.58 C 115.606 143.587 115.507 143.624 115.42 143.685 L 113.46 145.061 C 113.373 145.122 113.305 145.202 113.27 145.283 C 113.235 145.363 113.236 145.44 113.272 145.493 Z M 120.463 154.344 C 120.579 154.509 120.853 154.518 121.075 154.363 L 122.28 153.517 L 122.953 154.417 C 123.079 154.587 123.366 154.595 123.593 154.436 C 123.702 154.36 123.782 154.255 123.815 154.144 C 123.848 154.032 123.831 153.925 123.77 153.844 L 122.858 152.663 C 122.799 152.587 122.701 152.543 122.584 152.542 C 122.468 152.541 122.344 152.582 122.24 152.656 L 120.652 153.77 C 120.433 153.923 120.348 154.18 120.463 154.344 Z M 121.206 152.083 C 121.322 152.238 121.591 152.24 121.809 152.088 C 122.025 151.937 122.105 151.689 121.987 151.535 L 120.75 149.935 C 120.698 149.866 120.607 149.828 120.499 149.83 C 120.392 149.831 120.275 149.871 120.176 149.942 C 119.969 150.087 119.888 150.321 119.996 150.464 Z M 124.587 153.271 C 124.718 153.437 125.009 153.443 125.237 153.283 L 130.988 149.247 C 131.215 149.087 131.275 148.835 131.122 148.685 L 127.548 145.164 C 127.418 145.036 127.146 145.05 126.938 145.196 L 121.679 148.887 C 121.579 148.957 121.505 149.05 121.472 149.147 C 121.44 149.244 121.452 149.337 121.506 149.404 Z M 120.709 148.405 C 120.817 148.539 121.071 148.533 121.276 148.389 L 126.479 144.737 C 126.685 144.593 126.749 144.376 126.625 144.253 C 126.565 144.194 126.469 144.165 126.357 144.172 C 126.246 144.178 126.129 144.221 126.032 144.289 L 121.251 147.645 L 120.67 146.937 C 120.619 146.876 120.532 146.844 120.429 146.848 C 120.326 146.852 120.214 146.892 120.119 146.959 C 119.921 147.098 119.842 147.316 119.945 147.445 Z M 118.857 146.079 C 118.903 146.138 118.985 146.168 119.085 146.163 C 119.184 146.159 119.292 146.12 119.387 146.054 L 120.799 145.063 C 120.892 144.997 120.962 144.911 120.993 144.825 C 121.024 144.737 121.013 144.657 120.964 144.6 C 120.915 144.544 120.83 144.516 120.73 144.522 C 120.63 144.528 120.521 144.568 120.429 144.633 L 119.031 145.614 C 118.838 145.749 118.759 145.957 118.857 146.079 Z M 118.166 145.213 C 118.211 145.27 118.291 145.298 118.387 145.293 C 118.485 145.288 118.59 145.248 118.682 145.184 L 123.527 141.784 C 123.618 141.719 123.686 141.638 123.713 141.555 C 123.741 141.473 123.728 141.398 123.675 141.347 L 121.106 138.814 C 121.011 138.721 120.791 138.747 120.614 138.871 L 116.124 142.022 C 115.947 142.147 115.869 142.33 115.951 142.432 Z M 125.163 152.404 L 122.47 149.128 L 127.026 145.931 L 130.035 148.985 Z M 118.684 144.521 L 116.746 142.165 L 120.63 139.439 L 122.795 141.636 Z M 125.253 150.982 C 125.315 151.057 125.418 151.098 125.538 151.099 C 125.658 151.098 125.786 151.057 125.892 150.982 L 128.301 149.291 C 128.407 149.217 128.482 149.116 128.509 149.013 C 128.536 148.91 128.513 148.812 128.445 148.742 L 126.969 147.199 C 126.907 147.132 126.805 147.097 126.689 147.102 C 126.573 147.106 126.45 147.149 126.349 147.22 L 124.045 148.836 C 123.944 148.908 123.869 149.003 123.839 149.102 C 123.808 149.201 123.823 149.296 123.882 149.365 Z M 121.008 145.975 C 121.059 146.035 121.145 146.065 121.249 146.06 C 121.352 146.056 121.464 146.015 121.56 145.948 L 123.721 144.431 C 123.817 144.364 123.887 144.277 123.915 144.189 C 123.944 144.101 123.93 144.02 123.875 143.963 C 123.762 143.846 123.512 143.861 123.314 144 L 121.175 145.501 C 120.978 145.639 120.904 145.851 121.008 145.975 Z M 118.854 143.432 C 118.947 143.542 119.175 143.526 119.363 143.395 L 121.399 141.966 C 121.586 141.835 121.656 141.642 121.555 141.536 L 120.493 140.426 C 120.447 140.379 120.369 140.356 120.276 140.366 C 120.182 140.375 120.081 140.414 119.994 140.475 L 118.034 141.851 C 117.947 141.912 117.881 141.99 117.85 142.068 C 117.819 142.146 117.825 142.219 117.867 142.268 Z M 125.812 150.149 L 124.88 149.082 L 126.432 147.993 L 127.399 149.035 Z M 119.357 142.762 L 118.687 141.995 L 120.006 141.07 L 120.701 141.819 Z M 122.368 143.614 C 122.42 143.67 122.506 143.697 122.609 143.691 C 122.712 143.683 122.823 143.642 122.916 143.577 L 123.975 142.833 L 125.014 143.888 C 125.132 144.008 125.391 143.991 125.593 143.85 C 125.793 143.709 125.858 143.499 125.738 143.38 L 124.475 142.136 C 124.363 142.025 124.116 142.045 123.923 142.181 L 122.526 143.162 C 122.433 143.227 122.364 143.311 122.335 143.396 C 122.305 143.48 122.317 143.56 122.368 143.614 Z M 141.843 146.476 L 124.692 133.34 L 127.631 131.278 C 129.256 130.137 131.463 129.735 132.618 130.408 L 146.453 138.458 C 148.462 139.627 148.318 141.932 146.011 143.551 Z M 133.848 137.324 C 134.397 137.711 135.493 137.588 136.267 137.045 C 137.041 136.502 137.193 135.749 136.615 135.383 C 136.048 135.024 134.979 135.167 134.236 135.689 C 133.492 136.211 133.312 136.945 133.848 137.324 Z M 134.682 136.739 C 134.46 136.587 134.526 136.3 134.831 136.086 C 135.137 135.871 135.552 135.818 135.781 135.968 C 136.011 136.118 135.949 136.407 135.638 136.625 C 135.328 136.843 134.906 136.893 134.682 136.739 Z" + }, + { + "id": "473408", + "layerId": "93732", + "propertyName": "pathData", + "startTime": 1500, + "endTime": 3000, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "path", + "fromValue": "M 95.735 174.051 C 96.216 176.927 98.994 177.791 101.887 175.76 L 146.919 144.158 C 149.812 142.128 149.945 139.221 147.404 137.791 L 133.359 129.888 C 131.917 129.076 129.18 129.587 127.19 130.984 L 96.207 152.727 C 94.216 154.124 92.806 156.524 93.078 158.157 Z M 96.687 173.384 L 93.821 157.636 C 93.581 156.321 94.708 154.383 96.334 153.241 L 99.273 151.179 L 105.794 171.775 L 101.626 174.7 C 99.32 176.319 97.103 175.671 96.687 173.384 Z M 99.171 166.875 C 99.226 167.106 99.473 167.151 99.721 166.977 C 99.97 166.802 100.124 166.475 100.067 166.247 L 97.957 157.968 C 97.914 157.796 97.704 157.78 97.49 157.93 C 97.276 158.08 97.136 158.342 97.177 158.515 Z M 106.802 171.068 L 99.983 150.68 L 123.981 133.839 L 140.836 147.183 Z M 111.517 162.443 C 111.607 162.638 111.863 162.668 112.09 162.509 L 117.842 158.472 C 118.069 158.313 118.162 158.037 118.052 157.857 L 115.463 153.645 C 115.369 153.493 115.125 153.486 114.918 153.632 L 109.658 157.323 C 109.559 157.393 109.478 157.491 109.433 157.596 C 109.388 157.702 109.384 157.806 109.421 157.885 Z M 108.879 156.707 C 108.952 156.866 109.178 156.879 109.382 156.736 L 110.13 156.211 C 110.229 156.143 110.308 156.046 110.351 155.944 C 110.394 155.841 110.398 155.742 110.361 155.667 L 109.026 152.978 C 108.992 152.911 108.923 152.871 108.834 152.871 C 108.744 152.869 108.642 152.905 108.55 152.97 C 108.357 153.105 108.255 153.329 108.323 153.471 L 109.485 155.902 L 109.114 156.162 C 109.017 156.23 108.936 156.326 108.893 156.428 C 108.849 156.53 108.843 156.631 108.879 156.707 Z M 107.148 152.946 C 107.179 153.012 107.244 153.05 107.33 153.052 C 107.417 153.055 107.518 153.018 107.61 152.955 L 108.994 151.984 C 109.085 151.918 109.16 151.832 109.201 151.739 C 109.242 151.648 109.247 151.561 109.213 151.496 L 107.683 148.525 L 113.186 144.663 C 113.272 144.603 113.339 144.525 113.375 144.445 C 113.41 144.367 113.409 144.293 113.373 144.241 C 113.337 144.19 113.268 144.165 113.183 144.17 C 113.097 144.176 113.001 144.214 112.916 144.274 L 107.144 148.325 C 106.966 148.449 106.87 148.645 106.93 148.763 L 108.404 151.735 L 107.719 152.216 L 107.146 151.018 C 107.116 150.956 107.053 150.921 106.97 150.922 C 106.887 150.921 106.79 150.956 106.702 151.019 C 106.518 151.148 106.418 151.357 106.477 151.487 Z M 112.168 161.523 L 110.32 157.655 L 114.875 154.458 L 117.04 158.104 Z M 112.481 159.946 C 112.571 160.129 112.824 160.152 113.047 159.997 L 115.455 158.307 C 115.562 158.231 115.644 158.126 115.685 158.013 C 115.725 157.9 115.721 157.79 115.673 157.705 L 114.618 155.866 C 114.573 155.788 114.489 155.741 114.386 155.736 C 114.282 155.731 114.167 155.769 114.065 155.841 L 111.762 157.457 C 111.55 157.606 111.446 157.863 111.531 158.033 Z M 113.111 159.062 L 112.46 157.798 L 114.012 156.709 L 114.698 157.948 Z M 110.534 154.05 C 110.612 154.198 110.837 154.205 111.037 154.065 C 111.134 153.997 111.211 153.903 111.253 153.806 C 111.294 153.707 111.297 153.612 111.259 153.541 L 110.431 151.992 C 110.395 151.926 110.325 151.888 110.234 151.888 C 110.143 151.887 110.041 151.924 109.948 151.988 C 109.755 152.124 109.656 152.346 109.728 152.485 Z M 111.466 154.891 C 111.547 155.045 111.779 155.054 111.984 154.91 L 115.333 152.56 C 115.432 152.491 115.508 152.396 115.546 152.298 C 115.585 152.199 115.582 152.104 115.537 152.034 L 114.879 151 C 114.837 150.934 114.758 150.896 114.661 150.896 C 114.563 150.896 114.454 150.935 114.359 151.002 C 114.161 151.141 114.069 151.367 114.156 151.508 L 114.634 152.288 L 111.688 154.355 C 111.59 154.424 111.512 154.518 111.471 154.619 C 111.429 154.719 111.427 154.817 111.466 154.891 Z M 109.902 151.013 C 109.936 151.077 110.005 151.113 110.094 151.112 C 110.184 151.113 110.287 151.077 110.378 151.012 L 112.454 149.555 C 112.645 149.42 112.735 149.208 112.657 149.08 L 111.828 147.732 C 111.755 147.612 111.546 147.62 111.363 147.748 L 109.365 149.15 C 109.277 149.211 109.205 149.295 109.165 149.381 C 109.125 149.467 109.121 149.549 109.152 149.61 Z M 108.677 148.718 C 108.741 148.839 108.942 148.834 109.124 148.706 L 112.091 146.624 L 112.636 147.479 C 112.672 147.537 112.743 147.568 112.833 147.564 C 112.923 147.561 113.023 147.524 113.113 147.461 C 113.299 147.33 113.386 147.127 113.308 147.008 L 112.616 145.954 C 112.58 145.9 112.511 145.871 112.424 145.876 C 112.338 145.88 112.24 145.917 112.154 145.977 L 108.887 148.271 C 108.8 148.331 108.729 148.413 108.69 148.497 C 108.65 148.581 108.645 148.66 108.677 148.718 Z M 110.46 150.293 L 109.947 149.367 L 111.292 148.423 L 111.83 149.331 Z M 111.133 151.499 C 111.209 151.636 111.429 151.638 111.624 151.501 L 111.977 151.254 L 112.41 152.009 C 112.45 152.077 112.525 152.115 112.62 152.116 C 112.715 152.117 112.822 152.08 112.918 152.013 C 113.014 151.946 113.09 151.854 113.129 151.76 C 113.168 151.664 113.167 151.573 113.127 151.505 L 112.683 150.758 L 113.742 150.015 C 113.836 149.949 113.91 149.861 113.948 149.769 C 113.985 149.677 113.984 149.59 113.943 149.527 C 113.903 149.465 113.827 149.43 113.734 149.432 C 113.64 149.433 113.536 149.471 113.442 149.536 L 112.045 150.517 L 111.346 151.007 C 111.153 151.143 111.058 151.362 111.133 151.499 Z M 118.869 157.284 C 118.923 157.37 119.018 157.422 119.134 157.429 C 119.25 157.435 119.377 157.396 119.486 157.319 L 121.95 155.59 C 122.059 155.514 122.14 155.407 122.175 155.295 C 122.21 155.182 122.196 155.072 122.136 154.991 C 122.077 154.909 121.977 154.861 121.859 154.857 C 121.741 154.853 121.615 154.894 121.507 154.97 L 119.072 156.678 C 118.964 156.754 118.881 156.86 118.844 156.973 C 118.805 157.087 118.815 157.198 118.869 157.284 Z M 118.068 156.024 C 118.12 156.107 118.212 156.155 118.325 156.161 C 118.437 156.166 118.56 156.127 118.667 156.053 C 118.889 155.897 118.977 155.633 118.866 155.464 L 117.52 153.416 L 118.288 152.877 L 118.865 153.727 C 118.974 153.886 119.236 153.892 119.454 153.741 C 119.558 153.668 119.636 153.568 119.672 153.462 C 119.708 153.357 119.699 153.255 119.646 153.179 L 118.863 152.064 C 118.813 151.992 118.724 151.951 118.617 151.95 C 118.51 151.949 118.392 151.989 118.292 152.06 L 116.775 153.125 C 116.674 153.195 116.595 153.293 116.557 153.395 C 116.519 153.498 116.525 153.598 116.572 153.672 Z M 113.346 148.596 C 113.384 148.657 113.458 148.69 113.551 148.688 C 113.643 148.685 113.747 148.648 113.839 148.583 L 115.918 147.124 C 116.009 147.059 116.081 146.975 116.116 146.887 C 116.15 146.8 116.146 146.719 116.104 146.661 L 115.177 145.382 C 115.094 145.268 114.879 145.281 114.695 145.41 C 114.606 145.471 114.537 145.553 114.502 145.636 C 114.467 145.718 114.469 145.796 114.508 145.851 L 115.261 146.924 L 113.544 148.128 C 113.453 148.191 113.382 148.276 113.344 148.364 C 113.307 148.451 113.307 148.535 113.346 148.596 Z M 116.642 151.258 C 116.738 151.402 116.983 151.402 117.188 151.258 L 119.789 149.432 C 119.888 149.364 119.962 149.271 119.996 149.175 C 120.029 149.08 120.021 148.989 119.97 148.923 C 119.919 148.858 119.832 148.822 119.727 148.824 C 119.622 148.827 119.509 148.867 119.411 148.935 L 116.837 150.742 C 116.74 150.81 116.664 150.903 116.628 151 C 116.591 151.097 116.596 151.189 116.642 151.258 Z M 115.962 150.24 C 116.055 150.378 116.292 150.377 116.494 150.235 L 117.949 149.214 C 118.15 149.073 118.232 148.85 118.134 148.716 C 118.036 148.584 117.797 148.59 117.598 148.729 L 116.157 149.74 C 115.958 149.879 115.871 150.103 115.962 150.24 Z M 114.993 148.79 C 115.035 148.853 115.112 148.886 115.207 148.884 C 115.304 148.882 115.41 148.844 115.503 148.779 L 117.622 147.292 L 118.496 148.462 C 118.597 148.594 118.84 148.588 119.041 148.448 C 119.242 148.307 119.322 148.085 119.221 147.954 L 118.154 146.572 C 118.109 146.513 118.027 146.482 117.93 146.487 C 117.832 146.491 117.725 146.531 117.633 146.596 L 115.187 148.312 C 114.994 148.448 114.907 148.661 114.993 148.79 Z M 113.272 145.493 C 113.349 145.606 113.558 145.594 113.74 145.467 L 115.389 144.309 L 116.467 145.75 C 116.51 145.807 116.586 145.836 116.68 145.831 C 116.775 145.826 116.879 145.788 116.97 145.724 C 117.061 145.66 117.131 145.576 117.165 145.491 C 117.198 145.406 117.193 145.327 117.149 145.271 L 115.898 143.651 C 115.858 143.598 115.785 143.573 115.696 143.58 C 115.606 143.587 115.507 143.624 115.42 143.685 L 113.46 145.061 C 113.373 145.122 113.305 145.202 113.27 145.283 C 113.235 145.363 113.236 145.44 113.272 145.493 Z M 120.463 154.344 C 120.579 154.509 120.853 154.518 121.075 154.363 L 122.28 153.517 L 122.953 154.417 C 123.079 154.587 123.366 154.595 123.593 154.436 C 123.702 154.36 123.782 154.255 123.815 154.144 C 123.848 154.032 123.831 153.925 123.77 153.844 L 122.858 152.663 C 122.799 152.587 122.701 152.543 122.584 152.542 C 122.468 152.541 122.344 152.582 122.24 152.656 L 120.652 153.77 C 120.433 153.923 120.348 154.18 120.463 154.344 Z M 121.206 152.083 C 121.322 152.238 121.591 152.24 121.809 152.088 C 122.025 151.937 122.105 151.689 121.987 151.535 L 120.75 149.935 C 120.698 149.866 120.607 149.828 120.499 149.83 C 120.392 149.831 120.275 149.871 120.176 149.942 C 119.969 150.087 119.888 150.321 119.996 150.464 Z M 124.587 153.271 C 124.718 153.437 125.009 153.443 125.237 153.283 L 130.988 149.247 C 131.215 149.087 131.275 148.835 131.122 148.685 L 127.548 145.164 C 127.418 145.036 127.146 145.05 126.938 145.196 L 121.679 148.887 C 121.579 148.957 121.505 149.05 121.472 149.147 C 121.44 149.244 121.452 149.337 121.506 149.404 Z M 120.709 148.405 C 120.817 148.539 121.071 148.533 121.276 148.389 L 126.479 144.737 C 126.685 144.593 126.749 144.376 126.625 144.253 C 126.565 144.194 126.469 144.165 126.357 144.172 C 126.246 144.178 126.129 144.221 126.032 144.289 L 121.251 147.645 L 120.67 146.937 C 120.619 146.876 120.532 146.844 120.429 146.848 C 120.326 146.852 120.214 146.892 120.119 146.959 C 119.921 147.098 119.842 147.316 119.945 147.445 Z M 118.857 146.079 C 118.903 146.138 118.985 146.168 119.085 146.163 C 119.184 146.159 119.292 146.12 119.387 146.054 L 120.799 145.063 C 120.892 144.997 120.962 144.911 120.993 144.825 C 121.024 144.737 121.013 144.657 120.964 144.6 C 120.915 144.544 120.83 144.516 120.73 144.522 C 120.63 144.528 120.521 144.568 120.429 144.633 L 119.031 145.614 C 118.838 145.749 118.759 145.957 118.857 146.079 Z M 118.166 145.213 C 118.211 145.27 118.291 145.298 118.387 145.293 C 118.485 145.288 118.59 145.248 118.682 145.184 L 123.527 141.784 C 123.618 141.719 123.686 141.638 123.713 141.555 C 123.741 141.473 123.728 141.398 123.675 141.347 L 121.106 138.814 C 121.011 138.721 120.791 138.747 120.614 138.871 L 116.124 142.022 C 115.947 142.147 115.869 142.33 115.951 142.432 Z M 125.163 152.404 L 122.47 149.128 L 127.026 145.931 L 130.035 148.985 Z M 118.684 144.521 L 116.746 142.165 L 120.63 139.439 L 122.795 141.636 Z M 125.253 150.982 C 125.315 151.057 125.418 151.098 125.538 151.099 C 125.658 151.098 125.786 151.057 125.892 150.982 L 128.301 149.291 C 128.407 149.217 128.482 149.116 128.509 149.013 C 128.536 148.91 128.513 148.812 128.445 148.742 L 126.969 147.199 C 126.907 147.132 126.805 147.097 126.689 147.102 C 126.573 147.106 126.45 147.149 126.349 147.22 L 124.045 148.836 C 123.944 148.908 123.869 149.003 123.839 149.102 C 123.808 149.201 123.823 149.296 123.882 149.365 Z M 121.008 145.975 C 121.059 146.035 121.145 146.065 121.249 146.06 C 121.352 146.056 121.464 146.015 121.56 145.948 L 123.721 144.431 C 123.817 144.364 123.887 144.277 123.915 144.189 C 123.944 144.101 123.93 144.02 123.875 143.963 C 123.762 143.846 123.512 143.861 123.314 144 L 121.175 145.501 C 120.978 145.639 120.904 145.851 121.008 145.975 Z M 118.854 143.432 C 118.947 143.542 119.175 143.526 119.363 143.395 L 121.399 141.966 C 121.586 141.835 121.656 141.642 121.555 141.536 L 120.493 140.426 C 120.447 140.379 120.369 140.356 120.276 140.366 C 120.182 140.375 120.081 140.414 119.994 140.475 L 118.034 141.851 C 117.947 141.912 117.881 141.99 117.85 142.068 C 117.819 142.146 117.825 142.219 117.867 142.268 Z M 125.812 150.149 L 124.88 149.082 L 126.432 147.993 L 127.399 149.035 Z M 119.357 142.762 L 118.687 141.995 L 120.006 141.07 L 120.701 141.819 Z M 122.368 143.614 C 122.42 143.67 122.506 143.697 122.609 143.691 C 122.712 143.683 122.823 143.642 122.916 143.577 L 123.975 142.833 L 125.014 143.888 C 125.132 144.008 125.391 143.991 125.593 143.85 C 125.793 143.709 125.858 143.499 125.738 143.38 L 124.475 142.136 C 124.363 142.025 124.116 142.045 123.923 142.181 L 122.526 143.162 C 122.433 143.227 122.364 143.311 122.335 143.396 C 122.305 143.48 122.317 143.56 122.368 143.614 Z M 141.843 146.476 L 124.692 133.34 L 127.631 131.278 C 129.256 130.137 131.463 129.735 132.618 130.408 L 146.453 138.458 C 148.462 139.627 148.318 141.932 146.011 143.551 Z M 133.848 137.324 C 134.397 137.711 135.493 137.588 136.267 137.045 C 137.041 136.502 137.193 135.749 136.615 135.383 C 136.048 135.024 134.979 135.167 134.236 135.689 C 133.492 136.211 133.312 136.945 133.848 137.324 Z M 134.682 136.739 C 134.46 136.587 134.526 136.3 134.831 136.086 C 135.137 135.871 135.552 135.818 135.781 135.968 C 136.011 136.118 135.949 136.407 135.638 136.625 C 135.328 136.843 134.906 136.893 134.682 136.739 Z", + "toValue": "M 148.07 180.798 C 151.224 180.798 153.778 177.982 153.759 174.53 L 153.458 121.293 C 153.439 117.904 150.88 115.145 147.76 115.145 L 124.753 115.145 C 121.632 115.145 119.089 117.904 119.089 121.293 L 119.089 174.53 C 119.089 177.982 121.66 180.798 124.814 180.798 Z M 148.064 179.577 L 124.813 179.577 C 122.262 179.577 120.203 177.321 120.203 174.53 L 120.202 169.491 L 152.618 169.491 L 152.646 174.53 C 152.661 177.321 150.614 179.577 148.064 179.577 Z M 142.879 175.14 C 143.186 175.14 143.435 174.866 143.433 174.53 C 143.432 174.193 143.182 173.921 142.874 173.921 L 129.971 173.921 C 129.662 173.921 129.414 174.193 129.414 174.53 C 129.415 174.866 129.665 175.14 129.972 175.14 Z M 152.61 168.273 L 120.202 168.273 L 120.194 127.453 L 152.387 127.453 Z M 147.428 160.359 C 147.692 160.359 147.906 160.125 147.905 159.835 L 147.871 152.5 C 147.869 152.211 147.653 151.977 147.389 151.977 L 140.676 151.977 C 140.411 151.977 140.197 152.211 140.197 152.5 L 140.222 159.835 C 140.224 159.973 140.275 160.107 140.365 160.205 C 140.455 160.303 140.578 160.359 140.706 160.359 Z M 138.784 160.359 C 139.05 160.358 139.264 160.123 139.263 159.835 L 139.259 158.781 C 139.259 158.642 139.209 158.509 139.118 158.41 C 139.027 158.311 138.905 158.256 138.778 158.256 L 133.978 158.256 C 133.851 158.256 133.729 158.311 133.639 158.409 C 133.549 158.509 133.5 158.642 133.5 158.781 C 133.501 159.07 133.715 159.305 133.981 159.305 L 138.301 159.305 L 138.302 159.835 C 138.303 159.973 138.353 160.107 138.444 160.205 C 138.535 160.303 138.657 160.359 138.784 160.359 Z M 132.063 160.359 C 132.19 160.359 132.312 160.303 132.401 160.205 C 132.492 160.107 132.542 159.973 132.542 159.835 L 132.537 157.737 C 132.537 157.598 132.486 157.465 132.395 157.366 C 132.306 157.268 132.182 157.214 132.055 157.214 L 125.817 157.214 L 125.807 148.319 C 125.807 148.18 125.756 148.046 125.667 147.948 C 125.577 147.85 125.455 147.796 125.328 147.795 C 125.201 147.795 125.079 147.85 124.989 147.948 C 124.899 148.046 124.848 148.18 124.849 148.319 L 124.858 157.737 C 124.858 158.026 125.073 158.261 125.339 158.261 L 131.579 158.261 L 131.58 159.31 L 129.18 159.31 C 129.052 159.31 128.93 159.365 128.841 159.463 C 128.75 159.562 128.7 159.695 128.701 159.835 C 128.701 160.123 128.916 160.358 129.181 160.359 Z M 146.943 159.31 L 141.181 159.31 L 141.158 153.024 L 146.914 153.024 Z M 145.497 158.261 C 145.763 158.261 145.977 158.026 145.976 157.737 L 145.962 154.594 C 145.961 154.455 145.91 154.322 145.819 154.224 C 145.729 154.126 145.607 154.07 145.479 154.07 L 142.602 154.07 C 142.474 154.07 142.353 154.126 142.263 154.224 C 142.173 154.322 142.123 154.455 142.124 154.594 L 142.136 157.737 C 142.137 158.026 142.353 158.261 142.618 158.261 Z M 145.013 157.214 L 143.094 157.214 L 143.085 155.118 L 145.005 155.118 Z M 136.855 157.208 C 137.119 157.208 137.333 156.973 137.333 156.684 C 137.332 156.545 137.282 156.412 137.191 156.313 C 137.102 156.215 136.979 156.16 136.852 156.16 L 133.973 156.16 C 133.846 156.16 133.724 156.215 133.634 156.313 C 133.544 156.412 133.495 156.545 133.495 156.684 C 133.496 156.973 133.711 157.208 133.976 157.208 Z M 138.773 156.689 C 139.037 156.689 139.251 156.455 139.25 156.166 L 139.235 151.449 C 139.235 151.31 139.183 151.177 139.094 151.079 C 139.003 150.981 138.881 150.926 138.754 150.926 L 136.837 150.926 C 136.71 150.926 136.588 150.981 136.498 151.079 C 136.409 151.177 136.359 151.31 136.36 151.449 C 136.36 151.738 136.575 151.971 136.84 151.972 L 138.278 151.972 L 138.291 156.166 C 138.292 156.305 138.342 156.438 138.433 156.536 C 138.523 156.635 138.646 156.689 138.773 156.689 Z M 132.054 156.166 C 132.181 156.166 132.303 156.111 132.393 156.012 C 132.483 155.914 132.534 155.781 132.532 155.641 L 132.526 152.5 C 132.525 152.211 132.309 151.977 132.045 151.977 L 129.167 151.977 C 128.903 151.977 128.688 152.211 128.688 152.5 L 128.694 155.641 C 128.694 155.781 128.745 155.913 128.835 156.011 C 128.925 156.109 129.047 156.165 129.175 156.166 Z M 127.256 156.166 C 127.521 156.165 127.735 155.931 127.734 155.641 L 127.728 150.932 L 129.646 150.932 C 129.773 150.932 129.895 150.876 129.984 150.778 C 130.074 150.68 130.124 150.546 130.124 150.408 C 130.123 150.12 129.908 149.885 129.643 149.885 L 127.248 149.885 C 127.12 149.885 126.998 149.941 126.909 150.038 C 126.818 150.136 126.768 150.269 126.768 150.408 L 126.774 155.641 C 126.774 155.781 126.826 155.914 126.915 156.012 C 127.006 156.111 127.128 156.166 127.256 156.166 Z M 131.571 155.118 L 129.653 155.118 L 129.649 153.024 L 131.568 153.024 Z M 133.971 155.112 C 134.235 155.112 134.449 154.878 134.449 154.589 L 134.448 154.065 L 135.886 154.065 C 136.013 154.065 136.136 154.01 136.225 153.912 C 136.316 153.813 136.365 153.681 136.365 153.541 C 136.365 153.403 136.314 153.269 136.224 153.171 C 136.133 153.073 136.011 153.017 135.884 153.017 L 134.445 153.017 L 134.442 151.448 C 134.44 151.31 134.39 151.176 134.299 151.078 C 134.21 150.98 134.088 150.926 133.961 150.926 C 133.833 150.926 133.711 150.981 133.622 151.079 C 133.532 151.177 133.482 151.31 133.482 151.448 L 133.487 153.541 L 133.489 154.589 C 133.491 154.878 133.706 155.112 133.971 155.112 Z M 147.384 150.932 C 147.511 150.932 147.633 150.876 147.722 150.778 C 147.812 150.68 147.862 150.546 147.861 150.408 L 147.846 147.273 C 147.846 147.135 147.794 147.001 147.705 146.903 C 147.614 146.805 147.492 146.75 147.365 146.75 C 147.238 146.75 147.115 146.806 147.026 146.904 C 146.937 147.001 146.887 147.135 146.887 147.273 L 146.903 150.408 C 146.903 150.546 146.953 150.68 147.044 150.778 C 147.134 150.876 147.256 150.931 147.384 150.932 Z M 145.466 150.932 C 145.593 150.932 145.715 150.876 145.804 150.778 C 145.894 150.68 145.944 150.546 145.943 150.408 C 145.942 150.118 145.726 149.885 145.462 149.885 L 142.107 149.885 L 142.103 148.84 L 143.541 148.84 C 143.804 148.84 144.018 148.606 144.017 148.319 C 144.017 148.18 143.966 148.046 143.876 147.948 C 143.785 147.85 143.663 147.795 143.536 147.795 L 141.619 147.795 C 141.492 147.795 141.37 147.85 141.28 147.948 C 141.191 148.046 141.141 148.18 141.142 148.319 L 141.149 150.408 C 141.149 150.546 141.201 150.68 141.291 150.778 C 141.381 150.876 141.503 150.932 141.631 150.932 Z M 132.042 150.932 C 132.17 150.932 132.292 150.876 132.381 150.778 C 132.472 150.68 132.522 150.546 132.521 150.408 L 132.515 147.267 C 132.513 147.128 132.463 146.996 132.372 146.898 C 132.283 146.8 132.161 146.745 132.034 146.745 L 129.158 146.745 C 128.895 146.745 128.681 146.98 128.681 147.267 C 128.681 147.405 128.731 147.539 128.822 147.637 C 128.911 147.735 129.034 147.79 129.161 147.791 L 131.558 147.791 L 131.563 150.408 C 131.563 150.546 131.613 150.679 131.704 150.777 C 131.793 150.875 131.915 150.931 132.042 150.932 Z M 138.749 149.363 C 139.013 149.361 139.228 149.128 139.226 148.84 L 139.215 145.185 C 139.214 145.046 139.163 144.913 139.074 144.815 C 138.983 144.718 138.861 144.662 138.734 144.662 C 138.607 144.662 138.486 144.716 138.395 144.815 C 138.306 144.913 138.256 145.046 138.256 145.185 L 138.268 148.84 C 138.268 148.978 138.319 149.112 138.41 149.209 C 138.5 149.307 138.622 149.363 138.749 149.363 Z M 136.832 149.363 C 137.097 149.361 137.309 149.128 137.309 148.84 L 137.303 146.75 C 137.302 146.462 137.088 146.229 136.823 146.229 C 136.559 146.229 136.346 146.462 136.346 146.75 L 136.352 148.84 C 136.353 149.128 136.568 149.361 136.832 149.363 Z M 133.957 149.363 C 134.084 149.363 134.205 149.307 134.294 149.209 C 134.385 149.112 134.435 148.978 134.434 148.84 L 134.427 145.705 L 136.822 145.705 C 137.085 145.705 137.299 145.472 137.298 145.185 C 137.298 144.897 137.083 144.662 136.818 144.662 L 133.944 144.662 C 133.818 144.662 133.696 144.718 133.607 144.815 C 133.517 144.913 133.467 145.046 133.467 145.185 L 133.476 148.84 C 133.477 149.128 133.692 149.361 133.957 149.363 Z M 127.246 148.84 C 127.511 148.84 127.724 148.606 127.724 148.319 L 127.72 145.705 L 131.073 145.705 C 131.2 145.706 131.322 145.651 131.411 145.553 C 131.501 145.456 131.551 145.322 131.551 145.185 C 131.551 145.045 131.501 144.913 131.41 144.815 C 131.321 144.716 131.199 144.662 131.071 144.662 L 127.24 144.662 C 127.113 144.662 126.991 144.716 126.901 144.815 C 126.812 144.913 126.762 145.045 126.762 145.185 L 126.765 148.319 C 126.765 148.457 126.817 148.589 126.906 148.688 C 126.997 148.786 127.119 148.84 127.246 148.84 Z M 145.452 147.795 C 145.716 147.795 145.93 147.562 145.929 147.273 L 145.923 145.705 L 147.36 145.705 C 147.624 145.706 147.837 145.472 147.836 145.185 C 147.836 145.046 147.784 144.913 147.695 144.815 C 147.604 144.716 147.482 144.662 147.355 144.662 L 145.439 144.662 C 145.312 144.662 145.19 144.718 145.1 144.815 C 145.011 144.913 144.962 145.046 144.962 145.185 L 144.971 147.273 C 144.973 147.562 145.188 147.795 145.452 147.795 Z M 143.529 145.705 C 143.792 145.705 144.005 145.472 144.005 145.185 C 144.003 144.897 143.788 144.662 143.523 144.662 L 140.651 144.662 C 140.523 144.662 140.401 144.716 140.312 144.815 C 140.222 144.913 140.172 145.046 140.172 145.185 C 140.173 145.472 140.389 145.706 140.653 145.705 Z M 147.35 143.618 C 147.614 143.618 147.828 143.385 147.827 143.097 L 147.792 135.803 C 147.791 135.515 147.575 135.283 147.311 135.283 L 140.617 135.283 C 140.353 135.283 140.139 135.515 140.141 135.803 L 140.166 143.097 C 140.166 143.235 140.217 143.368 140.307 143.466 C 140.398 143.564 140.52 143.618 140.647 143.618 Z M 138.731 143.618 C 138.995 143.618 139.209 143.385 139.207 143.097 L 139.183 135.803 C 139.182 135.516 138.968 135.283 138.704 135.283 C 138.578 135.283 138.455 135.337 138.366 135.436 C 138.277 135.533 138.226 135.665 138.228 135.803 L 138.248 142.575 L 136.812 142.575 C 136.686 142.575 136.564 142.63 136.474 142.728 C 136.385 142.826 136.335 142.958 136.336 143.097 C 136.337 143.385 136.551 143.618 136.816 143.618 Z M 133.942 143.618 C 134.069 143.618 134.191 143.564 134.28 143.466 C 134.37 143.368 134.42 143.235 134.42 143.097 L 134.415 141.011 C 134.415 140.873 134.364 140.74 134.274 140.642 C 134.183 140.544 134.061 140.49 133.934 140.49 C 133.808 140.49 133.686 140.545 133.597 140.642 C 133.507 140.74 133.457 140.873 133.458 141.011 L 133.463 143.097 C 133.463 143.385 133.678 143.618 133.942 143.618 Z M 132.026 143.618 C 132.153 143.618 132.275 143.564 132.365 143.466 C 132.455 143.368 132.506 143.235 132.505 143.097 L 132.489 135.803 C 132.488 135.665 132.438 135.533 132.347 135.434 C 132.258 135.337 132.136 135.283 132.008 135.283 L 125.315 135.283 C 125.051 135.283 124.837 135.515 124.837 135.803 L 124.844 143.097 C 124.844 143.385 125.059 143.618 125.324 143.618 Z M 146.866 142.575 L 141.12 142.575 L 141.099 136.324 L 146.838 136.324 Z M 131.546 142.575 L 125.801 142.575 L 125.794 136.324 L 131.534 136.324 Z M 145.425 141.533 C 145.552 141.533 145.673 141.477 145.763 141.38 C 145.852 141.282 145.903 141.15 145.903 141.011 L 145.889 137.885 C 145.887 137.747 145.837 137.614 145.746 137.517 C 145.656 137.419 145.534 137.365 145.408 137.365 L 142.537 137.365 C 142.411 137.365 142.289 137.419 142.2 137.517 C 142.11 137.614 142.06 137.747 142.061 137.885 L 142.073 141.011 C 142.074 141.15 142.124 141.282 142.215 141.38 C 142.304 141.477 142.427 141.533 142.554 141.533 Z M 135.852 141.533 C 135.979 141.533 136.1 141.477 136.19 141.38 C 136.28 141.282 136.33 141.15 136.33 141.011 L 136.321 137.885 C 136.321 137.747 136.269 137.614 136.18 137.517 C 136.089 137.419 135.968 137.365 135.841 137.365 C 135.576 137.365 135.364 137.598 135.364 137.885 L 135.372 141.011 C 135.374 141.298 135.588 141.531 135.852 141.533 Z M 130.108 141.533 C 130.372 141.533 130.586 141.3 130.585 141.011 L 130.58 137.885 C 130.579 137.598 130.365 137.365 130.1 137.365 L 127.231 137.365 C 127.104 137.365 126.982 137.419 126.893 137.517 C 126.803 137.614 126.753 137.747 126.753 137.885 L 126.756 141.011 C 126.756 141.15 126.807 141.282 126.898 141.38 C 126.987 141.477 127.109 141.533 127.236 141.533 Z M 144.943 140.49 L 143.029 140.49 L 143.02 138.407 L 144.934 138.407 Z M 129.628 140.49 L 127.712 140.49 L 127.71 138.407 L 129.624 138.407 Z M 133.929 138.407 C 134.056 138.407 134.178 138.351 134.268 138.254 C 134.357 138.156 134.408 138.024 134.408 137.885 L 134.403 136.324 L 136.794 136.324 C 137.059 136.324 137.272 136.091 137.27 135.803 C 137.269 135.516 137.055 135.283 136.792 135.283 L 133.921 135.283 C 133.658 135.283 133.445 135.516 133.445 135.803 L 133.45 137.885 C 133.45 138.024 133.501 138.156 133.592 138.254 C 133.681 138.351 133.803 138.407 133.929 138.407 Z M 152.381 126.252 L 120.194 126.252 L 120.193 121.292 C 120.193 118.552 122.23 116.341 124.754 116.341 L 147.765 116.341 C 150.289 116.341 152.338 118.552 152.353 121.292 Z M 136.279 123.282 C 137.285 123.282 138.108 122.384 138.105 121.292 C 138.102 120.201 137.273 119.305 136.268 119.305 C 135.263 119.305 134.439 120.203 134.442 121.292 C 134.445 122.384 135.274 123.282 136.279 123.282 Z M 136.277 122.083 C 135.869 122.083 135.547 121.735 135.546 121.292 C 135.545 120.849 135.861 120.502 136.272 120.502 C 136.68 120.502 137 120.85 137.001 121.292 C 137.002 121.735 136.683 122.083 136.277 122.083 Z" + }, + { + "id": "474862", + "layerId": "93748", + "propertyName": "pathData", + "startTime": 1500, + "endTime": 3000, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "path", + "fromValue": "M 111.517 162.443 L 121.106 138.814 L 63.998 106.871 L 59.253 107.642 Z", + "toValue": "M 54.667 124.179 L 72.039 121.048 L 63.998 106.871 L 59.253 107.642 Z" + }, + { + "id": "5886667", + "layerId": "93748", + "propertyName": "fillAlpha", + "startTime": 0, + "endTime": 1500, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "number", + "fromValue": 0.5, + "toValue": 0.8 + }, + { + "id": "5886668", + "layerId": "93748", + "propertyName": "fillAlpha", + "startTime": 1500, + "endTime": 3000, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "number", + "fromValue": 0.8, + "toValue": 0.5 + } + ] + } + } +} \ No newline at end of file diff --git a/img/animations/kiosk_portrait_kt0345/turn-frame00.svg b/img/animations/kiosk_portrait_kt0345/turn-frame00.svg new file mode 100644 index 00000000..b67e181d --- /dev/null +++ b/img/animations/kiosk_portrait_kt0345/turn-frame00.svg @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/animations/kiosk_portrait_kt0345/turn.shapeshifter b/img/animations/kiosk_portrait_kt0345/turn.shapeshifter new file mode 100644 index 00000000..235b3125 --- /dev/null +++ b/img/animations/kiosk_portrait_kt0345/turn.shapeshifter @@ -0,0 +1,116 @@ +{ + "version": 1, + "layers": { + "vectorLayer": { + "id": "9838950", + "name": "vector", + "type": "vector", + "width": 158, + "height": 190, + "children": [ + { + "id": "9838951", + "name": "path_3", + "type": "path", + "pathData": "M 58.464 50.437 L 148.444 50.437 L 148.444 55.429 L 59.75 55.429 Z", + "fillColor": "#c4c4c4", + "strokeColor": "#000000", + "strokeWidth": 2.74173 + }, + { + "id": "9838952", + "name": "path_4", + "type": "path", + "pathData": "M 55.629 61.223 L 65.903 125.546 L 60.309 126.562 L 49.351 60.456 Z", + "fillColor": "#c4c4c4", + "strokeColor": "#000000", + "strokeWidth": 2.74173, + "strokeLinecap": "round", + "strokeLinejoin": "round" + }, + { + "id": "9838953", + "name": "path_5", + "type": "path", + "pathData": "M 104.456 145.955 L 78.518 155.218 L 96.063 155.218 L 90.433 180.335 C 90.417 180.405 90.501 180.463 90.622 180.463 L 118.69 180.463 C 118.81 180.463 118.894 180.405 118.877 180.335 L 112.952 155.218 L 130.066 155.218 Z", + "fillColor": "#50a167", + "strokeWidth": 1 + }, + { + "id": "9838954", + "name": "path_2", + "type": "path", + "pathData": "M 44.104 54.964 C 44.104 54.964 50.971 66.014 57.434 59.955 C 63.897 53.897 54.889 45.914 54.889 45.914 Z", + "fillColor": "#c4c4c4", + "strokeColor": "#000000", + "strokeWidth": 2.74173 + }, + { + "id": "9838955", + "name": "path_1", + "type": "path", + "pathData": "M 11.779 189.129 L 11.779 21.869 L 46.624 21.869 L 62.584 39.457 L 30.574 66.317 L 30.574 188.961 Z", + "fillColor": "#c4c4c4", + "strokeColor": "#000000", + "strokeWidth": 2.74173, + "strokeLinecap": "round", + "strokeLinejoin": "round" + } + ] + }, + "hiddenLayerIds": [] + }, + "timeline": { + "animation": { + "id": "9838956", + "name": "anim", + "duration": 3000, + "blocks": [ + { + "id": "9838957", + "layerId": "9838951", + "propertyName": "pathData", + "startTime": 0, + "endTime": 1500, + "interpolator": "ACCELERATE_DECELERATE", + "type": "path", + "fromValue": "M 58.464 50.437 L 148.444 50.437 L 148.444 55.429 L 59.75 55.429 Z", + "toValue": "M 55.629 61.223 L 65.903 125.546 L 60.309 126.562 L 49.351 60.456 Z" + }, + { + "id": "9838958", + "layerId": "9838952", + "propertyName": "pathData", + "startTime": 0, + "endTime": 1500, + "interpolator": "ACCELERATE_DECELERATE", + "type": "path", + "fromValue": "M 55.629 61.223 L 65.903 125.546 L 60.309 126.562 L 49.351 60.456 Z", + "toValue": "M 58.464 50.437 L 148.444 50.437 L 148.444 55.429 L 59.75 55.429 Z" + }, + { + "id": "9898467", + "layerId": "9838953", + "propertyName": "pathData", + "startTime": 0, + "endTime": 1500, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "path", + "fromValue": "M 104.456 145.955 L 78.518 155.218 L 96.063 155.218 L 90.433 180.335 C 90.417 180.405 90.501 180.463 90.622 180.463 L 118.69 180.463 C 118.81 180.463 118.894 180.405 118.877 180.335 L 112.952 155.218 L 130.066 155.218 Z", + "toValue": "M 104.456 135.372 L 78.518 144.635 L 96.063 144.635 L 90.433 169.752 C 90.417 169.822 90.501 169.879 90.622 169.879 L 118.69 169.879 C 118.81 169.879 118.894 169.822 118.877 169.752 L 112.952 144.635 L 130.066 144.635 Z" + }, + { + "id": "9901503", + "layerId": "9838953", + "propertyName": "pathData", + "startTime": 1500, + "endTime": 3000, + "interpolator": "FAST_OUT_SLOW_IN", + "type": "path", + "fromValue": "M 104.456 135.372 L 78.518 144.635 L 96.063 144.635 L 90.433 169.752 C 90.417 169.822 90.501 169.879 90.622 169.879 L 118.69 169.879 C 118.81 169.879 118.894 169.822 118.877 169.752 L 112.952 144.635 L 130.066 144.635 Z", + "toValue": "M 104.456 145.955 L 78.518 155.218 L 96.063 155.218 L 90.433 180.335 C 90.417 180.405 90.501 180.463 90.622 180.463 L 118.69 180.463 C 118.81 180.463 118.894 180.405 118.877 180.335 L 112.952 155.218 L 130.066 155.218 Z" + } + ] + } + } +} \ No newline at end of file diff --git a/pretixscan/app/src/main/AndroidManifest.xml b/pretixscan/app/src/main/AndroidManifest.xml index ed63bebb..7ce6b5b8 100644 --- a/pretixscan/app/src/main/AndroidManifest.xml +++ b/pretixscan/app/src/main/AndroidManifest.xml @@ -16,6 +16,7 @@ + @@ -105,6 +106,13 @@ android:label="@string/headline_please_reinstall" android:exported="false" android:theme="@style/AppTheme.NoActionBar" /> + diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/AppConfig.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/AppConfig.kt index d770ed44..8b5862ef 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/AppConfig.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/AppConfig.kt @@ -247,6 +247,14 @@ class AppConfig(ctx: Context) : ConfigStore { get() = prefs.getBoolean(PREFS_KEY_MULTI_EVENT_MODE, false) set(value) = prefs.edit().putBoolean(PREFS_KEY_MULTI_EVENT_MODE, value).apply() + var timeAfterGateOpen: Int + get() = try { + Integer.parseInt(default_prefs.getString(PREFS_KEY_KIOSK_GATE_B2S_TIMEOUT, "200")!!) + } catch (e: Exception) { + 200 + } + set(value) = default_prefs.edit().putString(PREFS_KEY_KIOSK_GATE_B2S_TIMEOUT, value.toString()).apply() + override fun getDeviceKnownName(): String { return prefs.getString(PREFS_KEY_DEVICE_KNOWN_NAME, "")!! } @@ -403,6 +411,10 @@ class AppConfig(ctx: Context) : ConfigStore { get() = default_prefs.getBoolean(PREFS_KEY_SYNC_ORDERS, true) set(value) = default_prefs.edit().putBoolean(PREFS_KEY_SYNC_ORDERS, value).apply() + var kioskOutOfOrder: Boolean + get() = default_prefs.getBoolean(PREFS_KEY_KIOSK_OOO, false) + set(value) = default_prefs.edit().putBoolean(PREFS_KEY_KIOSK_OOO, value).apply() + override fun getKnownLiveEventSlugs(): Set { return default_prefs.getStringSet(PREFS_KEY_KNOWN_LIVE_EVENT_SLUGS, emptySet()) as Set } @@ -465,6 +477,8 @@ class AppConfig(ctx: Context) : ConfigStore { val PREFS_KEY_SEARCH_DISABLE = "pref_search_disable" val PREFS_KEY_KIOSK_MODE = "pref_kiosk_mode" val PREFS_KEY_MULTI_EVENT_MODE = "multi_event_mode" + val PREFS_KEY_KIOSK_OOO = "pref_kiosk_out_of_order" + val PREFS_KEY_KIOSK_GATE_B2S_TIMEOUT = "gate_back_to_start_timeout" val PREFS_KEY_NFC_READER_TYPE = "pref_nfc_type" private const val PREFS_KEY_KNOWN_LIVE_EVENT_SLUGS = "cache_known_live_event_slugs" private const val PREFS_KEY_SCREEN_ALWAYS_ON = "pref_screen_always_on" diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/hardware/KioskHardware.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/hardware/KioskHardware.kt new file mode 100644 index 00000000..f3d3a7f3 --- /dev/null +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/hardware/KioskHardware.kt @@ -0,0 +1,33 @@ +package eu.pretix.pretixscan.droid.hardware + +import android.os.Build +import eu.pretix.pretixscan.droid.BuildConfig + +object KioskHardware { + fun isTR51(): Boolean { + return Build.MODEL == "pretix TR51" + } + + fun isZebra(): Boolean { + return Build.BRAND == "Zebra" && Build.MODEL.startsWith("CC6") + } + + fun isNewland(): Boolean { + return Build.BRAND in listOf( + "NewLand", + "Newland" + ) && Build.MODEL.startsWith("CC6") || Build.MODEL.startsWith("NQ") + } + + fun isSeuic(): Boolean { + return Build.BRAND == "SEUIC" && Build.MODEL.startsWith("AUTOID Pad Air") + } + + fun isM3(): Boolean { + return Build.BRAND == "M3" && Build.MODEL.startsWith("M3PC") + } + + fun isWA1053T(): Boolean { + return BuildConfig.DEBUG && Build.MODEL == "rk3399-Android10" + } +} \ No newline at end of file diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt new file mode 100644 index 00000000..541a642d --- /dev/null +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt @@ -0,0 +1,858 @@ +package eu.pretix.pretixscan.droid.ui + +import Mf0aesKeySet +import android.app.ProgressDialog +import android.content.Context +import android.content.Intent +import android.content.RestrictionsManager +import android.media.AudioManager +import android.media.MediaPlayer +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.util.Base64 +import android.view.KeyEvent +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.AppCompatButton +import androidx.preference.PreferenceManager +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import eu.pretix.libpretixnfc.android.hardware.NfcDisabled +import eu.pretix.libpretixnfc.android.hardware.NfcHandler +import eu.pretix.libpretixnfc.android.hardware.NfcUnsupported +import eu.pretix.libpretixnfc.android.hardware.getNfcHandler +import eu.pretix.libpretixnfc.communication.ChipReadError +import eu.pretix.libpretixsync.api.PretixApi +import eu.pretix.libpretixsync.check.CheckException +import eu.pretix.libpretixsync.check.OnlineCheckProvider +import eu.pretix.libpretixsync.check.TicketCheckProvider +import eu.pretix.libpretixsync.db.Answer +import eu.pretix.libpretixsync.db.ReusableMediaType +import eu.pretix.libpretixsync.models.db.toModel +import eu.pretix.libpretixsync.sync.SyncManager +import eu.pretix.libpretixsync.utils.getActiveMediaTypes +import eu.pretix.libpretixui.android.questions.QuestionsDialog +import eu.pretix.libpretixui.android.questions.QuestionsDialogInterface +import eu.pretix.libpretixui.android.scanning.HardwareScanner +import eu.pretix.libpretixui.android.scanning.ScanReceiver +import eu.pretix.libpretixui.android.scanning.ScannerView +import eu.pretix.pretixscan.droid.AndroidHttpClientFactory +import eu.pretix.pretixscan.droid.AndroidSentryImplementation +import eu.pretix.pretixscan.droid.AppConfig +import eu.pretix.pretixscan.droid.BuildConfig +import eu.pretix.pretixscan.droid.EventSelection +import eu.pretix.pretixscan.droid.PretixScan +import eu.pretix.pretixscan.droid.R +import eu.pretix.pretixscan.droid.connectivity.ConnectivityChangedListener +import eu.pretix.pretixscan.utils.SettingsManager +import io.sentry.Sentry +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.json.JSONObject +import java.io.IOException +import java.lang.Integer.max +import java.nio.charset.Charset +import java.security.MessageDigest +import java.util.Locale + +interface ReloadableActivity { + fun reload() +} + +enum class ResultCardState { + HIDDEN, + SHOWN +} + +enum class ResultState { + EMPTY, + LOADING, + ERROR, + DIALOG_QUESTIONS, + DIALOG_EXCHANGE, + WARNING, + SUCCESS, + SUCCESS_EXIT +} + +abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, ScannerView.ResultHandler, MediaPlayer.OnCompletionListener, + ConnectivityChangedListener, NfcHandler.OnChipReadListener { + protected val REQ_EVENT = 1 + + private lateinit var sm: SyncManager + lateinit var conf: AppConfig + val bgScope = CoroutineScope(Dispatchers.IO) + private val handler = Handler() + var mediaPlayers: MutableMap = mutableMapOf() + + var lastScanTime: Long = 0 + var lastScanCode: String = "" + var lastIgnoreUnpaid: Boolean = false + var lastScanSourceType: ReusableMediaType = ReusableMediaType.BARCODE + var lastScanResult: TicketCheckProvider.CheckResult? = null + var keyboardBuffer: String = "" + var dialog: QuestionsDialogInterface? = null + private var pdialog: ProgressDialog? = null + protected var networkCallback: ConnectivityManager.NetworkCallback? = null + + private val dataWedgeHelper = DataWedgeHelper(this) + + var syncMessage = "" + + private var pendingPinAction: ((pin: String) -> Unit)? = null + + var nfcHandler: NfcHandler? = null + + private val hardwareScanner = HardwareScanner(object : ScanReceiver { + override fun scanResult(result: String) { + if (result == lastScanCode && System.currentTimeMillis() - lastScanTime < 2500) { + return + } + lastScanTime = System.currentTimeMillis() + lastScanCode = result + lastScanSourceType = ReusableMediaType.BARCODE + lastIgnoreUnpaid = false + lastScanResult = null + handleScan( + result, + lastScanSourceType.serverName!!, + null, + !conf.unpaidAsk + ) + } + }) + + override fun reload() { + reloadSyncStatus() + reloadNfcHandler() + } + + abstract fun reloadSyncStatus() + + fun syncStatusText(): String { + var text = "" + val diff = System.currentTimeMillis() - conf.lastDownload + if ((application as PretixScan).syncLock.isLocked) { + if (syncMessage != "") { + text = syncMessage + } else { + text = getString(R.string.sync_status_progress); + } + } else if (conf.lastDownload == 0L) { + text = getString(R.string.sync_status_never); + } else if (diff > 24 * 3600 * 1000) { + val days = (diff / (24 * 3600 * 1000)).toInt() + text = getResources().getQuantityString(R.plurals.sync_status_time_days, days, days); + } else if (diff > 3600 * 1000) { + val hours = (diff / (3600 * 1000)).toInt() + text = getResources().getQuantityString(R.plurals.sync_status_time_hours, hours, hours); + } else if (diff > 60 * 1000) { + val mins = (diff / (60 * 1000)).toInt() + text = getResources().getQuantityString(R.plurals.sync_status_time_minutes, mins, mins); + } else { + text = getString(R.string.sync_status_now); + } + + if (!(application as PretixScan).syncLock.isLocked) { + val checkins = (application as PretixScan).db.scanQueuedCheckInQueries.count().executeAsOne().toInt() + val calls = (application as PretixScan).db.scanQueuedCallQueries.count().executeAsOne().toInt() + text += " (" + resources.getQuantityString(R.plurals.sync_status_pending, checkins + calls, checkins + calls) + ")" + } + + return text + } + + override fun onCompletion(p0: MediaPlayer?) { + p0?.seekTo(0) + } + + @SuppressWarnings("ResourceType") + private fun buildMediaPlayer() { + val resourceIds = listOf(R.raw.enter, R.raw.exit, R.raw.error, R.raw.beep, R.raw.attention) + for (r in resourceIds) { + val mediaPlayer = MediaPlayer() + mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC) + mediaPlayer.setOnCompletionListener(this) + // mediaPlayer.setOnErrorListener(this) + try { + val file = resources.openRawResourceFd(r) + try { + mediaPlayer.setDataSource(file.fileDescriptor, file.startOffset, file.length) + } finally { + file.close(); + } + mediaPlayer.setVolume(0.2f, 0.2f) + mediaPlayer.prepare() + mediaPlayers[r] = mediaPlayer + } catch (ioe: IOException) { + } + } + } + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + conf = AppConfig(this) + + getRestrictions(this) + + volumeControlStream = AudioManager.STREAM_MUSIC + buildMediaPlayer() + + if (!conf.deviceRegistered) { + registerDevice() + return + } + try { + setupApi() + } catch (e: IllegalStateException) { + // IllegalStateException is thrown by db access -> Migrations.minVersionCallback + Sentry.captureException(e) + panicPleaseReinstall() + return + } + + if (conf.synchronizedEvents.isEmpty()) { + selectEvent() + } else if (conf.lastDownload < 1) { + syncNow() + } + scheduleSync() + + if (dataWedgeHelper.isInstalled) { + try { + dataWedgeHelper.install() + } catch (e: IOException) { + e.printStackTrace() + } + } + + supportFragmentManager.setFragmentResultListener(PinDialog.RESULT_PIN, this) { _, bundle -> + val pin = bundle.getString(PinDialog.RESULT_PIN) + if (pin != null && conf.verifyPin(pin)) { + (supportFragmentManager.findFragmentByTag(PinDialog.TAG) as? PinDialog)?.dismiss() + pendingPinAction?.let { it(pin) } + } + } + } + + open fun setupApi() { + val api = PretixApi.fromConfig(conf, AndroidHttpClientFactory(application as PretixScan)) + + sm = SyncManager( + conf, + api, + AndroidSentryImplementation(), + (application as PretixScan).db, + (application as PretixScan).fileStorage, + 60000L, + 5 * 60000L, + if (conf.syncOrders) SyncManager.Profile.PRETIXSCAN else SyncManager.Profile.PRETIXSCAN_ONLINE, + conf.printBadges, + BuildConfig.VERSION_CODE, + JSONObject(), + Build.BRAND, + Build.MODEL, + (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) Build.VERSION.BASE_OS else "").ifEmpty { "Android" }, + Build.VERSION.RELEASE, + "pretixSCAN Android", + BuildConfig.VERSION_NAME, + try { + conf.keyStore.getOrCreateRsaPubKey("device")?.toString(Charset.defaultCharset()) + } catch (e: NotImplementedError) { null }, + null, + (application as PretixScan).connectivityHelper + ) + } + + open fun selectEvent() { + val intent = Intent(this, EventConfigActivity::class.java) + startWithPIN(intent, "switch_event", REQ_EVENT, null) + } + + private fun registerDevice() { + val intent = Intent(this, WelcomeActivity::class.java) + startActivity(intent) + finish() + } + + private fun panicPleaseReinstall() { + val intent = Intent(this, PleaseReinstallActivity::class.java) + startActivity(intent) + finish() + } + + private val syncRunnable = Runnable { + syncMessage = "" + val activity = this + bgScope.launch { + if (!(application as PretixScan).syncLock.tryLock()) { + runOnUiThread { + reloadSyncStatus() + } + scheduleSync() + return@launch + } + val prefs = PreferenceManager.getDefaultSharedPreferences(activity) + try { + if (prefs.getBoolean("pref_sync_auto", true)) { + val result = sm.sync(false) { + runOnUiThread { + syncMessage = it + reloadSyncStatus() + } + } + if (result.isDataDownloaded) { + runOnUiThread { + reload() + } + } + } + runOnUiThread { + reloadSyncStatus() + scheduleSync() + } + } catch (e: SyncManager.EventSwitchRequested) { + runOnUiThread { + conf.eventSelection = listOf(EventSelection( + eventSlug = e.eventSlug, + eventName = e.eventName, + subEventId = e.subeventId, + checkInList = e.checkinlistId, + dateFrom = null, + dateTo = null + )) + setupApi() + syncNow() + reload() + } + } catch (e: Exception) { + runOnUiThread { + reload() + } + } finally { + (application as PretixScan).syncLock.unlock() + runOnUiThread { + reloadSyncStatus() + } + } + } + } + + fun scheduleSync() { + handler.removeCallbacks(syncRunnable) + handler.postDelayed(syncRunnable, 1000) + } + + fun syncNow() { + if (isDestroyed) return + syncMessage = "" + pdialog = ProgressDialog(this).apply { + isIndeterminate = true + setMessage(getString(R.string.progress_syncing)) + setTitle(R.string.progress_syncing) + setCanceledOnTouchOutside(false) + setCancelable(false) + show() + } + val activity = this + bgScope.launch { + if (!(application as PretixScan).syncLock.tryLock()) { + runOnUiThread { + MaterialAlertDialogBuilder(activity).setMessage(R.string.error_sync_in_background).create().show() + pdialog?.dismiss() + } + return@launch + } + try { + sm.sync(true) { current_action -> + runOnUiThread { + if (isDestroyed) { + return@runOnUiThread + } + reloadSyncStatus() + syncMessage = current_action + pdialog?.setMessage(current_action) + } + } + runOnUiThread { + if (isDestroyed) { + return@runOnUiThread + } + reload() + pdialog?.dismiss() + if (conf.lastFailedSync > 0) { + MaterialAlertDialogBuilder(activity).setMessage(conf.lastFailedSyncMsg).create().show() + } + } + } catch (e: SyncManager.EventSwitchRequested) { + runOnUiThread { + if (isDestroyed) { + return@runOnUiThread + } + pdialog?.dismiss() + conf.eventSelection = listOf(EventSelection( + eventSlug = e.eventSlug, + eventName = e.eventName, + subEventId = e.subeventId, + checkInList = e.checkinlistId, + dateFrom = null, + dateTo = null + )) + setupApi() + syncNow() + reload() + } + } catch (e: Exception) { + e.printStackTrace() + runOnUiThread { + if (isDestroyed) { + return@runOnUiThread + } + Sentry.captureException(e) + pdialog?.dismiss() + MaterialAlertDialogBuilder(activity) + .setMessage(e.message ?: getString(R.string.error_unknown_exception)) + .create().show() + } + } finally { + (application as PretixScan).syncLock.unlock() + } + } + } + + override fun onResume() { + super.onResume() + try { + reload() + setupApi() + } catch (e: IllegalStateException) { + // IllegalStateException is thrown by db access -> Migrations.minVersionCallback + Sentry.captureException(e) + panicPleaseReinstall() + return + } + getRestrictions(this) + + scheduleSync() + + hardwareScanner.start(this) + (application as PretixScan).connectivityHelper.addListener(this) + + val networkRequest = NetworkRequest.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) + .build() + val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + networkCallback = object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + onNetworkAvailable(connectivityManager, network) + } + + override fun onCapabilitiesChanged( + network: Network, + networkCapabilities: NetworkCapabilities + ) { + onNetworkChanged(connectivityManager, network) + } + + override fun onLost(network: Network) { + onNetworkLost(connectivityManager, network) + } + } + onNetworkLost(connectivityManager, null) + connectivityManager.registerNetworkCallback(networkRequest, networkCallback!!) + } + + protected open fun onNetworkAvailable(connectivityManager: ConnectivityManager, network: Network) { + (application as PretixScan).connectivityHelper.setHardOffline(false) + } + + protected open fun onNetworkChanged(connectivityManager: ConnectivityManager, network: Network) { + } + + protected open fun onNetworkLost(connectivityManager: ConnectivityManager, network: Network?) { + (application as PretixScan).connectivityHelper.setHardOffline(connectivityManager.activeNetworkInfo?.isConnectedOrConnecting != true) + } + + override fun onPause() { + handler.removeCallbacks(syncRunnable) + (application as PretixScan).connectivityHelper.removeListener(this) + super.onPause() + hardwareScanner.stop(this) + nfcHandler?.stop() + + if (networkCallback != null) { + val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + connectivityManager.unregisterNetworkCallback(networkCallback!!) + } + } + + open fun handleScan( + raw_result: String, + source_type: String, + answers: MutableList?, + ignore_unpaid: Boolean = false, + exchange_medium_type: String? = null, + exchange_medium_identifier: String? = null, + ) { + if (dialog?.isShowing() == true) { + /* + * Skip scan if a dialog is still in front. This forces users to answer the questions asked. + */ + return + } + //hardwareScanner.stop(this) // Fixme: Raphael Review comment: Where is this line coming from? It never get started again, so currently with this branch you can only scan once. } + + val result = + if (Regex("^HC1:[0-9A-Z $%*+-./:]+$").matches(raw_result.uppercase(Locale.getDefault()))) { + /* + * This is a bit of a hack. pretixSCAN 1.11-2.8.2 supports checking digital COVID vaccination + * certificates. When scanning them at the correct time, we have a high level of privacy + * since we do not store any personal data contained in the certificate. However, if you + * accidentally scan the certificate when you are supposed to scan a ticket, our fancy + * error log will cause the verbatim vaccination certificate to be stored on the server. + * Not really our fault, but also not really nice to store that sensitive health info. + * However, it's still helpful for debugging to see how often an invalid code was scanned. + * So if we encounter something that looks like an EU DGC, we'll just transform it into + * a hashed version. + * + * This hack is safe for pretix' default signature schemes, as they would never generate + * a QR code starting with ``HC1:``, but it could theoretically be unsafe for third-party + * plugins. + */ + val md = MessageDigest.getInstance("SHA-256") + md.update(raw_result.toByteArray(Charset.defaultCharset())) + "HC1:hashed:" + Base64.encodeToString(md.digest(), Base64.URL_SAFE) + } else { + raw_result + } + + // FIXME: don't play sound for media exchange + if (answers == null && !ignore_unpaid && !conf.offlineMode && conf.sounds) { + mediaPlayers[R.raw.beep]?.start() + } + + bgScope.launch { + var checkResult: TicketCheckProvider.CheckResult? = null + val provider = (application as PretixScan).getCheckProvider(conf) + val startedAt = System.currentTimeMillis() + try { + checkResult = provider.check( + conf.eventSelectionToMap(), + result, + source_type, + answers, + ignore_unpaid, + conf.printBadges, + when (conf.scanType) { + "exit" -> TicketCheckProvider.CheckInType.EXIT + else -> TicketCheckProvider.CheckInType.ENTRY + }, + allowQuestions = !conf.ignoreQuestions, + exchange_medium_type = exchange_medium_type, + exchange_medium_identifier = exchange_medium_identifier, + ) + if (provider is OnlineCheckProvider) { + if (checkResult?.type == TicketCheckProvider.CheckResult.Type.ERROR) { + (application as PretixScan).connectivityHelper.recordError() + } else { + (application as PretixScan).connectivityHelper.recordSuccess(System.currentTimeMillis() - startedAt) + } + } + } catch (e: CheckException) { + (application as PretixScan).connectivityHelper.recordError() + checkResult = TicketCheckProvider.CheckResult(TicketCheckProvider.CheckResult.Type.INVALID, getString(R.string.error_exception)) + checkResult.reasonExplanation = e.message + } catch (e: Exception) { + Sentry.captureException(e) + if (BuildConfig.DEBUG) { + e.printStackTrace() + } + (application as PretixScan).connectivityHelper.recordError() + checkResult = TicketCheckProvider.CheckResult(TicketCheckProvider.CheckResult.Type.INVALID, getString(R.string.error_unknown_exception)) + } + runOnUiThread { + displayScanResult(checkResult!!, answers, ignore_unpaid) + } + } + } + + abstract fun displayScanResult(result: TicketCheckProvider.CheckResult, answers: MutableList?, ignore_unpaid: Boolean = false) + + fun showQuestionsDialog(res: TicketCheckProvider.CheckResult, + secret: String, + sourceType: ReusableMediaType, + ignore_unpaid: Boolean, + values: Map?, + isResumed: Boolean, + retryHandler: ((String, ReusableMediaType, MutableList, Boolean) -> Unit)): QuestionsDialogInterface { + val questions = res.requiredAnswers!!.map { it.question.toModel() } + for (q in questions) { + q.resolveDependency(questions) + } + + val values_ = if (values == null) { + val v = mutableMapOf() + res.requiredAnswers!!.forEach { + if (!it.currentValue.isNullOrBlank()) { + v[it.question.toModel().identifier] = it.currentValue!! + } + } + v + } else { + values + } + val attendeeName = if (conf.hideNames || res.position?.isNull("attendee_name") != false) { + "" + } else { + res.position?.optString("attendee_name") + } + + var attendeeDOB: String? = null + if (!conf.hideNames) { + val qlen = res.position?.getJSONArray("answers")?.length() ?: 0 + for (i in 0 until qlen) { + val answ = res.position!!.getJSONArray("answers")!!.getJSONObject(i) + if (answ.getString("question_identifier") == "dob") { + attendeeDOB = answ.getString("answer") + } + } + } + + return eu.pretix.libpretixui.android.questions.showQuestionsDialog( + QuestionsDialog.Companion.QuestionsType.LINE_ITEM_QUESTIONS, + this, + questions, + values_, + null, + null, + { answers -> retryHandler(secret, sourceType, answers, ignore_unpaid) }, + null, + attendeeName, + attendeeDOB, + res.orderCodeAndPositionId(), + if (res.ticket != null) { + if (res.variation != null) { + res.ticket + " – " + res.variation + } else { + res.ticket + } + } else { + null + }, + !conf.useCamera, + isResumed + ) + } + + override fun handleResult(rawResult: ScannerView.Result) { + val s = rawResult.text + if (s == lastScanCode && System.currentTimeMillis() - lastScanTime < 5000) { + return + } + lastScanTime = System.currentTimeMillis() + lastScanCode = s + lastScanSourceType = ReusableMediaType.BARCODE + lastScanResult = null + lastIgnoreUnpaid = false + handleScan( + s, + lastScanSourceType.serverName!!, + null, + !conf.unpaidAsk + ) + } + + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + if ((event.action != KeyEvent.ACTION_DOWN && event.action != KeyEvent.ACTION_MULTIPLE) || (currentFocus is TextView && currentFocus !is AppCompatButton)) { + return super.dispatchKeyEvent(event) + } + return when (event.keyCode) { + KeyEvent.KEYCODE_ENTER -> { + if (keyboardBuffer.isEmpty()) { + false + } + lastScanTime = System.currentTimeMillis() + lastScanCode = keyboardBuffer + lastScanSourceType = ReusableMediaType.BARCODE + lastScanResult = null + lastIgnoreUnpaid = false + handleScan( + keyboardBuffer, + lastScanSourceType.serverName!!, + null, + !conf.unpaidAsk + ) + keyboardBuffer = "" + true + } + KeyEvent.KEYCODE_UNKNOWN -> { + keyboardBuffer += event.characters + true + } + else -> { + val codepoint = event.keyCharacterMap.get(event.keyCode, event.metaState) + if (codepoint > 0) { + keyboardBuffer += codepoint.toChar().toString().repeat(max(event.repeatCount, 1)) + true + } else { + super.dispatchKeyEvent(event) + } + } + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == REQ_EVENT) { + if (resultCode == RESULT_OK) { + setupApi() + scheduleSync() + reload() + } + } else if (dialog?.handleActivityResult(requestCode, resultCode, data) == true) { + return + } else { + super.onActivityResult(requestCode, resultCode, data) + } + } + + fun pinProtect(key: String, valid: ((pin: String) -> Unit)) { + if (!conf.requiresPin(key)) { + valid("") + return + } + pendingPinAction = valid + PinDialog().show(supportFragmentManager) + } + + fun startWithPIN(intent: Intent, key: String, resultCode: Int? = null, bundle: Bundle? = null) { + pinProtect(key) { pin -> + intent.putExtra("pin", pin) + if (resultCode != null) { + startActivityForResult(intent, resultCode, bundle) + } else { + startActivity(intent) + } + } + } + + fun getRestrictions(ctx: Context) { + val myRestrictionsMgr = ctx.getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager? + ?: return + val restrictions = myRestrictionsMgr.applicationRestrictions + val prefs = PreferenceManager.getDefaultSharedPreferences(ctx) + + for (key in restrictions.keySet()) { + if (key == "pref_auto_print_badges_option") { + prefs.edit().putString(key, restrictions.getString(key)).apply() + } else { + prefs.edit().putBoolean(key, restrictions.getBoolean(key)).apply() + } + } + } + + override fun onConnectivityChanged() { + runOnUiThread { + reload() + } + } + + override fun chipReadSuccessfully(identifier: String, mediaType: ReusableMediaType) { + if (identifier.startsWith("08")) { + runOnUiThread { + //showLoadingCard() + displayScanResult( + TicketCheckProvider.CheckResult(TicketCheckProvider.CheckResult.Type.ERROR, getString(R.string.nfc_random_uid), false), + null + ) + } + return + } + + lastScanTime = System.currentTimeMillis() + lastScanCode = identifier + lastScanSourceType = mediaType + lastScanResult = null + lastIgnoreUnpaid = false + + handleScan( + identifier, + mediaType.serverName!!, + null, + !conf.unpaidAsk + ) + } + + override fun chipReadError(error: ChipReadError, identifier: String?) { + val error = when (error) { + ChipReadError.IO_ERROR -> getString(R.string.nfc_read_error) + ChipReadError.UNKNOWN_CHIP_TYPE -> getString(R.string.nfc_unknown_chip_type) + ChipReadError.FOREIGN_CHIP -> getString(R.string.nfc_foreign_chip) + ChipReadError.EMPTY_CHIP -> getString(R.string.nfc_empty_chip) + else -> error.toString() + } + + runOnUiThread { + //showLoadingCard() + displayScanResult( + TicketCheckProvider.CheckResult(TicketCheckProvider.CheckResult.Type.ERROR, error, false), + null + ) + } + } + + fun reloadNfcHandler() { + if (conf.synchronizedEvents.isNotEmpty()) { + val eventSlug = conf.synchronizedEvents.first() + val settingsManager = SettingsManager(application) + val useRandomIdForNewTags = settingsManager.getBySlug(eventSlug)?.json?.optBoolean("reusable_media_type_nfc_mf0aes_random_uid", false) ?: false + val activeMediaTypes = getActiveMediaTypes( + settingsManager, + eventSlug + ) + if (!activeMediaTypes.any { it.isNfcBased() }) { + if (nfcHandler?.getState() == NfcHandler.NfcState.RUNNING) { + nfcHandler?.stop() + } + return + } + if (nfcHandler?.getState() != NfcHandler.NfcState.RUNNING || activeMediaTypes.toSet() != nfcHandler?.getMediaTypes()?.toSet()) { + nfcHandler?.stop() + val keySets = (this.applicationContext as PretixScan).db.mediumKeySetQueries.selectAll() + .executeAsList() + .map { it.toModel() } + .map { + Mf0aesKeySet( + it.publicId, + it.organizer == conf.organizerSlug && it.active, + conf.keyStore.decryptRsa( + "device", + eu.pretix.libpretixsync.utils.codec.binary.Base64.decodeBase64(it.uidKey.toByteArray(Charset.defaultCharset())) + ), + conf.keyStore.decryptRsa( + "device", + eu.pretix.libpretixsync.utils.codec.binary.Base64.decodeBase64(it.diversificationKey.toByteArray(Charset.defaultCharset())) + ), + ) + } + + nfcHandler = getNfcHandler(this, keySets, useRandomIdForNewTags, nfcReaderType = conf.nfcReaderType) + nfcHandler!!.setOnChipReadListener(this) + try { + nfcHandler!!.start(activeMediaTypes) + } catch (_: NfcUnsupported) { + // silently ignore, else users on non-nfc-devices are warned all the time + } catch (_: NfcDisabled) { + // silently ignore, we may want to show an unobtrusive warning in the future + } + } + } + } +} diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/GateUtils.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/GateUtils.kt new file mode 100644 index 00000000..7de23093 --- /dev/null +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/GateUtils.kt @@ -0,0 +1,32 @@ +package eu.pretix.pretixscan.droid.ui + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.ResultReceiver + + +fun openGate( + context: Context, + recv: ResultReceiver? +) { + val intent = Intent() + intent.action = "eu.pretix.pretixscan.gate.OPEN_GATE" + + if (isPackageInstalled("eu.pretix.ktIOservice", context.packageManager)) { + intent.`package` = "eu.pretix.ktIOservice" + intent.component = + ComponentName("eu.pretix.ktIOservice", "eu.pretix.ktIOservice.GateService") + } else { + throw Exception("error_print_no_app") + } + if (recv != null) { + intent.putExtra("resultreceiver", receiverForSending(recv)) + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent) + } else { + context.startService(intent) + } +} \ No newline at end of file diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt new file mode 100644 index 00000000..33258847 --- /dev/null +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt @@ -0,0 +1,815 @@ +package eu.pretix.pretixscan.droid.ui + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.content.res.Configuration +import android.graphics.PointF +import android.graphics.drawable.Animatable2 +import android.graphics.drawable.AnimatedVectorDrawable +import android.graphics.drawable.Drawable +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.os.LocaleList +import android.os.Looper +import android.os.ResultReceiver +import android.util.DisplayMetrics +import android.view.KeyEvent +import android.view.MotionEvent +import android.view.View +import android.view.Window +import android.view.WindowInsets +import android.view.WindowInsetsController +import androidx.annotation.RequiresApi +import androidx.appcompat.content.res.AppCompatResources +import androidx.core.content.ContextCompat +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import eu.pretix.libpretixsync.api.PretixApi +import eu.pretix.libpretixsync.check.TicketCheckProvider +import eu.pretix.libpretixsync.db.Answer +import eu.pretix.pretixscan.droid.AndroidHttpClientFactory +import eu.pretix.pretixscan.droid.BuildConfig +import eu.pretix.pretixscan.droid.PretixScan +import eu.pretix.pretixscan.droid.R +import eu.pretix.pretixscan.droid.databinding.ActivityKioskBinding +import eu.pretix.pretixscan.droid.hardware.LED +import java.util.IllformedLocaleException +import java.util.Locale + + +class KioskActivity : BaseScanActivity() { + companion object { + /** + * Some older devices needs a small delay between UI widget updates + * and a change of the status and navigation bar. + */ + private const val UI_ANIMATION_DELAY = 300 + + enum class KioskState { + WaitingForScan, + ReadingBarcode, + Checking, + Rejected, + NeedAnswers, + Printing, + GateOpen, + OutOfOrder, + } + + enum class NetworkType { + Unknown, + None, + Cellular, + Wifi, + Ethernet + } + } + + private lateinit var binding: ActivityKioskBinding + private val hideHandler = Handler(Looper.myLooper()!!) + private val backToStartHandler = Handler(Looper.myLooper()!!) + private val printTimeoutHandler = Handler(Looper.myLooper()!!) + private val gateTimeoutHandler = Handler(Looper.myLooper()!!) + var networkType = NetworkType.Unknown + var state = KioskState.WaitingForScan + set(value) { + if (BuildConfig.DEBUG) { + println("Setting state from $state to $value") + } + field = value + } + var lastTicketRequireAttention = false + var localizedContext: Context? = null + + fun setTemporaryLocale(overrideLocale: String) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + return + } + val localeBuilder = Locale.Builder() + try { + localeBuilder.setLanguage(overrideLocale.split("-").first()) + } catch (e: IllformedLocaleException) { + return + } + if (overrideLocale.contains("-")) { + try { + // Is this a region, like pt-br? + localeBuilder.setRegion(overrideLocale.split("-").last().uppercase()) + } catch (e: IllformedLocaleException) { + try { + // Is this a variant, like zh-hans? + localeBuilder.setVariant(overrideLocale.split("-").last()) + } catch (e: IllformedLocaleException) { + // Is it neither, like de-informal? Ignore! + } + } + } + + val conf = Configuration(resources.configuration) + conf.setLocales(LocaleList(localeBuilder.build(), Locale.getDefault())) + localizedContext = createConfigurationContext(conf) + } + + private fun localizedString(stringId: Int): String { + return (localizedContext ?: this).getString(stringId) + } + + val backToStart = Runnable { + when (state) { + KioskState.GateOpen, + KioskState.NeedAnswers, + KioskState.Checking, + KioskState.ReadingBarcode, + KioskState.Rejected -> { + state = KioskState.WaitingForScan + localizedContext = null + updateUi() + } + else -> {} + } + } + + val printTimeout = Runnable { + if (state == KioskState.Printing) { + binding.tvOutOfOrderMessage.text = "Printing failed by timeout" + state = KioskState.OutOfOrder + updateUi() + } + } + + val gateTimeout = Runnable { + if (state == KioskState.GateOpen) { + binding.tvOutOfOrderMessage.text = "Gate opening failed by timeout" + state = KioskState.OutOfOrder + updateUi() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + requestWindowFeature(Window.FEATURE_NO_TITLE) + + binding = ActivityKioskBinding.inflate(layoutInflater) + setContentView(binding.root) + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + @SuppressLint("SetTextI18n") + binding.tvDeviceInfo.text = "#${conf.devicePosId}" + } + + val loopCallback = + @RequiresApi(Build.VERSION_CODES.M) object : Animatable2.AnimationCallback() { + override fun onAnimationEnd(drawable: Drawable) { + (drawable as AnimatedVectorDrawable).start() + } + } + + fun resetAnimations() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + (binding.ivKioskAnimation.drawable as? AnimatedVectorDrawable)?.apply { + unregisterAnimationCallback(loopCallback) + registerAnimationCallback(loopCallback) + start() + } + (binding.ivKioskAnimation2.drawable as? AnimatedVectorDrawable)?.apply { + unregisterAnimationCallback(loopCallback) + registerAnimationCallback(loopCallback) + start() + } + (binding.ivKioskAnimation3.drawable as? AnimatedVectorDrawable)?.apply { + unregisterAnimationCallback(loopCallback) + registerAnimationCallback(loopCallback) + start() + } + } + } + + override fun onPostCreate(savedInstanceState: Bundle?) { + super.onPostCreate(savedInstanceState) + fullscreen() + updateUi() + resetAnimations() + } + + override fun onStop() { + super.onStop() + LED(this).off() + } + + fun fullscreen() { + supportActionBar?.hide() + hideHandler.postDelayed({ + if (Build.VERSION.SDK_INT >= 30) { + window.insetsController?.hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars()) + window.insetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + } else { + // Note that some of these constants are new as of API 16 (Jelly Bean) + // and API 19 (KitKat). It is safe to use them, as they are inlined + // at compile-time and do nothing on earlier devices. + window.decorView.systemUiVisibility = + View.SYSTEM_UI_FLAG_LOW_PROFILE or + View.SYSTEM_UI_FLAG_FULLSCREEN or + View.SYSTEM_UI_FLAG_LAYOUT_STABLE or + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + } + }, UI_ANIMATION_DELAY.toLong()) + } + + override fun onResume() { + super.onResume() + fullscreen() + if (conf.kioskOutOfOrder) { + conf.kioskOutOfOrder = true + state = KioskState.OutOfOrder + binding.tvOutOfOrderMessage.text = "" + } + val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + updateNetworkType(connectivityManager) + updateUi() + } + + private fun updateNetworkType(connectivityManager: ConnectivityManager) { + fun setNetworkType(nt: NetworkType) { + if (nt != networkType) { + networkType = nt + runOnUiThread { + updateUi() + } + } + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + setNetworkType(NetworkType.Unknown) + } else if (connectivityManager.activeNetwork == null) { + setNetworkType(NetworkType.None) + } else { + val netCaps = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + if (netCaps == null) { + setNetworkType(NetworkType.Unknown) + } else if (netCaps.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { + setNetworkType(NetworkType.Ethernet) + } else if (netCaps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + setNetworkType(NetworkType.Wifi) + } else if (netCaps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + setNetworkType(NetworkType.Cellular) + } else { + setNetworkType(NetworkType.Unknown) + } + } + } + + override fun onNetworkAvailable(connectivityManager: ConnectivityManager, network: Network) { + super.onNetworkAvailable(connectivityManager, network) + updateNetworkType(connectivityManager) + } + + override fun onNetworkLost(connectivityManager: ConnectivityManager, network: Network?) { + super.onNetworkLost(connectivityManager, network) + updateNetworkType(connectivityManager) + } + + override fun onNetworkChanged(connectivityManager: ConnectivityManager, network: Network) { + super.onNetworkChanged(connectivityManager, network) + updateNetworkType(connectivityManager) + } + + override fun reload() { + super.reload() + binding.ivOfflineIcon.visibility = if (conf.offlineMode) View.VISIBLE else View.GONE + } + + override fun reloadSyncStatus() { + if (conf.lastFailedSync > conf.lastSync || System.currentTimeMillis() - conf.lastDownload > 5 * 60 * 1000) { + binding.ivSyncStatusIcon.setImageDrawable( + ContextCompat.getDrawable( + this, + R.drawable.ic_warning_red_24dp + ) + ) + binding.ivSyncStatusIcon.setColorFilter( + ContextCompat.getColor( + this, + R.color.pretix_brand_red + ) + ) + binding.tvSyncStatus.visibility = if (conf.proxyMode) View.GONE else View.VISIBLE + binding.tvSyncStatus.text = syncStatusText() + } else { + binding.ivSyncStatusIcon.setImageDrawable( + ContextCompat.getDrawable( + this, + R.drawable.ic_baseline_check_circle_24 + ) + ) + binding.ivSyncStatusIcon.setColorFilter( + ContextCompat.getColor( + this, + R.color.pretix_brand_green + ) + ) + binding.tvSyncStatus.visibility = View.GONE + } + } + + override fun displayScanResult( + result: TicketCheckProvider.CheckResult, + answers: MutableList?, + ignore_unpaid: Boolean + ) { + /* // FIXME: preparation for libpretixsync#54 + if (result.locale != null) { + setTemporaryLocale(result.locale!!) + } + */ + lastTicketRequireAttention = result.isRequireAttention + var isPrintable = false + val mayBePrintable = (conf.printBadges && + result.scanType != TicketCheckProvider.CheckInType.EXIT && + result.position != null) + if (mayBePrintable) { + val badgeLayout = + getBadgeLayout((application as PretixScan).db, result.position!!, result.eventSlug!!) + if (badgeLayout != null) { + isPrintable = true + } + } + val shouldAutoPrint = isPrintable && when (conf.autoPrintBadges) { + "false" -> false + "true" -> { + result.type == TicketCheckProvider.CheckResult.Type.VALID + } + + "once" -> { + result.type == TicketCheckProvider.CheckResult.Type.VALID && + !isPreviouslyPrinted((application as PretixScan).db, result.position!!) + } + + else -> false + } + + when (result.type) { + TicketCheckProvider.CheckResult.Type.VALID -> when (result.scanType) { + TicketCheckProvider.CheckInType.ENTRY -> { + if (shouldAutoPrint) { + state = KioskState.Printing + } else { + state = KioskState.GateOpen + } + if (conf.sounds) { + if (result.isRequireAttention) { + mediaPlayers[R.raw.attention]?.start() + } else { + mediaPlayers[R.raw.enter]?.start() + } + } + } + + TicketCheckProvider.CheckInType.EXIT -> { + state = KioskState.GateOpen + if (conf.sounds) { + mediaPlayers[R.raw.exit]?.start() + } + } + } + + TicketCheckProvider.CheckResult.Type.INVALID, + TicketCheckProvider.CheckResult.Type.ERROR, + TicketCheckProvider.CheckResult.Type.UNPAID, + TicketCheckProvider.CheckResult.Type.CANCELED, + TicketCheckProvider.CheckResult.Type.PRODUCT, + TicketCheckProvider.CheckResult.Type.RULES, + TicketCheckProvider.CheckResult.Type.AMBIGUOUS, + TicketCheckProvider.CheckResult.Type.REVOKED, + TicketCheckProvider.CheckResult.Type.UNAPPROVED, + TicketCheckProvider.CheckResult.Type.BLOCKED, + TicketCheckProvider.CheckResult.Type.INVALID_TIME, + TicketCheckProvider.CheckResult.Type.USED -> { + state = KioskState.Rejected + if (conf.sounds) { + mediaPlayers[R.raw.error]?.start() + } + } + + TicketCheckProvider.CheckResult.Type.ANSWERS_REQUIRED -> { + state = KioskState.NeedAnswers + if (conf.sounds) { + mediaPlayers[R.raw.error]?.start() + } + } + + else -> { + binding.tvOutOfOrderMessage.text = "Unknown Scan Result Type" + state = KioskState.OutOfOrder + if (conf.sounds) { + mediaPlayers[R.raw.error]?.start() + } + } + } + + if (state == KioskState.Rejected) { + if (result.message == null) { + result.message = when (result.type!!) { + TicketCheckProvider.CheckResult.Type.INVALID -> localizedString(R.string.scan_result_invalid) + TicketCheckProvider.CheckResult.Type.USED -> localizedString(R.string.scan_result_used) + TicketCheckProvider.CheckResult.Type.RULES -> localizedString(R.string.scan_result_rules) + TicketCheckProvider.CheckResult.Type.AMBIGUOUS -> localizedString(R.string.scan_result_ambiguous) + TicketCheckProvider.CheckResult.Type.REVOKED -> localizedString(R.string.scan_result_revoked) + TicketCheckProvider.CheckResult.Type.UNAPPROVED -> localizedString(R.string.scan_result_unapproved) + TicketCheckProvider.CheckResult.Type.INVALID_TIME -> localizedString(R.string.scan_result_invalid_time) + TicketCheckProvider.CheckResult.Type.BLOCKED -> localizedString(R.string.scan_result_blocked) + TicketCheckProvider.CheckResult.Type.UNPAID -> localizedString(R.string.scan_result_unpaid) + TicketCheckProvider.CheckResult.Type.CANCELED -> localizedString(R.string.scan_result_canceled) + TicketCheckProvider.CheckResult.Type.PRODUCT -> localizedString(R.string.scan_result_product) + else -> null + } + } + binding.tvRejectedMessage.text = result.message + binding.tvRejectedReason.visibility = + if (result.reasonExplanation.isNullOrBlank()) View.GONE else View.VISIBLE + binding.tvRejectedReason.text = result.reasonExplanation + } + + updateUi() + + if (state == KioskState.Printing) { + val recv = object : ResultReceiver(null) { + override fun onReceiveResult(resultCode: Int, resultData: Bundle?) { + super.onReceiveResult(resultCode, resultData) + printTimeoutHandler.removeCallbacks(printTimeout) + if (resultCode == 0) { + val api = PretixApi.fromConfig( + conf, + AndroidHttpClientFactory(application as PretixScan) + ) + logSuccessfulPrint( + api, + (application as PretixScan).db, + result.eventSlug!!, + result.position!!.getLong("id"), + "badge" + ) + runOnUiThread { + state = KioskState.GateOpen + updateUi() + openGate() + } + } else { + // printing failed + runOnUiThread { + binding.tvOutOfOrderMessage.text = "Printing failed" + state = KioskState.OutOfOrder + updateUi() + } + } + } + } + if (shouldAutoPrint) { + printTimeoutHandler.postDelayed(printTimeout, 15_000) + printBadge( + this, + (application as PretixScan).db, + (application as PretixScan).fileStorage, + result.position!!, + result.eventSlug!!, + recv + ) + } + } else if (state == KioskState.GateOpen) { + openGate() + } else if (state == KioskState.NeedAnswers || state == KioskState.Rejected) { + backToStartHandler.postDelayed(backToStart, 5_000) + } + } + + fun openGate() { + openGate(this, object : ResultReceiver(null) { + override fun onReceiveResult(resultCode: Int, resultData: Bundle?) { + super.onReceiveResult(resultCode, resultData) + gateTimeoutHandler.removeCallbacks(gateTimeout) + if (resultCode == 0) { + runOnUiThread { + backToStartHandler.postDelayed(backToStart, conf.timeAfterGateOpen.toLong()) + } + } else if (resultCode == 2) { + // Gate opened, but did not turn. + // TODO: Revert checkin? + runOnUiThread { + backToStartHandler.postDelayed(backToStart, conf.timeAfterGateOpen.toLong()) + } + } else { + // gate opening failed + runOnUiThread { + binding.tvOutOfOrderMessage.text = "Gate opening failed" + state = KioskState.OutOfOrder + updateUi() + } + } + } + }) + gateTimeoutHandler.postDelayed(gateTimeout, 30_000) + } + + + fun updateUi() { + binding.clWaitingForScan.visibility = View.GONE + binding.clPrinting.visibility = View.GONE + binding.clRejected.visibility = View.GONE + binding.clGate.visibility = View.GONE + binding.clChecking.visibility = View.GONE + binding.llOutOfOrder.visibility = View.GONE + val led = LED(this) + + when (state) { + KioskState.WaitingForScan -> { + binding.clWaitingForScan.visibility = View.VISIBLE + led.success(blink = false) + } + + KioskState.Rejected -> { + binding.clRejected.visibility = View.VISIBLE + led.error(blink = true) + } + + KioskState.NeedAnswers -> { + binding.tvRejectedMessage.text = localizedString(R.string.kiosk_text_questions_reject) + binding.tvRejectedReason.visibility = View.VISIBLE + binding.tvRejectedReason.text = localizedString(R.string.kiosk_text_questions_reject_sub) + binding.clRejected.visibility = View.VISIBLE + backToStartHandler.postDelayed(backToStart, 3_000) + led.error(blink = true) + } + + KioskState.Printing -> { + binding.clPrinting.visibility = View.VISIBLE + binding.tvPrinting.text = localizedString(R.string.kiosk_text_print) + if (lastTicketRequireAttention) { + led.attention(blink = true) + } else { + led.success(blink = true) + } + } + + KioskState.Checking -> { + binding.clChecking.visibility = View.VISIBLE + binding.tvChecking.text = getString(R.string.kiosk_text_checking) + led.progress(blink = false) + } + + KioskState.ReadingBarcode -> { + binding.clChecking.visibility = View.VISIBLE + binding.tvChecking.text = getString(R.string.kiosk_text_reading) + led.progress(blink = false) + } + + KioskState.GateOpen -> { + binding.clGate.visibility = View.VISIBLE + binding.tvGate.text = localizedString(R.string.kiosk_text_gate) + if (lastTicketRequireAttention) { + led.attention() + } else { + led.success() + } + } + + KioskState.OutOfOrder -> { + binding.llOutOfOrder.visibility = View.VISIBLE + led.error(blink = false) + } + } + if (conf.scanType == "exit") { + binding.ivDirectionIcon.setImageDrawable(AppCompatResources.getDrawable(this, R.drawable.ic_exit_white_24dp)) + } else { + binding.ivDirectionIcon.setImageDrawable(AppCompatResources.getDrawable(this, R.drawable.ic_entry_gray_24dp)) + } + + binding.ivNetworkIcon.visibility = if (networkType == NetworkType.Unknown) { + View.INVISIBLE + } else { + View.VISIBLE + } + binding.ivNetworkIcon.setImageDrawable(AppCompatResources.getDrawable(this, when (networkType) { + NetworkType.Unknown -> R.drawable.ic_wifi_unknown + NetworkType.None -> R.drawable.ic_wifi_off + NetworkType.Cellular -> R.drawable.ic_cell_tower_24px + NetworkType.Wifi -> R.drawable.ic_wifi_24px + NetworkType.Ethernet -> R.drawable.ic_ethernet_24px + })) + } + + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + val r = super.dispatchKeyEvent(event) + if (state == KioskState.WaitingForScan && keyboardBuffer.isNotBlank()) { + state = KioskState.ReadingBarcode + if (conf.sounds) { + mediaPlayers[R.raw.beep]?.start() + } + updateUi() + backToStartHandler.postDelayed(backToStart, 3_000) + } + return r + } + + + fun openMenu(pin: String) { + val optstrings = arrayOf( + getString(R.string.action_label_settings), + getString(R.string.action_sync), + getString(R.string.operation_select_event), + // TODO: Change direction + if (conf.kioskOutOfOrder) + getString(R.string.action_label_remove_out_of_order) + else + getString(R.string.action_label_out_of_order) + ) + MaterialAlertDialogBuilder(this) + .setItems(optstrings) { _, i -> + when (optstrings[i]) { + getString(R.string.action_label_settings) -> { + val intent = Intent(this, SettingsActivity::class.java) + intent.putExtra("pin", pin) + startActivity(intent) + } + getString(R.string.action_sync) -> { + syncNow() + } + getString(R.string.operation_select_event) -> { + val intent = Intent(this, EventConfigActivity::class.java) + startActivityForResult(intent, REQ_EVENT, null) + } + getString(R.string.action_label_out_of_order) -> { + conf.kioskOutOfOrder = true + state = KioskState.OutOfOrder + binding.tvOutOfOrderMessage.text = "" + updateUi() + } + getString(R.string.action_label_remove_out_of_order) -> { + conf.kioskOutOfOrder = false + localizedContext = null + state = KioskState.WaitingForScan + updateUi() + } + } + } + .show() + } + + override fun handleScan( + raw_result: String, + source_type: String, + answers: MutableList?, + ignore_unpaid: Boolean, + exchange_medium_type: String?, + exchange_medium_identifier: String? + ) { + if (conf.requiresPin("settings") && conf.verifyPin(raw_result)) { + openMenu(raw_result) + return + } + + when (state) { + KioskState.WaitingForScan, + KioskState.ReadingBarcode, + KioskState.GateOpen, + KioskState.Rejected -> { + // that's fine, handle it + } + + KioskState.Checking, + KioskState.NeedAnswers, + KioskState.Printing, + KioskState.OutOfOrder -> { + // waiting for user, for printer, gate or administrative action. ignoring scan. + lastScanCode = "" // do not consider scan "used" + return + } + } + + // ignore scan if same ticket was scanned two times, but gate is already open + if (raw_result == lastScanCode && state == KioskState.GateOpen) { + lastScanCode = "" // do not consider scan "used" + return + } + + backToStartHandler.removeCallbacks(backToStart) + printTimeoutHandler.removeCallbacks(printTimeout) + gateTimeoutHandler.removeCallbacks(gateTimeout) + state = KioskState.Checking + updateUi() + super.handleScan( + raw_result, + lastScanSourceType.serverName!!, + answers, + ignore_unpaid, + exchange_medium_type, + exchange_medium_identifier + ) + } + + + val pointerDownPositions = mutableMapOf(); + val pointerUpPositions = mutableMapOf(); + var lowestPoint = PointF(); + + override fun onTouchEvent(event: MotionEvent): Boolean { + /* + We support the following gestures. + + 1.) Open menu with PIN, two finger gesture that looks like this: + + <-------------X + X-------------> + + 2.) Forget current out-of-order state (e.g. after printer was fixed): + + X ^ + \ / + \ / + \ / + \ / + \/ + + */ + when (event.actionMasked) { + MotionEvent.ACTION_DOWN -> { + pointerDownPositions.clear() + pointerUpPositions.clear() + lowestPoint = PointF(0f, 0f) + pointerDownPositions[event.getPointerId(0)] = PointF(event.getX(0), event.getY(0)) + return true + } + + MotionEvent.ACTION_POINTER_DOWN -> { + pointerDownPositions[event.getPointerId(event.actionIndex)] = + PointF(event.getX(event.actionIndex), event.getY(event.actionIndex)) + return true + } + + MotionEvent.ACTION_POINTER_UP -> { + pointerUpPositions[event.getPointerId(event.actionIndex)] = + PointF(event.getX(event.actionIndex), event.getY(event.actionIndex)) + return true + } + + MotionEvent.ACTION_MOVE -> { + if (event.getY(event.actionIndex) > lowestPoint.y) { + lowestPoint = + PointF(event.getX(event.actionIndex), event.getY(event.actionIndex)) + } + return true + } + + MotionEvent.ACTION_UP -> { + pointerUpPositions[event.getPointerId(0)] = PointF(event.getX(0), event.getY(0)) + + val displaymetrics = DisplayMetrics() + windowManager.defaultDisplay.getMetrics(displaymetrics) + val height: Int = displaymetrics.heightPixels + val width: Int = displaymetrics.widthPixels + + if (pointerUpPositions.size == 2 && pointerDownPositions.size == 2) { + val fingerIds = pointerDownPositions.keys.toList() + val upperFingerId = + if (pointerDownPositions[fingerIds[0]]!!.y < pointerDownPositions[fingerIds[1]]!!.y) { + fingerIds[0] + } else { + fingerIds[1] + } + val lowerFingerId = pointerDownPositions.keys.first { it != upperFingerId } + + val gestureDetected = + (pointerUpPositions[upperFingerId]!!.x - pointerDownPositions[upperFingerId]!!.x < -0.5 * width) && + (pointerUpPositions[lowerFingerId]!!.x - pointerDownPositions[lowerFingerId]!!.x > 0.5 * width) && + (pointerUpPositions[upperFingerId]!!.y < pointerUpPositions[lowerFingerId]!!.y) + if (gestureDetected) { + pinProtect("settings") { pin -> + openMenu(pin) + } + } + } else if (pointerUpPositions.size == 1 && pointerDownPositions.size == 1) { + val fingerId = pointerDownPositions.keys.first() + val gestureDetected = + (pointerDownPositions[fingerId]!!.x - lowestPoint.x < -0.2 * width) && + (pointerUpPositions[fingerId]!!.x - lowestPoint.x > 0.2 * width) && + (lowestPoint.y - pointerUpPositions[fingerId]!!.y > 0.2 * height) && + (lowestPoint.y - pointerDownPositions[fingerId]!!.y > 0.2 * height) + if (gestureDetected && state == KioskState.OutOfOrder && !conf.kioskOutOfOrder) { + localizedContext = null + state = KioskState.WaitingForScan + updateUi() + } + } + return true + } + + else -> return super.onTouchEvent(event) + } + } + +} \ No newline at end of file diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index 452e186c..c70f048a 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -1,33 +1,21 @@ package eu.pretix.pretixscan.droid.ui -import Mf0aesKeySet import android.Manifest import android.animation.LayoutTransition import android.animation.ObjectAnimator import android.animation.ValueAnimator -import android.app.ProgressDialog import android.content.Context import android.content.DialogInterface import android.content.Intent -import android.content.RestrictionsManager import android.content.pm.PackageManager import android.content.res.ColorStateList import android.graphics.drawable.Drawable -import android.media.AudioManager -import android.media.MediaPlayer -import android.net.ConnectivityManager -import android.net.Network -import android.net.NetworkCapabilities -import android.net.NetworkRequest -import android.os.Build import android.os.Bundle import android.os.Handler import android.os.ResultReceiver import android.text.SpannableString import android.text.SpannableStringBuilder -import android.util.Base64 import android.util.DisplayMetrics -import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.view.View @@ -36,10 +24,7 @@ import android.view.animation.Animation import android.view.animation.BounceInterpolator import android.view.animation.DecelerateInterpolator import android.view.animation.LinearInterpolator -import android.widget.TextView import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.AppCompatButton import androidx.appcompat.widget.SearchView import androidx.core.animation.doOnEnd import androidx.core.app.ActivityOptionsCompat @@ -50,23 +35,14 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.databinding.DataBindingUtil import androidx.databinding.ObservableField -import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.module.SimpleModule -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.snackbar.Snackbar -import eu.pretix.libpretixnfc.android.hardware.NfcDisabled -import eu.pretix.libpretixnfc.android.hardware.NfcHandler -import eu.pretix.libpretixnfc.android.hardware.NfcUnsupported -import eu.pretix.libpretixnfc.android.hardware.getNfcHandler -import eu.pretix.libpretixnfc.communication.ChipReadError import eu.pretix.libpretixsync.api.PretixApi import eu.pretix.libpretixsync.check.CheckException -import eu.pretix.libpretixsync.check.OnlineCheckProvider import eu.pretix.libpretixsync.check.TicketCheckProvider import eu.pretix.libpretixsync.db.Answer import eu.pretix.libpretixsync.db.ReusableMediaType @@ -75,55 +51,20 @@ import eu.pretix.libpretixsync.serialization.JSONArrayDeserializer import eu.pretix.libpretixsync.serialization.JSONArraySerializer import eu.pretix.libpretixsync.serialization.JSONObjectDeserializer import eu.pretix.libpretixsync.serialization.JSONObjectSerializer -import eu.pretix.libpretixsync.sync.SyncManager -import eu.pretix.libpretixsync.utils.getActiveMediaTypes -import eu.pretix.libpretixui.android.questions.QuestionsDialog -import eu.pretix.libpretixui.android.questions.QuestionsDialogInterface -import eu.pretix.libpretixui.android.scanning.HardwareScanner -import eu.pretix.libpretixui.android.scanning.ScanReceiver import eu.pretix.libpretixui.android.scanning.ScannerView import eu.pretix.pretixscan.droid.* -import eu.pretix.pretixscan.droid.connectivity.ConnectivityChangedListener import eu.pretix.pretixscan.droid.databinding.ActivityMainBinding +import eu.pretix.pretixscan.droid.hardware.KioskHardware import eu.pretix.pretixscan.droid.hardware.LED import eu.pretix.pretixscan.droid.ui.ResultState.* import eu.pretix.pretixscan.droid.ui.info.EventinfoActivity -import eu.pretix.pretixscan.utils.SettingsManager import io.sentry.Sentry -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.json.JSONArray import org.json.JSONObject import splitties.toast.toast -import java.io.IOException -import java.lang.Integer.max -import java.nio.charset.Charset -import java.security.MessageDigest import java.text.SimpleDateFormat import java.time.format.DateTimeFormatter -import java.util.* - - -interface ReloadableActivity { - fun reload() -} - -enum class ResultCardState { - HIDDEN, - SHOWN -} - -enum class ResultState { - EMPTY, - LOADING, - ERROR, - DIALOG_QUESTIONS, - DIALOG_EXCHANGE, - WARNING, - SUCCESS, - SUCCESS_EXIT -} class ViewDataHolder(private val ctx: Context) { val resultState = ObservableField() @@ -172,66 +113,20 @@ class ViewDataHolder(private val ctx: Context) { } } -class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.ResultHandler, MediaPlayer.OnCompletionListener, ConnectivityChangedListener, NfcHandler.OnChipReadListener { - - private val REQ_EVENT = 1 - +class MainActivity : BaseScanActivity() { private lateinit var binding: ActivityMainBinding - private lateinit var api: PretixApi - private lateinit var sm: SyncManager - private lateinit var conf: AppConfig - private val bgScope = CoroutineScope(Dispatchers.IO) - private val handler = Handler() private val hideHandler = Handler() private var hideAnimation: ValueAnimator? = null private var card_state = ResultCardState.HIDDEN private var view_data = ViewDataHolder(this) - private var mediaPlayers: MutableMap = mutableMapOf() - - private var lastScanTime: Long = 0 - private var lastScanCode: String = "" - private var lastIgnoreUnpaid: Boolean = false - private var lastScanSourceType: ReusableMediaType = ReusableMediaType.BARCODE - private var lastScanResult: TicketCheckProvider.CheckResult? = null - private var keyboardBuffer: String = "" - private var dialog: QuestionsDialogInterface? = null - private var pdialog: ProgressDialog? = null - private var networkCallback: ConnectivityManager.NetworkCallback? = null - - private val dataWedgeHelper = DataWedgeHelper(this) private var searchAdapter: SearchListAdapter? = null private var searchFilter = "" - private var syncMessage = "" - - private var pendingPinAction: ((pin: String) -> Unit)? = null - - private var nfcHandler: NfcHandler? = null - companion object { const val PERMISSIONS_REQUEST_CAMERA = 1337 } - private val hardwareScanner = HardwareScanner(object : ScanReceiver { - override fun scanResult(result: String) { - if (result == lastScanCode && System.currentTimeMillis() - lastScanTime < 2500) { - return - } - lastScanTime = System.currentTimeMillis() - lastScanCode = result - lastScanSourceType = ReusableMediaType.BARCODE - lastIgnoreUnpaid = false - lastScanResult = null - handleScan( - result, - lastScanSourceType.serverName!!, - null, - !conf.unpaidAsk - ) - } - }) - override fun reload() { reloadSyncStatus() @@ -291,8 +186,6 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result } view_data.configDetails.set(confdetails.trim()) view_data.isOffline.set(conf.offlineMode) - - reloadNfcHandler() } private fun setSearchFilter(f: String) { @@ -351,43 +244,15 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result } } - fun reloadSyncStatus() { + override fun reloadSyncStatus() { if (conf.lastFailedSync > conf.lastSync || System.currentTimeMillis() - conf.lastDownload > 5 * 60 * 1000) { binding.textViewStatus.setTextColor(ContextCompat.getColor(this, R.color.pretix_brand_red)) } else { binding.textViewStatus.setTextColor(ContextCompat.getColor(this, R.color.pretix_brand_green)) } binding.textViewStatus.visibility = if (conf.proxyMode) View.GONE else View.VISIBLE - var text = "" - val diff = System.currentTimeMillis() - conf.lastDownload - if ((application as PretixScan).syncLock.isLocked) { - if (syncMessage != "") { - text = syncMessage - } else { - text = getString(R.string.sync_status_progress); - } - } else if (conf.lastDownload == 0L) { - text = getString(R.string.sync_status_never); - } else if (diff > 24 * 3600 * 1000) { - val days = (diff / (24 * 3600 * 1000)).toInt() - text = getResources().getQuantityString(R.plurals.sync_status_time_days, days, days); - } else if (diff > 3600 * 1000) { - val hours = (diff / (3600 * 1000)).toInt() - text = getResources().getQuantityString(R.plurals.sync_status_time_hours, hours, hours); - } else if (diff > 60 * 1000) { - val mins = (diff / (60 * 1000)).toInt() - text = getResources().getQuantityString(R.plurals.sync_status_time_minutes, mins, mins); - } else { - text = getString(R.string.sync_status_now); - } - - if (!(application as PretixScan).syncLock.isLocked) { - val checkins = (application as PretixScan).db.scanQueuedCheckInQueries.count().executeAsOne().toInt() - val calls = (application as PretixScan).db.scanQueuedCallQueries.count().executeAsOne().toInt() - text += " (" + resources.getQuantityString(R.plurals.sync_status_pending, checkins + calls, checkins + calls) + ")" - } - binding.textViewStatus.setText(text) + binding.textViewStatus.text = syncStatusText() } override fun onRequestPermissionsResult(requestCode: Int, @@ -447,39 +312,11 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result } } - override fun onCompletion(p0: MediaPlayer?) { - p0?.seekTo(0) - } - - @SuppressWarnings("ResourceType") - private fun buildMediaPlayer() { - val resourceIds = listOf(R.raw.enter, R.raw.exit, R.raw.error, R.raw.beep, R.raw.attention) - for (r in resourceIds) { - val mediaPlayer = MediaPlayer() - mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC) - mediaPlayer.setOnCompletionListener(this) - // mediaPlayer.setOnErrorListener(this) - try { - val file = resources.openRawResourceFd(r) - try { - mediaPlayer.setDataSource(file.fileDescriptor, file.startOffset, file.length) - } finally { - file.close(); - } - mediaPlayer.setVolume(0.2f, 0.2f) - mediaPlayer.prepare() - mediaPlayers[r] = mediaPlayer - } catch (ioe: IOException) { - } - } - } - public override fun onCreate(savedInstanceState: Bundle?) { + override fun onCreate(savedInstanceState: Bundle?) { + binding = DataBindingUtil.setContentView(this, R.layout.activity_main) super.onCreate(savedInstanceState) - conf = AppConfig(this) - getRestrictions(this) - binding = DataBindingUtil.setContentView(this, R.layout.activity_main) view_data.resultState.set(EMPTY) view_data.scanType.set(conf.scanType) view_data.hardwareScan.set(!conf.useCamera) @@ -509,53 +346,18 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result WindowInsetsCompat.CONSUMED } - volumeControlStream = AudioManager.STREAM_MUSIC - buildMediaPlayer() - if (!conf.deviceRegistered) { - registerDevice() - return - } - try { - setupApi() - } catch (e: IllegalStateException) { - // IllegalStateException is thrown by db access -> Migrations.minVersionCallback - Sentry.captureException(e) - panicPleaseReinstall() - return - } setUpEventListeners() - if (conf.synchronizedEvents.isEmpty()) { - selectEvent() - } else if (conf.lastDownload < 1) { - syncNow() - } - scheduleSync() + checkPermission(Manifest.permission.CAMERA, PERMISSIONS_REQUEST_CAMERA) hideCard() hideSearchCard() binding.cardResult.layoutTransition.enableTransitionType(LayoutTransition.CHANGING) - if (dataWedgeHelper.isInstalled) { - try { - dataWedgeHelper.install() - } catch (e: IOException) { - e.printStackTrace() - } - } - binding.recyclerViewSearch.layoutManager = LinearLayoutManager(this) binding.recyclerViewSearch.addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(binding.recyclerViewSearch.context, androidx.recyclerview.widget.DividerItemDecoration.VERTICAL)) - - supportFragmentManager.setFragmentResultListener(PinDialog.RESULT_PIN, this) { _, bundle -> - val pin = bundle.getString(PinDialog.RESULT_PIN) - if (pin != null && conf.verifyPin(pin)) { - (supportFragmentManager.findFragmentByTag(PinDialog.TAG) as? PinDialog)?.dismiss() - pendingPinAction?.let { it(pin) } - } - } } private fun eventButtonText(): String { @@ -567,41 +369,16 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result } } - private fun setupApi() { + override fun setupApi() { val ebt = eventButtonText() if (binding.mainToolbar.event != null && binding.mainToolbar.event.text != ebt) { // can be null if search bar is open binding.mainToolbar.event.text = ebt (binding.mainToolbar.event.parent as View?)?.forceLayout() } - api = PretixApi.fromConfig(conf, AndroidHttpClientFactory(application as PretixScan)) - - sm = SyncManager( - conf, - api, - AndroidSentryImplementation(), - (application as PretixScan).db, - (application as PretixScan).fileStorage, - 60000L, - 5 * 60000L, - if (conf.syncOrders) SyncManager.Profile.PRETIXSCAN else SyncManager.Profile.PRETIXSCAN_ONLINE, - conf.printBadges, - BuildConfig.VERSION_CODE, - JSONObject(), - Build.BRAND, - Build.MODEL, - (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) Build.VERSION.BASE_OS else "").ifEmpty { "Android" }, - Build.VERSION.RELEASE, - "pretixSCAN Android", - BuildConfig.VERSION_NAME, - try { - conf.keyStore.getOrCreateRsaPubKey("device")?.toString(Charset.defaultCharset()) - } catch (e: NotImplementedError) { null }, - null, - (application as PretixScan).connectivityHelper - ) + super.setupApi() } - private fun selectEvent() { + override fun selectEvent() { val intent = Intent(this, EventConfigActivity::class.java) if (binding.mainToolbar.event != null && ViewCompat.isLaidOut(binding.mainToolbar.event)) { val options = ActivityOptionsCompat.makeSceneTransitionAnimation( @@ -612,26 +389,6 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result } } - private fun snackbar(message: String) { - Snackbar.make(findViewById(R.id.root_layout), message, Snackbar.LENGTH_LONG).show(); - } - - private fun snackbar(message: Int) { - Snackbar.make(findViewById(R.id.root_layout), message, Snackbar.LENGTH_LONG).show(); - } - - private fun registerDevice() { - val intent = Intent(this, WelcomeActivity::class.java) - startActivity(intent) - finish() - } - - private fun panicPleaseReinstall() { - val intent = Intent(this, PleaseReinstallActivity::class.java) - startActivity(intent) - finish() - } - private val hideRunnable = Runnable { runOnUiThread { if (dialog != null && dialog!!.isShowing()) { @@ -667,157 +424,8 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result hideAnimation!!.start() } - private val syncRunnable = Runnable { - syncMessage = "" - val activity = this - bgScope.launch { - if (!(application as PretixScan).syncLock.tryLock()) { - runOnUiThread { - reloadSyncStatus() - } - scheduleSync() - return@launch - } - val prefs = PreferenceManager.getDefaultSharedPreferences(activity) - try { - if (prefs.getBoolean("pref_sync_auto", true)) { - val result = sm.sync(false) { - runOnUiThread { - syncMessage = it - reloadSyncStatus() - } - } - if (result.isDataDownloaded) { - runOnUiThread { - reload() - } - } - } - runOnUiThread { - reloadSyncStatus() - scheduleSync() - } - } catch (e: SyncManager.EventSwitchRequested) { - runOnUiThread { - conf.eventSelection = listOf(EventSelection( - eventSlug = e.eventSlug, - eventName = e.eventName, - subEventId = e.subeventId, - checkInList = e.checkinlistId, - dateFrom = null, - dateTo = null - )) - setupApi() - syncNow() - reload() - } - } catch (e: Exception) { - runOnUiThread { - reload() - } - } finally { - (application as PretixScan).syncLock.unlock() - runOnUiThread { - reloadSyncStatus() - } - } - } - } - - fun scheduleSync() { - handler.removeCallbacks(syncRunnable) - handler.postDelayed(syncRunnable, 1000) - } - - fun syncNow() { - if (isDestroyed) return - syncMessage = "" - pdialog = ProgressDialog(this).apply { - isIndeterminate = true - setMessage(getString(R.string.progress_syncing)) - setTitle(R.string.progress_syncing) - setCanceledOnTouchOutside(false) - setCancelable(false) - show() - } - val activity = this - bgScope.launch { - if (!(application as PretixScan).syncLock.tryLock()) { - runOnUiThread { - MaterialAlertDialogBuilder(activity).setMessage(R.string.error_sync_in_background).create().show() - pdialog?.dismiss() - } - return@launch - } - try { - sm.sync(true) { current_action -> - runOnUiThread { - if (isDestroyed) { - return@runOnUiThread - } - reloadSyncStatus() - syncMessage = current_action - pdialog?.setMessage(current_action) - } - } - runOnUiThread { - if (isDestroyed) { - return@runOnUiThread - } - reload() - pdialog?.dismiss() - if (conf.lastFailedSync > 0) { - MaterialAlertDialogBuilder(activity).setMessage(conf.lastFailedSyncMsg).create().show() - } - } - } catch (e: SyncManager.EventSwitchRequested) { - runOnUiThread { - if (isDestroyed) { - return@runOnUiThread - } - pdialog?.dismiss() - conf.eventSelection = listOf(EventSelection( - eventSlug = e.eventSlug, - eventName = e.eventName, - subEventId = e.subeventId, - checkInList = e.checkinlistId, - dateFrom = null, - dateTo = null - )) - setupApi() - syncNow() - reload() - } - } catch (e: Exception) { - e.printStackTrace() - runOnUiThread { - if (isDestroyed) { - return@runOnUiThread - } - Sentry.captureException(e) - pdialog?.dismiss() - MaterialAlertDialogBuilder(activity) - .setMessage(e.message ?: getString(R.string.error_unknown_exception)) - .create().show() - } - } finally { - (application as PretixScan).syncLock.unlock() - } - } - } - override fun onResume() { super.onResume() - try { - reload() - setupApi() - } catch (e: IllegalStateException) { - // IllegalStateException is thrown by db access -> Migrations.minVersionCallback - Sentry.captureException(e) - panicPleaseReinstall() - return - } - getRestrictions(this) view_data.kioskMode.set(conf.kioskMode) if (conf.kioskMode) { @@ -825,13 +433,18 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result window.decorView.apply { systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN } + if (KioskHardware.isTR51() || KioskHardware.isWA1053T()) { + val intent = Intent(this, KioskActivity::class.java) + startActivity(intent) + finish() + return + } } else { supportActionBar?.show() } scheduleSync() - hardwareScanner.start(this) LED(this).off() if (conf.useCamera && ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { @@ -845,60 +458,23 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result setKioskAnimation() reloadCameraState() - - (application as PretixScan).connectivityHelper.addListener(this) - - val networkRequest = NetworkRequest.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .build() - val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - fun check() { - (application as PretixScan).connectivityHelper.setHardOffline(connectivityManager.activeNetworkInfo?.isConnectedOrConnecting != true) - } - - networkCallback = object : ConnectivityManager.NetworkCallback() { - override fun onAvailable(network: Network) { - (application as PretixScan).connectivityHelper.setHardOffline(false) - } - - override fun onLost(network: Network) { - check() - } - } - check() - connectivityManager.registerNetworkCallback(networkRequest, networkCallback!!) } private fun setKioskAnimation() { if (!conf.kioskMode) return - val drawable = when (Build.BRAND) { - "Zebra" -> if (Build.MODEL.startsWith("CC6")) { + val drawable = when { + KioskHardware.isZebra() -> { R.drawable.avd_kiosk_widescreen_barcode_bottom - } else { - null } - "NewLand" -> if (Build.MODEL.startsWith("NQ")) { + KioskHardware.isNewland() -> { R.drawable.avd_kiosk_widescreen_barcode_bottom - } else { - null } - "Newland" -> if (Build.MODEL.startsWith("NLS-NQ")) { + KioskHardware.isSeuic() -> { R.drawable.avd_kiosk_widescreen_barcode_bottom - } else { - null } - "SEUIC" -> if (Build.MODEL.startsWith("AUTOID Pad Air")) { + KioskHardware.isM3() -> { R.drawable.avd_kiosk_widescreen_barcode_bottom - } else { - null - } - "M3" -> if (Build.MODEL.startsWith("M3PC")) { - R.drawable.avd_kiosk_widescreen_barcode_bottom - } else { - null } else -> null } ?: return @@ -995,194 +571,47 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result } override fun onPause() { - handler.removeCallbacks(syncRunnable) - (application as PretixScan).connectivityHelper.removeListener(this) - nfcHandler?.stop() super.onPause() if (conf.useCamera) { binding.scannerView.stopCamera() } - hardwareScanner.stop(this) LED(this).off() - - if (networkCallback != null) { - val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - connectivityManager.unregisterNetworkCallback(networkCallback!!) - } } - override fun onStop() { - LED(this).off() - super.onStop() - } - - fun handleScan( + override fun handleScan( raw_result: String, source_type: String, answers: MutableList?, - ignore_unpaid: Boolean = false, - exchange_medium_type: String? = null, - exchange_medium_identifier: String? = null, + ignore_unpaid: Boolean, + exchange_medium_type: String?, + exchange_medium_identifier: String?, ) { - if (conf.kioskMode && conf.requiresPin("settings") && conf.verifyPin(raw_result)) { - supportActionBar?.show() - return - } - if (dialog?.isShowing() == true) { /* * Skip scan if a dialog is still in front. This forces users to answer the questions asked. */ return } + LED(this).off() - val result = if (Regex("^HC1:[0-9A-Z $%*+-./:]+$").matches(raw_result.uppercase(Locale.getDefault()))) { - /* - * This is a bit of a hack. pretixSCAN 1.11-2.8.2 supports checking digital COVID vaccination - * certificates. When scanning them at the correct time, we have a high level of privacy - * since we do not store any personal data contained in the certificate. However, if you - * accidentally scan the certificate when you are supposed to scan a ticket, our fancy - * error log will cause the verbatim vaccination certificate to be stored on the server. - * Not really our fault, but also not really nice to store that sensitive health info. - * However, it's still helpful for debugging to see how often an invalid code was scanned. - * So if we encounter something that looks like an EU DGC, we'll just transform it into - * a hashed version. - * - * This hack is safe for pretix' default signature schemes, as they would never generate - * a QR code starting with ``HC1:``, but it could theoretically be unsafe for third-party - * plugins. - */ - val md = MessageDigest.getInstance("SHA-256") - md.update(raw_result.toByteArray(Charset.defaultCharset())) - "HC1:hashed:" + Base64.encodeToString(md.digest(), Base64.URL_SAFE) - } else { - raw_result + if (conf.kioskMode && conf.requiresPin("settings") && conf.verifyPin(raw_result)) { + supportActionBar?.show() + return } showLoadingCard() hideSearchCard() - - // FIXME: don't play sound for media exchange - if (answers == null && !ignore_unpaid && !conf.offlineMode && conf.sounds) { - mediaPlayers[R.raw.beep]?.start() - } - - bgScope.launch { - var checkResult: TicketCheckProvider.CheckResult? = null - val provider = (application as PretixScan).getCheckProvider(conf) - val startedAt = System.currentTimeMillis() - try { - checkResult = provider.check( - conf.eventSelectionToMap(), - result, - source_type, - answers, - ignore_unpaid, - conf.printBadges, - when (conf.scanType) { - "exit" -> TicketCheckProvider.CheckInType.EXIT - else -> TicketCheckProvider.CheckInType.ENTRY - }, - allowQuestions = !conf.ignoreQuestions, - exchange_medium_type = exchange_medium_type, - exchange_medium_identifier = exchange_medium_identifier, - ) - if (provider is OnlineCheckProvider) { - if (checkResult?.type == TicketCheckProvider.CheckResult.Type.ERROR) { - (application as PretixScan).connectivityHelper.recordError() - } else { - (application as PretixScan).connectivityHelper.recordSuccess(System.currentTimeMillis() - startedAt) - } - } - } catch (e: CheckException) { - (application as PretixScan).connectivityHelper.recordError() - checkResult = TicketCheckProvider.CheckResult(TicketCheckProvider.CheckResult.Type.INVALID, getString(R.string.error_exception)) - checkResult.reasonExplanation = e.message - } catch (e: Exception) { - Sentry.captureException(e) - if (BuildConfig.DEBUG) { - e.printStackTrace() - } - (application as PretixScan).connectivityHelper.recordError() - checkResult = TicketCheckProvider.CheckResult(TicketCheckProvider.CheckResult.Type.INVALID, getString(R.string.error_unknown_exception)) - } - runOnUiThread { - displayScanResult(checkResult!!, answers, ignore_unpaid) - } - } - } - - fun showQuestionsDialog(res: TicketCheckProvider.CheckResult, - secret: String, - sourceType: ReusableMediaType, - ignore_unpaid: Boolean, - values: Map?, - isResumed: Boolean, - retryHandler: ((String, ReusableMediaType, MutableList, Boolean) -> Unit)): QuestionsDialogInterface { - - val questions = res.requiredAnswers!!.map { it.question.toModel() } - for (q in questions) { - q.resolveDependency(questions) - } - - val values_ = if (values == null) { - val v = mutableMapOf() - res.requiredAnswers!!.forEach { - if (!it.currentValue.isNullOrBlank()) { - v[it.question.toModel().identifier] = it.currentValue!! - } - } - v - } else { - values - } - val attendeeName = if (conf.hideNames || res.position?.isNull("attendee_name") != false) { - "" - } else { - res.position?.optString("attendee_name") - } - - var attendeeDOB: String? = null - if (!conf.hideNames) { - val qlen = res.position?.getJSONArray("answers")?.length() ?: 0 - for (i in 0 until qlen) { - val answ = res.position!!.getJSONArray("answers")!!.getJSONObject(i) - if (answ.getString("question_identifier") == "dob") { - attendeeDOB = answ.getString("answer") - } - } - } - - return eu.pretix.libpretixui.android.questions.showQuestionsDialog( - QuestionsDialog.Companion.QuestionsType.LINE_ITEM_QUESTIONS, - this, - questions, - values_, - null, - null, - { answers -> retryHandler(secret, sourceType, answers, ignore_unpaid) }, - null, - attendeeName, - attendeeDOB, - res.orderCodeAndPositionId(), - if (res.ticket != null) { - if (res.variation != null) { - res.ticket + " – " + res.variation - } else { - res.ticket - } - } else { - null - }, - !conf.useCamera, - isResumed + super.handleScan( + raw_result, + lastScanSourceType.serverName!!, + answers, + ignore_unpaid, + exchange_medium_type, + exchange_medium_identifier ) } - fun displayScanResult(result: TicketCheckProvider.CheckResult, answers: MutableList?, ignore_unpaid: Boolean = false) { - lastScanResult = result - lastIgnoreUnpaid = ignore_unpaid - + override fun displayScanResult(result: TicketCheckProvider.CheckResult, answers: MutableList?, ignore_unpaid: Boolean) { if (conf.sounds) when (result.type) { TicketCheckProvider.CheckResult.Type.VALID -> when (result.scanType) { @@ -1467,77 +896,9 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result return } - val s = rawResult.text - if (s == lastScanCode && System.currentTimeMillis() - lastScanTime < 5000) { - return - } - lastScanTime = System.currentTimeMillis() - lastScanCode = s - lastScanSourceType = ReusableMediaType.BARCODE - lastScanResult = null - lastIgnoreUnpaid = false - handleScan( - s, - lastScanSourceType.serverName!!, - null, - !conf.unpaidAsk - ) + super.handleResult(rawResult) } - override fun dispatchKeyEvent(event: KeyEvent): Boolean { - if ((event.action != KeyEvent.ACTION_DOWN && event.action != KeyEvent.ACTION_MULTIPLE) || (currentFocus is TextView && currentFocus !is AppCompatButton)) { - return super.dispatchKeyEvent(event) - } - return when (event.keyCode) { - KeyEvent.KEYCODE_ENTER -> { - if (keyboardBuffer.isEmpty()) { - false - } - lastScanTime = System.currentTimeMillis() - lastScanCode = keyboardBuffer - lastScanSourceType = ReusableMediaType.BARCODE - lastScanResult = null - lastIgnoreUnpaid = false - handleScan( - keyboardBuffer, - lastScanSourceType.serverName!!, - null, - !conf.unpaidAsk - ) - keyboardBuffer = "" - true - } - KeyEvent.KEYCODE_UNKNOWN -> { - keyboardBuffer += event.characters - true - } - else -> { - val codepoint = event.keyCharacterMap.get(event.keyCode, event.metaState) - if (codepoint > 0) { - keyboardBuffer += codepoint.toChar().toString().repeat(max(event.repeatCount, 1)) - true - } else { - super.dispatchKeyEvent(event) - } - } - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQ_EVENT) { - if (resultCode == RESULT_OK) { - setupApi() - scheduleSync() - reload() - } - } else if (dialog?.handleActivityResult(requestCode, resultCode, data) == true) { - return - } else { - super.onActivityResult(requestCode, resultCode, data) - } - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { // Inflate the menu to use in the action bar val inflater = menuInflater @@ -1581,26 +942,6 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result return super.onCreateOptionsMenu(menu) } - fun pinProtect(key: String, valid: ((pin: String) -> Unit)) { - if (!conf.requiresPin(key)) { - valid("") - return - } - pendingPinAction = valid - PinDialog().show(supportFragmentManager) - } - - fun startWithPIN(intent: Intent, key: String, resultCode: Int? = null, bundle: Bundle? = null) { - pinProtect(key) { pin -> - intent.putExtra("pin", pin) - if (resultCode != null) { - startActivityForResult(intent, resultCode, bundle) - } else { - startActivity(intent) - } - } - } - override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.action_settings -> { @@ -1632,28 +973,6 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result return super.onOptionsItemSelected(item) } - - fun getRestrictions(ctx: Context) { - val myRestrictionsMgr = ctx.getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager? - ?: return - val restrictions = myRestrictionsMgr.applicationRestrictions - val prefs = PreferenceManager.getDefaultSharedPreferences(ctx) - - for (key in restrictions.keySet()) { - if (key == "pref_auto_print_badges_option") { - prefs.edit().putString(key, restrictions.getString(key)).apply() - } else { - prefs.edit().putBoolean(key, restrictions.getBoolean(key)).apply() - } - } - } - - override fun onConnectivityChanged() { - runOnUiThread { - reload() - } - } - override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) @@ -1742,108 +1061,4 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result outState.putString("result", om.writeValueAsString(lastScanResult)) } } - - override fun chipReadSuccessfully(identifier: String, mediaType: ReusableMediaType) { - // delegate to the dialog if it is active - if (dialog?.isShowing() == true && dialog is NfcDialogInterface) { - (dialog as NfcDialogInterface).chipReadSuccessfully(identifier, mediaType) - return - } - - if (identifier.startsWith("08")) { - runOnUiThread { - showLoadingCard() - displayScanResult( - TicketCheckProvider.CheckResult(TicketCheckProvider.CheckResult.Type.ERROR, getString(R.string.nfc_random_uid), false), - null - ) - } - return - } - - lastScanTime = System.currentTimeMillis() - lastScanCode = identifier - lastScanSourceType = mediaType - lastScanResult = null - lastIgnoreUnpaid = false - - handleScan( - identifier, - mediaType.serverName!!, - null, - !conf.unpaidAsk - ) - } - - override fun chipReadError(error: ChipReadError, identifier: String?) { - // delegate to the dialog if it is active - if (dialog?.isShowing() == true && dialog is NfcDialogInterface) { - (dialog as NfcDialogInterface).chipReadError(error, identifier) - return - } - - val error = when (error) { - ChipReadError.IO_ERROR -> getString(R.string.nfc_read_error) - ChipReadError.UNKNOWN_CHIP_TYPE -> getString(R.string.nfc_unknown_chip_type) - ChipReadError.FOREIGN_CHIP -> getString(R.string.nfc_foreign_chip) - ChipReadError.EMPTY_CHIP -> getString(R.string.nfc_empty_chip) - else -> error.toString() - } - - runOnUiThread { - showLoadingCard() - displayScanResult( - TicketCheckProvider.CheckResult(TicketCheckProvider.CheckResult.Type.ERROR, error, false), - null - ) - } - } - - private fun reloadNfcHandler() { - if (conf.synchronizedEvents.isNotEmpty()) { - val eventSlug = conf.synchronizedEvents.first() - val settingsManager = SettingsManager(application) - val useRandomIdForNewTags = settingsManager.getBySlug(eventSlug)?.json?.optBoolean("reusable_media_type_nfc_mf0aes_random_uid", false) ?: false - val activeMediaTypes = getActiveMediaTypes( - settingsManager, - eventSlug - ) - if (!activeMediaTypes.any { it.isNfcBased() }) { - if (nfcHandler?.getState() == NfcHandler.NfcState.RUNNING) { - nfcHandler?.stop() - } - return - } - if (nfcHandler?.getState() != NfcHandler.NfcState.RUNNING || activeMediaTypes.toSet() != nfcHandler?.getMediaTypes()?.toSet()) { - nfcHandler?.stop() - val keySets = (this.applicationContext as PretixScan).db.mediumKeySetQueries.selectAll() - .executeAsList() - .map { it.toModel() } - .map { - Mf0aesKeySet( - it.publicId, - it.organizer == conf.organizerSlug && it.active, - conf.keyStore.decryptRsa( - "device", - eu.pretix.libpretixsync.utils.codec.binary.Base64.decodeBase64(it.uidKey.toByteArray(Charset.defaultCharset())) - ), - conf.keyStore.decryptRsa( - "device", - eu.pretix.libpretixsync.utils.codec.binary.Base64.decodeBase64(it.diversificationKey.toByteArray(Charset.defaultCharset())) - ), - ) - } - - nfcHandler = getNfcHandler(this, keySets, useRandomIdForNewTags, nfcReaderType = conf.nfcReaderType) - nfcHandler!!.setOnChipReadListener(this) - try { - nfcHandler!!.start(activeMediaTypes) - } catch (_: NfcUnsupported) { - // silently ignore, else users on non-nfc-devices are warned all the time - } catch (_: NfcDisabled) { - // silently ignore, we may want to show an unobtrusive warning in the future - } - } - } - } } diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/Settings.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/Settings.kt index d542ade1..2f84131b 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/Settings.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/Settings.kt @@ -32,6 +32,7 @@ import eu.pretix.pretixscan.droid.AppConfig import eu.pretix.pretixscan.droid.BuildConfig import eu.pretix.pretixscan.droid.PretixScan import eu.pretix.pretixscan.droid.R +import eu.pretix.pretixscan.droid.hardware.KioskHardware import java.io.BufferedReader import java.io.IOException import java.io.InputStream @@ -184,6 +185,7 @@ class SettingsFragment : PreferenceFragmentCompat() { return@setOnPreferenceChangeListener true } + findPreference("gate_back_to_start_timeout")?.isVisible = KioskHardware.isTR51() || KioskHardware.isWA1053T() } private fun asset_dialog(@RawRes htmlRes: Int, @StringRes title: Int) { diff --git a/pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_print.xml b/pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_print.xml new file mode 100644 index 00000000..e8dff339 --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_print.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_scan.xml b/pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_scan.xml new file mode 100644 index 00000000..16eeb378 --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_scan.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_turn.xml b/pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_turn.xml new file mode 100644 index 00000000..b2cd99dc --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/avd_kiosk_portrait_kt0345_turn.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pretixscan/app/src/main/res/drawable/ic_block_24px.xml b/pretixscan/app/src/main/res/drawable/ic_block_24px.xml new file mode 100644 index 00000000..540750b3 --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/ic_block_24px.xml @@ -0,0 +1,11 @@ + + + diff --git a/pretixscan/app/src/main/res/drawable/ic_cell_tower_24px.xml b/pretixscan/app/src/main/res/drawable/ic_cell_tower_24px.xml new file mode 100644 index 00000000..bec3c49c --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/ic_cell_tower_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/pretixscan/app/src/main/res/drawable/ic_circle_24dp.xml b/pretixscan/app/src/main/res/drawable/ic_circle_24dp.xml new file mode 100644 index 00000000..cfa1d2e1 --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/ic_circle_24dp.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/pretixscan/app/src/main/res/drawable/ic_error_stop_24px.xml b/pretixscan/app/src/main/res/drawable/ic_error_stop_24px.xml new file mode 100644 index 00000000..e462b1dd --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/ic_error_stop_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/pretixscan/app/src/main/res/drawable/ic_ethernet_24px.xml b/pretixscan/app/src/main/res/drawable/ic_ethernet_24px.xml new file mode 100644 index 00000000..d0703365 --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/ic_ethernet_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/pretixscan/app/src/main/res/drawable/ic_wifi_24px.xml b/pretixscan/app/src/main/res/drawable/ic_wifi_24px.xml new file mode 100644 index 00000000..57ea38fa --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/ic_wifi_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/pretixscan/app/src/main/res/drawable/ic_wifi_off.xml b/pretixscan/app/src/main/res/drawable/ic_wifi_off.xml new file mode 100644 index 00000000..6a6df2ca --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/ic_wifi_off.xml @@ -0,0 +1,10 @@ + + + diff --git a/pretixscan/app/src/main/res/drawable/ic_wifi_unknown.xml b/pretixscan/app/src/main/res/drawable/ic_wifi_unknown.xml new file mode 100644 index 00000000..d5d250ab --- /dev/null +++ b/pretixscan/app/src/main/res/drawable/ic_wifi_unknown.xml @@ -0,0 +1,10 @@ + + + diff --git a/pretixscan/app/src/main/res/layout/activity_kiosk.xml b/pretixscan/app/src/main/res/layout/activity_kiosk.xml new file mode 100644 index 00000000..63d8fc45 --- /dev/null +++ b/pretixscan/app/src/main/res/layout/activity_kiosk.xml @@ -0,0 +1,427 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pretixscan/app/src/main/res/values-de/strings.xml b/pretixscan/app/src/main/res/values-de/strings.xml index c08a2bad..e51f25f1 100644 --- a/pretixscan/app/src/main/res/values-de/strings.xml +++ b/pretixscan/app/src/main/res/values-de/strings.xml @@ -189,4 +189,14 @@ Bitte einen NFC-Chip einlesen Es wird ein NFC-Chip auf UID-Basis benötigt Es wird ein NFC Mifare Ultralight AES-Chip benötigt + Ticketkontrolle hier nicht möglich + Bitte wenden Sie sich an das Einlasspersonal + Bitte scannen Sie Ihr Ticket + Barcode wird gelesen… + Ticket wird geprüft… + Bitte entnehmen Sie Ihr Badge + Bitte gehen Sie durch das Drehkreuz + Außer Betrieb + Außer Betrieb nehmen + Wieder in Betrieb nehmen diff --git a/pretixscan/app/src/main/res/values/color.xml b/pretixscan/app/src/main/res/values/color.xml index f6682f37..8ba9c441 100644 --- a/pretixscan/app/src/main/res/values/color.xml +++ b/pretixscan/app/src/main/res/values/color.xml @@ -20,4 +20,10 @@ #00000000 #99323232 #00000000 + #C699DC + #FF039BE5 + #FF01579B + #FF40C4FF + #FF00B0FF + #66000000 diff --git a/pretixscan/app/src/main/res/values/strings.xml b/pretixscan/app/src/main/res/values/strings.xml index 0f790e03..b38769d5 100644 --- a/pretixscan/app/src/main/res/values/strings.xml +++ b/pretixscan/app/src/main/res/values/strings.xml @@ -189,4 +189,14 @@ The scanned NFC chip is not supported by this version of pretixSCAN A UID-based NFC chip is needed A NFC Mifare Ultralight AES chip is needed + Please scan your ticket + Reading barcode… + Checking your ticket… + Please take your badge + Please proceed through the gate + Ticket cannot be validated here + Please reach out to our staff + Out of order + Take out of order + Re-enable operation diff --git a/pretixscan/app/src/main/res/values/styles.xml b/pretixscan/app/src/main/res/values/styles.xml index 2a08af3a..f1810da4 100644 --- a/pretixscan/app/src/main/res/values/styles.xml +++ b/pretixscan/app/src/main/res/values/styles.xml @@ -71,4 +71,15 @@ @color/white @color/white + + + + diff --git a/pretixscan/app/src/main/res/values/themes.xml b/pretixscan/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..36d0f7af --- /dev/null +++ b/pretixscan/app/src/main/res/values/themes.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/pretixscan/app/src/main/res/xml/preferences.xml b/pretixscan/app/src/main/res/xml/preferences.xml index 2b4256fa..22d8e59a 100644 --- a/pretixscan/app/src/main/res/xml/preferences.xml +++ b/pretixscan/app/src/main/res/xml/preferences.xml @@ -1,6 +1,6 @@ + xmlns:app="http://schemas.android.com/apk/res-auto"> + \ No newline at end of file diff --git a/pretixscan/libpretixnfc-repo b/pretixscan/libpretixnfc-repo index a102b4cb..9dac99ed 160000 --- a/pretixscan/libpretixnfc-repo +++ b/pretixscan/libpretixnfc-repo @@ -1 +1 @@ -Subproject commit a102b4cbd27032d9b2b21ac291104a64864c8601 +Subproject commit 9dac99edbf0717547addbd072b3d047ef12b5991 diff --git a/pretixscan/libpretixui-repo b/pretixscan/libpretixui-repo index 338de804..49459285 160000 --- a/pretixscan/libpretixui-repo +++ b/pretixscan/libpretixui-repo @@ -1 +1 @@ -Subproject commit 338de80431bc923e9844b970536b1efeabb0efed +Subproject commit 49459285f6c4e3690ebb947c6394d2abe1191d44 From ec544aa28f5852e04b3721145f6b0229b06409d8 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Fri, 8 May 2026 16:20:37 +0200 Subject: [PATCH 2/7] Remove additional, unused colors --- pretixscan/app/src/main/res/values/color.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pretixscan/app/src/main/res/values/color.xml b/pretixscan/app/src/main/res/values/color.xml index 8ba9c441..a1105d85 100644 --- a/pretixscan/app/src/main/res/values/color.xml +++ b/pretixscan/app/src/main/res/values/color.xml @@ -21,9 +21,5 @@ #99323232 #00000000 #C699DC - #FF039BE5 - #FF01579B - #FF40C4FF - #FF00B0FF #66000000 From c2643d2bb9556ef84ebef8ff8194fcae0d225f5d Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Fri, 8 May 2026 16:33:46 +0200 Subject: [PATCH 3/7] super.reload() in MainActivity, too. --- .../src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index c70f048a..40509337 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -128,7 +128,7 @@ class MainActivity : BaseScanActivity() { } override fun reload() { - reloadSyncStatus() + super.reload() var confdetails = "" if (conf.deviceKnownGateName.isNotBlank()) { From b684f3523d922bb351d4dfedafbd5472fb329a6c Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Fri, 8 May 2026 16:44:26 +0200 Subject: [PATCH 4/7] Move showLoadingCard() into BaseScanActivity --- .../java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt | 6 ++++-- .../main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt index 541a642d..94a0ed47 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt @@ -586,6 +586,8 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann abstract fun displayScanResult(result: TicketCheckProvider.CheckResult, answers: MutableList?, ignore_unpaid: Boolean = false) + open fun showLoadingCard() {} + fun showQuestionsDialog(res: TicketCheckProvider.CheckResult, secret: String, sourceType: ReusableMediaType, @@ -767,7 +769,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann override fun chipReadSuccessfully(identifier: String, mediaType: ReusableMediaType) { if (identifier.startsWith("08")) { runOnUiThread { - //showLoadingCard() + showLoadingCard() displayScanResult( TicketCheckProvider.CheckResult(TicketCheckProvider.CheckResult.Type.ERROR, getString(R.string.nfc_random_uid), false), null @@ -800,7 +802,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann } runOnUiThread { - //showLoadingCard() + showLoadingCard() displayScanResult( TicketCheckProvider.CheckResult(TicketCheckProvider.CheckResult.Type.ERROR, error, false), null diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index 40509337..e1291ba1 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -506,7 +506,7 @@ class MainActivity : BaseScanActivity() { LED(this).off() } - fun showLoadingCard() { + override fun showLoadingCard() { stopHidingTimer() binding.cardResult.clearAnimation() view_data.resultState.set(LOADING) From 3299e832183a5fca692a8e65bc804a7bb48ea614 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Fri, 8 May 2026 16:51:57 +0200 Subject: [PATCH 5/7] Add translatable Strings for Gate Opening Failed message; Do not crash if ktIO is not installed --- .../pretix/pretixscan/droid/ui/GateUtils.kt | 2 +- .../pretixscan/droid/ui/KioskActivity.kt | 52 +++++++++++-------- .../app/src/main/res/values-de/strings.xml | 1 + .../app/src/main/res/values/strings.xml | 1 + 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/GateUtils.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/GateUtils.kt index 7de23093..61fd47e9 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/GateUtils.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/GateUtils.kt @@ -19,7 +19,7 @@ fun openGate( intent.component = ComponentName("eu.pretix.ktIOservice", "eu.pretix.ktIOservice.GateService") } else { - throw Exception("error_print_no_app") + throw Exception("ktIO service not found") } if (recv != null) { intent.putExtra("resultreceiver", receiverForSending(recv)) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt index 33258847..165ef22a 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt @@ -489,31 +489,39 @@ class KioskActivity : BaseScanActivity() { } fun openGate() { - openGate(this, object : ResultReceiver(null) { - override fun onReceiveResult(resultCode: Int, resultData: Bundle?) { - super.onReceiveResult(resultCode, resultData) - gateTimeoutHandler.removeCallbacks(gateTimeout) - if (resultCode == 0) { - runOnUiThread { - backToStartHandler.postDelayed(backToStart, conf.timeAfterGateOpen.toLong()) - } - } else if (resultCode == 2) { - // Gate opened, but did not turn. - // TODO: Revert checkin? - runOnUiThread { - backToStartHandler.postDelayed(backToStart, conf.timeAfterGateOpen.toLong()) - } - } else { - // gate opening failed - runOnUiThread { - binding.tvOutOfOrderMessage.text = "Gate opening failed" - state = KioskState.OutOfOrder - updateUi() + try { + openGate(this, object : ResultReceiver(null) { + override fun onReceiveResult(resultCode: Int, resultData: Bundle?) { + super.onReceiveResult(resultCode, resultData) + gateTimeoutHandler.removeCallbacks(gateTimeout) + if (resultCode == 0) { + runOnUiThread { + backToStartHandler.postDelayed(backToStart, conf.timeAfterGateOpen.toLong()) + } + } else if (resultCode == 2) { + // Gate opened, but did not turn. + // TODO: Revert checkin? + runOnUiThread { + backToStartHandler.postDelayed(backToStart, conf.timeAfterGateOpen.toLong()) + } + } else { + // gate opening failed + runOnUiThread { + binding.tvOutOfOrderMessage.text = resources.getString(R.string.gate_opening_failed) + state = KioskState.OutOfOrder + updateUi() + } } } + }) + gateTimeoutHandler.postDelayed(gateTimeout, 30_000) + } catch (e: Exception) { + runOnUiThread { + binding.tvOutOfOrderMessage.text = e.localizedMessage + state = KioskState.OutOfOrder + updateUi() } - }) - gateTimeoutHandler.postDelayed(gateTimeout, 30_000) + } } diff --git a/pretixscan/app/src/main/res/values-de/strings.xml b/pretixscan/app/src/main/res/values-de/strings.xml index e51f25f1..0558ee27 100644 --- a/pretixscan/app/src/main/res/values-de/strings.xml +++ b/pretixscan/app/src/main/res/values-de/strings.xml @@ -199,4 +199,5 @@ Außer Betrieb Außer Betrieb nehmen Wieder in Betrieb nehmen + Öffnung der Drehsperre fehlgeschlagen diff --git a/pretixscan/app/src/main/res/values/strings.xml b/pretixscan/app/src/main/res/values/strings.xml index b38769d5..098b64a6 100644 --- a/pretixscan/app/src/main/res/values/strings.xml +++ b/pretixscan/app/src/main/res/values/strings.xml @@ -199,4 +199,5 @@ Out of order Take out of order Re-enable operation + Gate opening failed From ddb0aa5cc55b88e7b5aad241118d9f57635c3ebb Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Fri, 8 May 2026 17:03:57 +0200 Subject: [PATCH 6/7] Translate some more untranslatable strings --- .../eu/pretix/pretixscan/droid/ui/KioskActivity.kt | 11 ++++++----- pretixscan/app/src/main/res/values-de/strings.xml | 6 +++++- pretixscan/app/src/main/res/values/strings.xml | 6 +++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt index 165ef22a..74ed45b1 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt @@ -135,7 +135,7 @@ class KioskActivity : BaseScanActivity() { val printTimeout = Runnable { if (state == KioskState.Printing) { - binding.tvOutOfOrderMessage.text = "Printing failed by timeout" + binding.tvOutOfOrderMessage.text = resources.getString(R.string.kiosk_error_printing_failed_timeout) state = KioskState.OutOfOrder updateUi() } @@ -143,7 +143,7 @@ class KioskActivity : BaseScanActivity() { val gateTimeout = Runnable { if (state == KioskState.GateOpen) { - binding.tvOutOfOrderMessage.text = "Gate opening failed by timeout" + binding.tvOutOfOrderMessage.text = resources.getString(R.string.kiosk_error_gate_opening_failed_timeout) state = KioskState.OutOfOrder updateUi() } @@ -405,7 +405,7 @@ class KioskActivity : BaseScanActivity() { } else -> { - binding.tvOutOfOrderMessage.text = "Unknown Scan Result Type" + binding.tvOutOfOrderMessage.text = resources.getString(R.string.kiosk_error_unknown_scan_result_type) state = KioskState.OutOfOrder if (conf.sounds) { mediaPlayers[R.raw.error]?.start() @@ -463,7 +463,7 @@ class KioskActivity : BaseScanActivity() { } else { // printing failed runOnUiThread { - binding.tvOutOfOrderMessage.text = "Printing failed" + binding.tvOutOfOrderMessage.text = resources.getString(R.string.kiosk_error_printing_failed) state = KioskState.OutOfOrder updateUi() } @@ -507,7 +507,7 @@ class KioskActivity : BaseScanActivity() { } else { // gate opening failed runOnUiThread { - binding.tvOutOfOrderMessage.text = resources.getString(R.string.gate_opening_failed) + binding.tvOutOfOrderMessage.text = resources.getString(R.string.kiosk_error_gate_opening_failed) state = KioskState.OutOfOrder updateUi() } @@ -588,6 +588,7 @@ class KioskActivity : BaseScanActivity() { KioskState.OutOfOrder -> { binding.llOutOfOrder.visibility = View.VISIBLE + conf.kioskOutOfOrder = true led.error(blink = false) } } diff --git a/pretixscan/app/src/main/res/values-de/strings.xml b/pretixscan/app/src/main/res/values-de/strings.xml index 0558ee27..cfbb6383 100644 --- a/pretixscan/app/src/main/res/values-de/strings.xml +++ b/pretixscan/app/src/main/res/values-de/strings.xml @@ -199,5 +199,9 @@ Außer Betrieb Außer Betrieb nehmen Wieder in Betrieb nehmen - Öffnung der Drehsperre fehlgeschlagen + Öffnung der Drehsperre fehlgeschlagen + Timeout bei Öffnung der Drehsperre + Druck fehlgeschlagen + Drucker Timeout + Unbekannter Scanergebnis Typus diff --git a/pretixscan/app/src/main/res/values/strings.xml b/pretixscan/app/src/main/res/values/strings.xml index 098b64a6..82726525 100644 --- a/pretixscan/app/src/main/res/values/strings.xml +++ b/pretixscan/app/src/main/res/values/strings.xml @@ -199,5 +199,9 @@ Out of order Take out of order Re-enable operation - Gate opening failed + Gate opening failed + Gate opening failed by timeout + Printing failed + Printing failed by timeout + Unknown Scan Result Type From 50b27c054bb640ac71b2c273aba6b034cbec2689 Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Thu, 18 Jun 2026 20:42:17 +0200 Subject: [PATCH 7/7] Update TR51 detection to new brand/model --- .../java/eu/pretix/pretixscan/droid/hardware/KioskHardware.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/hardware/KioskHardware.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/hardware/KioskHardware.kt index f3d3a7f3..9436d8a1 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/hardware/KioskHardware.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/hardware/KioskHardware.kt @@ -5,7 +5,7 @@ import eu.pretix.pretixscan.droid.BuildConfig object KioskHardware { fun isTR51(): Boolean { - return Build.MODEL == "pretix TR51" + return Build.BRAND == "pretix" && Build.MODEL == "TR51" } fun isZebra(): Boolean {