diff --git a/mailbox-android/artwork/single_scan_left.svg b/mailbox-android/artwork/single_scan_left.svg new file mode 100644 index 0000000000000000000000000000000000000000..67a41171dcc275a1c24b44c28a611e84d5ee6e04 --- /dev/null +++ b/mailbox-android/artwork/single_scan_left.svg @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + id="svg947" + version="1.1" + viewBox="0 0 45.925076 33.828953" + height="33.828953mm" + width="45.925076mm"> + <defs + id="defs941" /> + <metadata + id="metadata944"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + transform="translate(-64.727926,-18.525999)" + id="layer1"> + <path + id="path4201" + d="m 105.13157,40.687883 -0.60567,-0.60567 -1.00006,-0.33804 c -0.54933,-0.18311 -1.22542,-0.42257 -1.50713,-0.52116 l -0.52116,-0.18311 0.49299,-0.0282 c 1.155,-0.0563 1.83109,-0.56341 2.0142,-1.5353 0.11269,-0.5775 0.15494,-2.43676 0.11269,-4.64816 -0.0282,-1.14091 -0.0282,-2.16914 0,-2.29591 0.0141,-0.12677 0.0704,-0.33805 0.12676,-0.4789 0.16903,-0.49299 0.0423,-1.67616 -0.26762,-2.47902 -0.0423,-0.126771 -0.26762,-0.59158 -0.49298,-1.042311 -0.59159,-1.155 -0.63384,-1.2536 -0.69018,-1.47896 -0.0704,-0.253539 -0.0282,-0.760609 0.0704,-0.957809 0.0986,-0.183101 0.30988,-0.408471 0.45073,-0.492981 0.18311,-0.0986 0.36622,0.0141 0.66201,0.40847 0.4789,0.633841 1.97194,2.732561 2.2114,3.12694 0.52115,0.84513 0.84511,1.577561 1.12682,2.648051 0.0986,0.35213 0.26762,0.98597 0.3803,1.42262 0.11269,0.43664 0.38031,1.4367 0.57751,2.22548 l 0.36621,1.4367 0.64793,0.73244 c 0.36621,0.40847 0.81695,0.91555 1.01414,1.12683 0.1972,0.22536 0.35214,0.42255 0.35214,0.45073 0,0.0422 -4.85945,4.127 -4.91578,4.127 0.0141,-0.0141 -0.26763,-0.29579 -0.60567,-0.61976 z m -13.226128,-2.30999 c -0.183108,-0.0845 -0.309878,-0.1972 -0.408475,-0.32396 -0.29579,-0.38031 -0.281707,0.33804 -0.26762,-9.64846 l 0.01408,-9.014611 0.0986,-0.16902 c 0.140856,-0.26762 0.281707,-0.40848 0.521155,-0.54933 l 0.225367,-0.12677 5.324258,-0.0141 c 5.986263,-0.0141 5.549623,-0.0282 5.929923,0.30988 0.12677,0.11268 0.25354,0.2817 0.30988,0.40847 0.0986,0.22537 0.0986,0.22537 0.11268,2.00012 l 0.0141,1.77475 -0.25354,-0.0141 c -0.19719,-0.0141 -0.29579,0 -0.45073,0.0704 -0.2817,0.140851 -0.54932,0.408471 -0.71835,0.718351 l -0.14086,0.28171 v -1.802931 -1.80292 h -4.732645 -4.732671 v 7.22578 7.225771 h 4.732671 4.732675 l 0.0141,-4.84535 c 0.0141,-4.648161 0.0141,-4.845361 0.0845,-4.63408 0.0422,0.11269 0.25353,0.563419 0.4789,0.98598 0.77469,1.49304 0.76061,1.39444 0.76061,6.64827 0,3.88755 -0.0141,4.2256 -0.23945,4.66225 -0.15494,0.30987 -0.38031,0.52115 -0.71836,0.66201 l -0.23945,0.0986 -5.084803,-0.0141 -5.098891,0.0141 -0.267621,-0.12676 z m 5.97218,-0.71835 c 0.408474,-0.18311 0.633841,-0.52116 0.619755,-0.92963 0,-0.5775 -0.436645,-1.01415 -1.000059,-1.01415 -0.295791,0 -0.507071,0.0845 -0.732436,0.30988 -0.309877,0.30987 -0.39439,0.76061 -0.18311,1.16908 0.0986,0.19719 0.352134,0.42256 0.563412,0.49299 0.21128,0.0704 0.549328,0.0704 0.732438,-0.0282 z" + style="stroke-width:0.1408533" /> + <g + id="g12" + transform="matrix(0.14085331,0,0,0.14085331,59.664122,-30.386687)"> + <path + d="m 256.5,410.2 h 2.3 v 2.3 h 2.3 v 2.3 h -11.5 v -2.3 h 2.3 v -4.6 h 4.6 z m 30,20.8 h 2.3 v -2.3 h -2.3 z m -27.7,-20.8 h 2.3 v -2.3 h -2.3 z m 32.3,20.8 h 2.3 v -2.3 h -2.3 z m -27.7,2.3 H 268 V 431 h -4.6 z m 20.8,0 V 431 h -2.3 v 2.3 z m -9.2,0 h 2.3 v -4.6 H 275 Z m -18.5,-30 v 2.3 h 6.9 v -2.3 z m -2.3,2.3 v -2.3 h -4.6 v 4.6 h 2.3 v -2.3 z m 6.9,-4.6 H 245 v -16.1 h 16.1 z m -2.3,-13.8 h -11.5 v 11.5 h 11.5 z m -9.2,41.5 h 6.9 v -6.9 h -6.9 z m 23,-2.4 v 2.3 h 2.3 v -2.3 z m -16.1,-36.8 h -6.9 v 6.9 h 6.9 z m 36.9,-4.6 V 401 h -16.1 v -16.1 z m -2.3,2.3 h -11.5 v 11.5 h 11.5 z M 245,417.1 h 16.1 v 16.1 H 245 Z m 2.3,13.9 h 11.5 v -11.5 h -11.5 z m 0,-27.7 H 245 v 11.5 h 2.3 z m 30,11.5 v 2.3 h 2.3 v -2.3 z m -7,9.3 v -2.3 H 268 v 2.3 h -4.6 v 4.6 h 4.6 v 2.3 h 2.3 v -4.6 h 2.3 v -2.3 z m -6.9,-30 h 4.6 v -2.3 h -4.6 z m 18.5,18.4 h 4.6 v 2.3 h 2.3 v -6.9 h -2.3 v -4.6 h -2.3 v 6.9 h -6.9 v 2.3 h 2.3 v 2.3 h 2.3 z m 2.3,6.9 h -2.3 v -2.3 h -2.3 v 4.6 h -6.9 v 2.3 h 4.6 v 4.6 h 2.3 v 2.3 h 2.3 v -4.6 h 9.2 V 424 h -6.9 z m 0,0 h 2.3 v -4.6 h -2.3 z m -18.5,0 v -2.3 h 2.3 v -2.3 h 2.3 v -2.3 h 2.3 v -4.6 h 6.9 v -4.6 h -2.3 v 2.3 H 275 v -9.2 h -2.3 v -4.6 h 2.3 v -6.9 h -2.3 v 4.6 h -2.3 v -4.6 h -6.9 v 4.6 h 2.3 v -2.3 h 2.3 v 4.6 h 2.3 v 6.9 h 2.3 v 2.3 h -2.3 v 4.6 H 268 V 401 h -2.3 v -2.3 h -2.3 v 4.6 h 2.3 v 2.3 h -2.3 v 6.9 h 2.3 v -4.6 h 2.3 v 4.6 h -2.3 v 2.3 h -2.3 v 6.9 h 4.6 v -2.3 z m 25.4,2.3 v -2.3 h -4.6 v 2.3 z m -2.3,-32.2 h -6.9 v 6.9 h 6.9 z m -18.5,29.9 h 4.6 v -2.3 h -2.3 v -2.3 h -2.3 z m 4.7,-4.6 v -2.3 h -2.3 v 2.3 z m 13.8,-9.2 h 4.6 v -2.3 h -4.6 z m 2.3,18.5 h 2.3 v -2.3 h -2.3 z m 0,-13.9 h 2.3 v -2.3 h -2.3 z M 270.3,398.7 H 268 v 2.3 h 2.3 z m 0,0" + id="path10" /> + </g> + <path + style="fill:#ffffff;stroke-width:0.11463147" + d="m 84.684129,41.768943 c 0.332434,-0.14902 0.515845,-0.42414 0.50438,-0.75657 0,-0.46999 -0.355357,-0.82535 -0.813884,-0.82535 -0.240726,0 -0.412673,0.0688 -0.596081,0.25219 -0.252189,0.25219 -0.320969,0.61901 -0.149023,0.95144 0.08024,0.16048 0.286579,0.3439 0.458527,0.40122 0.171946,0.0573 0.44706,0.0573 0.596081,-0.0229 z" + id="path4201-3" /> + <path + style="fill:none;fill-opacity:1;stroke:#faf800;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 81.570254,22.330762 11.180312,-1.85531 v 14.451551 h 9.465344 l -2e-5,-14.451551 h -9.465324 z" + id="path2320" /> + <path + style="fill:none;stroke:#e3f500;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 92.750566,34.927003 81.570254,22.330762 Z" + id="path2322" /> + <path + style="fill:none;stroke:#eaf600;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 81.570254,22.330762 20.645636,-1.85531 z" + id="path2324" /> + <path + style="fill:#ffff3f;fill-opacity:1;stroke:#def400;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 81.570254,22.330762 C 102.21591,34.927003 102.21591,34.927003 102.21591,34.927003 Z" + id="path2326" /> + <g + id="layer1-36" + transform="matrix(0.26458333,0,0,0.26458333,74.179616,-80.938799)"> + <g + id="g12-6" + transform="matrix(0.43325279,0,0,0.43325279,91.730541,226.47373)" /> + <path + id="path45" + d="M 6.3197128,399.9791 H 53.486093 v 72.01294 H 6.3899028 Z" + style="fill:#ffffff;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> + <path + style="stroke-width:0.11463147" + d="m 88.553764,28.771453 v -1.467281 h -3.851597 -3.85162 v 5.880591 5.8806 h 3.85162 3.851616 l 0.01146,-3.94332 m 1.253099,6.156561 c -0.126095,0.252189 -0.335965,0.715199 -0.955061,0.61815 l -0.194871,0.0802 -4.138197,-0.0115 -4.149659,0.0115 -0.217803,-0.10317 -2.3e-5,1.1e-5 c -0.149019,-0.0688 -0.252191,-0.160481 -0.332431,-0.263651 -0.240728,-0.3095 -0.229264,0.27512 -0.217799,-7.85225 l 0.01146,-7.336421 0.08024,-0.13756 c 0.114631,-0.2178 0.22926,-0.33243 0.424135,-0.447059 l 0.183413,-0.10317 4.333068,-0.0115 c 4.871836,-0.0115 4.516479,-0.0229 4.825986,0.25219 0.103167,0.0917 0.206333,0.22926 0.252188,0.33243 0.08024,0.18341 0.08024,0.18341 0.0917,1.62777" + id="path54" /> + <path + d="m 83.326564,32.932583 h 0.263652 v 0.26365 h 0.263652 v 0.26365 h -1.318263 v -0.26365 h 0.263653 v -0.5273 h 0.527306 z m 3.438944,2.38433 h 0.263652 v -0.26365 h -0.263652 z m -3.175292,-2.38433 h 0.263652 v -0.26365 h -0.263652 z m 3.702596,2.38433 h 0.263653 v -0.26365 h -0.263653 z m -3.175291,0.26366 h 0.527304 v -0.26366 h -0.527304 z m 2.384334,0 v -0.26366 h -0.263652 v 0.26366 z m -1.05461,0 h 0.263652 v -0.52731 h -0.263652 z m -2.120681,-3.43895 v 0.26365 h 0.790957 v -0.26365 z m -0.263654,0.26365 v -0.26365 h -0.527305 v 0.52731 h 0.263653 v -0.26366 z m 0.790958,-0.5273 h -1.845567 v -1.84557 h 1.845567 z m -0.263652,-1.58192 h -1.318263 v 1.31827 h 1.318263 z m -1.054611,4.75721 h 0.790959 v -0.79096 h -0.790959 z m 2.636525,-0.27511 v 0.26365 h 0.263653 v -0.26365 z m -1.845566,-4.21844 h -0.790959 v 0.79095 h 0.790959 z m 4.229901,-0.52731 v 1.84557 h -1.845568 v -1.84557 z m -0.263653,0.26365 h -1.318261 v 1.31827 h 1.318261 z m -5.284511,3.42749 h 1.845567 v 1.84556 h -1.845567 z m 0.263652,1.59337 h 1.318263 v -1.31826 h -1.318263 z m 0,-3.17529 h -0.263652 v 1.31826 h 0.263652 z m 3.438944,1.31826 v 0.26366 h 0.263654 v -0.26366 z m -0.802419,1.06608 v -0.26366 h -0.263653 v 0.26366 h -0.527304 v 0.5273 h 0.527304 v 0.26365 h 0.263653 v -0.5273 h 0.263652 v -0.26365 z m -0.790957,-3.43895 h 0.527304 v -0.26365 h -0.527304 z m 2.120682,2.10922 h 0.527305 v 0.26365 h 0.263652 v -0.79095 h -0.263652 v -0.52731 h -0.263653 v 0.79096 h -0.790958 v 0.26365 h 0.263654 v 0.26365 h 0.263652 z m 0.263652,0.79096 h -0.263652 v -0.26365 h -0.263652 v 0.5273 h -0.790958 v 0.26365 h 0.527304 v 0.52731 h 0.263654 v 0.26365 h 0.263652 v -0.5273 h 1.054609 v -0.26366 h -0.790957 z m 0,0 h 0.263653 v -0.52731 h -0.263653 z m -2.120682,0 v -0.26365 h 0.263652 v -0.26366 h 0.263653 v -0.26365 h 0.263652 v -0.5273 h 0.790957 v -0.52731 h -0.263652 v 0.26365 h -0.25219 v -1.05461 h -0.263652 v -0.5273 h 0.263652 v -0.79096 h -0.263652 v 0.52731 H 84.91994 v -0.52731 h -0.790956 v 0.52731 h 0.263652 v -0.26366 h 0.263652 v 0.52731 h 0.263652 v 0.79096 h 0.263653 v 0.26365 H 84.91994 v 0.5273 h -0.275115 v -0.5273 h -0.263652 v -0.26365 h -0.263652 v 0.5273 h 0.263652 v 0.26365 h -0.263652 v 0.79096 h 0.263652 v -0.5273 h 0.263652 v 0.5273 h -0.263652 v 0.26365 h -0.263652 v 0.79096 h 0.527304 v -0.26365 z m 2.911639,0.26365 v -0.26365 h -0.527304 v 0.26365 z m -0.263652,-3.69113 h -0.790957 v 0.79095 h 0.790957 z m -2.120682,3.42748 h 0.527305 v -0.26365 H 85.17213 v -0.26366 h -0.263652 z m 0.538767,-0.52731 v -0.26365 h -0.263652 v 0.26365 z m 1.581915,-1.05461 h 0.527305 v -0.26365 H 87.02916 Z m 0.263652,2.12069 h 0.263653 v -0.26366 h -0.263653 z m 0,-1.59338 h 0.263653 v -0.26365 h -0.263653 z m -2.384334,-1.31826 h -0.263653 v 0.26365 h 0.263653 z m 0,0" + id="path10-7" + style="stroke-width:0.11463147" /> + <path + id="path1142" + d="m 72.007595,51.537803 0.798536,-0.798539 1.318511,-0.44569 c 0.724254,-0.241419 1.615642,-0.55712 1.987054,-0.68711 l 0.687112,-0.241419 -0.649971,-0.0371 c -1.522789,-0.0743 -2.414177,-0.74282 -2.655595,-2.024192 -0.148564,-0.7614 -0.204276,-3.212719 -0.148564,-6.1283 0.03714,-1.50422 0.03714,-2.85987 0,-3.027 -0.01857,-0.16714 -0.09285,-0.4457 -0.167136,-0.6314 -0.222847,-0.64998 -0.05571,-2.2099 0.352842,-3.26843 0.05571,-0.16713 0.352841,-0.77996 0.64997,-1.37422 0.779965,-1.52279 0.835677,-1.65279 0.909959,-1.94991 0.09285,-0.33427 0.03714,-1.00282 -0.09285,-1.2628 -0.129994,-0.24142 -0.408553,-0.53855 -0.594259,-0.64997 -0.241417,-0.13 -0.482835,0.0186 -0.872817,0.53854 -0.631401,0.83568 -2.599883,3.6027 -2.915584,4.12267 -0.687112,1.11424 -1.114235,2.07991 -1.485647,3.49127 -0.129994,0.46427 -0.352841,1.29995 -0.501406,1.87563 -0.148565,0.57569 -0.501406,1.8942 -0.761394,2.93416 l -0.482836,1.894201 -0.854247,0.965669 c -0.482835,0.53854 -1.077094,1.207081 -1.337083,1.48565 -0.259988,0.278551 -0.464264,0.55711 -0.464264,0.594249 0,0.0557 6.406854,5.441191 6.481137,5.441191 -0.01857,-0.0186 0.371412,-0.389988 0.798535,-0.81711 z" + style="stroke-width:0.1857059" /> + <path + id="path1140" + d="m 89.44538,48.492224 c 0.241418,-0.111421 0.408553,-0.259979 0.538547,-0.427119 0.389983,-0.501402 0.371412,0.445698 0.352842,-12.720852 l -0.01857,-11.885181 -0.129994,-0.22285 c -0.185706,-0.35284 -0.371411,-0.53854 -0.687111,-0.72425 l -0.29713,-0.16714 -7.019683,-0.0186 c -7.892502,-0.0186 -7.316813,-0.0371 -7.818219,0.40855 -0.167136,0.14857 -0.334271,0.37141 -0.408553,0.53855 -0.129995,0.29713 -0.129995,0.29713 -0.148565,2.63702 l -0.01857,2.339901 0.334271,-0.0186 c 0.259988,-0.0186 0.389982,0 0.594258,0.0929 0.371412,0.1857 0.724253,0.538541 0.9471,0.947101 l 0.185706,0.371409 v -2.377041 -2.37703 h 6.239721 6.239719 v 9.526711 9.52671 H 82.11 75.870281 l -0.01857,-6.38828 c -0.01857,-6.1283 -0.01857,-6.38828 -0.111424,-6.10973 -0.05571,0.14857 -0.33427,0.74283 -0.6314,1.29995 -1.021382,1.96848 -1.002812,1.83848 -1.002812,8.76531 0,5.12549 0.01857,5.571181 0.315701,6.146869 0.204276,0.408551 0.501405,0.687113 0.947099,0.872821 l 0.3157,0.12999 6.722554,0.0186 6.722555,0.0186 0.3157,-0.204279 z" + style="fill:#800000;stroke-width:0.1857059" /> + <path + id="path4201-1" + d="m 81.571449,47.545124 c -0.538547,-0.241411 -0.835676,-0.687109 -0.817106,-1.22565 0,-0.7614 0.575688,-1.33709 1.318512,-1.33709 0.389983,0 0.668541,0.111429 0.965671,0.40855 0.408554,0.408559 0.519977,1.002819 0.241417,1.54136 -0.129994,0.25999 -0.464263,0.55712 -0.742823,0.64997 -0.278559,0.0929 -0.705683,0.0929 -0.965671,-0.0371 z" + style="fill:#ffffff;stroke-width:0.1857059" /> + <path + id="path5402" + d="m 92.750563,20.475452 8e-6,14.451471 9.465329,8e-5 -1e-5,-14.451631 z" + style="opacity:0.5;fill:#ffff3f;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> +</svg> diff --git a/mailbox-android/build.gradle b/mailbox-android/build.gradle index 9484df25e818642306667c69928429b2e71a13f3..14959aada4aae49b5f60bff6ddd82e30f4a1701d 100644 --- a/mailbox-android/build.gradle +++ b/mailbox-android/build.gradle @@ -66,6 +66,7 @@ dependencies { androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.0.2" androidTestCompileOnly 'javax.annotation:jsr250-api:1.0' androidTestImplementation 'junit:junit:4.12' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' } def getStdout = { command, defaultValue -> diff --git a/mailbox-android/src/main/AndroidManifest.xml b/mailbox-android/src/main/AndroidManifest.xml index f0393787f38624b1e8d862fc5969e1cb5c77a8ef..21284bbb2758b241f49bb986c840163d3fd4e2c4 100644 --- a/mailbox-android/src/main/AndroidManifest.xml +++ b/mailbox-android/src/main/AndroidManifest.xml @@ -100,7 +100,7 @@ </activity> <activity - android:name=".android.keyagreement.MailboxExchangeActivity" + android:name=".android.keyagreement.KeyAgreementActivity" android:label="@string/pairing_title" android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:theme="@style/BriarTheme.NoActionBar"> diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/activity/ActivityComponent.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/activity/ActivityComponent.java index a3cb355f346d2ba3e5cbd030161659f8add74f18..852e2320354e447a8d1038acab98ef6e22dfcfe3 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/activity/ActivityComponent.java +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/activity/ActivityComponent.java @@ -7,11 +7,11 @@ import org.briarproject.mailbox.android.StartupFailureActivity; import org.briarproject.mailbox.android.fragment.ScreenFilterDialogFragment; import org.briarproject.mailbox.android.keyagreement.ContactExchangeErrorFragment; import org.briarproject.mailbox.android.keyagreement.KeyAgreementActivity; -import org.briarproject.mailbox.android.keyagreement.KeyAgreementFragment; -import org.briarproject.mailbox.android.keyagreement.MailboxExchangeActivity; -import org.briarproject.mailbox.android.login.IntroFragment; +import org.briarproject.mailbox.android.keyagreement.ScanQrCodeFragment; +import org.briarproject.mailbox.android.keyagreement.ShowQrCodeFragment; import org.briarproject.mailbox.android.login.ChangePasswordActivity; import org.briarproject.mailbox.android.login.DozeFragment; +import org.briarproject.mailbox.android.login.IntroFragment; import org.briarproject.mailbox.android.login.OpenDatabaseActivity; import org.briarproject.mailbox.android.login.PasswordActivity; import org.briarproject.mailbox.android.login.PasswordFragment; @@ -51,8 +51,6 @@ public interface ActivityComponent { void inject(KeyAgreementActivity activity); - void inject(MailboxExchangeActivity activity); - void inject(SettingsActivity activity); void inject(ChangePasswordActivity activity); @@ -71,8 +69,6 @@ public interface ActivityComponent { void inject( org.briarproject.mailbox.android.keyagreement.IntroFragment fragment); - void inject(KeyAgreementFragment fragment); - void inject(SettingsFragment fragment); void inject(ScreenFilterDialogFragment fragment); @@ -80,4 +76,8 @@ public interface ActivityComponent { void inject(ContactExchangeErrorFragment fragment); void inject(OverviewFragment overviewFragment); + + void inject(ScanQrCodeFragment scanQrCodeFragment); + + void inject(ShowQrCodeFragment showQrCodeFragment); } diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/activity/ActivityModule.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/activity/ActivityModule.java index 8dde48a5ce724dcae81fb139a94b67c3c7dfa9d7..59451e78541978092f068a915b28aacf996510f4 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/activity/ActivityModule.java +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/activity/ActivityModule.java @@ -6,6 +6,8 @@ import org.briarproject.mailbox.android.controller.BriarController; import org.briarproject.mailbox.android.controller.BriarControllerImpl; import org.briarproject.mailbox.android.controller.DbController; import org.briarproject.mailbox.android.controller.DbControllerImpl; +import org.briarproject.mailbox.android.keyagreement.KeyAgreementController; +import org.briarproject.mailbox.android.keyagreement.KeyAgreementControllerImpl; import org.briarproject.mailbox.android.login.PasswordController; import org.briarproject.mailbox.android.login.PasswordControllerImpl; import org.briarproject.mailbox.android.login.SetupController; @@ -46,6 +48,13 @@ public class ActivityModule { return setupController; } + @ActivityScope + @Provides + KeyAgreementController provideKeyAgreementController( + KeyAgreementControllerImpl keyAgreementController) { + return keyAgreementController; + } + @ActivityScope @Provides PasswordController providePasswordController( diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementActivity.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementActivity.java index f0d36a9dc3b9c618684afb07de082f618429f596..cdf35210d02ffa7ef6bbe5cc8bf5f79ea490eefe 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementActivity.java +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementActivity.java @@ -17,7 +17,16 @@ import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.widget.Toast; +import org.briarproject.bramble.api.contact.ContactExchangeListener; +import org.briarproject.bramble.api.contact.ContactExchangeTask; +import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventBus; +import org.briarproject.bramble.api.event.EventListener; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.identity.IdentityManager; +import org.briarproject.bramble.api.keyagreement.event.KeyAgreementAbortedEvent; +import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFailedEvent; +import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent; @@ -29,9 +38,9 @@ import org.briarproject.mailbox.android.activity.BriarActivity; import org.briarproject.mailbox.android.fragment.BaseFragment; import org.briarproject.mailbox.android.fragment.BaseFragment.BaseFragmentListener; import org.briarproject.mailbox.android.keyagreement.IntroFragment.IntroScreenSeenListener; -import org.briarproject.mailbox.android.keyagreement.KeyAgreementFragment.KeyAgreementEventListener; import org.briarproject.mailbox.android.util.UiUtils; +import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.annotation.Nullable; @@ -50,25 +59,30 @@ import static org.briarproject.mailbox.android.activity.RequestCodes.REQUEST_PER @MethodsNotNullByDefault @ParametersNotNullByDefault -public abstract class KeyAgreementActivity extends BriarActivity implements - BaseFragmentListener, IntroScreenSeenListener, - KeyAgreementEventListener { - - private enum BluetoothState { - UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED - } +public class KeyAgreementActivity extends BriarActivity implements + BaseFragmentListener, IntroScreenSeenListener, EventListener, + KeyAgreementEventListener, ContactExchangeListener { private static final Logger LOG = Logger.getLogger(KeyAgreementActivity.class.getName()); - @Inject EventBus eventBus; + @Inject + @IoExecutor + Executor ioExecutor; + @Inject + volatile ContactExchangeTask contactExchangeTask; + @Inject + volatile IdentityManager identityManager; + @Inject + volatile KeyAgreementController keyAgreementController; private boolean isResumed = false, enableWasRequested = false; private boolean continueClicked, gotCameraPermission; private BluetoothState bluetoothState = BluetoothState.UNKNOWN; private BroadcastReceiver bluetoothReceiver = null; + @Override public void injectActivity(ActivityComponent component) { component.inject(this); @@ -78,7 +92,10 @@ public abstract class KeyAgreementActivity extends BriarActivity implements @Override public void onCreate(@Nullable Bundle state) { super.onCreate(state); + keyAgreementController.onStart(state != null); setContentView(R.layout.activity_fragment_container_toolbar); + // Disable screen timeout + findViewById(R.id.fragmentContainer).setKeepScreenOn(true); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); @@ -113,7 +130,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements isResumed = true; // Workaround for // https://code.google.com/p/android/issues/detail?id=190966 - if (canShowQrCodeFragment()) showQrCodeFragment(); + if (canShowQrCodeFragment()) showCameraFragment(); } private boolean canShowQrCodeFragment() { @@ -123,6 +140,20 @@ public abstract class KeyAgreementActivity extends BriarActivity implements && bluetoothState != BluetoothState.WAITING; } + @Override + public void onStart() { + super.onStart(); + eventBus.addListener(this); + // keyAgreementController.onStart(); + } + + @Override + protected void onStop() { + super.onStop(); + eventBus.removeListener(this); +// keyAgreementController.onStop(); + } + @Override protected void onPause() { super.onPause(); @@ -134,7 +165,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements continueClicked = true; if (checkPermissions()) { if (shouldRequestEnableBluetooth()) requestEnableBluetooth(); - else if (canShowQrCodeFragment()) showQrCodeFragment(); + else if (canShowQrCodeFragment()) showCameraFragment(); } } @@ -164,7 +195,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements eventBus.broadcast(new BluetoothEnabledEvent()); enableWasRequested = false; } - if (canShowQrCodeFragment()) showQrCodeFragment(); + if (canShowQrCodeFragment()) showCameraFragment(); } @Override @@ -174,12 +205,24 @@ public abstract class KeyAgreementActivity extends BriarActivity implements setBluetoothState(BluetoothState.REFUSED); } - private void showQrCodeFragment() { + private void showCameraFragment() { continueClicked = false; // FIXME #824 FragmentManager fm = getSupportFragmentManager(); - if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) { - BaseFragment f = KeyAgreementFragment.newInstance(); + if (fm.findFragmentByTag(ScanQrCodeFragment.TAG) == null) { + BaseFragment f = ScanQrCodeFragment.newInstance(); + fm.beginTransaction() + .replace(R.id.fragmentContainer, f, f.getUniqueTag()) + .addToBackStack(f.getUniqueTag()) + .commit(); + } + } + + public void showQrCodeFragment() { + // FIXME #824 + FragmentManager fm = getSupportFragmentManager(); + if (fm.findFragmentByTag(ShowQrCodeFragment.TAG) == null) { + BaseFragment f = ShowQrCodeFragment.newInstance(); fm.beginTransaction() .replace(R.id.fragmentContainer, f, f.getUniqueTag()) .addToBackStack(f.getUniqueTag()) @@ -255,6 +298,61 @@ public abstract class KeyAgreementActivity extends BriarActivity implements } } + @Override + public void contactExchangeSucceeded(Author remoteAuthor) { + runOnUiThreadUnlessDestroyed(() -> { + String contactName = remoteAuthor.getName(); + String format = getString(R.string.contact_added_toast); + String text = String.format(format, contactName); + Toast.makeText(KeyAgreementActivity.this, text, LENGTH_LONG) + .show(); + supportFinishAfterTransition(); + }); + } + + @Override + public void duplicateContact(Author remoteAuthor) { + runOnUiThreadUnlessDestroyed(() -> { + String contactName = remoteAuthor.getName(); + String format = getString(R.string.contact_already_exists); + String text = String.format(format, contactName); + Toast.makeText(KeyAgreementActivity.this, text, LENGTH_LONG) + .show(); + finish(); + }); + } + + @Override + public void finish() { + keyAgreementController.onStop(); + } + + @Override + public void contactExchangeFailed() { + showErrorExplanationFragmentAndReset(); + } + + @Override + public void eventOccurred(Event e) { + if (e instanceof KeyAgreementFailedEvent) { + showErrorExplanationFragmentAndReset(); + } else if (e instanceof KeyAgreementAbortedEvent) { + showErrorExplanationFragmentAndReset(); + } + } + + private void showErrorExplanationFragmentAndReset() { + runOnUiThreadUnlessDestroyed( + () -> { + keyAgreementController.reset(); + showErrorFragment(R.string.connection_error_explanation); + }); + } + + private enum BluetoothState { + UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED + } + private class BluetoothStateReceiver extends BroadcastReceiver { @Override diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementController.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementController.java new file mode 100644 index 0000000000000000000000000000000000000000..5efdf76bb1ca0bafa1fdee5145df25c208887789 --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementController.java @@ -0,0 +1,35 @@ +package org.briarproject.mailbox.android.keyagreement; + +import android.support.annotation.UiThread; + +import org.briarproject.bramble.api.contact.ContactExchangeListener; +import org.briarproject.bramble.api.keyagreement.KeyAgreementResult; +import org.briarproject.bramble.api.keyagreement.Payload; +import org.briarproject.mailbox.android.controller.handler.UiResultHandler; + +public interface KeyAgreementController { + /** + * Call this when your lifecycle starts, + * so the listener will be called when information changes. + * @param resuming + */ + @UiThread + void onStart(boolean resuming); + + /** + * Call this when your lifecycle stops, + * so that the controller knows it can stops listening to events. + */ + @UiThread + void onStop(); + + void getQrCode(UiResultHandler<Payload> resultHandler); + + void setRemoteQrCode(Payload remotePayload); + + void startContactExchange(KeyAgreementResult result, + ContactExchangeListener listener); + + void reset(); + +} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementControllerImpl.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementControllerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..a0f82bdca0ec7082327b54293c27bba049443f0f --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementControllerImpl.java @@ -0,0 +1,158 @@ +package org.briarproject.mailbox.android.keyagreement; + +import android.support.annotation.UiThread; + +import org.briarproject.bramble.api.contact.ContactExchangeListener; +import org.briarproject.bramble.api.contact.ContactExchangeTask; +import org.briarproject.bramble.api.db.DatabaseExecutor; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.event.EventBus; +import org.briarproject.bramble.api.event.EventListener; +import org.briarproject.bramble.api.identity.IdentityManager; +import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.keyagreement.KeyAgreementResult; +import org.briarproject.bramble.api.keyagreement.KeyAgreementTask; +import org.briarproject.bramble.api.keyagreement.Payload; +import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent; +import org.briarproject.bramble.api.lifecycle.IoExecutor; +import org.briarproject.mailbox.android.controller.handler.UiResultHandler; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import javax.inject.Inject; +import javax.inject.Provider; + +import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.api.contact.ContactType.MAILBOX_OWNER; +import static org.briarproject.bramble.api.contact.ContactType.PRIVATE_MAILBOX; +import static org.briarproject.bramble.util.LogUtils.logException; + +public class KeyAgreementControllerImpl implements KeyAgreementController, + EventListener { + + private static final Logger LOG = + Logger.getLogger(KeyAgreementControllerImpl.class.getName()); + + private final Executor dbExecutor, ioExecutor; + private final EventBus eventBus; + private final Provider<KeyAgreementTask> keyAgreementTaskProvider; + private volatile KeyAgreementTask task; + private volatile Payload localPayload; + private volatile CountDownLatch waitingForTask; + private IdentityManager identityManager; + private final ContactExchangeTask contactExchangeTask; + + @Inject + KeyAgreementControllerImpl(@DatabaseExecutor Executor dbExecutor, + @IoExecutor Executor ioExecutor, + EventBus eventBus, + Provider<KeyAgreementTask> keyAgreementTaskProvider, + IdentityManager identityManager, + ContactExchangeTask contactExchangeTask) { + this.dbExecutor = dbExecutor; + this.ioExecutor = ioExecutor; + this.eventBus = eventBus; + this.keyAgreementTaskProvider = keyAgreementTaskProvider; + this.identityManager = identityManager; + this.contactExchangeTask = contactExchangeTask; + } + + @Override + public void onStart(boolean resuming) { + eventBus.addListener(this); + if (!resuming) startListening(); + } + + @Override + public void onStop() { + eventBus.removeListener(this); + stopListening(); + } + + @Override + public void reset() { + localPayload = null; + startListening(); + } + + @UiThread + private void startListening() { + waitingForTask = new CountDownLatch(1); + KeyAgreementTask oldTask = task; + KeyAgreementTask newTask = keyAgreementTaskProvider.get(); + task = newTask; + ioExecutor.execute(() -> { + if (oldTask != null) oldTask.stopListening(); + newTask.listen(); + }); + } + + @UiThread + private void stopListening() { + KeyAgreementTask oldTask = task; + ioExecutor.execute(() -> { + if (oldTask != null) oldTask.stopListening(); + }); + } + + @Override + public synchronized void getQrCode(UiResultHandler<Payload> resultHandler) { + ioExecutor.execute(() -> { + try { + waitingForTask.await(); + } catch (InterruptedException e) { + //TODO: Log + return; + } + resultHandler.onResultUi(localPayload); + }); + } + + @Override + public void setRemoteQrCode(Payload remotePayload) { + ioExecutor.execute(() -> { + try { + waitingForTask.await(); + } catch (InterruptedException e) { + //TODO: Log + return; + } + task.connectAndRunProtocol(remotePayload); + }); + } + + @Override + public void startContactExchange(KeyAgreementResult result, + ContactExchangeListener listener) { + dbExecutor.execute(() -> { + LocalAuthor localAuthor; + // Load the local pseudonym + try { + localAuthor = identityManager.getLocalAuthor(); + } catch (DbException e) { + logException(LOG, WARNING, e); + listener.contactExchangeFailed(); + return; + } + + // Exchange contact details + contactExchangeTask.startExchange(listener, + localAuthor, result.getMasterKey(), + result.getConnection(), result.getTransportId(), + result.wasAlice(), MAILBOX_OWNER, PRIVATE_MAILBOX); + }); + } + + @Override + public void eventOccurred(Event e) { + if (e instanceof KeyAgreementListeningEvent) { + localPayload = + ((KeyAgreementListeningEvent) e).getLocalPayload(); + waitingForTask.countDown(); + } + + } +} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementEventListener.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementEventListener.java new file mode 100644 index 0000000000000000000000000000000000000000..5f02e12dd00fd0a910da90a43aa874d473fa1466 --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementEventListener.java @@ -0,0 +1,10 @@ +package org.briarproject.mailbox.android.keyagreement; + +import org.briarproject.bramble.api.contact.ContactExchangeListener; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +@NotNullByDefault +interface KeyAgreementEventListener extends ContactExchangeListener { + + void showQrCodeFragment(); +} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementFragment.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementFragment.java deleted file mode 100644 index 1603de2c29f3db4cf13e4203002b73bab860a368..0000000000000000000000000000000000000000 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/KeyAgreementFragment.java +++ /dev/null @@ -1,371 +0,0 @@ -package org.briarproject.mailbox.android.keyagreement; - -import android.content.Context; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.support.annotation.UiThread; -import android.util.DisplayMetrics; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.LinearLayout.LayoutParams; -import android.widget.TextView; -import android.widget.Toast; - -import com.google.zxing.Result; - -import org.briarproject.bramble.api.UnsupportedVersionException; -import org.briarproject.bramble.api.event.Event; -import org.briarproject.bramble.api.event.EventBus; -import org.briarproject.bramble.api.keyagreement.KeyAgreementResult; -import org.briarproject.bramble.api.keyagreement.KeyAgreementTask; -import org.briarproject.bramble.api.keyagreement.Payload; -import org.briarproject.bramble.api.keyagreement.PayloadEncoder; -import org.briarproject.bramble.api.keyagreement.PayloadParser; -import org.briarproject.bramble.api.keyagreement.event.KeyAgreementAbortedEvent; -import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFailedEvent; -import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent; -import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent; -import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent; -import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent; -import org.briarproject.bramble.api.lifecycle.IoExecutor; -import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.mailbox.R; -import org.briarproject.mailbox.android.activity.ActivityComponent; -import org.briarproject.mailbox.android.fragment.BaseEventFragment; -import org.briarproject.mailbox.android.view.QrCodeView; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.concurrent.Executor; -import java.util.logging.Logger; - -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Provider; - -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; -import static android.view.View.INVISIBLE; -import static android.view.View.VISIBLE; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.widget.LinearLayout.HORIZONTAL; -import static android.widget.Toast.LENGTH_LONG; -import static java.util.logging.Level.INFO; -import static java.util.logging.Level.WARNING; -import static org.briarproject.bramble.util.LogUtils.logException; - -@MethodsNotNullByDefault -@ParametersNotNullByDefault -public class KeyAgreementFragment extends BaseEventFragment - implements QrCodeDecoder.ResultCallback, QrCodeView.FullscreenListener { - - static final String TAG = KeyAgreementFragment.class.getName(); - - private static final Logger LOG = Logger.getLogger(TAG); - private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); - - @Inject - Provider<KeyAgreementTask> keyAgreementTaskProvider; - @Inject - PayloadEncoder payloadEncoder; - @Inject - PayloadParser payloadParser; - @Inject - @IoExecutor - Executor ioExecutor; - @Inject - EventBus eventBus; - - private CameraView cameraView; - private LinearLayout cameraOverlay; - private View statusView; - private QrCodeView qrCodeView; - private TextView status; - - private boolean gotRemotePayload; - private volatile boolean gotLocalPayload; - private KeyAgreementTask task; - private KeyAgreementEventListener listener; - - public static KeyAgreementFragment newInstance() { - Bundle args = new Bundle(); - KeyAgreementFragment fragment = new KeyAgreementFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - listener = (KeyAgreementEventListener) context; - } - - @Override - public void injectFragment(ActivityComponent component) { - component.inject(this); - } - - @Override - public String getUniqueTag() { - return TAG; - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_keyagreement_qr, container, - false); - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - cameraView = view.findViewById(R.id.camera_view); - cameraOverlay = view.findViewById(R.id.camera_overlay); - statusView = view.findViewById(R.id.status_container); - status = view.findViewById(R.id.connect_status); - qrCodeView = view.findViewById(R.id.qr_code_view); - qrCodeView.setFullscreenListener(this); - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - getActivity().setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR); - cameraView.setPreviewConsumer(new QrCodeDecoder(this)); - } - - @Override - public void onStart() { - super.onStart(); - try { - cameraView.start(); - } catch (CameraException e) { - logCameraExceptionAndFinish(e); - } - startListening(); - } - - @Override - public void setFullscreen(boolean fullscreen) { - LayoutParams statusParams, qrCodeParams; - if (fullscreen) { - // Grow the QR code view to fill its parent - statusParams = new LayoutParams(0, 0, 0f); - qrCodeParams = new LayoutParams(MATCH_PARENT, MATCH_PARENT, 1f); - } else { - // Shrink the QR code view to fill half its parent - if (cameraOverlay.getOrientation() == HORIZONTAL) { - statusParams = new LayoutParams(0, MATCH_PARENT, 1f); - qrCodeParams = new LayoutParams(0, MATCH_PARENT, 1f); - } else { - statusParams = new LayoutParams(MATCH_PARENT, 0, 1f); - qrCodeParams = new LayoutParams(MATCH_PARENT, 0, 1f); - } - } - statusView.setLayoutParams(statusParams); - qrCodeView.setLayoutParams(qrCodeParams); - cameraOverlay.invalidate(); - } - - @Override - public void onStop() { - super.onStop(); - stopListening(); - try { - cameraView.stop(); - } catch (CameraException e) { - logCameraExceptionAndFinish(e); - } - } - - @UiThread - private void logCameraExceptionAndFinish(CameraException e) { - logException(LOG, WARNING, e); - Toast.makeText(getActivity(), R.string.camera_error, - LENGTH_LONG).show(); - finish(); - } - - @UiThread - private void startListening() { - KeyAgreementTask oldTask = task; - KeyAgreementTask newTask = keyAgreementTaskProvider.get(); - task = newTask; - ioExecutor.execute(() -> { - if (oldTask != null) oldTask.stopListening(); - newTask.listen(); - }); - } - - @UiThread - private void stopListening() { - KeyAgreementTask oldTask = task; - ioExecutor.execute(() -> { - if (oldTask != null) oldTask.stopListening(); - }); - } - - @UiThread - private void reset() { - // If we've stopped the camera view, restart it - if (gotRemotePayload) { - try { - cameraView.start(); - } catch (CameraException e) { - logCameraExceptionAndFinish(e); - return; - } - } - statusView.setVisibility(INVISIBLE); - cameraView.setVisibility(VISIBLE); - gotRemotePayload = false; - gotLocalPayload = false; - startListening(); - } - - @UiThread - private void qrCodeScanned(String content) { - try { - byte[] payloadBytes = content.getBytes(ISO_8859_1); - if (LOG.isLoggable(INFO)) - LOG.info("Remote payload is " + payloadBytes.length + " bytes"); - Payload remotePayload = payloadParser.parse(payloadBytes); - gotRemotePayload = true; - cameraView.stop(); - cameraView.setVisibility(INVISIBLE); - statusView.setVisibility(VISIBLE); - status.setText(R.string.connecting_to_device); - task.connectAndRunProtocol(remotePayload); - } catch (UnsupportedVersionException e) { - reset(); - String msg = getString(R.string.qr_code_unsupported, - getString(R.string.app_name)); - showNextFragment(ContactExchangeErrorFragment.newInstance(msg)); - } catch (CameraException e) { - logCameraExceptionAndFinish(e); - } catch (IOException | IllegalArgumentException e) { - LOG.log(WARNING, "QR Code Invalid", e); - reset(); - Toast.makeText(getActivity(), R.string.qr_code_invalid, - LENGTH_LONG).show(); - } - } - - @Override - public void eventOccurred(Event e) { - if (e instanceof KeyAgreementListeningEvent) { - KeyAgreementListeningEvent event = (KeyAgreementListeningEvent) e; - gotLocalPayload = true; - setQrCode(event.getLocalPayload()); - } else if (e instanceof KeyAgreementFailedEvent) { - keyAgreementFailed(); - } else if (e instanceof KeyAgreementWaitingEvent) { - keyAgreementWaiting(); - } else if (e instanceof KeyAgreementStartedEvent) { - keyAgreementStarted(); - } else if (e instanceof KeyAgreementAbortedEvent) { - KeyAgreementAbortedEvent event = (KeyAgreementAbortedEvent) e; - keyAgreementAborted(event.didRemoteAbort()); - } else if (e instanceof KeyAgreementFinishedEvent) { - keyAgreementFinished(((KeyAgreementFinishedEvent) e).getResult()); - } - } - - private void keyAgreementFailed() { - runOnUiThreadUnlessDestroyed(() -> { - reset(); - listener.keyAgreementFailed(); - }); - } - - private void keyAgreementWaiting() { - runOnUiThreadUnlessDestroyed( - () -> status.setText(listener.keyAgreementWaiting())); - } - - private void keyAgreementStarted() { - runOnUiThreadUnlessDestroyed(() -> { - qrCodeView.setVisibility(INVISIBLE); - statusView.setVisibility(VISIBLE); - status.setText(listener.keyAgreementStarted()); - }); - } - - private void keyAgreementAborted(boolean remoteAborted) { - runOnUiThreadUnlessDestroyed(() -> { - reset(); - listener.keyAgreementAborted(remoteAborted); - }); - } - - private void keyAgreementFinished(KeyAgreementResult result) { - runOnUiThreadUnlessDestroyed(() -> { - statusView.setVisibility(VISIBLE); - status.setText(listener.keyAgreementFinished(result)); - }); - } - - private void setQrCode(Payload localPayload) { - Context context = getContext(); - if (context == null) return; - DisplayMetrics dm = context.getResources().getDisplayMetrics(); - ioExecutor.execute(() -> { - byte[] payloadBytes = payloadEncoder.encode(localPayload); - if (LOG.isLoggable(INFO)) { - LOG.info("Local payload is " + payloadBytes.length - + " bytes"); - } - // Use ISO 8859-1 to encode bytes directly as a string - String content = new String(payloadBytes, ISO_8859_1); - Bitmap qrCode = QrCodeUtils.createQrCode(dm, content); - runOnUiThreadUnlessDestroyed(() -> qrCodeView.setQrCode(qrCode)); - }); - } - - @Override - public void handleResult(Result result) { - runOnUiThreadUnlessDestroyed(() -> { - LOG.info("Got result from decoder"); - // Ignore results until the KeyAgreementTask is ready - if (!gotLocalPayload) return; - if (!gotRemotePayload) qrCodeScanned(result.getText()); - }); - } - - @Override - protected void finish() { - getActivity().getSupportFragmentManager().popBackStack(); - } - - @NotNullByDefault - interface KeyAgreementEventListener { - - @UiThread - void keyAgreementFailed(); - - // Should return a string to be displayed as status. - @UiThread - @Nullable - String keyAgreementWaiting(); - - // Should return a string to be displayed as status. - @UiThread - @Nullable - String keyAgreementStarted(); - - // Will show an error fragment. - @UiThread - void keyAgreementAborted(boolean remoteAborted); - - // Should return a string to be displayed as status. - @UiThread - @Nullable - String keyAgreementFinished(KeyAgreementResult result); - } -} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/MailboxExchangeActivity.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/MailboxExchangeActivity.java deleted file mode 100644 index add34227bdd5d9084d2bb6c19896359e7fb64c8e..0000000000000000000000000000000000000000 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/MailboxExchangeActivity.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.briarproject.mailbox.android.keyagreement; - -import android.os.Bundle; -import android.support.annotation.UiThread; -import android.widget.Toast; - -import org.briarproject.bramble.api.contact.ContactExchangeListener; -import org.briarproject.bramble.api.contact.ContactExchangeTask; -import org.briarproject.bramble.api.db.DbException; -import org.briarproject.bramble.api.identity.Author; -import org.briarproject.bramble.api.identity.IdentityManager; -import org.briarproject.bramble.api.identity.LocalAuthor; -import org.briarproject.bramble.api.keyagreement.KeyAgreementResult; -import org.briarproject.mailbox.R; -import org.briarproject.mailbox.android.activity.ActivityComponent; - -import java.util.logging.Logger; - -import javax.annotation.Nullable; -import javax.inject.Inject; - -import static android.widget.Toast.LENGTH_LONG; -import static java.util.logging.Level.WARNING; -import static org.briarproject.bramble.api.contact.ContactType.MAILBOX_OWNER; -import static org.briarproject.bramble.api.contact.ContactType.PRIVATE_MAILBOX; -import static org.briarproject.bramble.util.LogUtils.logException; - -public class MailboxExchangeActivity extends KeyAgreementActivity implements - ContactExchangeListener { - - private static final Logger LOG = - Logger.getLogger(MailboxExchangeActivity.class.getName()); - - // Fields that are accessed from background threads must be volatile - @Inject - volatile ContactExchangeTask contactExchangeTask; - @Inject - volatile IdentityManager identityManager; - - @Override - public void injectActivity(ActivityComponent component) { - component.inject(this); - } - - @Override - public void onCreate(@Nullable Bundle state) { - super.onCreate(state); - getSupportActionBar().setTitle(R.string.pairing_title); - } - - protected void startContactExchange(KeyAgreementResult result) { - runOnDbThread(() -> { - LocalAuthor localAuthor; - // Load the local pseudonym - try { - localAuthor = identityManager.getLocalAuthor(); - } catch (DbException e) { - logException(LOG, WARNING, e); - contactExchangeFailed(); - return; - } - - // Exchange contact details - contactExchangeTask.startExchange(MailboxExchangeActivity.this, - localAuthor, result.getMasterKey(), - result.getConnection(), result.getTransportId(), - result.wasAlice(), PRIVATE_MAILBOX, MAILBOX_OWNER); - }); - } - - @Override - public void contactExchangeSucceeded(Author remoteAuthor) { - runOnUiThreadUnlessDestroyed(() -> { - String contactName = remoteAuthor.getName(); - String format = getString(R.string.contact_added_toast); - String text = String.format(format, contactName); - Toast.makeText(MailboxExchangeActivity.this, text, LENGTH_LONG) - .show(); - supportFinishAfterTransition(); - }); - } - - @Override - public void duplicateContact(Author remoteAuthor) { - runOnUiThreadUnlessDestroyed(() -> { - String contactName = remoteAuthor.getName(); - String format = getString(R.string.contact_already_exists); - String text = String.format(format, contactName); - Toast.makeText(MailboxExchangeActivity.this, text, LENGTH_LONG) - .show(); - finish(); - }); - } - - @Override - public void contactExchangeFailed() { - runOnUiThreadUnlessDestroyed(() -> { - showErrorFragment(R.string.connection_error_explanation); - }); - } - - @UiThread - @Override - public void keyAgreementFailed() { - showErrorFragment(R.string.connection_error_explanation); - } - - @UiThread - @Override - public String keyAgreementWaiting() { - return getString(R.string.waiting_for_contact_to_scan); - } - - @UiThread - @Override - public String keyAgreementStarted() { - return getString(R.string.authenticating_with_device); - } - - @UiThread - @Override - public void keyAgreementAborted(boolean remoteAborted) { - showErrorFragment(R.string.connection_error_explanation); - } - - @UiThread - @Override - public String keyAgreementFinished(KeyAgreementResult result) { - startContactExchange(result); - return getString(R.string.exchanging_contact_details); - } -} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/ScanQrCodeFragment.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/ScanQrCodeFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..f89f5fdd4eae5217074bc471a41a08ade5195e27 --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/ScanQrCodeFragment.java @@ -0,0 +1,159 @@ +package org.briarproject.mailbox.android.keyagreement; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.UiThread; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.google.zxing.Result; + +import org.briarproject.bramble.api.UnsupportedVersionException; +import org.briarproject.bramble.api.keyagreement.Payload; +import org.briarproject.bramble.api.keyagreement.PayloadParser; +import org.briarproject.mailbox.R; +import org.briarproject.mailbox.android.activity.ActivityComponent; +import org.briarproject.mailbox.android.fragment.BaseFragment; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.logging.Logger; + +import javax.annotation.Nullable; +import javax.inject.Inject; + +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; +import static android.widget.Toast.LENGTH_LONG; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.util.LogUtils.logException; + +public class ScanQrCodeFragment extends BaseFragment + implements QrCodeDecoder.ResultCallback { + + static final String TAG = ScanQrCodeFragment.class.getName(); + + private static final Logger LOG = Logger.getLogger(TAG); + private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + + @Inject + PayloadParser payloadParser; + @Inject + KeyAgreementController keyAgreementController; + + private CameraView cameraView; + private KeyAgreementEventListener listener; + private volatile boolean gotRemotePayload; + + public static ScanQrCodeFragment newInstance() { + Bundle args = new Bundle(); + ScanQrCodeFragment fragment = new ScanQrCodeFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + listener = (KeyAgreementEventListener) context; + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + } + + @Override + public String getUniqueTag() { + return TAG; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_keyagreement_scan, container, + false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + cameraView = view.findViewById(R.id.camera_view); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + getActivity().setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR); + cameraView.setPreviewConsumer(new QrCodeDecoder(this)); + } + + @Override + public void onStart() { + super.onStart(); + try { + cameraView.start(); + } catch (CameraException e) { + logCameraExceptionAndFinish(e); + } + } + + @Override + public void onStop() { + super.onStop(); + try { + cameraView.stop(); + } catch (CameraException e) { + logCameraExceptionAndFinish(e); + } + } + + @UiThread + private void logCameraExceptionAndFinish(CameraException e) { + logException(LOG, WARNING, e); + Toast.makeText(getActivity(), R.string.camera_error, + LENGTH_LONG).show(); + finish(); + } + + @Override + public void handleResult(Result result) { + runOnUiThreadUnlessDestroyed(() -> { + LOG.info("Got result from decoder"); + if (!gotRemotePayload) qrCodeScanned(result.getText()); + }); + } + + @UiThread + private void qrCodeScanned(String content) { + try { + byte[] payloadBytes = content.getBytes(ISO_8859_1); + if (LOG.isLoggable(INFO)) + LOG.info("Remote payload is " + payloadBytes.length + " bytes"); + Payload remotePayload = payloadParser.parse(payloadBytes); + gotRemotePayload = true; + cameraView.stop(); + keyAgreementController.setRemoteQrCode(remotePayload); + listener.showQrCodeFragment(); + } catch (UnsupportedVersionException e) { + String msg = getString(R.string.qr_code_unsupported, + getString(R.string.app_name)); + showNextFragment(ContactExchangeErrorFragment.newInstance(msg)); + } catch (CameraException e) { + logCameraExceptionAndFinish(e); + } catch (IOException | IllegalArgumentException e) { + LOG.log(WARNING, "QR Code Invalid", e); + Toast.makeText(getActivity(), R.string.qr_code_invalid, + LENGTH_LONG).show(); + } + } + + @Override + protected void finish() { + getActivity().getSupportFragmentManager().popBackStack(); + } +} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/ShowQrCodeFragment.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/ShowQrCodeFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..1a96035bb0f5aa02fdfc608a97a9ae6dbd16d3a7 --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/keyagreement/ShowQrCodeFragment.java @@ -0,0 +1,180 @@ +package org.briarproject.mailbox.android.keyagreement; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.util.DisplayMetrics; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.LinearInterpolator; +import android.widget.ProgressBar; +import android.widget.TextView; + +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants; +import org.briarproject.bramble.api.keyagreement.Payload; +import org.briarproject.bramble.api.keyagreement.PayloadEncoder; +import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent; +import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent; +import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent; +import org.briarproject.bramble.api.lifecycle.IoExecutor; +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.mailbox.R; +import org.briarproject.mailbox.android.activity.ActivityComponent; +import org.briarproject.mailbox.android.controller.handler.UiResultHandler; +import org.briarproject.mailbox.android.fragment.BaseEventFragment; +import org.briarproject.mailbox.android.view.QrCodeView; + +import java.nio.charset.Charset; +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.inject.Inject; + +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; +import static java.util.logging.Level.INFO; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +public class ShowQrCodeFragment extends BaseEventFragment { + static final String TAG = ShowQrCodeFragment.class.getName(); + + private static final Logger LOG = Logger.getLogger(TAG); + private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + + @Inject + PayloadEncoder payloadEncoder; + @Inject + @IoExecutor + Executor ioExecutor; + @Inject + KeyAgreementController keyAgreementController; + + private QrCodeView qrCodeView; + private TextView status; + private ProgressBar progressBar; + private Animator animation; + private TextView instruction; + + private KeyAgreementEventListener listener; + + public static ShowQrCodeFragment newInstance() { + Bundle args = new Bundle(); + ShowQrCodeFragment fragment = new ShowQrCodeFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + listener = (KeyAgreementEventListener) context; + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + } + + @Override + public String getUniqueTag() { + return TAG; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_keyagreement_qr, container, + false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + qrCodeView = view.findViewById(R.id.qr_code_view); + status = view.findViewById(R.id.status); + instruction = view.findViewById(R.id.instruction); + progressBar = view.findViewById(R.id.timer); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + getActivity().setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR); + keyAgreementController.getQrCode(new UiResultHandler<Payload>(this) { + @Override + public void onResultUi(@Nonnull Payload payload) { + showQrCode(payload); + } + }); + } + + private void showQrCode(Payload payload) { + Context context = getContext(); + if (context == null) return; + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + ioExecutor.execute(() -> { + byte[] payloadBytes = payloadEncoder.encode(payload); + if (LOG.isLoggable(INFO)) { + LOG.info("Local payload is " + payloadBytes.length + + " bytes"); + } + // Use ISO 8859-1 to encode bytes directly as a string + String content = new String(payloadBytes, ISO_8859_1); + Bitmap qrCode = QrCodeUtils.createQrCode(dm, content); + runOnUiThreadUnlessDestroyed(() -> { + qrCodeView.setQrCode(qrCode); + startCountdown(); + }); + }); + } + + private void startCountdown() { + animation = ObjectAnimator.ofInt(progressBar, "progress", 100, 0); + animation.setDuration(KeyAgreementConstants.CONNECTION_TIMEOUT); + animation.setInterpolator(new LinearInterpolator()); + animation.start(); + } + + @Override + protected void finish() { + getActivity().getSupportFragmentManager().popBackStack(); + } + + @Override + public void eventOccurred(Event e) { + if (e instanceof KeyAgreementWaitingEvent) { + runOnUiThreadUnlessDestroyed(() -> { + if (animation != null) { + animation.cancel(); + animation.start(); + } + }); + } else if (e instanceof KeyAgreementStartedEvent) { + runOnUiThreadUnlessDestroyed(() -> { + animation.cancel(); + progressBar.setVisibility(INVISIBLE); + instruction.setVisibility(INVISIBLE); + qrCodeView.setVisibility(INVISIBLE); + status.setVisibility(VISIBLE); + status.setText(R.string.authenticating_with_device); + }); + } else if (e instanceof KeyAgreementFinishedEvent) { + runOnUiThreadUnlessDestroyed(() -> { + keyAgreementController.startContactExchange( + ((KeyAgreementFinishedEvent) e).getResult(), listener); + status.setText(R.string.exchanging_contact_details); + }); + } + } +} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/overview/OverviewFragment.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/overview/OverviewFragment.java index a0b8f2d73507b8af8a30fadd162aedf44a325ef0..e986e095528de0515260237b54869cb1e6ca0d86 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/overview/OverviewFragment.java +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/overview/OverviewFragment.java @@ -24,7 +24,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.mailbox.R; import org.briarproject.mailbox.android.activity.ActivityComponent; import org.briarproject.mailbox.android.fragment.BaseEventFragment; -import org.briarproject.mailbox.android.keyagreement.MailboxExchangeActivity; +import org.briarproject.mailbox.android.keyagreement.KeyAgreementActivity; import org.briarproject.mailbox.android.viewmodels.MailboxOwnerStatusViewModel; import java.util.ArrayList; @@ -155,7 +155,7 @@ public class OverviewFragment extends BaseEventFragment { switch (item.getItemId()) { case R.id.action_add_contact: Intent intent = - new Intent(getContext(), MailboxExchangeActivity.class); + new Intent(getContext(), KeyAgreementActivity.class); startActivity(intent); return true; default: diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/view/QrCodeView.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/view/QrCodeView.java index d6dbc7ee56852151afe9881e0da14cdc4b7210d3..9c7426a6b0fb24743141a158b1c9bcc255aa87b2 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/view/QrCodeView.java +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/view/QrCodeView.java @@ -15,49 +15,24 @@ import org.briarproject.mailbox.R; public class QrCodeView extends FrameLayout { - private final ImageView qrCodeImageView; - private boolean fullscreen = false; - private FullscreenListener listener; - - public QrCodeView(@NonNull Context context, - @Nullable AttributeSet attrs) { - super(context, attrs); - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(R.layout.qr_code_view, this, true); - qrCodeImageView = findViewById(R.id.qr_code); - ImageView fullscreenButton = findViewById(R.id.fullscreen_button); - fullscreenButton.setOnClickListener(v -> { - fullscreen = !fullscreen; - if (!fullscreen) { - fullscreenButton.setImageResource( - R.drawable.ic_fullscreen_black_48dp); - } else { - fullscreenButton.setImageResource( - R.drawable.ic_fullscreen_exit_black_48dp); - } - if (listener != null) - listener.setFullscreen(fullscreen); - } - ); - } - - @UiThread - public void setQrCode(Bitmap qrCode) { - qrCodeImageView.setImageBitmap(qrCode); - // Simple fade-in animation - AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f); - anim.setDuration(200); - qrCodeImageView.startAnimation(anim); - } - - @UiThread - public void setFullscreenListener(FullscreenListener listener) { - this.listener = listener; - } - - public interface FullscreenListener { - void setFullscreen(boolean fullscreen); - } + private final ImageView qrCodeImageView; + + public QrCodeView(@NonNull Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.qr_code_view, this, true); + qrCodeImageView = findViewById(R.id.qr_code); + } + + @UiThread + public void setQrCode(Bitmap qrCode) { + qrCodeImageView.setImageBitmap(qrCode); + // Simple fade-in animation + AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f); + anim.setDuration(200); + qrCodeImageView.startAnimation(anim); + } } diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/viewmodels/BaseViewModel.java b/mailbox-android/src/main/java/org/briarproject/mailbox/android/viewmodels/BaseViewModel.java index c2e3a1b15465ca06147037b57bac9abb61d90570..2f00729cbe7912603736ac3114fef2631fc49c3b 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/viewmodels/BaseViewModel.java +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/viewmodels/BaseViewModel.java @@ -9,9 +9,7 @@ import org.briarproject.mailbox.android.MailboxApplication; public abstract class BaseViewModel extends AndroidViewModel { - public BaseViewModel( - @NonNull - Application application) { + public BaseViewModel(@NonNull Application application) { super(application); AndroidComponent applicationComponent = ((MailboxApplication) getApplication()) @@ -22,7 +20,6 @@ public abstract class BaseViewModel extends AndroidViewModel { .androidComponent(applicationComponent) .viewModelModule(new ViewModelModule()) .build(); - injectViewModel(viewModelComponent); } diff --git a/mailbox-android/src/main/res/layout/fragment_keyagreement_qr.xml b/mailbox-android/src/main/res/layout/fragment_keyagreement_qr.xml index cc53978b5ec509d17d7d1e68afb2943267a43d33..449f5b905d5ab535aa492c997e3a85da684824bd 100644 --- a/mailbox-android/src/main/res/layout/fragment_keyagreement_qr.xml +++ b/mailbox-android/src/main/res/layout/fragment_keyagreement_qr.xml @@ -1,52 +1,61 @@ <?xml version="1.0" encoding="utf-8"?> -<FrameLayout +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> - <org.briarproject.mailbox.android.keyagreement.CameraView - android:id="@+id/camera_view" + + <org.briarproject.mailbox.android.view.QrCodeView + android:id="@+id/qr_code_view" + android:layout_width="match_parent" + android:layout_height="0dp" + android:background="@android:color/white" + app:layout_constraintBottom_toTopOf="@+id/instruction" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"/> + + + <TextView + android:id="@+id/instruction" android:layout_width="match_parent" - android:layout_height="match_parent"/> + android:layout_height="wrap_content" + android:background="@color/window_background" + android:gravity="center" + android:paddingBottom="@dimen/margin_large" + android:paddingTop="@dimen/margin_large" + android:text="Use your Briar phone to scan this Qr Code" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/qr_code_view"/> - <LinearLayout - android:id="@+id/camera_overlay" + <ProgressBar + android:id="@+id/timer" + style="@style/Widget.AppCompat.ProgressBar.Horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@android:color/transparent" + android:max="100" + android:progress="100" + android:rotation="180" + app:layout_constraintTop_toTopOf="@+id/instruction" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent"/> + + <TextView + android:id="@+id/status" android:layout_width="match_parent" android:layout_height="match_parent" - android:baselineAligned="false" - android:orientation="vertical"> - - <LinearLayout - android:id="@+id/status_container" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" - android:gravity="center" - android:orientation="vertical" - android:padding="@dimen/margin_medium" - android:visibility="invisible" - tools:visibility="visible"> - - <ProgressBar - style="?android:attr/progressBarStyleLarge" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - - <TextView - android:id="@+id/connect_status" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:paddingTop="@dimen/margin_large" - tools:text="Connection failed"/> - </LinearLayout> - - <org.briarproject.mailbox.android.view.QrCodeView - android:id="@+id/qr_code_view" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" - android:background="@android:color/white"/> - </LinearLayout> -</FrameLayout> + android:gravity="center" + android:padding="@dimen/margin_large" + android:text="Exchanging contact details" + android:visibility="invisible" + app:layout_constraintBottom_toTopOf="@+id/timer" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"/> + + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/mailbox-android/src/main/res/layout/fragment_keyagreement_scan.xml b/mailbox-android/src/main/res/layout/fragment_keyagreement_scan.xml new file mode 100644 index 0000000000000000000000000000000000000000..ffa6162ebe57e19461751d393c08cd3b7f467d9c --- /dev/null +++ b/mailbox-android/src/main/res/layout/fragment_keyagreement_scan.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.support.constraint.ConstraintLayout + android:id="@+id/frameLayout" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <org.briarproject.mailbox.android.keyagreement.CameraView + android:id="@+id/camera_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"/> + + <TextView + android:id="@+id/instruction" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@color/window_background" + android:gravity="center" + android:paddingBottom="@dimen/margin_large" + android:paddingTop="@dimen/margin_large" + android:text="Scan the QrCode on your Briar phone" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + tools:text="Scan the QrCode on your Briar phone"/> + + +</android.support.constraint.ConstraintLayout> diff --git a/mailbox-android/src/main/res/layout/qr_code_view.xml b/mailbox-android/src/main/res/layout/qr_code_view.xml index 9062428d0e6b3456b06b1e91a8e952cd3963e44d..03f3dde245d4923001836f43a6791b1e3e91fb44 100644 --- a/mailbox-android/src/main/res/layout/qr_code_view.xml +++ b/mailbox-android/src/main/res/layout/qr_code_view.xml @@ -9,7 +9,10 @@ style="?android:attr/progressBarStyleLarge" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center"/> + android:layout_gravity="center" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"/> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" @@ -20,20 +23,11 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@string/qr_code" - android:scaleType="fitCenter"/> - - <ImageView - android:id="@+id/fullscreen_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_margin="@dimen/margin_small" - android:alpha="0.54" - android:background="?selectableItemBackground" - android:contentDescription="@string/show_qr_code_fullscreen" - android:src="@drawable/ic_fullscreen_black_48dp" + android:scaleType="fitCenter" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintRight_toRightOf="parent"/> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"/> </android.support.constraint.ConstraintLayout> </merge>