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..9436d8a1
--- /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.BRAND == "pretix" && Build.MODEL == "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..94a0ed47
--- /dev/null
+++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt
@@ -0,0 +1,860 @@
+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)
+
+ open fun showLoadingCard() {}
+
+ 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..61fd47e9
--- /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("ktIO service not found")
+ }
+ 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..74ed45b1
--- /dev/null
+++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt
@@ -0,0 +1,824 @@
+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 = resources.getString(R.string.kiosk_error_printing_failed_timeout)
+ state = KioskState.OutOfOrder
+ updateUi()
+ }
+ }
+
+ val gateTimeout = Runnable {
+ if (state == KioskState.GateOpen) {
+ binding.tvOutOfOrderMessage.text = resources.getString(R.string.kiosk_error_gate_opening_failed_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 = resources.getString(R.string.kiosk_error_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 = resources.getString(R.string.kiosk_error_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() {
+ 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.kiosk_error_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()
+ }
+ }
+ }
+
+
+ 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
+ conf.kioskOutOfOrder = true
+ 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..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
@@ -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,68 +113,22 @@ 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()
+ super.reload()
var confdetails = ""
if (conf.deviceKnownGateName.isNotBlank()) {
@@ -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
@@ -930,7 +506,7 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ScannerView.Result
LED(this).off()
}
- fun showLoadingCard() {
+ override fun showLoadingCard() {
stopHidingTimer()
binding.cardResult.clearAnimation()
view_data.resultState.set(LOADING)
@@ -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..cfbb6383 100644
--- a/pretixscan/app/src/main/res/values-de/strings.xml
+++ b/pretixscan/app/src/main/res/values-de/strings.xml
@@ -189,4 +189,19 @@
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
+ Ö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/color.xml b/pretixscan/app/src/main/res/values/color.xml
index f6682f37..a1105d85 100644
--- a/pretixscan/app/src/main/res/values/color.xml
+++ b/pretixscan/app/src/main/res/values/color.xml
@@ -20,4 +20,6 @@
#00000000
#99323232
#00000000
+ #C699DC
+ #66000000
diff --git a/pretixscan/app/src/main/res/values/strings.xml b/pretixscan/app/src/main/res/values/strings.xml
index 0f790e03..82726525 100644
--- a/pretixscan/app/src/main/res/values/strings.xml
+++ b/pretixscan/app/src/main/res/values/strings.xml
@@ -189,4 +189,19 @@
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
+ Gate opening failed
+ Gate opening failed by timeout
+ Printing failed
+ Printing failed by timeout
+ Unknown Scan Result Type
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