mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Merge branch '2065-transfer-data-ui' into '1802-sync-via-removable-storage'
Implement UI of transfer data feature See merge request briar/briar!1486
This commit is contained in:
@@ -12,4 +12,6 @@ public interface FeatureFlags {
|
||||
boolean shouldEnableDisappearingMessages();
|
||||
|
||||
boolean shouldEnableConnectViaBluetooth();
|
||||
|
||||
boolean shouldEnableTransferData();
|
||||
}
|
||||
|
||||
@@ -63,8 +63,6 @@ abstract class RemovableDriveTaskImpl implements RemovableDriveTask {
|
||||
synchronized (lock) {
|
||||
observers.add(o);
|
||||
state = this.state;
|
||||
}
|
||||
if (state.isFinished()) {
|
||||
eventExecutor.execute(() -> o.accept(state));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ public class TestFeatureFlagModule {
|
||||
public boolean shouldEnableConnectViaBluetooth() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableTransferData() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
35
briar-android/artwork/transfer_data.svg
Normal file
35
briar-android/artwork/transfer_data.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="155" height="155"
|
||||
viewBox="0 0 155 158.05972" fill="none" version="1.1" id="svg24"
|
||||
sodipodi:docname="transfer_data.svg" inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
|
||||
<defs id="defs11" />
|
||||
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
|
||||
objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2" inkscape:window-width="1920" inkscape:window-height="982"
|
||||
id="namedview9" showgrid="false" inkscape:zoom="1.4523073" inkscape:cx="8.2820655"
|
||||
inkscape:cy="-28.56208" inkscape:window-x="1920" inkscape:window-y="72"
|
||||
inkscape:window-maximized="0" inkscape:current-layer="svg24" />
|
||||
<metadata id="metadata30">
|
||||
<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 id="g844" transform="translate(0,3.0301033)">
|
||||
<path id="path2" style="fill:#82c91e"
|
||||
d="m 34.888672,24.835938 c -0.929975,-0.02143 -1.778628,0.612487 -1.994141,1.554687 -0.2463,1.0768 0.427106,2.148231 1.503907,2.394531 l 5.339843,1.222656 c -6.856612,4.77068 -12.781629,10.788147 -17.445312,17.767579 -5.4771,8.1968 -9.072016,17.49215 -10.541016,27.21875 -0.217,1.4367 0.827383,2.738037 2.271485,2.898437 1.4442,0.1604 2.74109,-0.880406 2.96289,-2.316406 1.3729,-8.8885 4.672941,-17.381253 9.681641,-24.876953 4.280712,-6.40624 9.717062,-11.928555 16.003906,-16.316407 l -1.271484,5.558594 c -0.2463,1.0768 0.427106,2.150185 1.503906,2.396485 1.0767,0.2463 2.150184,-0.427207 2.396484,-1.503907 l 2.076172,-9.080078 c 0.167435,-0.732007 0.107609,-1.457562 -0.109375,-2.123047 -0.03323,-0.347577 -0.135561,-0.692647 -0.314453,-1.017578 -0.353538,-0.6421 -0.935043,-1.078378 -1.585937,-1.257812 -0.310286,-0.16723 -0.63775,-0.310162 -0.998047,-0.392578 l -9.078125,-2.076172 c -0.134588,-0.03079 -0.269491,-0.04772 -0.402344,-0.05078 z m 76.982418,2.876953 c -0.836,0.0548 -1.63328,0.526718 -2.08203,1.316406 -0.717,1.2635 -0.27106,2.863965 0.96094,3.634765 8.143,5.095901 14.99795,12.028813 20.00195,20.257813 4.93587,8.11686 7.91993,17.252829 8.7461,26.689453 l -4.25,-3.394531 c -0.863,-0.6893 -2.12055,-0.548547 -2.81055,0.314453 -0.689,0.8631 -0.54855,2.121147 0.31445,2.810547 l 7.27735,5.8125 c 1.726,1.3788 4.24209,1.097194 5.62109,-0.628906 l 5.8125,-7.277344 c 0.69,-0.863 0.54855,-2.121147 -0.31445,-2.810547 -0.863,-0.6894 -2.12155,-0.548647 -2.81055,0.314453 l -3.58984,4.494141 c -0.8855,-10.27436 -4.12841,-20.220841 -9.50196,-29.056641 -5.469,-8.9943 -12.97353,-16.564369 -21.89453,-22.105469 -0.46312,-0.287512 -0.97886,-0.40397 -1.48047,-0.371093 z M 45.191406,137.83008 c -0.851573,0.0281 -1.670687,0.44648 -2.164062,1.20898 -0.7895,1.219 -0.44326,2.85471 0.80664,3.59571 9.034,5.356 19.211344,8.51779 29.714844,9.21679 10.5035,0.699 21.011881,-1.08626 30.675782,-5.19726 1.337,-0.569 1.89734,-2.14303 1.27734,-3.45703 -0.621,-1.313 -2.18834,-1.86969 -3.52734,-1.30469 -8.852401,3.73 -18.466473,5.34798 -28.076172,4.70898 -9.609701,-0.64 -18.922925,-3.51767 -27.203126,-8.38867 -0.469612,-0.27637 -0.992962,-0.39966 -1.503906,-0.38281 z" />
|
||||
<path id="path8" style="fill:#8a9cb3"
|
||||
d="M 68.474609,0 C 66.724209,0 65.046294,0.6958487 63.808594,1.9335938 62.570894,3.1713338 61.875,4.8491794 61.875,6.5996094 V 46.199219 c 0,1.7504 0.695894,3.430267 1.933594,4.667969 1.2377,1.237698 2.915615,1.933593 4.666015,1.933593 h 19.800782 c 1.7504,0 3.428315,-0.695895 4.666015,-1.933593 1.2377,-1.237702 1.933594,-2.917569 1.933594,-4.667969 V 6.5996094 c 0,-1.75043 -0.695894,-3.4282757 -1.933594,-4.6660156 C 91.703706,0.6958487 90.025791,0 88.275391,0 Z m 0,3.3007812 h 19.800782 c 0.8752,0 1.715084,0.3459738 2.333984,0.9648438 0.6188,0.61887 0.964844,1.4587744 0.964844,2.3339844 V 46.199219 c 0,0.8752 -0.346044,1.715084 -0.964844,2.333984 C 89.990475,49.152003 89.150591,49.5 88.275391,49.5 H 68.474609 c -0.8752,0 -1.715084,-0.347997 -2.333984,-0.966797 -0.6188,-0.6189 -0.964844,-1.458784 -0.964844,-2.333984 V 6.5996094 c 0,-0.87521 0.346044,-1.7151144 0.964844,-2.3339844 0.6189,-0.61887 1.458784,-0.9648437 2.333984,-0.9648438 z M 11.25,97 0,108.40039 v 22.79883 C 0,133.28922 1.6875,135 3.75,135 h 22.5 c 2.0625,0 3.75,-1.71078 3.75,-3.80078 V 100.80078 C 30,98.710782 28.3125,97 26.25,97 Z M 131,100.59961 c -0.796,0 -1.55909,0.31591 -2.12109,0.87891 -0.563,0.562 -0.87891,1.32509 -0.87891,2.12109 v 13.5 c -0.796,0 -1.55909,0.31591 -2.12109,0.87891 -0.563,0.562 -0.87891,1.32509 -0.87891,2.12109 v 16.5 h 3 v -16.5 h 24 v 16.5 h 3 v -16.5 c 0,-0.796 -0.31591,-1.55909 -0.87891,-2.12109 -0.562,-0.563 -1.32509,-0.87891 -2.12109,-0.87891 v -13.5 c 0,-0.796 -0.31591,-1.55909 -0.87891,-2.12109 -0.562,-0.563 -1.32509,-0.87891 -2.12109,-0.87891 z M 12.806641,100.80078 H 26.25 v 30.39844 H 3.75 V 109.97656 Z M 131,103.59961 h 18 v 13.5 H 131 Z M 9.375,106.5 v 7.59961 h 3.75 V 106.5 Z m 5.625,0 v 7.59961 h 3.75 V 106.5 Z m 5.625,0 v 7.59961 h 3.75 V 106.5 Z" />
|
||||
</g>
|
||||
<path
|
||||
d="m 78.375,46.2 c 0.8752,0 1.7146,-0.3477 2.3335,-0.9666 0.6188,-0.6188 0.9665,-1.4582 0.9665,-2.3334 0,-0.8752 -0.3477,-1.7146 -0.9665,-2.3335 C 80.0896,39.9477 79.2502,39.6 78.375,39.6 c -0.8752,0 -1.7146,0.3477 -2.3334,0.9665 -0.6189,0.6189 -0.9666,1.4583 -0.9666,2.3335 0,0.8752 0.3477,1.7146 0.9666,2.3334 0.6188,0.6189 1.4582,0.9666 2.3334,0.9666 z"
|
||||
fill="#8a9cb3" id="path10" />
|
||||
<path d="m 134,109.6 h 4.5 v 3 H 134 Z" fill="#8a9cb3" id="path14" />
|
||||
<path d="m 141.5,109.6 h 4.5 v 3 h -4.5 z" fill="#8a9cb3" id="path16" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
36
briar-android/artwork/transfer_data_receive.svg
Normal file
36
briar-android/artwork/transfer_data_receive.svg
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="120" height="52"
|
||||
viewBox="0 0 120 52" fill="none" version="1.1" id="svg24"
|
||||
sodipodi:docname="transfer_data_receive.svg" inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
|
||||
<defs id="defs11" />
|
||||
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
|
||||
objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2" inkscape:window-width="1920" inkscape:window-height="982"
|
||||
id="namedview9" showgrid="false" inkscape:zoom="4.1077454" inkscape:cx="81.48675"
|
||||
inkscape:cy="18.42645" inkscape:window-x="1920" inkscape:window-y="72"
|
||||
inkscape:window-maximized="0" inkscape:current-layer="svg24" inkscape:document-rotation="0"
|
||||
fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" />
|
||||
<metadata id="metadata30">
|
||||
<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>
|
||||
<path id="path8"
|
||||
style="fill:#8a9cb3;stroke:none;stroke-width:0.749281;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 15.97512,14.267003 c -0.596207,0 -1.167798,0.237677 -1.588736,0.659365 -0.421693,0.42094 -0.659371,0.992537 -0.659371,1.588743 v 10.111264 c -0.596207,0 -1.16631,0.236178 -1.58725,0.657875 -0.421682,0.420938 -0.659361,0.992529 -0.659361,1.588736 v 12.35937 h 2.246611 v -12.35937 h 17.977398 v 12.359369 h 2.24662 V 28.872986 c 0,-0.596207 -0.23619,-1.167798 -0.65788,-1.588737 -0.42094,-0.421697 -0.99253,-0.657875 -1.58874,-0.657875 V 16.515111 c 0,-0.596206 -0.23768,-1.167804 -0.65937,-1.588743 -0.42094,-0.421688 -0.99252,-0.659365 -1.58873,-0.659365 z m 0,2.248108 H 29.456311 V 26.626375 H 15.97512 Z m 2.24662,2.224241 v 2.246609 h 3.369915 v -2.246609 z m 5.618031,0 v 2.246609 h 3.36993 v -2.246609 z"
|
||||
sodipodi:nodetypes="scsccsccccccsccscssccccccccccccccc" />
|
||||
<path
|
||||
d="m 47.827564,24.463076 v 3.073847 h 18.443085 l -8.45308,8.453081 2.182432,2.182432 12.172435,-12.172438 -12.172435,-12.172434 -2.182432,2.182431 8.45308,8.45308 z"
|
||||
id="path895" style="fill:#82c91e;fill-opacity:1;stroke:none;stroke-width:1.53692" />
|
||||
<path id="path8-3"
|
||||
style="fill:#8a9cb3;stroke:none;stroke-width:0.985186;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="M 93.999021,5.9999999e-8 C 92.275189,5.9999999e-8 90.624219,0.68560941 89.405306,1.9045678 88.186391,3.1235212 87.500737,4.7763832 87.500737,6.5002454 V 45.497793 c 0,1.723833 0.685654,3.378723 1.904569,4.597639 C 90.624219,51.314343 92.275189,52 93.999021,52 h 19.500739 c 1.72383,0 3.37676,-0.685657 4.59567,-1.904568 C 119.31435,48.876515 120,47.221626 120,45.497793 V 6.5002454 C 120,4.7763832 119.31435,3.1235212 118.09543,1.9045678 116.87652,0.68560941 115.22359,5.9999999e-8 113.49976,5.9999999e-8 Z m 0,3.252084740000001 h 19.500739 c 0.86192,0 1.68931,0.339865 2.29881,0.9493417 0.60941,0.6094767 0.95131,1.4368929 0.95131,2.2988189 V 45.497793 c 0,0.861917 -0.3419,1.689315 -0.95131,2.29882 -0.6095,0.609408 -1.43689,0.953264 -2.29881,0.953264 H 93.999021 c -0.861916,0 -1.687352,-0.343856 -2.296858,-0.953264 -0.609407,-0.609505 -0.951304,-1.436903 -0.951304,-2.29882 V 6.5002454 c 0,-0.861926 0.341897,-1.6893422 0.951304,-2.2988189 0.609506,-0.6094767 1.434942,-0.9493417 2.296858,-0.9493417 z m 9.750369,32.7640582 c -0.86191,0 -1.68745,0.341895 -2.29686,0.951303 -0.6095,0.609506 -0.95326,1.436904 -0.95326,2.298819 0,0.861916 0.34376,1.687452 0.95326,2.29686 0.60941,0.609507 1.43495,0.951302 2.29686,0.951302 0.86192,0 1.68931,-0.341797 2.29882,-0.951302 0.6094,-0.609408 0.9513,-1.434942 0.9513,-2.29686 0,-0.861915 -0.3419,-1.689311 -0.9513,-2.298819 -0.60951,-0.609408 -1.4369,-0.951303 -2.29882,-0.951303 z"
|
||||
sodipodi:nodetypes="sssssssscsssssscsscsscsscsssscssscs" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
37
briar-android/artwork/transfer_data_send.svg
Normal file
37
briar-android/artwork/transfer_data_send.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="120" height="52"
|
||||
viewBox="0 0 120 52" fill="none" version="1.1" id="svg24"
|
||||
sodipodi:docname="transfer_data_send.svg" inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
|
||||
<defs id="defs11" />
|
||||
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
|
||||
objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2" inkscape:window-width="1920" inkscape:window-height="982"
|
||||
id="namedview9" showgrid="false" inkscape:zoom="4.1077454" inkscape:cx="35.020218"
|
||||
inkscape:cy="26.319497" inkscape:window-x="1920" inkscape:window-y="72"
|
||||
inkscape:window-maximized="1" inkscape:current-layer="svg24" inkscape:document-rotation="0"
|
||||
fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0"
|
||||
lock-margins="true" />
|
||||
<metadata id="metadata30">
|
||||
<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>
|
||||
<path id="path8"
|
||||
style="fill:#8a9cb3;stroke:none;stroke-width:0.749281;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 91.223138,12.30985 c -0.596207,0 -1.167798,0.237677 -1.588736,0.659365 -0.421693,0.42094 -0.659371,0.992537 -0.659371,1.588743 v 10.111264 c -0.596207,0 -1.16631,0.236178 -1.58725,0.657875 -0.421682,0.420938 -0.659361,0.992529 -0.659361,1.588736 v 12.35937 h 2.246611 v -12.35937 h 17.977399 v 12.359369 h 2.24662 V 26.915833 c 0,-0.596207 -0.23619,-1.167798 -0.65788,-1.588737 -0.42094,-0.421697 -0.99253,-0.657875 -1.58874,-0.657875 V 14.557958 c 0,-0.596206 -0.23768,-1.167804 -0.65937,-1.588743 -0.42094,-0.421688 -0.99252,-0.659365 -1.58873,-0.659365 z m 0,2.248108 H 104.70433 V 24.669222 H 91.223138 Z m 2.24662,2.224241 v 2.246609 h 3.369915 v -2.246609 z m 5.618031,0 v 2.246609 h 3.369931 v -2.246609 z"
|
||||
sodipodi:nodetypes="scsccsccccccsccscssccccccccccccccc" />
|
||||
<path
|
||||
d="m 47.827564,24.463076 v 3.073847 h 18.443085 l -8.45308,8.453081 2.182432,2.182432 12.172435,-12.172438 -12.172435,-12.172434 -2.182432,2.182431 8.45308,8.45308 z"
|
||||
id="path895" style="fill:#82c91e;fill-opacity:1;stroke:none;stroke-width:1.53692" />
|
||||
<path id="path8-3"
|
||||
style="fill:#8a9cb3;stroke:none;stroke-width:0.985186;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="M 6.4982841,0 C 4.7744519,0 3.1234817,0.68560936 1.9045686,1.9045677 0.68565358,3.1235212 0,4.7763832 0,6.5002454 V 45.497793 c 0,1.723832 0.68565358,3.378724 1.9045686,4.597639 C 3.1234817,51.314343 4.7744519,52 6.4982841,52 H 25.999019 c 1.723832,0 3.376764,-0.685657 4.595677,-1.904568 1.218914,-1.218917 1.904569,-2.873807 1.904568,-4.597639 V 6.5002454 c 0,-1.7238622 -0.685654,-3.3767242 -1.904568,-4.5956777 C 29.375783,0.68560936 27.722851,0 25.999019,0 Z m 0,3.2520847 H 25.999019 c 0.861916,0 1.689313,0.339865 2.29882,0.9493418 0.609407,0.6094767 0.951302,1.4368929 0.951302,2.2988189 V 45.497793 c 0,0.861916 -0.341895,1.689315 -0.951302,2.29882 -0.609507,0.609408 -1.436904,0.953264 -2.29882,0.953264 H 6.4982841 c -0.8619161,0 -1.6873525,-0.343856 -2.2968577,-0.953264 C 3.5920186,47.187108 3.2501219,46.359709 3.2501219,45.497793 V 6.5002454 c 0,-0.861926 0.3418967,-1.6893422 0.9513045,-2.2988189 C 4.8109316,3.5919497 5.636368,3.2520847 6.4982841,3.2520847 Z M 16.248652,36.016144 c -0.861916,0 -1.68745,0.341895 -2.296859,0.951302 -0.609506,0.609506 -0.953264,1.436905 -0.953264,2.29882 0,0.861916 0.343758,1.687452 0.953264,2.29686 0.609409,0.609507 1.434943,0.951302 2.296859,0.951302 0.861916,0 1.689312,-0.341797 2.298819,-0.951302 0.609408,-0.609408 0.951303,-1.434942 0.951303,-2.29686 0,-0.861915 -0.341895,-1.689311 -0.951303,-2.29882 -0.609507,-0.609407 -1.436903,-0.951302 -2.298819,-0.951302 z"
|
||||
sodipodi:nodetypes="sssssssscsssssscsscsscsscsssscssscs" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -437,6 +437,15 @@
|
||||
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="org.briarproject.briar.android.removabledrive.RemovableDriveActivity"
|
||||
android:label="@string/removable_drive_menu_title"
|
||||
android:parentActivityName="org.briarproject.briar.android.conversation.ConversationActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.briarproject.briar.android.conversation.ConversationActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.contact.add.remote.PendingContactListActivity"
|
||||
android:label="@string/pending_contact_requests"
|
||||
|
||||
@@ -38,6 +38,9 @@ import org.briarproject.briar.android.attachment.media.MediaModule;
|
||||
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
|
||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
||||
import org.briarproject.briar.android.removabledrive.ChooserFragment;
|
||||
import org.briarproject.briar.android.removabledrive.ReceiveFragment;
|
||||
import org.briarproject.briar.android.removabledrive.SendFragment;
|
||||
import org.briarproject.briar.android.settings.ConnectionsFragment;
|
||||
import org.briarproject.briar.android.settings.NotificationsFragment;
|
||||
import org.briarproject.briar.android.settings.SecurityFragment;
|
||||
@@ -212,4 +215,10 @@ public interface AndroidComponent
|
||||
void inject(SecurityFragment securityFragment);
|
||||
|
||||
void inject(NotificationsFragment notificationsFragment);
|
||||
|
||||
void inject(ChooserFragment chooserFragment);
|
||||
|
||||
void inject(SendFragment sendFragment);
|
||||
|
||||
void inject(ReceiveFragment receiveFragment);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
||||
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
|
||||
import org.briarproject.bramble.plugin.file.AndroidRemovableDrivePluginFactory;
|
||||
import org.briarproject.bramble.plugin.file.RemovableDriveModule;
|
||||
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
|
||||
import org.briarproject.bramble.plugin.tor.AndroidTorPluginFactory;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
@@ -43,6 +42,7 @@ import org.briarproject.briar.android.login.LoginModule;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerModule;
|
||||
import org.briarproject.briar.android.privategroup.conversation.GroupConversationModule;
|
||||
import org.briarproject.briar.android.privategroup.list.GroupListModule;
|
||||
import org.briarproject.briar.android.removabledrive.TransferDataModule;
|
||||
import org.briarproject.briar.android.reporting.DevReportModule;
|
||||
import org.briarproject.briar.android.settings.SettingsModule;
|
||||
import org.briarproject.briar.android.sharing.SharingModule;
|
||||
@@ -93,7 +93,7 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
GroupListModule.class,
|
||||
GroupConversationModule.class,
|
||||
SharingModule.class,
|
||||
RemovableDriveModule.class
|
||||
TransferDataModule.class,
|
||||
})
|
||||
public class AppModule {
|
||||
|
||||
@@ -307,6 +307,11 @@ public class AppModule {
|
||||
public boolean shouldEnableConnectViaBluetooth() {
|
||||
return IS_DEBUG_BUILD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableTransferData() {
|
||||
return IS_DEBUG_BUILD;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ import org.briarproject.briar.android.privategroup.memberlist.GroupMemberModule;
|
||||
import org.briarproject.briar.android.privategroup.reveal.GroupRevealModule;
|
||||
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
|
||||
import org.briarproject.briar.android.privategroup.reveal.RevealContactsFragment;
|
||||
import org.briarproject.briar.android.removabledrive.RemovableDriveActivity;
|
||||
import org.briarproject.briar.android.reporting.CrashFragment;
|
||||
import org.briarproject.briar.android.reporting.CrashReportActivity;
|
||||
import org.briarproject.briar.android.reporting.ReportFormFragment;
|
||||
@@ -176,6 +177,8 @@ public interface ActivityComponent {
|
||||
|
||||
void inject(CrashReportActivity crashReportActivity);
|
||||
|
||||
void inject(RemovableDriveActivity activity);
|
||||
|
||||
// Fragments
|
||||
|
||||
void inject(SetupFragment fragment);
|
||||
|
||||
@@ -11,8 +11,5 @@ public interface RequestCodes {
|
||||
int REQUEST_DOZE_WHITELISTING = 9;
|
||||
int REQUEST_UNLOCK = 11;
|
||||
int REQUEST_KEYGUARD_UNLOCK = 12;
|
||||
int REQUEST_ATTACH_IMAGE = 13;
|
||||
int REQUEST_SAVE_ATTACHMENT = 14;
|
||||
int REQUEST_AVATAR_IMAGE = 15;
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||
import org.briarproject.briar.android.util.RequestBluetoothDiscoverable;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.RequestBluetoothDiscoverable;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.BaseActivity;
|
||||
import org.briarproject.briar.android.contact.ContactItem;
|
||||
import org.briarproject.briar.android.util.RequestBluetoothDiscoverable;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.RequestBluetoothDiscoverable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.transition.Slide;
|
||||
@@ -34,7 +35,6 @@ import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
@@ -54,6 +54,9 @@ import org.briarproject.briar.android.forum.ForumActivity;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||
import org.briarproject.briar.android.introduction.IntroductionActivity;
|
||||
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
||||
import org.briarproject.briar.android.removabledrive.RemovableDriveActivity;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.GetImageAdvanced;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.GetMultipleImagesAdvanced;
|
||||
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||
import org.briarproject.briar.android.view.ImagePreview;
|
||||
@@ -92,6 +95,7 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
@@ -121,6 +125,7 @@ import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static androidx.core.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
|
||||
import static androidx.lifecycle.Lifecycle.State.STARTED;
|
||||
import static androidx.recyclerview.widget.SortedList.INVALID_POSITION;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.sort;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.logging.Level.INFO;
|
||||
@@ -132,7 +137,6 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
import static org.briarproject.bramble.util.StringUtils.join;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ATTACH_IMAGE;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION;
|
||||
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENTS;
|
||||
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
||||
@@ -192,6 +196,12 @@ public class ConversationActivity extends BriarActivity
|
||||
requireNonNull(name);
|
||||
loadMessages();
|
||||
};
|
||||
private final ActivityResultLauncher<String> launcher = SDK_INT >= 18 ?
|
||||
registerForActivityResult(new GetMultipleImagesAdvanced(),
|
||||
this::onImagesChosen) :
|
||||
registerForActivityResult(new GetImageAdvanced(), uri -> {
|
||||
if (uri != null) onImagesChosen(singletonList(uri));
|
||||
});
|
||||
|
||||
private AttachmentRetriever attachmentRetriever;
|
||||
private ConversationViewModel viewModel;
|
||||
@@ -314,9 +324,6 @@ public class ConversationActivity extends BriarActivity
|
||||
.make(list, R.string.introduction_sent,
|
||||
Snackbar.LENGTH_SHORT)
|
||||
.show();
|
||||
} else if (request == REQUEST_ATTACH_IMAGE && result == RESULT_OK) {
|
||||
// TODO: remove cast when removing feature flag
|
||||
((TextAttachmentController) sendController).onImageReceived(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,6 +382,10 @@ public class ConversationActivity extends BriarActivity
|
||||
if (!featureFlags.shouldEnableConnectViaBluetooth()) {
|
||||
menu.findItem(R.id.action_connect_via_bluetooth).setVisible(false);
|
||||
}
|
||||
// Transfer Data feature only supported on API 19+
|
||||
if (SDK_INT >= 19 && featureFlags.shouldEnableTransferData()) {
|
||||
menu.findItem(R.id.action_transfer_data).setVisible(true);
|
||||
}
|
||||
// enable alias and bluetooth action once available
|
||||
observeOnce(viewModel.getContactItem(), this, contact -> {
|
||||
menu.findItem(R.id.action_set_alias).setEnabled(true);
|
||||
@@ -415,6 +426,11 @@ public class ConversationActivity extends BriarActivity
|
||||
new BluetoothConnecterDialogFragment().show(fm,
|
||||
BluetoothConnecterDialogFragment.TAG);
|
||||
return true;
|
||||
} else if (itemId == R.id.action_transfer_data) {
|
||||
Intent intent = new Intent(this, RemovableDriveActivity.class);
|
||||
intent.putExtra(CONTACT_ID, contactId.getInt());
|
||||
startActivity(intent);
|
||||
return true;
|
||||
} else if (itemId == R.id.action_delete_all_messages) {
|
||||
askToDeleteAllMessages();
|
||||
return true;
|
||||
@@ -760,8 +776,13 @@ public class ConversationActivity extends BriarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachImage(Intent intent) {
|
||||
startActivityForResult(intent, REQUEST_ATTACH_IMAGE);
|
||||
public void onAttachImageClicked() {
|
||||
launcher.launch("image/*");
|
||||
}
|
||||
|
||||
private void onImagesChosen(@Nullable List<Uri> uris) {
|
||||
// TODO: remove cast when removing feature flag
|
||||
((TextAttachmentController) sendController).onImageReceived(uris);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.android.conversation;
|
||||
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.transition.Fade;
|
||||
import android.transition.Transition;
|
||||
@@ -21,6 +22,7 @@ import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.CreateDocumentAdvanced;
|
||||
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
||||
import org.briarproject.briar.android.view.PullDownLayout;
|
||||
|
||||
@@ -28,6 +30,7 @@ import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AlertDialog.Builder;
|
||||
@@ -38,9 +41,6 @@ import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import static android.content.Intent.ACTION_CREATE_DOCUMENT;
|
||||
import static android.content.Intent.CATEGORY_OPENABLE;
|
||||
import static android.content.Intent.EXTRA_TITLE;
|
||||
import static android.graphics.Color.TRANSPARENT;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.View.GONE;
|
||||
@@ -51,7 +51,6 @@ import static android.view.View.VISIBLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
import static com.google.android.material.snackbar.Snackbar.LENGTH_LONG;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SAVE_ATTACHMENT;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute;
|
||||
import static org.briarproject.briar.android.util.UiUtils.getDialogIcon;
|
||||
|
||||
@@ -80,6 +79,10 @@ public class ImageActivity extends BriarActivity
|
||||
private List<AttachmentItem> attachments;
|
||||
private MessageId conversationMessageId;
|
||||
|
||||
private final ActivityResultLauncher<String> launcher =
|
||||
registerForActivityResult(new CreateDocumentAdvanced(),
|
||||
this::onImageUriSelected);
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
@@ -177,16 +180,6 @@ public class ImageActivity extends BriarActivity
|
||||
layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int request, int result,
|
||||
@Nullable Intent data) {
|
||||
super.onActivityResult(request, result, data);
|
||||
if (request == REQUEST_SAVE_ATTACHMENT && result == RESULT_OK &&
|
||||
data != null) {
|
||||
viewModel.saveImage(getVisibleAttachment(), data.getData());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullStart() {
|
||||
appBarLayout.animate()
|
||||
@@ -270,8 +263,9 @@ public class ImageActivity extends BriarActivity
|
||||
private void showSaveImageDialog() {
|
||||
OnClickListener okListener = (dialog, which) -> {
|
||||
if (SDK_INT >= 19) {
|
||||
Intent intent = getCreationIntent();
|
||||
startActivityForResult(intent, REQUEST_SAVE_ATTACHMENT);
|
||||
String name = viewModel.getFileName() + "." +
|
||||
getVisibleAttachment().getExtension();
|
||||
launcher.launch(name);
|
||||
} else {
|
||||
viewModel.saveImage(getVisibleAttachment());
|
||||
}
|
||||
@@ -285,13 +279,9 @@ public class ImageActivity extends BriarActivity
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@RequiresApi(api = 19)
|
||||
private Intent getCreationIntent() {
|
||||
Intent intent = new Intent(ACTION_CREATE_DOCUMENT);
|
||||
intent.addCategory(CATEGORY_OPENABLE);
|
||||
intent.setType(getVisibleAttachment().getMimeType());
|
||||
intent.putExtra(EXTRA_TITLE, viewModel.getFileName());
|
||||
return intent;
|
||||
private void onImageUriSelected(@Nullable Uri uri) {
|
||||
if (uri == null) return;
|
||||
viewModel.saveImage(getVisibleAttachment(), uri);
|
||||
}
|
||||
|
||||
private void onImageSaveStateChanged(@Nullable Boolean error) {
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -45,6 +44,7 @@ import androidx.annotation.UiThread;
|
||||
import static android.media.MediaScannerConnection.scanFile;
|
||||
import static android.os.Environment.DIRECTORY_PICTURES;
|
||||
import static android.os.Environment.getExternalStoragePublicDirectory;
|
||||
import static java.util.Locale.US;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
@@ -111,7 +111,7 @@ public class ImageViewModel extends DbViewModel implements EventListener {
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void expectAttachments(List<AttachmentItem> attachments) {
|
||||
void expectAttachments(List<AttachmentItem> attachments) {
|
||||
for (AttachmentItem item : attachments) {
|
||||
// no need to track items that are in a final state already
|
||||
if (item.getState().isFinal()) continue;
|
||||
@@ -226,8 +226,7 @@ public class ImageViewModel extends DbViewModel implements EventListener {
|
||||
}
|
||||
|
||||
String getFileName() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd",
|
||||
Locale.getDefault());
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HHmmss", US);
|
||||
return sdf.format(new Date());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package org.briarproject.briar.android.fragment;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.widget.ImageViewCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import static android.view.View.FOCUS_DOWN;
|
||||
|
||||
/**
|
||||
* A fragment to be used at the end of a user flow
|
||||
* where the user should not have the option to go back.
|
||||
* Here, we only show final information
|
||||
* before finishing the related activity.
|
||||
*/
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class FinalFragment extends Fragment {
|
||||
|
||||
public static final String TAG = FinalFragment.class.getName();
|
||||
|
||||
public static final String ARG_TITLE = "title";
|
||||
public static final String ARG_ICON = "icon";
|
||||
public static final String ARG_ICON_TINT = "iconTint";
|
||||
public static final String ARG_TEXT = "text";
|
||||
|
||||
public static FinalFragment newInstance(
|
||||
@StringRes int title,
|
||||
@DrawableRes int icon,
|
||||
@ColorRes int iconTint,
|
||||
@StringRes int text) {
|
||||
FinalFragment f = new FinalFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_TITLE, title);
|
||||
args.putInt(ARG_ICON, icon);
|
||||
args.putInt(ARG_ICON_TINT, iconTint);
|
||||
args.putInt(ARG_TEXT, text);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
private ScrollView scrollView;
|
||||
protected Button buttonView;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View v = inflater
|
||||
.inflate(R.layout.fragment_final, container, false);
|
||||
|
||||
scrollView = (ScrollView) v;
|
||||
ImageView iconView = v.findViewById(R.id.iconView);
|
||||
TextView titleView = v.findViewById(R.id.titleView);
|
||||
TextView textView = v.findViewById(R.id.textView);
|
||||
buttonView = v.findViewById(R.id.button);
|
||||
|
||||
Bundle args = requireArguments();
|
||||
titleView.setText(args.getInt(ARG_TITLE));
|
||||
iconView.setImageResource(args.getInt(ARG_ICON));
|
||||
int color = getResources().getColor(args.getInt(ARG_ICON_TINT));
|
||||
ColorStateList tint = ColorStateList.valueOf(color);
|
||||
ImageViewCompat.setImageTintList(iconView, tint);
|
||||
textView.setText(args.getInt(ARG_TEXT));
|
||||
|
||||
buttonView.setOnClickListener(view -> onBackButtonPressed());
|
||||
|
||||
AppCompatActivity a = (AppCompatActivity) requireActivity();
|
||||
a.setTitle(args.getInt(ARG_TITLE));
|
||||
ActionBar actionBar = a.getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(false);
|
||||
actionBar.setHomeButtonEnabled(false);
|
||||
}
|
||||
a.getOnBackPressedDispatcher().addCallback(
|
||||
getViewLifecycleOwner(), new OnBackPressedCallback(true) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
onBackButtonPressed();
|
||||
}
|
||||
});
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
// Scroll down in case the screen is small, so the button is visible
|
||||
scrollView.post(() -> scrollView.fullScroll(FOCUS_DOWN));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the action that the system back button
|
||||
* and the button at the bottom will perform.
|
||||
*/
|
||||
protected void onBackButtonPressed() {
|
||||
requireActivity().supportFinishAfterTransition();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.briarproject.briar.android.removabledrive;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.view.View.FOCUS_DOWN;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ChooserFragment extends Fragment {
|
||||
|
||||
public final static String TAG = ChooserFragment.class.getName();
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private RemovableDriveViewModel viewModel;
|
||||
private ScrollView scrollView;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
FragmentActivity activity = requireActivity();
|
||||
getAndroidComponent(activity).inject(this);
|
||||
viewModel = new ViewModelProvider(activity, viewModelFactory)
|
||||
.get(RemovableDriveViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_transfer_data_chooser,
|
||||
container, false);
|
||||
|
||||
scrollView = (ScrollView) v;
|
||||
Button sendButton = v.findViewById(R.id.sendButton);
|
||||
sendButton.setOnClickListener(i -> viewModel.startSendData());
|
||||
|
||||
Button receiveButton = v.findViewById(R.id.receiveButton);
|
||||
receiveButton.setOnClickListener(i -> viewModel.startReceiveData());
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
requireActivity().setTitle(R.string.removable_drive_menu_title);
|
||||
TransferDataState state = viewModel.getState().getValue();
|
||||
if (state instanceof TransferDataState.TaskAvailable) {
|
||||
// we can't come back here now to start another task
|
||||
// as we only support one per ViewModel instance
|
||||
requireActivity().supportFinishAfterTransition();
|
||||
} else {
|
||||
// Scroll down in case the screen is small, so the button is visible
|
||||
scrollView.post(() -> scrollView.fullScroll(FOCUS_DOWN));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.briarproject.briar.android.removabledrive;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.fragment.FinalFragment;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ErrorFragment extends FinalFragment {
|
||||
|
||||
public static ErrorFragment newInstance(@StringRes int title,
|
||||
@StringRes int text) {
|
||||
ErrorFragment f = new ErrorFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_TITLE, title);
|
||||
args.putInt(ARG_ICON, R.drawable.alerts_and_states_error);
|
||||
args.putInt(ARG_ICON_TINT, R.color.briar_red_500);
|
||||
args.putInt(ARG_TEXT, text);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View v = super.onCreateView(inflater, container, savedInstanceState);
|
||||
buttonView.setText(R.string.try_again_button);
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBackButtonPressed() {
|
||||
// Re-create this activity when going back in failed state.
|
||||
// This will also re-create the ViewModel, so we start fresh.
|
||||
Intent i = requireActivity().getIntent();
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package org.briarproject.briar.android.removabledrive;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.GetContentAdvanced;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.view.View.FOCUS_DOWN;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ReceiveFragment extends Fragment {
|
||||
|
||||
final static String TAG = ReceiveFragment.class.getName();
|
||||
|
||||
private final ActivityResultLauncher<String> launcher =
|
||||
registerForActivityResult(new GetContentAdvanced(),
|
||||
this::onDocumentChosen);
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private RemovableDriveViewModel viewModel;
|
||||
private ScrollView scrollView;
|
||||
private Button button;
|
||||
private ProgressBar progressBar;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
FragmentActivity activity = requireActivity();
|
||||
getAndroidComponent(activity).inject(this);
|
||||
viewModel = new ViewModelProvider(activity, viewModelFactory)
|
||||
.get(RemovableDriveViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_transfer_data_receive,
|
||||
container, false);
|
||||
|
||||
scrollView = (ScrollView) v;
|
||||
progressBar = v.findViewById(R.id.progressBar);
|
||||
button = v.findViewById(R.id.fileButton);
|
||||
button.setOnClickListener(view ->
|
||||
launcher.launch("*/*")
|
||||
);
|
||||
viewModel.getOldTaskResumedEvent()
|
||||
.observeEvent(getViewLifecycleOwner(), this::onOldTaskResumed);
|
||||
viewModel.getState()
|
||||
.observe(getViewLifecycleOwner(), this::onStateChanged);
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
requireActivity().setTitle(R.string.removable_drive_title_receive);
|
||||
// Scroll down in case the screen is small, so the button is visible
|
||||
scrollView.post(() -> scrollView.fullScroll(FOCUS_DOWN));
|
||||
}
|
||||
|
||||
private void onOldTaskResumed(boolean resumed) {
|
||||
if (resumed) {
|
||||
Toast.makeText(requireContext(),
|
||||
R.string.removable_drive_ongoing, LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void onStateChanged(TransferDataState state) {
|
||||
if (state instanceof TransferDataState.NoDataToSend) {
|
||||
throw new IllegalStateException();
|
||||
} else if (state instanceof TransferDataState.Ready) {
|
||||
button.setEnabled(true);
|
||||
} else if (state instanceof TransferDataState.TaskAvailable) {
|
||||
button.setEnabled(false);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void onDocumentChosen(@Nullable Uri uri) {
|
||||
if (uri == null) return;
|
||||
viewModel.importData(uri);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package org.briarproject.briar.android.removabledrive;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.file.RemovableDriveTask;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.fragment.FinalFragment;
|
||||
import org.briarproject.briar.android.removabledrive.RemovableDriveViewModel.Action;
|
||||
import org.briarproject.briar.android.removabledrive.TransferDataState.TaskAvailable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||
|
||||
@RequiresApi(19)
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class RemovableDriveActivity extends BriarActivity {
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private RemovableDriveViewModel viewModel;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
|
||||
viewModel = new ViewModelProvider(this, viewModelFactory)
|
||||
.get(RemovableDriveViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = requireNonNull(getIntent());
|
||||
int contactId = intent.getIntExtra(CONTACT_ID, -1);
|
||||
if (contactId == -1) throw new IllegalArgumentException("ContactId");
|
||||
viewModel.setContactId(new ContactId(contactId));
|
||||
|
||||
setContentView(R.layout.activity_fragment_container);
|
||||
|
||||
viewModel.getActionEvent().observeEvent(this, this::onActionReceived);
|
||||
viewModel.getState().observe(this, this::onStateChanged);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
Fragment f = new ChooserFragment();
|
||||
String tag = ChooserFragment.TAG;
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.fragmentContainer, f, tag)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void onActionReceived(Action action) {
|
||||
Fragment f;
|
||||
String tag;
|
||||
if (action == Action.SEND) {
|
||||
f = new SendFragment();
|
||||
tag = SendFragment.TAG;
|
||||
} else if (action == Action.RECEIVE) {
|
||||
f = new ReceiveFragment();
|
||||
tag = ReceiveFragment.TAG;
|
||||
} else throw new AssertionError();
|
||||
showFragment(getSupportFragmentManager(), f, tag);
|
||||
}
|
||||
|
||||
private void onStateChanged(TransferDataState state) {
|
||||
if (!(state instanceof TaskAvailable)) return;
|
||||
RemovableDriveTask.State s = ((TaskAvailable) state).state;
|
||||
if (s.isFinished()) {
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
Action action;
|
||||
// We can't simply rely on viewModel.getActionEvent()
|
||||
// as that might have been destroyed in the meantime.
|
||||
if (fm.findFragmentByTag(SendFragment.TAG) != null) {
|
||||
action = Action.SEND;
|
||||
} else if (fm.findFragmentByTag(ReceiveFragment.TAG) != null) {
|
||||
action = Action.RECEIVE;
|
||||
} else {
|
||||
action = requireNonNull(
|
||||
viewModel.getActionEvent().getLastValue());
|
||||
}
|
||||
Fragment f;
|
||||
if (s.isSuccess()) f = getSuccessFragment(action);
|
||||
else f = getErrorFragment(action);
|
||||
showFragment(getSupportFragmentManager(), f, FinalFragment.TAG);
|
||||
}
|
||||
}
|
||||
|
||||
private Fragment getSuccessFragment(Action action) {
|
||||
@StringRes int title, text;
|
||||
if (action == Action.SEND) {
|
||||
title = R.string.removable_drive_success_send_title;
|
||||
text = R.string.removable_drive_success_send_text;
|
||||
} else if (action == Action.RECEIVE) {
|
||||
title = R.string.removable_drive_success_receive_title;
|
||||
text = R.string.removable_drive_success_receive_text;
|
||||
} else throw new AssertionError();
|
||||
return FinalFragment.newInstance(title,
|
||||
R.drawable.ic_check_circle_outline, R.color.briar_brand_green,
|
||||
text);
|
||||
}
|
||||
|
||||
private Fragment getErrorFragment(Action action) {
|
||||
@StringRes int title, text;
|
||||
if (action == Action.SEND) {
|
||||
title = R.string.removable_drive_error_send_title;
|
||||
text = R.string.removable_drive_error_send_text;
|
||||
} else if (action == Action.RECEIVE) {
|
||||
title = R.string.removable_drive_error_receive_title;
|
||||
text = R.string.removable_drive_error_receive_text;
|
||||
} else throw new AssertionError();
|
||||
return ErrorFragment.newInstance(title, text);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,97 +5,186 @@ import android.net.Uri;
|
||||
|
||||
import org.briarproject.bramble.api.Consumer;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.file.RemovableDriveManager;
|
||||
import org.briarproject.bramble.api.plugin.file.RemovableDriveTask;
|
||||
import org.briarproject.bramble.api.plugin.file.RemovableDriveTask.State;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import static java.util.Locale.US;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.PROP_URI;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
class RemovableDriveViewModel extends AndroidViewModel {
|
||||
class RemovableDriveViewModel extends DbViewModel {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(RemovableDriveViewModel.class.getName());
|
||||
enum Action {SEND, RECEIVE}
|
||||
|
||||
private final RemovableDriveManager manager;
|
||||
|
||||
private final ConcurrentHashMap<Consumer<State>, RemovableDriveTask>
|
||||
observers = new ConcurrentHashMap<>();
|
||||
private final MutableLiveEvent<Action> action = new MutableLiveEvent<>();
|
||||
private final MutableLiveEvent<Boolean> oldTaskResumed =
|
||||
new MutableLiveEvent<>();
|
||||
private final MutableLiveData<TransferDataState> state =
|
||||
new MutableLiveData<>();
|
||||
@Nullable
|
||||
private ContactId contactId = null;
|
||||
@Nullable
|
||||
private RemovableDriveTask task = null;
|
||||
@Nullable
|
||||
private Consumer<State> taskObserver = null;
|
||||
|
||||
@Inject
|
||||
RemovableDriveViewModel(Application app,
|
||||
RemovableDriveViewModel(
|
||||
Application app,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager,
|
||||
TransactionManager db,
|
||||
AndroidExecutor androidExecutor,
|
||||
RemovableDriveManager removableDriveManager) {
|
||||
super(app);
|
||||
|
||||
super(app, dbExecutor, lifecycleManager, db, androidExecutor);
|
||||
this.manager = removableDriveManager;
|
||||
}
|
||||
|
||||
String getFileName() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS", US);
|
||||
return sdf.format(new Date());
|
||||
}
|
||||
|
||||
|
||||
LiveData<State> write(ContactId contactId, Uri uri) {
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put(PROP_URI, uri.toString());
|
||||
return observe(manager.startWriterTask(contactId, p));
|
||||
}
|
||||
|
||||
LiveData<State> read(Uri uri) {
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put(PROP_URI, uri.toString());
|
||||
return observe(manager.startReaderTask(p));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
LiveData<State> ongoingWrite() {
|
||||
RemovableDriveTask task = manager.getCurrentWriterTask();
|
||||
if (task == null) {
|
||||
return null;
|
||||
}
|
||||
return observe(task);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
LiveData<State> ongoingRead() {
|
||||
RemovableDriveTask task = manager.getCurrentReaderTask();
|
||||
if (task == null) {
|
||||
return null;
|
||||
}
|
||||
return observe(task);
|
||||
}
|
||||
|
||||
private LiveData<State> observe(RemovableDriveTask task) {
|
||||
MutableLiveData<State> state = new MutableLiveData<>();
|
||||
Consumer<State> observer = state::postValue;
|
||||
task.addObserver(observer);
|
||||
observers.put(observer, task);
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
for (Map.Entry<Consumer<State>, RemovableDriveTask> entry
|
||||
: observers.entrySet()) {
|
||||
entry.getValue().removeObserver(entry.getKey());
|
||||
if (task != null) {
|
||||
// when we have a task, we must have an observer for it
|
||||
Consumer<State> observer = requireNonNull(taskObserver);
|
||||
task.removeObserver(observer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this as soon as it becomes available.
|
||||
*/
|
||||
void setContactId(ContactId contactId) {
|
||||
this.contactId = contactId;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void startSendData() {
|
||||
action.setEvent(Action.SEND);
|
||||
|
||||
// check if there is already a send/write task
|
||||
task = manager.getCurrentWriterTask();
|
||||
if (task == null) {
|
||||
// check if there's even something to send
|
||||
ContactId c = requireNonNull(contactId);
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
if (!manager.isTransportSupportedByContact(c)) {
|
||||
state.postValue(new TransferDataState.NotSupported());
|
||||
} else if (manager.isWriterTaskNeeded(c)) {
|
||||
state.postValue(new TransferDataState.Ready());
|
||||
} else {
|
||||
state.postValue(new TransferDataState.NoDataToSend());
|
||||
}
|
||||
} catch (DbException e) {
|
||||
handleException(e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// observe old task
|
||||
taskObserver =
|
||||
s -> state.setValue(new TransferDataState.TaskAvailable(s));
|
||||
task.addObserver(taskObserver);
|
||||
oldTaskResumed.setEvent(true);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void startReceiveData() {
|
||||
action.setEvent(Action.RECEIVE);
|
||||
|
||||
// check if there is already a receive/read task
|
||||
task = manager.getCurrentReaderTask();
|
||||
if (task == null) {
|
||||
state.setValue(new TransferDataState.Ready());
|
||||
} else {
|
||||
// observe old task
|
||||
taskObserver =
|
||||
s -> state.setValue(new TransferDataState.TaskAvailable(s));
|
||||
task.addObserver(taskObserver);
|
||||
oldTaskResumed.setEvent(true);
|
||||
}
|
||||
}
|
||||
|
||||
String getFileName() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", US);
|
||||
return sdf.format(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this only when in {@link TransferDataState.Ready}.
|
||||
*/
|
||||
@UiThread
|
||||
void exportData(Uri uri) {
|
||||
// starting an action more than once is not supported for simplicity
|
||||
if (task != null) throw new IllegalStateException();
|
||||
|
||||
// from now on, we are not re-usable
|
||||
// (because gets a state update right away on the UiThread)
|
||||
taskObserver =
|
||||
s -> state.setValue(new TransferDataState.TaskAvailable(s));
|
||||
|
||||
// start the writer task for this contact and observe it
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put(PROP_URI, uri.toString());
|
||||
ContactId c = requireNonNull(contactId);
|
||||
task = manager.startWriterTask(c, p);
|
||||
task.addObserver(taskObserver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this only when in {@link TransferDataState.Ready}.
|
||||
*/
|
||||
@UiThread
|
||||
void importData(Uri uri) {
|
||||
// starting an action more than once is not supported for simplicity
|
||||
if (task != null) throw new IllegalStateException();
|
||||
|
||||
// from now on, we are not re-usable
|
||||
// (because gets a state update right away on the UiThread)
|
||||
taskObserver =
|
||||
s -> state.setValue(new TransferDataState.TaskAvailable(s));
|
||||
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put(PROP_URI, uri.toString());
|
||||
task = manager.startReaderTask(p);
|
||||
task.addObserver(taskObserver);
|
||||
}
|
||||
|
||||
LiveEvent<Action> getActionEvent() {
|
||||
return action;
|
||||
}
|
||||
|
||||
LiveEvent<Boolean> getOldTaskResumedEvent() {
|
||||
return oldTaskResumed;
|
||||
}
|
||||
|
||||
LiveData<TransferDataState> getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package org.briarproject.briar.android.removabledrive;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.file.RemovableDriveTask;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.CreateDocumentAdvanced;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.View.FOCUS_DOWN;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class SendFragment extends Fragment {
|
||||
|
||||
final static String TAG = SendFragment.class.getName();
|
||||
|
||||
private final ActivityResultLauncher<String> launcher =
|
||||
registerForActivityResult(new CreateDocumentAdvanced(),
|
||||
this::onDocumentCreated);
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private RemovableDriveViewModel viewModel;
|
||||
private ScrollView scrollView;
|
||||
private TextView introTextView;
|
||||
private Button button;
|
||||
private ProgressBar progressBar;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
FragmentActivity activity = requireActivity();
|
||||
getAndroidComponent(activity).inject(this);
|
||||
viewModel = new ViewModelProvider(activity, viewModelFactory)
|
||||
.get(RemovableDriveViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_transfer_data_send,
|
||||
container, false);
|
||||
|
||||
scrollView = (ScrollView) v;
|
||||
introTextView = v.findViewById(R.id.introTextView);
|
||||
progressBar = v.findViewById(R.id.progressBar);
|
||||
button = v.findViewById(R.id.fileButton);
|
||||
button.setOnClickListener(view ->
|
||||
launcher.launch(viewModel.getFileName())
|
||||
);
|
||||
|
||||
viewModel.getOldTaskResumedEvent()
|
||||
.observeEvent(getViewLifecycleOwner(), this::onOldTaskResumed);
|
||||
viewModel.getState()
|
||||
.observe(getViewLifecycleOwner(), this::onStateChanged);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
requireActivity().setTitle(R.string.removable_drive_title_send);
|
||||
// Scroll down in case the screen is small, so the button is visible
|
||||
scrollView.post(() -> scrollView.fullScroll(FOCUS_DOWN));
|
||||
}
|
||||
|
||||
private void onOldTaskResumed(boolean resumed) {
|
||||
if (resumed) {
|
||||
Toast.makeText(requireContext(),
|
||||
R.string.removable_drive_ongoing, LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void onStateChanged(TransferDataState state) {
|
||||
if (state instanceof TransferDataState.NoDataToSend) {
|
||||
introTextView.setText(R.string.removable_drive_send_no_data);
|
||||
button.setEnabled(false);
|
||||
} else if (state instanceof TransferDataState.NotSupported) {
|
||||
introTextView.setText(R.string.removable_drive_send_not_supported);
|
||||
button.setEnabled(false);
|
||||
} else if (state instanceof TransferDataState.Ready) {
|
||||
button.setEnabled(true);
|
||||
} else if (state instanceof TransferDataState.TaskAvailable) {
|
||||
button.setEnabled(false);
|
||||
RemovableDriveTask.State s =
|
||||
((TransferDataState.TaskAvailable) state).state;
|
||||
if (s.getTotal() > 0L && progressBar.getVisibility() != VISIBLE) {
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
progressBar.setMax(100);
|
||||
}
|
||||
int progress = s.getTotal() == 0 ? 0 : // no div by null
|
||||
(int) ((double) s.getDone() / s.getTotal() * 100);
|
||||
if (SDK_INT >= 24) {
|
||||
progressBar.setProgress(progress, true);
|
||||
} else {
|
||||
progressBar.setProgress(progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onDocumentCreated(@Nullable Uri uri) {
|
||||
if (uri == null) return;
|
||||
viewModel.exportData(uri);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,11 +8,12 @@ import dagger.Module;
|
||||
import dagger.multibindings.IntoMap;
|
||||
|
||||
@Module
|
||||
public interface RemovableDriveModule {
|
||||
public interface TransferDataModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(RemovableDriveViewModel.class)
|
||||
ViewModel bindRemovableDriveViewModel(RemovableDriveViewModel removableDriveViewModel);
|
||||
ViewModel bindRemovableDriveViewModel(
|
||||
RemovableDriveViewModel removableDriveViewModel);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.briarproject.briar.android.removabledrive;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.file.RemovableDriveTask;
|
||||
|
||||
@NotNullByDefault
|
||||
abstract class TransferDataState {
|
||||
|
||||
/**
|
||||
* There is nothing we can send to the chosen contact.
|
||||
* This only applies to sending data, but not to receiving it.
|
||||
*/
|
||||
static class NoDataToSend extends TransferDataState {
|
||||
}
|
||||
|
||||
/**
|
||||
* The chosen contact does not support the transport, yet.
|
||||
* So we can't send them data this way.
|
||||
*/
|
||||
static class NotSupported extends TransferDataState {
|
||||
}
|
||||
|
||||
/**
|
||||
* We are ready to let the user select a file for sending or receiving data.
|
||||
*/
|
||||
static class Ready extends TransferDataState {
|
||||
}
|
||||
|
||||
/**
|
||||
* A task with state information is available and should be shown in the UI.
|
||||
*/
|
||||
static class TaskAvailable extends TransferDataState {
|
||||
final RemovableDriveTask.State state;
|
||||
|
||||
TaskAvailable(RemovableDriveTask.State state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.briar.android.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
@@ -9,9 +8,11 @@ import android.view.View;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.GetImageAdvanced;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
@@ -20,12 +21,9 @@ import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_AVATAR_IMAGE;
|
||||
import static org.briarproject.briar.android.util.UiUtils.createSelectImageIntent;
|
||||
import static org.briarproject.briar.android.util.UiUtils.triggerFeedback;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -45,6 +43,10 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
private SettingsViewModel viewModel;
|
||||
private AvatarPreference prefAvatar;
|
||||
|
||||
private final ActivityResultLauncher<String> launcher =
|
||||
registerForActivityResult(new GetImageAdvanced(),
|
||||
this::onImageSelected);
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
@@ -60,8 +62,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
prefAvatar = requireNonNull(findPreference(PREF_KEY_AVATAR));
|
||||
if (viewModel.shouldEnableProfilePictures()) {
|
||||
prefAvatar.setOnPreferenceClickListener(preference -> {
|
||||
Intent intent = createSelectImageIntent(false);
|
||||
startActivityForResult(intent, REQUEST_AVATAR_IMAGE);
|
||||
launcher.launch("image/*");
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
@@ -102,20 +103,11 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
requireActivity().setTitle(R.string.settings_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int request, int result,
|
||||
@Nullable Intent data) {
|
||||
super.onActivityResult(request, result, data);
|
||||
if (request == REQUEST_AVATAR_IMAGE && result == RESULT_OK) {
|
||||
if (data == null) return;
|
||||
Uri uri = data.getData();
|
||||
if (uri == null) return;
|
||||
|
||||
DialogFragment dialog =
|
||||
ConfirmAvatarDialogFragment.newInstance(uri);
|
||||
dialog.show(getParentFragmentManager(),
|
||||
ConfirmAvatarDialogFragment.TAG);
|
||||
}
|
||||
private void onImageSelected(@Nullable Uri uri) {
|
||||
if (uri == null) return;
|
||||
DialogFragment dialog = ConfirmAvatarDialogFragment.newInstance(uri);
|
||||
dialog.show(getParentFragmentManager(),
|
||||
ConfirmAvatarDialogFragment.TAG);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.briarproject.briar.android.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import androidx.activity.result.contract.ActivityResultContract;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.GetContent;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.GetMultipleContents;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import static android.app.Activity.RESULT_CANCELED;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
||||
import static android.bluetooth.BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION;
|
||||
import static android.content.Intent.EXTRA_MIME_TYPES;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
|
||||
|
||||
@NotNullByDefault
|
||||
public class ActivityLaunchers {
|
||||
|
||||
public static class CreateDocumentAdvanced extends CreateDocument {
|
||||
@Override
|
||||
public Intent createIntent(Context context, String input) {
|
||||
Intent i = super.createIntent(context, input);
|
||||
putShowAdvancedExtra(i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GetContentAdvanced extends GetContent {
|
||||
@Override
|
||||
public Intent createIntent(Context context, String input) {
|
||||
Intent i = super.createIntent(context, input);
|
||||
putShowAdvancedExtra(i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GetImageAdvanced extends GetContent {
|
||||
@Override
|
||||
public Intent createIntent(Context context, String input) {
|
||||
Intent i = super.createIntent(context, input);
|
||||
putShowAdvancedExtra(i);
|
||||
i.setType("image/*");
|
||||
if (SDK_INT >= 19)
|
||||
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(18)
|
||||
public static class GetMultipleImagesAdvanced extends GetMultipleContents {
|
||||
@Override
|
||||
public Intent createIntent(Context context, String input) {
|
||||
Intent i = super.createIntent(context, input);
|
||||
putShowAdvancedExtra(i);
|
||||
i.setType("image/*");
|
||||
if (SDK_INT >= 19)
|
||||
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RequestBluetoothDiscoverable
|
||||
extends ActivityResultContract<Integer, Boolean> {
|
||||
@Override
|
||||
public Intent createIntent(Context context, Integer duration) {
|
||||
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
|
||||
i.putExtra(EXTRA_DISCOVERABLE_DURATION, duration);
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean parseResult(int resultCode, @Nullable Intent intent) {
|
||||
return resultCode != RESULT_CANCELED;
|
||||
}
|
||||
}
|
||||
|
||||
private static void putShowAdvancedExtra(Intent i) {
|
||||
i.putExtra(SDK_INT <= 28 ? "android.content.extra.SHOW_ADVANCED" :
|
||||
"android.provider.extra.SHOW_ADVANCED", true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package org.briarproject.briar.android.util;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import androidx.activity.result.contract.ActivityResultContract;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import static android.app.Activity.RESULT_CANCELED;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
||||
import static android.bluetooth.BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION;
|
||||
|
||||
@NotNullByDefault
|
||||
public class RequestBluetoothDiscoverable
|
||||
extends ActivityResultContract<Integer, Boolean> {
|
||||
|
||||
@Override
|
||||
public Intent createIntent(Context context, Integer duration) {
|
||||
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
|
||||
i.putExtra(EXTRA_DISCOVERABLE_DURATION, duration);
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean parseResult(int resultCode, @Nullable Intent intent) {
|
||||
return resultCode != RESULT_CANCELED;
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,9 @@ import androidx.core.content.ContextCompat;
|
||||
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import androidx.core.util.Consumer;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
@@ -65,12 +67,7 @@ import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
|
||||
|
||||
import static android.content.Context.KEYGUARD_SERVICE;
|
||||
import static android.content.Context.POWER_SERVICE;
|
||||
import static android.content.Intent.ACTION_GET_CONTENT;
|
||||
import static android.content.Intent.ACTION_OPEN_DOCUMENT;
|
||||
import static android.content.Intent.CATEGORY_DEFAULT;
|
||||
import static android.content.Intent.CATEGORY_OPENABLE;
|
||||
import static android.content.Intent.EXTRA_ALLOW_MULTIPLE;
|
||||
import static android.content.Intent.EXTRA_MIME_TYPES;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.os.Build.MANUFACTURER;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
@@ -105,7 +102,6 @@ import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_RTL;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
|
||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
||||
@@ -135,6 +131,17 @@ public class UiUtils {
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
public static void showFragment(FragmentManager fm, Fragment f,
|
||||
@Nullable String tag) {
|
||||
fm.beginTransaction()
|
||||
.setCustomAnimations(R.anim.step_next_in,
|
||||
R.anim.step_previous_out, R.anim.step_previous_in,
|
||||
R.anim.step_next_out)
|
||||
.replace(R.id.fragmentContainer, f, tag)
|
||||
.addToBackStack(tag)
|
||||
.commit();
|
||||
}
|
||||
|
||||
public static String getContactDisplayName(Author author,
|
||||
@Nullable String alias) {
|
||||
String name = author.getName();
|
||||
@@ -297,18 +304,6 @@ public class UiUtils {
|
||||
};
|
||||
}
|
||||
|
||||
public static Intent createSelectImageIntent(boolean allowMultiple) {
|
||||
Intent intent = new Intent(SDK_INT >= 19 ?
|
||||
ACTION_OPEN_DOCUMENT : ACTION_GET_CONTENT);
|
||||
intent.setType("image/*");
|
||||
intent.addCategory(CATEGORY_OPENABLE);
|
||||
if (SDK_INT >= 19)
|
||||
intent.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
if (allowMultiple && SDK_INT >= 18)
|
||||
intent.putExtra(EXTRA_ALLOW_MULTIPLE, true);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static void showOnboardingDialog(Context ctx, String text) {
|
||||
new AlertDialog.Builder(ctx, R.style.OnboardingDialogTheme)
|
||||
.setMessage(text)
|
||||
@@ -334,6 +329,11 @@ public class UiUtils {
|
||||
return i;
|
||||
}
|
||||
|
||||
public static void putShowAdvancedExtra(Intent i) {
|
||||
i.putExtra(SDK_INT <= 28 ? "android.content.extra.SHOW_ADVANCED" :
|
||||
"android.provider.extra.SHOW_ADVANCED", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if location is enabled,
|
||||
* or it isn't required due to this being a SDK < 28 device.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.briar.android.view;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -15,13 +14,13 @@ import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItemResult;
|
||||
import org.briarproject.briar.android.attachment.AttachmentManager;
|
||||
import org.briarproject.briar.android.attachment.AttachmentResult;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
import org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.appcompat.app.AlertDialog.Builder;
|
||||
@@ -31,7 +30,6 @@ import androidx.lifecycle.Observer;
|
||||
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
|
||||
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.View.GONE;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static androidx.core.content.ContextCompat.getColor;
|
||||
@@ -143,38 +141,23 @@ public class TextAttachmentController extends TextSendController
|
||||
builder.show();
|
||||
return;
|
||||
}
|
||||
Intent intent = UiUtils.createSelectImageIntent(true);
|
||||
if (attachmentListener.getLifecycle().getCurrentState() != DESTROYED) {
|
||||
attachmentListener.onAttachImage(intent);
|
||||
attachmentListener.onAttachImageClicked();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called with the result Intent returned by the Activity started
|
||||
* with {@link UiUtils#createSelectImageIntent(boolean)}.
|
||||
* <p>
|
||||
* This method must be called at most once per call to
|
||||
* {@link AttachmentListener#onAttachImage(Intent)}.
|
||||
* Normally, this is true if called from
|
||||
* {@link AttachmentListener#onAttachImageClicked()}.
|
||||
* Normally, this is true if called from the launcher equivalent of
|
||||
* {@link Activity#onActivityResult(int, int, Intent)} since this is called
|
||||
* at most once per call to
|
||||
* {@link Activity#startActivityForResult(Intent, int)}.
|
||||
* at most once per call to {@link ActivityResultLauncher#launch(Object)}.
|
||||
*/
|
||||
@SuppressWarnings("JavadocReference")
|
||||
public void onImageReceived(@Nullable Intent resultData) {
|
||||
if (resultData == null) return;
|
||||
public void onImageReceived(@Nullable List<Uri> newUris) {
|
||||
if (newUris == null) return;
|
||||
if (loadingUris || !imageUris.isEmpty()) throw new AssertionError();
|
||||
List<Uri> newUris = new ArrayList<>();
|
||||
if (resultData.getData() != null) {
|
||||
newUris.add(resultData.getData());
|
||||
onNewUris(false, newUris);
|
||||
} else if (SDK_INT >= 18 && resultData.getClipData() != null) {
|
||||
ClipData clipData = resultData.getClipData();
|
||||
for (int i = 0; i < clipData.getItemCount(); i++) {
|
||||
newUris.add(clipData.getItemAt(i).getUri());
|
||||
}
|
||||
onNewUris(false, newUris);
|
||||
}
|
||||
onNewUris(false, newUris);
|
||||
}
|
||||
|
||||
private void onNewUris(boolean restart, List<Uri> newUris) {
|
||||
@@ -329,7 +312,7 @@ public class TextAttachmentController extends TextSendController
|
||||
@UiThread
|
||||
public interface AttachmentListener extends SendListener {
|
||||
|
||||
void onAttachImage(Intent intent);
|
||||
void onAttachImageClicked();
|
||||
|
||||
void onTooManyAttachments();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.arch.core.util.Function;
|
||||
@@ -50,7 +49,7 @@ public abstract class DbViewModel extends AndroidViewModel {
|
||||
protected final AndroidExecutor androidExecutor;
|
||||
|
||||
public DbViewModel(
|
||||
@NonNull Application application,
|
||||
Application application,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager,
|
||||
TransactionManager db,
|
||||
|
||||
11
briar-android/src/main/res/drawable/ic_arrow_back.xml
Normal file
11
briar-android/src/main/res/drawable/ic_arrow_back.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M16.59,7.58L10,14.17l-3.59,-3.58L5,12l5,5 8,-8zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" />
|
||||
</vector>
|
||||
21
briar-android/src/main/res/drawable/ic_transfer_data.xml
Normal file
21
briar-android/src/main/res/drawable/ic_transfer_data.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="155dp"
|
||||
android:height="155dp"
|
||||
android:viewportWidth="155"
|
||||
android:viewportHeight="155">
|
||||
<path
|
||||
android:fillColor="@color/briar_lime_400"
|
||||
android:pathData="m34.889,27.866c-0.93,-0.021 -1.779,0.612 -1.994,1.555 -0.246,1.077 0.427,2.148 1.504,2.395l5.34,1.223c-6.857,4.771 -12.782,10.788 -17.445,17.768 -5.477,8.197 -9.072,17.492 -10.541,27.219 -0.217,1.437 0.827,2.738 2.271,2.898 1.444,0.16 2.741,-0.88 2.963,-2.316 1.373,-8.889 4.673,-17.381 9.682,-24.877 4.281,-6.406 9.717,-11.929 16.004,-16.316l-1.271,5.559c-0.246,1.077 0.427,2.15 1.504,2.396 1.077,0.246 2.15,-0.427 2.396,-1.504l2.076,-9.08c0.167,-0.732 0.108,-1.458 -0.109,-2.123 -0.033,-0.348 -0.136,-0.693 -0.314,-1.018 -0.354,-0.642 -0.935,-1.078 -1.586,-1.258 -0.31,-0.167 -0.638,-0.31 -0.998,-0.393l-9.078,-2.076c-0.135,-0.031 -0.269,-0.048 -0.402,-0.051zM111.871,30.743c-0.836,0.055 -1.633,0.527 -2.082,1.316 -0.717,1.263 -0.271,2.864 0.961,3.635 8.143,5.096 14.998,12.029 20.002,20.258 4.936,8.117 7.92,17.253 8.746,26.689l-4.25,-3.395c-0.863,-0.689 -2.121,-0.549 -2.811,0.314 -0.689,0.863 -0.549,2.121 0.314,2.811l7.277,5.813c1.726,1.379 4.242,1.097 5.621,-0.629l5.813,-7.277c0.69,-0.863 0.549,-2.121 -0.314,-2.811 -0.863,-0.689 -2.122,-0.549 -2.811,0.314l-3.59,4.494c-0.886,-10.274 -4.128,-20.221 -9.502,-29.057 -5.469,-8.994 -12.974,-16.564 -21.895,-22.105 -0.463,-0.288 -0.979,-0.404 -1.48,-0.371zM45.191,140.86c-0.852,0.028 -1.671,0.446 -2.164,1.209 -0.789,1.219 -0.443,2.855 0.807,3.596 9.034,5.356 19.211,8.518 29.715,9.217 10.503,0.699 21.012,-1.086 30.676,-5.197 1.337,-0.569 1.897,-2.143 1.277,-3.457 -0.621,-1.313 -2.188,-1.87 -3.527,-1.305 -8.852,3.73 -18.466,5.348 -28.076,4.709 -9.61,-0.64 -18.923,-3.518 -27.203,-8.389 -0.47,-0.276 -0.993,-0.4 -1.504,-0.383z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="M68.475,3.03C66.724,3.03 65.046,3.726 63.809,4.964 62.571,6.201 61.875,7.879 61.875,9.63L61.875,49.229c0,1.75 0.696,3.43 1.934,4.668 1.238,1.238 2.916,1.934 4.666,1.934h19.801c1.75,0 3.428,-0.696 4.666,-1.934 1.238,-1.238 1.934,-2.918 1.934,-4.668L94.875,9.63c0,-1.75 -0.696,-3.428 -1.934,-4.666C91.704,3.726 90.026,3.03 88.275,3.03ZM68.475,6.331h19.801c0.875,0 1.715,0.346 2.334,0.965 0.619,0.619 0.965,1.459 0.965,2.334L91.574,49.229c0,0.875 -0.346,1.715 -0.965,2.334C89.99,52.182 89.151,52.53 88.275,52.53L68.475,52.53c-0.875,0 -1.715,-0.348 -2.334,-0.967 -0.619,-0.619 -0.965,-1.459 -0.965,-2.334L65.176,9.63c0,-0.875 0.346,-1.715 0.965,-2.334 0.619,-0.619 1.459,-0.965 2.334,-0.965zM11.25,100.03 L0,111.43v22.799C0,136.319 1.688,138.03 3.75,138.03h22.5c2.063,0 3.75,-1.711 3.75,-3.801L30,103.831C30,101.741 28.313,100.03 26.25,100.03ZM131,103.63c-0.796,0 -1.559,0.316 -2.121,0.879 -0.563,0.562 -0.879,1.325 -0.879,2.121v13.5c-0.796,0 -1.559,0.316 -2.121,0.879 -0.563,0.562 -0.879,1.325 -0.879,2.121v16.5h3v-16.5h24v16.5h3v-16.5c0,-0.796 -0.316,-1.559 -0.879,-2.121 -0.562,-0.563 -1.325,-0.879 -2.121,-0.879v-13.5c0,-0.796 -0.316,-1.559 -0.879,-2.121 -0.562,-0.563 -1.325,-0.879 -2.121,-0.879zM12.807,103.831L26.25,103.831v30.398L3.75,134.229L3.75,113.007ZM131,106.63h18v13.5L131,120.13ZM9.375,109.53v7.6h3.75L13.125,109.53ZM15,109.53v7.6h3.75L18.75,109.53ZM20.625,109.53v7.6h3.75L24.375,109.53Z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="m78.375,46.2c0.875,0 1.715,-0.348 2.333,-0.967 0.619,-0.619 0.966,-1.458 0.966,-2.333 0,-0.875 -0.348,-1.715 -0.966,-2.333C80.09,39.948 79.25,39.6 78.375,39.6c-0.875,0 -1.715,0.348 -2.333,0.966 -0.619,0.619 -0.967,1.458 -0.967,2.333 0,0.875 0.348,1.715 0.967,2.333 0.619,0.619 1.458,0.967 2.333,0.967z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="m134,109.6h4.5v3H134Z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="m141.5,109.6h4.5v3h-4.5z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="120dp"
|
||||
android:height="52dp"
|
||||
android:viewportWidth="120"
|
||||
android:viewportHeight="52">
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="m15.975,14.267c-0.596,0 -1.168,0.238 -1.589,0.659 -0.422,0.421 -0.659,0.993 -0.659,1.589v10.111c-0.596,0 -1.166,0.236 -1.587,0.658 -0.422,0.421 -0.659,0.993 -0.659,1.589v12.359h2.247v-12.359h17.977v12.359h2.247L33.951,28.873c0,-0.596 -0.236,-1.168 -0.658,-1.589 -0.421,-0.422 -0.993,-0.658 -1.589,-0.658L31.704,16.515c0,-0.596 -0.238,-1.168 -0.659,-1.589 -0.421,-0.422 -0.993,-0.659 -1.589,-0.659zM15.975,16.515L29.456,16.515L29.456,26.626L15.975,26.626ZM18.222,18.739v2.247h3.37v-2.247zM23.84,18.739v2.247h3.37v-2.247z" />
|
||||
<path
|
||||
android:fillColor="@color/briar_brand_green"
|
||||
android:pathData="m47.828,24.463v3.074h18.443l-8.453,8.453 2.182,2.182 12.172,-12.172 -12.172,-12.172 -2.182,2.182 8.453,8.453z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="M93.999,0C92.275,0 90.624,0.686 89.405,1.905 88.186,3.124 87.501,4.776 87.501,6.5L87.501,45.498c0,1.724 0.686,3.379 1.905,4.598C90.624,51.314 92.275,52 93.999,52h19.501c1.724,0 3.377,-0.686 4.596,-1.905C119.314,48.877 120,47.222 120,45.498L120,6.5C120,4.776 119.314,3.124 118.095,1.905 116.877,0.686 115.224,0 113.5,0ZM93.999,3.252h19.501c0.862,0 1.689,0.34 2.299,0.949 0.609,0.609 0.951,1.437 0.951,2.299L116.75,45.498c0,0.862 -0.342,1.689 -0.951,2.299 -0.609,0.609 -1.437,0.953 -2.299,0.953L93.999,48.75c-0.862,0 -1.687,-0.344 -2.297,-0.953 -0.609,-0.61 -0.951,-1.437 -0.951,-2.299L90.751,6.5c0,-0.862 0.342,-1.689 0.951,-2.299 0.61,-0.609 1.435,-0.949 2.297,-0.949zM103.749,36.016c-0.862,0 -1.687,0.342 -2.297,0.951 -0.609,0.61 -0.953,1.437 -0.953,2.299 0,0.862 0.344,1.687 0.953,2.297 0.609,0.61 1.435,0.951 2.297,0.951 0.862,0 1.689,-0.342 2.299,-0.951 0.609,-0.609 0.951,-1.435 0.951,-2.297 0,-0.862 -0.342,-1.689 -0.951,-2.299 -0.61,-0.609 -1.437,-0.951 -2.299,-0.951z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="120dp"
|
||||
android:height="52dp"
|
||||
android:viewportWidth="120"
|
||||
android:viewportHeight="52">
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="m91.223,12.31c-0.596,0 -1.168,0.238 -1.589,0.659 -0.422,0.421 -0.659,0.993 -0.659,1.589v10.111c-0.596,0 -1.166,0.236 -1.587,0.658 -0.422,0.421 -0.659,0.993 -0.659,1.589v12.359h2.247v-12.359h17.977v12.359h2.247L109.199,26.916c0,-0.596 -0.236,-1.168 -0.658,-1.589 -0.421,-0.422 -0.993,-0.658 -1.589,-0.658L106.952,14.558c0,-0.596 -0.238,-1.168 -0.659,-1.589 -0.421,-0.422 -0.993,-0.659 -1.589,-0.659zM91.223,14.558L104.704,14.558L104.704,24.669L91.223,24.669ZM93.47,16.782v2.247h3.37v-2.247zM99.088,16.782v2.247h3.37v-2.247z" />
|
||||
<path
|
||||
android:fillColor="@color/briar_brand_green"
|
||||
android:pathData="m47.828,24.463v3.074h18.443l-8.453,8.453 2.182,2.182 12.172,-12.172 -12.172,-12.172 -2.182,2.182 8.453,8.453z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="M6.498,0C4.774,0 3.123,0.686 1.905,1.905 0.686,3.124 0,4.776 0,6.5L0,45.498c0,1.724 0.686,3.379 1.905,4.598C3.123,51.314 4.774,52 6.498,52L25.999,52c1.724,0 3.377,-0.686 4.596,-1.905 1.219,-1.219 1.905,-2.874 1.905,-4.598L32.499,6.5c0,-1.724 -0.686,-3.377 -1.905,-4.596C29.376,0.686 27.723,0 25.999,0ZM6.498,3.252L25.999,3.252c0.862,0 1.689,0.34 2.299,0.949 0.609,0.609 0.951,1.437 0.951,2.299L29.249,45.498c0,0.862 -0.342,1.689 -0.951,2.299 -0.61,0.609 -1.437,0.953 -2.299,0.953L6.498,48.75c-0.862,0 -1.687,-0.344 -2.297,-0.953C3.592,47.187 3.25,46.36 3.25,45.498L3.25,6.5c0,-0.862 0.342,-1.689 0.951,-2.299C4.811,3.592 5.636,3.252 6.498,3.252ZM16.249,36.016c-0.862,0 -1.687,0.342 -2.297,0.951 -0.61,0.61 -0.953,1.437 -0.953,2.299 0,0.862 0.344,1.687 0.953,2.297 0.609,0.61 1.435,0.951 2.297,0.951 0.862,0 1.689,-0.342 2.299,-0.951 0.609,-0.609 0.951,-1.435 0.951,-2.297 0,-0.862 -0.342,-1.689 -0.951,-2.299 -0.61,-0.609 -1.437,-0.951 -2.299,-0.951z" />
|
||||
</vector>
|
||||
76
briar-android/src/main/res/layout/fragment_final.xml
Normal file
76
briar-android/src/main/res/layout/fragment_final.xml
Normal file
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView 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"
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iconView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="32dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/titleView"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.25"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintWidth_max="200dp"
|
||||
app:layout_constraintWidth_percent="0.4"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:srcCompat="@drawable/alerts_and_states_error"
|
||||
tools:tint="@color/briar_red_500" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginLeft="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginRight="32dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
||||
app:layout_constraintBottom_toTopOf="@+id/textView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/iconView"
|
||||
tools:text="@string/removable_drive_error_send_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintBottom_toTopOf="@+id/button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/titleView"
|
||||
tools:text="@string/removable_drive_error_send_text" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
style="@style/BriarButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/finish"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView 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"
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="32dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/introView"
|
||||
app:layout_constraintDimensionRatio="1,1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="spread"
|
||||
app:layout_constraintWidth_percent="0.4"
|
||||
app:srcCompat="@drawable/ic_transfer_data"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/introView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/removable_drive_intro"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/sendButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/sendButton"
|
||||
style="@style/BriarButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/removable_drive_title_send"
|
||||
app:layout_constraintBottom_toTopOf="@+id/receiveButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/receiveButton"
|
||||
style="@style/BriarButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/removable_drive_title_receive"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView 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"
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="32dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/progressBar"
|
||||
app:layout_constraintDimensionRatio="1,2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="spread"
|
||||
app:layout_constraintWidth_max="300dp"
|
||||
app:layout_constraintWidth_percent="0.6"
|
||||
app:srcCompat="@drawable/ic_transfer_data_send"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="32dp"
|
||||
android:indeterminate="true"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toTopOf="@+id/introTextView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/introTextView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/removable_drive_receive_intro"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fileButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/progressBar" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/fileButton"
|
||||
style="@style/BriarButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:enabled="false"
|
||||
android:text="@string/removable_drive_receive_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView 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"
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="32dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/progressBar"
|
||||
app:layout_constraintDimensionRatio="1,2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintWidth_percent="0.6"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="spread"
|
||||
app:layout_constraintWidth_max="300dp"
|
||||
app:srcCompat="@drawable/ic_transfer_data_send"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="32dp"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toTopOf="@+id/introTextView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/introTextView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/removable_drive_send_intro"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fileButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/progressBar" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/fileButton"
|
||||
style="@style/BriarButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:enabled="false"
|
||||
android:text="@string/removable_drive_send_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -10,12 +10,6 @@
|
||||
android:title="@string/introduction_menu_item"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_set_alias"
|
||||
android:enabled="false"
|
||||
android:title="@string/set_contact_alias"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_conversation_settings"
|
||||
android:title="@string/menu_item_disappearing_messages"
|
||||
@@ -23,21 +17,43 @@
|
||||
app:showAsAction="never"
|
||||
tools:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_connect_via_bluetooth"
|
||||
android:enabled="false"
|
||||
android:title="@string/menu_item_connect_via_bluetooth"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_delete_all_messages"
|
||||
android:title="@string/delete_all_messages"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_social_remove_person"
|
||||
android:icon="@drawable/action_delete_white"
|
||||
android:title="@string/delete_contact"
|
||||
app:showAsAction="never" />
|
||||
android:title="@string/network_settings_title"
|
||||
app:showAsAction="never">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/action_connect_via_bluetooth"
|
||||
android:enabled="false"
|
||||
android:title="@string/menu_item_connect_via_bluetooth"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_transfer_data"
|
||||
android:title="@string/removable_drive_menu_title"
|
||||
android:visible="false"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:title="@string/menu_contact"
|
||||
app:showAsAction="never">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/action_set_alias"
|
||||
android:enabled="false"
|
||||
android:title="@string/set_contact_alias"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_social_remove_person"
|
||||
android:title="@string/delete_contact"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
<string name="open">Open</string>
|
||||
<string name="change">Change</string>
|
||||
<string name="start">Start</string>
|
||||
<string name="finish">Finish</string>
|
||||
<string name="no_data">No data</string>
|
||||
<string name="ellipsis">…</string>
|
||||
<string name="text_too_long">The entered text is too long</string>
|
||||
@@ -233,6 +234,7 @@
|
||||
<string name="dialog_title_image_support">You can now send images to this contact</string>
|
||||
<string name="dialog_message_image_support">Tap this icon to attach images.</string>
|
||||
<string name="messaging_too_many_attachments_toast">Only the first %d images will be sent</string>
|
||||
<string name="menu_contact">Contact</string>
|
||||
|
||||
<!-- Adding Contacts -->
|
||||
|
||||
@@ -685,6 +687,30 @@
|
||||
<!-- Connections Screen -->
|
||||
<string name="transports_help_text">Briar can connect to your contacts via the Internet, Wi-Fi or Bluetooth.\n\nAll Internet connections go through the Tor network for privacy.\n\nIf a contact can be reached by multiple methods, Briar uses them in parallel.</string>
|
||||
|
||||
|
||||
<!-- Transfer Data via Removable Drives -->
|
||||
|
||||
<string name="removable_drive_menu_title">Transfer data</string>
|
||||
<string name="removable_drive_intro">You can send encrypted messages to your contact using removable storage such as USB flash drives or SD cards.\n\nIf your contact has sent you a removable drive containing encrypted messages, you can import the messages into Briar by using the receive button below.</string>
|
||||
<string name="removable_drive_title_send">Send data</string>
|
||||
<string name="removable_drive_title_receive">Receive data</string>
|
||||
<string name="removable_drive_send_intro">Tap the button below to create a new file containing the encrypted messages. You can choose where the file will be saved.\n\nIf you want to save the file on a removable drive, insert the drive now.</string>
|
||||
<string name="removable_drive_send_no_data">There are currently no messages waiting to be sent to this contact.</string>
|
||||
<string name="removable_drive_send_not_supported">This contact is using an old version of Briar which does not yet support this feature.</string>
|
||||
<string name="removable_drive_send_button">Choose file for export</string>
|
||||
<string name="removable_drive_ongoing">Please wait for ongoing task to complete</string>
|
||||
<string name="removable_drive_receive_intro">Tap the button below to choose the file that your contact sent you.\n\nIf the file is on a removable drive, insert the drive now.</string>
|
||||
<string name="removable_drive_receive_button">Choose file for import</string>
|
||||
<string name="removable_drive_success_send_title">Export successful</string>
|
||||
<string name="removable_drive_success_send_text">Data exported successfully. You now have 14 days to transport the file to your contact.\n\nIf the file is on a removable drive, use the notification in the status bar to eject the drive before unplugging it.</string>
|
||||
<string name="removable_drive_success_receive_title">Import successful</string>
|
||||
<string name="removable_drive_success_receive_text">All encrypted messages contained in this file have been received.</string>
|
||||
<string name="removable_drive_error_send_title">Error exporting data</string>
|
||||
<string name="removable_drive_error_send_text">There was an error writing data to the file.\n\nIf you are using a removable drive, ensure that it is properly inserted and try again.\n\nIf the error persists, please send feedback to let the Briar team know about the issue.</string>
|
||||
<string name="removable_drive_error_receive_title">Error importing data</string>
|
||||
<string name="removable_drive_error_receive_text">The selected file did not contain anything that Briar could recognize.\n\nPlease check that you chose the right file.\n\nIf your contact created the file more than 14 days ago, Briar will not be able to recognize it.</string>
|
||||
|
||||
|
||||
<!-- Screenshots -->
|
||||
|
||||
<!-- This is a name to be used in screenshots. Feel free to change it to a local name. -->
|
||||
|
||||
@@ -96,5 +96,6 @@ internal class HeadlessModule(private val appDir: File) {
|
||||
override fun shouldEnableProfilePictures() = false
|
||||
override fun shouldEnableDisappearingMessages() = false
|
||||
override fun shouldEnableConnectViaBluetooth() = false
|
||||
override fun shouldEnableTransferData() = false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user