From 527ac0c1e084368f023c93a49bd42d47fb269c61 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Tue, 8 Dec 2015 18:26:04 -0200 Subject: [PATCH] Show current step and total number of steps when adding a contact. UI was transferred into XML files and lots of redundant code was deleted such as the custom CodeEntryView. Closes #33 --- briar-android/artwork/bluetooth.svg | 99 ++++++++++++ briar-android/res/drawable/bluetooth.png | Bin 19988 -> 25814 bytes ...invitation_bluetooth_confirmation_code.xml | 123 +++++++++++++++ .../invitation_bluetooth_invitation_code.xml | 109 +++++++++++++ .../res/layout/invitation_bluetooth_start.xml | 128 +++++++++------- briar-android/res/layout/invitation_error.xml | 41 +++++ briar-android/res/layout/view_code_entry.xml | 16 ++ briar-android/res/values/strings.xml | 5 +- .../invitation/AddContactActivity.java | 75 +++++---- .../android/invitation/AddContactView.java | 13 +- .../invitation/ChooseIdentityView.java | 5 + .../android/invitation/CodeEntryListener.java | 6 - .../android/invitation/CodeEntryView.java | 103 ------------- .../invitation/ConfirmationCodeView.java | 144 +++++++++++++----- .../android/invitation/ConnectedView.java | 51 ------- .../invitation/ConnectionFailedView.java | 64 -------- .../android/invitation/ConnectionView.java | 53 ------- .../invitation/ContactDetailsView.java | 66 -------- ...odesDoNotMatchView.java => ErrorView.java} | 53 ++++--- .../invitation/InvitationCodeView.java | 118 +++++++++++--- .../invitation/WaitForContactView.java | 66 -------- 21 files changed, 745 insertions(+), 593 deletions(-) create mode 100644 briar-android/artwork/bluetooth.svg create mode 100644 briar-android/res/layout/invitation_bluetooth_confirmation_code.xml create mode 100644 briar-android/res/layout/invitation_bluetooth_invitation_code.xml create mode 100644 briar-android/res/layout/invitation_error.xml create mode 100644 briar-android/res/layout/view_code_entry.xml delete mode 100644 briar-android/src/org/briarproject/android/invitation/CodeEntryListener.java delete mode 100644 briar-android/src/org/briarproject/android/invitation/CodeEntryView.java delete mode 100644 briar-android/src/org/briarproject/android/invitation/ConnectedView.java delete mode 100644 briar-android/src/org/briarproject/android/invitation/ConnectionFailedView.java delete mode 100644 briar-android/src/org/briarproject/android/invitation/ConnectionView.java delete mode 100644 briar-android/src/org/briarproject/android/invitation/ContactDetailsView.java rename briar-android/src/org/briarproject/android/invitation/{CodesDoNotMatchView.java => ErrorView.java} (52%) delete mode 100644 briar-android/src/org/briarproject/android/invitation/WaitForContactView.java diff --git a/briar-android/artwork/bluetooth.svg b/briar-android/artwork/bluetooth.svg new file mode 100644 index 0000000000..951fa966c1 --- /dev/null +++ b/briar-android/artwork/bluetooth.svg @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="140.89767mm" + height="49.528343mm" + viewBox="0 0 499.24373 175.49413" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="bluetooth.svg" + inkscape:export-filename="bluetooth.png" + inkscape:export-xdpi="194.87476" + inkscape:export-ydpi="194.87476"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.98994949" + inkscape:cx="435.00934" + inkscape:cy="91.353462" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1012" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" /> + <metadata + id="metadata7"> + <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 + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-72.369616,-444.3511)"> + <path + style="fill:#000000" + d="m 532.17899,615.51093 -4.32657,-4.3343 -7.06956,-2.42737 c -3.88825,-1.33505 -8.72285,-2.99587 -10.74354,-3.6907 l -3.674,-1.26333 3.524,-0.1726 c 8.24183,-0.40367 12.98778,-4.00671 14.33634,-10.88389 0.79628,-4.06078 1.12887,-17.29805 0.83016,-33.04122 -0.15338,-8.08375 -0.16617,-15.41641 -0.0284,-16.29481 0.13775,-0.8784 0.53527,-2.42011 0.88339,-3.42602 1.22247,-3.53243 0.33996,-11.90828 -1.8577,-17.63146 -0.34848,-0.9075 -1.93434,-4.215 -3.52415,-7.35 -4.15849,-8.2003 -4.50458,-8.94188 -4.89292,-10.4841 -0.45865,-1.82145 -0.21161,-5.43531 0.4625,-6.7659 0.66625,-1.31505 2.15695,-2.91616 3.24622,-3.48662 1.29885,-0.68024 2.61568,0.12202 4.6864,2.85512 3.42153,4.51599 14.00135,19.44095 15.73191,22.19301 3.74551,5.95636 5.95558,11.16496 7.9722,18.78849 0.6547,2.475 1.88525,6.9975 2.73456,10.05 0.84931,3.0525 2.71404,10.15792 4.14385,15.78983 l 2.59964,10.23983 4.65,5.19319 c 2.5575,2.85626 5.7975,6.46371 7.2,8.01657 1.4025,1.55286 2.55,2.97543 2.55,3.16127 0,0.33546 -34.49955,29.29931 -34.89913,29.29931 -0.11475,0 -2.15559,-1.95044 -4.5352,-4.3343 z m -93.86507,-16.38777 c -1.25762,-0.62844 -2.20557,-1.3788 -2.91402,-2.30663 -2.08931,-2.73629 -1.95034,2.36868 -1.86433,-68.48249 l 0.0777,-64.03881 0.66066,-1.23494 c 1.0152,-1.89767 1.99201,-2.91087 3.73952,-3.87887 l 1.59982,-0.88619 37.78387,-0.0796 c 42.45592,-0.0894 39.40239,-0.2483 42.11646,2.19188 0.87544,0.78709 1.75715,1.95946 2.18393,2.90385 0.71264,1.57698 0.71613,1.63839 0.80561,14.20405 l 0.0899,12.62022 -1.79817,-0.13007 c -1.42577,-0.10313 -2.08143,0.007 -3.16601,0.5321 -2.01294,0.97445 -3.93993,2.89871 -5.11476,5.10753 l -1.03717,1.95 -0.007,-12.825 -0.007,-12.825 -33.6,0 -33.6,0 0,51.3 0,51.3 33.59873,0 33.59874,0 0.0763,-34.425 c 0.073,-32.96021 0.0982,-34.36117 0.59098,-32.925 0.28309,0.825 1.80562,3.9975 3.3834,7.05 5.49252,10.62624 5.40494,9.86009 5.39597,47.20335 -0.007,27.62122 -0.12358,29.95084 -1.66204,33.10906 -1.07144,2.19949 -2.71143,3.71042 -5.05823,4.66019 l -1.67381,0.6774 -36.1677,0.0797 -36.16769,0.0797 -1.864,-0.93145 z m 42.39939,-5.03813 c 2.87119,-1.30885 4.45771,-3.6784 4.43003,-6.61652 -0.0388,-4.11587 -3.1088,-7.22328 -7.1364,-7.22328 -2.11956,0 -3.56727,0.60889 -5.16364,2.17177 -2.24518,2.19807 -2.75398,5.43897 -1.30101,8.28704 0.71312,1.39782 2.52137,3.00905 3.96214,3.53045 1.49707,0.54176 3.84003,0.47454 5.20888,-0.14946 z" + id="path4201" + inkscape:connector-curvature="0" + sodipodi:nodetypes="scsscssscssssscssssscssssscsscsscsssscscscccccccccscscsscccccsssssss" /> + <path + style="fill:#000000" + d="m 111.80395,615.51093 4.32657,-4.3343 7.06956,-2.42737 c 3.88825,-1.33505 8.72285,-2.99587 10.74354,-3.6907 l 3.674,-1.26333 -3.524,-0.1726 c -8.24183,-0.40367 -12.98778,-4.00671 -14.33634,-10.88389 -0.79628,-4.06078 -1.12887,-17.29805 -0.83016,-33.04122 0.15338,-8.08375 0.16617,-15.41641 0.0284,-16.29481 -0.13775,-0.8784 -0.53527,-2.42011 -0.88339,-3.42602 -1.22247,-3.53243 -0.33996,-11.90828 1.8577,-17.63146 0.34848,-0.9075 1.93434,-4.215 3.52415,-7.35 4.15849,-8.2003 4.50458,-8.94188 4.89292,-10.4841 0.45865,-1.82145 0.21161,-5.43531 -0.4625,-6.7659 -0.66625,-1.31505 -2.15695,-2.91616 -3.24622,-3.48662 -1.29885,-0.68024 -2.61568,0.12202 -4.6864,2.85512 -3.42153,4.51599 -14.00135,19.44095 -15.73191,22.19301 -3.74551,5.95636 -5.955584,11.16496 -7.972204,18.78849 -0.6547,2.475 -1.88525,6.9975 -2.73456,10.05 -0.84931,3.0525 -2.71404,10.15792 -4.14385,15.78983 l -2.59964,10.23983 -4.65,5.19319 c -2.5575,2.85626 -5.7975,6.46371 -7.2,8.01657 -1.4025,1.55286 -2.55,2.97543 -2.55,3.16127 0,0.33546 34.499554,29.29931 34.899134,29.29931 0.11475,0 2.15559,-1.95044 4.53519,-4.3343 z m 93.86506,-16.38777 c 1.25762,-0.62844 2.20557,-1.3788 2.91402,-2.30663 2.08931,-2.73629 1.95034,2.36868 1.86433,-68.48249 l -0.0777,-64.03881 -0.66066,-1.23494 c -1.0152,-1.89767 -1.99201,-2.91087 -3.73952,-3.87887 l -1.59982,-0.88619 -37.78386,-0.0796 c -42.45592,-0.0894 -39.40239,-0.2483 -42.11646,2.19188 -0.87544,0.78709 -1.75715,1.95946 -2.18393,2.90385 -0.71264,1.57698 -0.71613,1.63839 -0.80561,14.20405 l -0.0899,12.62022 1.79817,-0.13007 c 1.42577,-0.10313 2.08143,0.007 3.16601,0.5321 2.01294,0.97445 3.93993,2.89871 5.11476,5.10753 l 1.03717,1.95 0.007,-12.825 0.007,-12.825 33.6,0 33.59999,0 0,51.3 0,51.3 -33.59872,0 -33.59874,0 -0.0763,-34.425 c -0.073,-32.96021 -0.0982,-34.36117 -0.59098,-32.925 -0.28309,0.825 -1.80562,3.9975 -3.3834,7.05 -5.49252,10.62624 -5.40494,9.86009 -5.39597,47.20335 0.007,27.62122 0.12358,29.95084 1.66204,33.10906 1.07144,2.19949 2.71143,3.71042 5.05823,4.66019 l 1.67381,0.6774 36.1677,0.0797 36.16768,0.0797 1.864,-0.93145 z m -42.39938,-5.03813 c -2.87119,-1.30885 -4.45771,-3.6784 -4.43003,-6.61652 0.0388,-4.11587 3.1088,-7.22328 7.1364,-7.22328 2.11956,0 3.56727,0.60889 5.16364,2.17177 2.24518,2.19807 2.75398,5.43897 1.30101,8.28704 -0.71312,1.39782 -2.52137,3.00905 -3.96214,3.53045 -1.49707,0.54176 -3.84003,0.47454 -5.20888,-0.14946 z" + id="path4201-1" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccsscssscssssscssssscsssscccsscsscsssscscscccccccccscscsscccccsssssss" /> + <g + id="g4838" + transform="matrix(0.08166146,0,0,0.08166146,285.28364,535.81196)"> + <rect + id="rect4270" + width="640" + height="976" + ry="291" + style="fill:#0a3d91" + x="129.51247" + y="-189.58334" /> + <path + id="path4272" + d="m 286.51248,140.41666 305,307.00003 -147,178 0,-636.000027 147,169.999997 -305,299.00003" + style="fill:none;stroke:#ffffff;stroke-width:53" + inkscape:connector-curvature="0" /> + </g> + <path + style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#0a3d91;stroke-width:7.55;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:7.55,22.65;stroke-dashoffset:0;stroke-opacity:1" + d="m 219.05071,474.19795 c 70.76376,46.48067 139.93534,38.03285 208.24431,-0.1739" + id="path4844" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + </g> +</svg> diff --git a/briar-android/res/drawable/bluetooth.png b/briar-android/res/drawable/bluetooth.png index b5e6ef06fc3ae965337813ee01dcfc195e59dfb5..f99091e6726ba462096b3b4abe568e86e874ece6 100644 GIT binary patch literal 25814 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 X=Hy;G`iLPk0lw*I7^+v?w|n_NG2#*Y literal 19988 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 Rc2EES002ovPDHLkV1hUWq#XbN diff --git a/briar-android/res/layout/invitation_bluetooth_confirmation_code.xml b/briar-android/res/layout/invitation_bluetooth_confirmation_code.xml new file mode 100644 index 0000000000..bf0d3ee68a --- /dev/null +++ b/briar-android/res/layout/invitation_bluetooth_confirmation_code.xml @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="@dimen/margin_activity_vertical" + android:paddingEnd="@dimen/margin_activity_horizontal" + android:paddingRight="@dimen/margin_activity_horizontal" + android:paddingStart="@dimen/margin_activity_horizontal" + android:paddingLeft="@dimen/margin_activity_horizontal" + android:paddingTop="@dimen/margin_activity_vertical"> + + <TextView + android:id="@+id/stepView" + style="@style/BriarTextBody" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:padding="@dimen/margin_medium" + tools:text="Step 3/3" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true"/> + + <TextView + android:id="@+id/connectedView" + style="@style/BriarTextTitle" + android:textSize="@dimen/text_size_large" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/connected_to_contact" + android:padding="@dimen/margin_medium" + android:layout_below="@+id/stepView" + android:layout_centerHorizontal="true" + android:drawableLeft="@drawable/navigation_accept" + android:drawableStart="@drawable/navigation_accept" + android:gravity="center_vertical"/> + + <TextView + android:id="@+id/yourConfirmationCodeView" + style="@style/BriarTextBody" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/your_confirmation_code" + android:padding="@dimen/margin_medium" + android:layout_below="@+id/connectedView" + android:layout_centerHorizontal="true"/> + + <TextView + android:id="@+id/codeView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="@dimen/margin_medium" + android:textSize="50sp" + android:layout_below="@+id/yourConfirmationCodeView" + android:layout_centerHorizontal="true" + tools:text="1337"/> + + <TextView + android:id="@+id/waitingView" + style="@style/BriarTextBody" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/searching_format" + android:layout_gravity="center_horizontal" + android:padding="@dimen/margin_medium" + android:layout_below="@+id/codeView" + android:layout_centerHorizontal="true" + android:visibility="gone" + android:gravity="center_horizontal"/> + + <ProgressBar + android:id="@+id/progressBar" + style="?android:attr/progressBarStyleLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:indeterminate="true" + android:layout_below="@+id/waitingView" + android:layout_centerHorizontal="true" + android:visibility="gone"/> + + <TextView + android:id="@+id/enterCodeTextView" + style="@style/BriarTextBody" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/enter_confirmation_code" + android:layout_gravity="center_horizontal" + android:padding="@dimen/margin_medium" + android:layout_below="@+id/codeView" + android:layout_centerHorizontal="true"/> + + <include + android:id="@+id/codeEntryView" + layout="@layout/view_code_entry" + android:layout_below="@+id/enterCodeTextView" + android:layout_centerHorizontal="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="@dimen/margin_medium"/> + + <Button + android:id="@+id/continueButton" + style="@style/BriarButton.Default" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/continue_button" + android:layout_gravity="center_horizontal" + android:enabled="false" + android:layout_below="@+id/codeEntryView" + android:layout_centerHorizontal="true" + android:layout_margin="@dimen/margin_medium"/> + + </RelativeLayout> +</ScrollView> \ No newline at end of file diff --git a/briar-android/res/layout/invitation_bluetooth_invitation_code.xml b/briar-android/res/layout/invitation_bluetooth_invitation_code.xml new file mode 100644 index 0000000000..875a58d08a --- /dev/null +++ b/briar-android/res/layout/invitation_bluetooth_invitation_code.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="@dimen/margin_activity_vertical" + android:paddingEnd="@dimen/margin_activity_horizontal" + android:paddingRight="@dimen/margin_activity_horizontal" + android:paddingStart="@dimen/margin_activity_horizontal" + android:paddingLeft="@dimen/margin_activity_horizontal" + android:paddingTop="@dimen/margin_activity_vertical"> + + <TextView + android:id="@+id/stepView" + style="@style/BriarTextBody" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:layout_marginTop="@dimen/margin_medium" + tools:text="Step 2/3" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true"/> + + <TextView + android:id="@+id/yourCodeView" + style="@style/BriarTextBody" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/your_invitation_code" + android:layout_marginTop="@dimen/margin_medium" + android:layout_below="@+id/stepView" + android:layout_centerHorizontal="true"/> + + <TextView + android:id="@+id/codeView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/margin_medium" + android:textSize="50sp" + android:layout_below="@+id/yourCodeView" + android:layout_centerHorizontal="true" + tools:text="1337"/> + + <TextView + android:id="@+id/waitingView" + style="@style/BriarTextBody" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/searching_format" + android:layout_gravity="center_horizontal" + android:layout_marginTop="@dimen/margin_medium" + android:layout_below="@+id/codeView" + android:layout_centerHorizontal="true" + android:visibility="gone" + android:gravity="center_horizontal"/> + + <ProgressBar + android:id="@+id/progressBar" + style="?android:attr/progressBarStyleLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:indeterminate="true" + android:layout_below="@+id/waitingView" + android:layout_centerHorizontal="true" + android:visibility="gone"/> + + <TextView + android:id="@+id/enterCodeTextView" + style="@style/BriarTextBody" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/enter_invitation_code" + android:layout_gravity="center_horizontal" + android:padding="@dimen/margin_medium" + android:layout_below="@+id/codeView" + android:layout_centerHorizontal="true"/> + + <include + android:id="@+id/codeEntryView" + layout="@layout/view_code_entry" + android:layout_below="@+id/enterCodeTextView" + android:layout_centerHorizontal="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="@dimen/margin_medium"/> + + <Button + android:id="@+id/continueButton" + style="@style/BriarButton.Default" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/continue_button" + android:layout_gravity="center_horizontal" + android:enabled="false" + android:layout_below="@+id/codeEntryView" + android:layout_centerHorizontal="true" + android:layout_margin="@dimen/margin_medium"/> + + </RelativeLayout> +</ScrollView> \ No newline at end of file diff --git a/briar-android/res/layout/invitation_bluetooth_start.xml b/briar-android/res/layout/invitation_bluetooth_start.xml index 855e9d0791..63bf3ea6d5 100644 --- a/briar-android/res/layout/invitation_bluetooth_start.xml +++ b/briar-android/res/layout/invitation_bluetooth_start.xml @@ -1,53 +1,77 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:padding="7dp"> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/your_nickname" - android:id="@+id/yourNicknameView" - android:padding="7dp"/> - - <Spinner - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:id="@+id/spinner" - android:spinnerMode="dropdown" - android:padding="7dp"/> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/face_to_face" - android:id="@+id/faceToFaceView" - android:padding="7dp"/> - - <ImageView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:id="@+id/imageView" - android:src="@drawable/bluetooth" - android:scaleType="fitCenter" - android:baselineAlignBottom="false" - android:cropToPadding="false" - android:layout_weight="1"/> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/bluetooth_explanation" - android:id="@+id/explanationView" - android:padding="7dp"/> - - <Button - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/continue_button" - android:id="@+id/continueButton" - android:layout_gravity="center_horizontal" - android:layout_weight="0"/> -</LinearLayout> \ No newline at end of file +<ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="@dimen/margin_activity_vertical" + android:paddingEnd="@dimen/margin_activity_horizontal" + android:paddingRight="@dimen/margin_activity_horizontal" + android:paddingStart="@dimen/margin_activity_horizontal" + android:paddingLeft="@dimen/margin_activity_horizontal" + android:paddingTop="@dimen/margin_activity_vertical"> + + <TextView + android:id="@+id/stepView" + style="@style/BriarTextBody" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end" + tools:text="Step 1/3"/> + + <TextView + android:id="@+id/yourNicknameView" + style="@style/BriarTextBody" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/your_nickname" + android:layout_marginTop="@dimen/margin_medium"/> + + <Spinner + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/spinner" + android:spinnerMode="dropdown" + android:layout_marginTop="@dimen/margin_medium"/> + + <TextView + android:id="@+id/faceToFaceView" + style="@style/BriarTextBody" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/face_to_face" + android:layout_marginTop="@dimen/margin_medium"/> + + <ImageView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/imageView" + android:src="@drawable/bluetooth" + android:scaleType="fitCenter" + android:adjustViewBounds="true" + android:layout_marginTop="@dimen/margin_medium"/> + + <TextView + android:id="@+id/explanationView" + style="@style/BriarTextBody" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/bluetooth_explanation" + android:layout_marginTop="@dimen/margin_medium"/> + + <Button + android:id="@+id/continueButton" + style="@style/BriarButton.Default" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/continue_button" + android:layout_gravity="center_horizontal" + android:layout_marginTop="@dimen/margin_medium"/> + </LinearLayout> + +</ScrollView> \ No newline at end of file diff --git a/briar-android/res/layout/invitation_error.xml b/briar-android/res/layout/invitation_error.xml new file mode 100644 index 0000000000..e1c94c3936 --- /dev/null +++ b/briar-android/res/layout/invitation_error.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingBottom="@dimen/margin_activity_vertical" + android:paddingEnd="@dimen/margin_activity_horizontal" + android:paddingStart="@dimen/margin_activity_horizontal" + android:paddingTop="@dimen/margin_activity_vertical"> + + <TextView + android:id="@+id/errorTextView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/connection_failed" + android:layout_gravity="center_horizontal" + android:textSize="@dimen/text_size_large" + android:drawableStart="@drawable/alerts_and_states_error" + android:drawableLeft="@drawable/alerts_and_states_error" + android:gravity="center_vertical" + android:padding="@dimen/margin_medium"/> + + <TextView + android:id="@+id/explanationTextView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/interfering" + android:layout_gravity="center_horizontal" + android:padding="@dimen/margin_medium"/> + + <Button + android:id="@+id/tryAgainButton" + style="@style/BriarButton.Default" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/try_again_button" + android:layout_gravity="center_horizontal" + android:layout_margin="@dimen/margin_medium"/> + +</LinearLayout> \ No newline at end of file diff --git a/briar-android/res/layout/view_code_entry.xml b/briar-android/res/layout/view_code_entry.xml new file mode 100644 index 0000000000..018915876e --- /dev/null +++ b/briar-android/res/layout/view_code_entry.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<EditText + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/codeEntryView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:inputType="number" + android:layout_gravity="center_horizontal" + android:textSize="@dimen/text_size_xlarge" + android:ems="4" + android:maxLines="1" + android:maxLength="6" + android:layout_margin="@dimen/margin_medium" + android:imeOptions="actionGo" + tools:text="123456"/> \ No newline at end of file diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 0076ca171c..8a6c06144b 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -42,7 +42,7 @@ <string name="continue_button">Continue</string> <string name="your_invitation_code">Your invitation code is</string> <string name="enter_invitation_code">Please enter your contact\'s invitation code:</string> - <string name="searching_format">Searching for %06d\u2026</string> + <string name="searching_format">Searching for contact with invitation code %06d\u2026</string> <string name="connection_failed">Connection failed</string> <string name="could_not_find_contact">Briar could not find your contact nearby</string> <string name="try_again_button">Try Again</string> @@ -111,8 +111,9 @@ <string name="notify_sound_setting_default">Default ringtone</string> <string name="notify_sound_setting_disabled">None</string> <string name="choose_ringtone_title">Choose ringtone</string> + <string name="step">Step %1$d/%2$d</string> <!-- Dialogs --> <string name="dialog_title_lost_password">Lost password</string> <string name="dialog_message_lost_password">Password recovery is not possible. Do you wish to delete your user, all contacts, and re-register ?</string> -</resources> \ No newline at end of file +</resources> diff --git a/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java b/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java index eb0a52e9da..ab318363be 100644 --- a/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java +++ b/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java @@ -1,13 +1,9 @@ package org.briarproject.android.invitation; -import static android.widget.Toast.LENGTH_LONG; -import static java.util.logging.Level.INFO; -import static java.util.logging.Level.WARNING; - -import java.util.Collection; -import java.util.logging.Logger; - -import javax.inject.Inject; +import android.bluetooth.BluetoothAdapter; +import android.content.Intent; +import android.os.Bundle; +import android.widget.Toast; import org.briarproject.R; import org.briarproject.android.BriarActivity; @@ -24,10 +20,17 @@ import org.briarproject.api.invitation.InvitationState; import org.briarproject.api.invitation.InvitationTask; import org.briarproject.api.invitation.InvitationTaskFactory; -import android.bluetooth.BluetoothAdapter; -import android.content.Intent; -import android.os.Bundle; -import android.widget.Toast; +import java.util.Collection; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import static android.widget.Toast.LENGTH_LONG; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; +import static org.briarproject.android.invitation.ConfirmationCodeView.ConfirmationState.CONNECTED; +import static org.briarproject.android.invitation.ConfirmationCodeView.ConfirmationState.WAIT_FOR_CONTACT; +import static org.briarproject.android.invitation.ConfirmationCodeView.ConfirmationState.DETAILS; public class AddContactActivity extends BriarActivity implements InvitationListener { @@ -86,9 +89,10 @@ implements InvitationListener { } else if (remoteInvitationCode == -1) { setView(new InvitationCodeView(this)); } else if (connectionFailed) { - setView(new ConnectionFailedView(this)); + setView(new ErrorView(this, R.string.connection_failed, + R.string.could_not_find_contact)); } else if (contactName == null) { - setView(new CodesDoNotMatchView(this)); + setView(new ErrorView(this, R.string.codes_do_not_match, R.string.interfering)); } else { showToastAndFinish(); return; @@ -113,24 +117,25 @@ implements InvitationListener { } else if (remoteInvitationCode == -1) { setView(new InvitationCodeView(this)); } else if (connectionFailed) { - setView(new ConnectionFailedView(this)); + setView(new ErrorView(AddContactActivity.this, R.string.connection_failed, + R.string.could_not_find_contact)); } else if (connected && localConfirmationCode == -1) { - setView(new ConnectedView(this)); + setView(new ConfirmationCodeView(this, CONNECTED)); } else if (localConfirmationCode == -1) { - setView(new ConnectionView(this)); + setView(new InvitationCodeView(this, true)); } else if (!localCompared) { setView(new ConfirmationCodeView(this)); } else if (!remoteCompared) { - setView(new WaitForContactView(this)); + setView(new ConfirmationCodeView(this, WAIT_FOR_CONTACT)); } else if (localMatched && remoteMatched) { if (contactName == null) { - setView(new ContactDetailsView(this)); + setView(new ConfirmationCodeView(this, DETAILS)); } else { showToastAndFinish(); return; } } else { - setView(new CodesDoNotMatchView(this)); + setView(new ErrorView(this, R.string.codes_do_not_match, R.string.interfering)); } } } @@ -276,7 +281,10 @@ implements InvitationListener { if (localAuthorId == null) throw new IllegalStateException(); if (localInvitationCode == -1) throw new IllegalStateException(); remoteInvitationCode = code; - setView(new ConnectionView(this)); + + // change UI to show a progress indicator + setView(new InvitationCodeView(this, true)); + task = invitationTaskFactory.createTask(localAuthorId, localInvitationCode, code, enableBluetooth); taskHandle = referenceManager.putReference(task, InvitationTask.class); @@ -295,13 +303,14 @@ implements InvitationListener { localCompared = true; if (code == remoteConfirmationCode) { localMatched = true; - if (remoteMatched) setView(new ContactDetailsView(this)); - else if (remoteCompared) setView(new CodesDoNotMatchView(this)); - else setView(new WaitForContactView(this)); + if (remoteMatched) setView(new ConfirmationCodeView(this, DETAILS)); + else if (remoteCompared) setView(new ErrorView(this, R.string.codes_do_not_match, + R.string.interfering)); + else setView(new ConfirmationCodeView(this, WAIT_FOR_CONTACT)); task.localConfirmationSucceeded(); } else { localMatched = false; - setView(new CodesDoNotMatchView(this)); + setView(new ErrorView(this, R.string.codes_do_not_match, R.string.interfering)); task.localConfirmationFailed(); } } @@ -314,7 +323,7 @@ implements InvitationListener { runOnUiThread(new Runnable() { public void run() { connected = true; - setView(new ConnectedView(AddContactActivity.this)); + setView(new ConfirmationCodeView(AddContactActivity.this, CONNECTED)); } }); } @@ -323,7 +332,8 @@ implements InvitationListener { runOnUiThread(new Runnable() { public void run() { connectionFailed = true; - setView(new ConnectionFailedView(AddContactActivity.this)); + setView(new ErrorView(AddContactActivity.this, R.string.connection_failed, + R.string.could_not_find_contact)); } }); } @@ -343,7 +353,8 @@ implements InvitationListener { runOnUiThread(new Runnable() { public void run() { connectionFailed = true; - setView(new ConnectionFailedView(AddContactActivity.this)); + setView(new ErrorView(AddContactActivity.this, R.string.connection_failed, + R.string.could_not_find_contact)); } }); } @@ -354,7 +365,7 @@ implements InvitationListener { remoteCompared = true; remoteMatched = true; if (localMatched) - setView(new ContactDetailsView(AddContactActivity.this)); + setView(new ConfirmationCodeView(AddContactActivity.this, DETAILS)); } }); } @@ -365,7 +376,8 @@ implements InvitationListener { remoteCompared = true; remoteMatched = false; if (localMatched) - setView(new CodesDoNotMatchView(AddContactActivity.this)); + setView(new ErrorView(AddContactActivity.this, R.string.codes_do_not_match, + R.string.interfering)); } }); } @@ -382,7 +394,8 @@ implements InvitationListener { public void pseudonymExchangeFailed() { runOnUiThread(new Runnable() { public void run() { - setView(new ConnectionFailedView(AddContactActivity.this)); + setView(new ErrorView(AddContactActivity.this, R.string.connection_failed, + R.string.could_not_find_contact)); } }); } diff --git a/briar-android/src/org/briarproject/android/invitation/AddContactView.java b/briar-android/src/org/briarproject/android/invitation/AddContactView.java index 2a6b978493..6a6b300be3 100644 --- a/briar-android/src/org/briarproject/android/invitation/AddContactView.java +++ b/briar-android/src/org/briarproject/android/invitation/AddContactView.java @@ -1,31 +1,22 @@ package org.briarproject.android.invitation; -import static android.view.Gravity.CENTER_HORIZONTAL; -import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH; - -import org.briarproject.android.util.LayoutUtils; - import android.content.Context; import android.widget.LinearLayout; abstract class AddContactView extends LinearLayout { - protected final int pad; - + static final public int CODE_LEN = 6; protected AddContactActivity container = null; AddContactView(Context ctx) { super(ctx); - pad = LayoutUtils.getPadding(ctx); } void init(AddContactActivity container) { this.container = container; - setLayoutParams(MATCH_MATCH); - setOrientation(VERTICAL); - setGravity(CENTER_HORIZONTAL); populate(); } abstract void populate(); + } diff --git a/briar-android/src/org/briarproject/android/invitation/ChooseIdentityView.java b/briar-android/src/org/briarproject/android/invitation/ChooseIdentityView.java index fc2e4524a3..f609eb93f2 100644 --- a/briar-android/src/org/briarproject/android/invitation/ChooseIdentityView.java +++ b/briar-android/src/org/briarproject/android/invitation/ChooseIdentityView.java @@ -49,6 +49,11 @@ implements OnItemSelectedListener, OnClickListener { (Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.invitation_bluetooth_start, this); + // current step + // TODO this could go into the ActionBar eventually + TextView step = (TextView) view.findViewById(R.id.stepView); + step.setText(String.format(ctx.getString(R.string.step), 1, 3)); + adapter = new LocalAuthorSpinnerAdapter(ctx, false); spinner = (Spinner) view.findViewById(R.id.spinner); spinner.setAdapter(adapter); diff --git a/briar-android/src/org/briarproject/android/invitation/CodeEntryListener.java b/briar-android/src/org/briarproject/android/invitation/CodeEntryListener.java deleted file mode 100644 index e63c8b1777..0000000000 --- a/briar-android/src/org/briarproject/android/invitation/CodeEntryListener.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.briarproject.android.invitation; - -interface CodeEntryListener { - - void codeEntered(int remoteCode); -} diff --git a/briar-android/src/org/briarproject/android/invitation/CodeEntryView.java b/briar-android/src/org/briarproject/android/invitation/CodeEntryView.java deleted file mode 100644 index d15bfdab37..0000000000 --- a/briar-android/src/org/briarproject/android/invitation/CodeEntryView.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.briarproject.android.invitation; - -import static android.content.Context.INPUT_METHOD_SERVICE; -import static android.text.InputType.TYPE_CLASS_NUMBER; -import static android.view.Gravity.CENTER; -import static android.view.Gravity.CENTER_HORIZONTAL; -import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY; -import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP; - -import org.briarproject.R; -import org.briarproject.android.util.LayoutUtils; - -import android.content.Context; -import android.view.KeyEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.inputmethod.InputMethodManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; - -class CodeEntryView extends LinearLayout -implements OnEditorActionListener, OnClickListener { - - private final int pad; - - private CodeEntryListener listener = null; - private EditText codeEntry = null; - private Button continueButton = null; - - public CodeEntryView(Context ctx) { - super(ctx); - pad = LayoutUtils.getPadding(ctx); - } - - void init(CodeEntryListener listener, String prompt) { - this.listener = listener; - setOrientation(VERTICAL); - setGravity(CENTER_HORIZONTAL); - - Context ctx = getContext(); - TextView enterCode = new TextView(ctx); - enterCode.setGravity(CENTER_HORIZONTAL); - enterCode.setPadding(pad, pad, pad, 0); - enterCode.setText(prompt); - addView(enterCode); - - LinearLayout innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - codeEntry = new EditText(ctx) { - @Override - protected void onTextChanged(CharSequence text, int start, - int lengthBefore, int lengthAfter) { - if (continueButton != null) - continueButton.setEnabled(getText().length() == 6); - } - }; - codeEntry.setId(1); // FIXME: State is not saved and restored - codeEntry.setTextSize(26); - codeEntry.setOnEditorActionListener(this); - codeEntry.setMinEms(5); - codeEntry.setMaxEms(5); - codeEntry.setMaxLines(1); - codeEntry.setInputType(TYPE_CLASS_NUMBER); - innerLayout.addView(codeEntry); - - continueButton = new Button(ctx); - continueButton.setLayoutParams(WRAP_WRAP); - continueButton.setText(R.string.continue_button); - continueButton.setEnabled(false); - continueButton.setOnClickListener(this); - innerLayout.addView(continueButton); - addView(innerLayout); - } - - public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) { - if (!validateAndReturnCode()) codeEntry.setText(""); - return true; - } - - public void onClick(View view) { - if (!validateAndReturnCode()) codeEntry.setText(""); - } - - private boolean validateAndReturnCode() { - String remoteCodeString = codeEntry.getText().toString(); - int remoteCode; - try { - remoteCode = Integer.parseInt(remoteCodeString); - } catch (NumberFormatException e) { - return false; - } - // Hide the soft keyboard - Object o = getContext().getSystemService(INPUT_METHOD_SERVICE); - ((InputMethodManager) o).toggleSoftInput(HIDE_IMPLICIT_ONLY, 0); - listener.codeEntered(remoteCode); - return true; - } -} diff --git a/briar-android/src/org/briarproject/android/invitation/ConfirmationCodeView.java b/briar-android/src/org/briarproject/android/invitation/ConfirmationCodeView.java index 7c52cd880b..118744cc92 100644 --- a/briar-android/src/org/briarproject/android/invitation/ConfirmationCodeView.java +++ b/briar-android/src/org/briarproject/android/invitation/ConfirmationCodeView.java @@ -1,60 +1,126 @@ package org.briarproject.android.invitation; -import static android.view.Gravity.CENTER; -import static android.view.Gravity.CENTER_HORIZONTAL; +import android.content.Context; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; import org.briarproject.R; -import android.content.Context; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; +import static android.content.Context.INPUT_METHOD_SERVICE; +import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY; + +class ConfirmationCodeView extends AddContactView { -class ConfirmationCodeView extends AddContactView implements CodeEntryListener { + public enum ConfirmationState { CONNECTED, ENTER_CODE, WAIT_FOR_CONTACT, DETAILS } + private ConfirmationState state; ConfirmationCodeView(Context ctx) { super(ctx); + this.state = ConfirmationState.ENTER_CODE; + } + + ConfirmationCodeView(Context ctx, ConfirmationState state) { + super(ctx); + this.state = state; } void populate() { removeAllViews(); Context ctx = getContext(); - LinearLayout innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - ImageView icon = new ImageView(ctx); - icon.setImageResource(R.drawable.navigation_accept); - innerLayout.addView(icon); - - TextView connected = new TextView(ctx); - connected.setTextSize(22); - connected.setPadding(pad, pad, pad, pad); - connected.setText(R.string.connected_to_contact); - innerLayout.addView(connected); - addView(innerLayout); - - TextView yourCode = new TextView(ctx); - yourCode.setGravity(CENTER_HORIZONTAL); - yourCode.setPadding(pad, pad, pad, pad); - yourCode.setText(R.string.your_confirmation_code); - addView(yourCode); - - TextView code = new TextView(ctx); - code.setGravity(CENTER_HORIZONTAL); - code.setTextSize(50); - code.setPadding(pad, 0, pad, pad); + + LayoutInflater inflater = (LayoutInflater) ctx.getSystemService + (Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.invitation_bluetooth_confirmation_code, this); + + // current step + // TODO this could go into the ActionBar eventually + TextView step = (TextView) view.findViewById(R.id.stepView); + step.setText(String.format(ctx.getString(R.string.step), 3, 3)); + + // local confirmation code + TextView code = (TextView) view.findViewById(R.id.codeView); int localCode = container.getLocalConfirmationCode(); code.setText(String.format("%06d", localCode)); - addView(code); - CodeEntryView codeEntry = new CodeEntryView(ctx); - String enter = container.getString(R.string.enter_confirmation_code); - codeEntry.init(this, enter); - addView(codeEntry); + if (state != ConfirmationState.ENTER_CODE) { + // hide views we no longer need + view.findViewById(R.id.enterCodeTextView).setVisibility(View.GONE); + view.findViewById(R.id.codeEntryView).setVisibility(View.GONE); + view.findViewById(R.id.continueButton).setVisibility(View.GONE); + + // show progress indicator + view.findViewById(R.id.progressBar).setVisibility(View.VISIBLE); + + // show what we are waiting for + TextView connecting = (TextView) view.findViewById(R.id.waitingView); + int textId; + if (state == ConfirmationState.CONNECTED) { + textId = R.string.calculating_confirmation_code; + view.findViewById(R.id.yourConfirmationCodeView).setVisibility(View.GONE); + view.findViewById(R.id.codeView).setVisibility(View.GONE); + } else if (state == ConfirmationState.WAIT_FOR_CONTACT) { + textId = R.string.waiting_for_contact; + } else { + textId = R.string.exchanging_contact_details; + } + connecting.setText(ctx.getString(textId)); + connecting.setVisibility(View.VISIBLE); + } + else { + // handle click on continue button + final EditText codeEntry = (EditText) view.findViewById(R.id.codeEntryView); + final Button continueButton = (Button) view.findViewById(R.id.continueButton); + continueButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + send(codeEntry); + } + }); + + // activate continue button only when we have a 6 digit (CODE_LEN) code + codeEntry.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + continueButton.setEnabled(codeEntry.getText().length() == CODE_LEN); + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + + codeEntry.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_GO && v.getText().length() == CODE_LEN) { + send(v); + return true; + } + return false; + } + }); + } } - public void codeEntered(int remoteCode) { - container.remoteConfirmationCodeEntered(remoteCode); + private void send(TextView codeEntry) { + int code = Integer.parseInt(codeEntry.getText().toString()); + container.remoteConfirmationCodeEntered(code); + + // Hide the soft keyboard + Object o = getContext().getSystemService(INPUT_METHOD_SERVICE); + ((InputMethodManager) o).hideSoftInputFromWindow(codeEntry.getWindowToken(), 0); } + } diff --git a/briar-android/src/org/briarproject/android/invitation/ConnectedView.java b/briar-android/src/org/briarproject/android/invitation/ConnectedView.java deleted file mode 100644 index b9a51abec6..0000000000 --- a/briar-android/src/org/briarproject/android/invitation/ConnectedView.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.briarproject.android.invitation; - -import static android.view.Gravity.CENTER; - -import org.briarproject.R; - -import android.content.Context; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; - -class ConnectedView extends AddContactView { - - ConnectedView(Context ctx) { - super(ctx); - } - - void populate() { - removeAllViews(); - Context ctx = getContext(); - LinearLayout innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - ImageView icon = new ImageView(ctx); - icon.setImageResource(R.drawable.navigation_accept); - innerLayout.addView(icon); - - TextView connected = new TextView(ctx); - connected.setTextSize(22); - connected.setPadding(pad, pad, pad, pad); - connected.setText(R.string.connected_to_contact); - innerLayout.addView(connected); - addView(innerLayout); - - innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - ProgressBar progress = new ProgressBar(ctx); - progress.setIndeterminate(true); - progress.setPadding(pad, pad, pad, pad); - innerLayout.addView(progress); - - TextView connecting = new TextView(ctx); - connecting.setText(R.string.calculating_confirmation_code); - innerLayout.addView(connecting); - addView(innerLayout); - } -} diff --git a/briar-android/src/org/briarproject/android/invitation/ConnectionFailedView.java b/briar-android/src/org/briarproject/android/invitation/ConnectionFailedView.java deleted file mode 100644 index 346d9788c4..0000000000 --- a/briar-android/src/org/briarproject/android/invitation/ConnectionFailedView.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.briarproject.android.invitation; - -import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE; -import static android.bluetooth.BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION; -import static android.view.Gravity.CENTER; -import static org.briarproject.android.invitation.AddContactActivity.REQUEST_BLUETOOTH; -import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP; - -import org.briarproject.R; - -import android.content.Context; -import android.content.Intent; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -class ConnectionFailedView extends AddContactView implements OnClickListener { - - private Button tryAgainButton = null; - - ConnectionFailedView(Context ctx) { - super(ctx); - } - - void populate() { - removeAllViews(); - Context ctx = getContext(); - LinearLayout innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - ImageView icon = new ImageView(ctx); - icon.setImageResource(R.drawable.alerts_and_states_error); - innerLayout.addView(icon); - - TextView failed = new TextView(ctx); - failed.setTextSize(22); - failed.setPadding(pad, pad, pad, pad); - failed.setText(R.string.connection_failed); - innerLayout.addView(failed); - addView(innerLayout); - - TextView couldNotFind = new TextView(ctx); - couldNotFind.setGravity(CENTER); - couldNotFind.setPadding(pad, 0, pad, pad); - couldNotFind.setText(R.string.could_not_find_contact); - addView(couldNotFind); - - tryAgainButton = new Button(ctx); - tryAgainButton.setLayoutParams(WRAP_WRAP); - tryAgainButton.setText(R.string.try_again_button); - tryAgainButton.setOnClickListener(this); - addView(tryAgainButton); - } - - public void onClick(View view) { - Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE); - i.putExtra(EXTRA_DISCOVERABLE_DURATION, 120); - container.startActivityForResult(i, REQUEST_BLUETOOTH); - } -} diff --git a/briar-android/src/org/briarproject/android/invitation/ConnectionView.java b/briar-android/src/org/briarproject/android/invitation/ConnectionView.java deleted file mode 100644 index e85b100c0c..0000000000 --- a/briar-android/src/org/briarproject/android/invitation/ConnectionView.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.briarproject.android.invitation; - -import static android.view.Gravity.CENTER; -import static android.view.Gravity.CENTER_HORIZONTAL; - -import org.briarproject.R; - -import android.content.Context; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; - -class ConnectionView extends AddContactView { - - ConnectionView(Context ctx) { - super(ctx); - } - - void populate() { - removeAllViews(); - Context ctx = getContext(); - TextView yourCode = new TextView(ctx); - yourCode.setGravity(CENTER_HORIZONTAL); - yourCode.setPadding(pad, pad, pad, pad); - yourCode.setText(R.string.your_invitation_code); - addView(yourCode); - - TextView code = new TextView(ctx); - code.setGravity(CENTER_HORIZONTAL); - code.setTextSize(50); - code.setPadding(pad, 0, pad, pad); - int localCode = container.getLocalInvitationCode(); - code.setText(String.format("%06d", localCode)); - addView(code); - - LinearLayout innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - ProgressBar progress = new ProgressBar(ctx); - progress.setPadding(pad, pad, pad, pad); - progress.setIndeterminate(true); - innerLayout.addView(progress); - - TextView connecting = new TextView(ctx); - int remoteCode = container.getRemoteInvitationCode(); - String format = container.getString(R.string.searching_format); - connecting.setText(String.format(format, remoteCode)); - innerLayout.addView(connecting); - - addView(innerLayout); - } -} diff --git a/briar-android/src/org/briarproject/android/invitation/ContactDetailsView.java b/briar-android/src/org/briarproject/android/invitation/ContactDetailsView.java deleted file mode 100644 index 9e8a83f78c..0000000000 --- a/briar-android/src/org/briarproject/android/invitation/ContactDetailsView.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.briarproject.android.invitation; - -import static android.view.Gravity.CENTER; -import static android.view.Gravity.CENTER_HORIZONTAL; - -import org.briarproject.R; - -import android.content.Context; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; - -class ContactDetailsView extends AddContactView { - - ContactDetailsView(Context ctx) { - super(ctx); - } - - void populate() { - removeAllViews(); - Context ctx = getContext(); - LinearLayout innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - ImageView icon = new ImageView(ctx); - icon.setImageResource(R.drawable.navigation_accept); - innerLayout.addView(icon); - - TextView connected = new TextView(ctx); - connected.setTextSize(22); - connected.setPadding(pad, pad, pad, pad); - connected.setText(R.string.connected_to_contact); - innerLayout.addView(connected); - addView(innerLayout); - - TextView yourCode = new TextView(ctx); - yourCode.setGravity(CENTER_HORIZONTAL); - yourCode.setPadding(pad, 0, pad, pad); - yourCode.setText(R.string.your_confirmation_code); - addView(yourCode); - - TextView code = new TextView(ctx); - code.setGravity(CENTER_HORIZONTAL); - code.setTextSize(50); - code.setPadding(pad, 0, pad, pad); - int localCode = container.getLocalConfirmationCode(); - code.setText(String.format("%06d", localCode)); - addView(code); - - innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - ProgressBar progress = new ProgressBar(ctx); - progress.setIndeterminate(true); - progress.setPadding(pad, pad, pad, pad); - innerLayout.addView(progress); - - TextView connecting = new TextView(ctx); - connecting.setText(R.string.exchanging_contact_details); - innerLayout.addView(connecting); - addView(innerLayout); - } -} diff --git a/briar-android/src/org/briarproject/android/invitation/CodesDoNotMatchView.java b/briar-android/src/org/briarproject/android/invitation/ErrorView.java similarity index 52% rename from briar-android/src/org/briarproject/android/invitation/CodesDoNotMatchView.java rename to briar-android/src/org/briarproject/android/invitation/ErrorView.java index ea800ccfc8..0f17a4ebf0 100644 --- a/briar-android/src/org/briarproject/android/invitation/CodesDoNotMatchView.java +++ b/briar-android/src/org/briarproject/android/invitation/ErrorView.java @@ -10,6 +10,7 @@ import org.briarproject.R; import android.content.Context; import android.content.Intent; +import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; @@ -17,41 +18,39 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -class CodesDoNotMatchView extends AddContactView implements OnClickListener { +class ErrorView extends AddContactView implements OnClickListener { - CodesDoNotMatchView(Context ctx) { + private final int error; + private final int explanation; + + ErrorView(Context ctx) { + super(ctx); + this.error = R.string.connection_failed; + this.explanation = R.string.could_not_find_contact; + } + + ErrorView(Context ctx, int error, int explanation) { super(ctx); + this.error = error; + this.explanation = explanation; } void populate() { removeAllViews(); Context ctx = getContext(); - LinearLayout innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - ImageView icon = new ImageView(ctx); - icon.setImageResource(R.drawable.alerts_and_states_error); - innerLayout.addView(icon); - - TextView failed = new TextView(ctx); - failed.setTextSize(22); - failed.setPadding(pad, pad, pad, pad); - failed.setText(R.string.codes_do_not_match); - innerLayout.addView(failed); - addView(innerLayout); - - TextView interfering = new TextView(ctx); - interfering.setGravity(CENTER); - interfering.setPadding(pad, 0, pad, pad); - interfering.setText(R.string.interfering); - addView(interfering); - - Button tryAgainButton = new Button(ctx); - tryAgainButton.setLayoutParams(WRAP_WRAP); - tryAgainButton.setText(R.string.try_again_button); + + LayoutInflater inflater = (LayoutInflater) ctx.getSystemService + (Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.invitation_error, this); + + TextView errorView = (TextView) view.findViewById(R.id.errorTextView); + errorView.setText(ctx.getString(error)); + + TextView explanationView = (TextView) view.findViewById(R.id.explanationTextView); + explanationView.setText(ctx.getString(explanation)); + + Button tryAgainButton = (Button) view.findViewById(R.id.tryAgainButton); tryAgainButton.setOnClickListener(this); - addView(tryAgainButton); } public void onClick(View view) { diff --git a/briar-android/src/org/briarproject/android/invitation/InvitationCodeView.java b/briar-android/src/org/briarproject/android/invitation/InvitationCodeView.java index 1f014fc0c9..ebd6fcc13e 100644 --- a/briar-android/src/org/briarproject/android/invitation/InvitationCodeView.java +++ b/briar-android/src/org/briarproject/android/invitation/InvitationCodeView.java @@ -1,42 +1,116 @@ package org.briarproject.android.invitation; -import static android.view.Gravity.CENTER_HORIZONTAL; +import android.content.Context; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; import org.briarproject.R; -import android.content.Context; -import android.widget.TextView; +import static android.content.Context.INPUT_METHOD_SERVICE; +import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY; -class InvitationCodeView extends AddContactView implements CodeEntryListener { +class InvitationCodeView extends AddContactView { - InvitationCodeView(Context ctx) { + private boolean waiting; + + InvitationCodeView(Context ctx, boolean waiting) { super(ctx); + this.waiting = waiting; + } + + InvitationCodeView(Context ctx) { + this(ctx, false); } void populate() { removeAllViews(); Context ctx = getContext(); - TextView yourCode = new TextView(ctx); - yourCode.setGravity(CENTER_HORIZONTAL); - yourCode.setPadding(pad, pad, pad, pad); - yourCode.setText(R.string.your_invitation_code); - addView(yourCode); - - TextView code = new TextView(ctx); - code.setGravity(CENTER_HORIZONTAL); - code.setTextSize(50); - code.setPadding(pad, 0, pad, pad); + + LayoutInflater inflater = (LayoutInflater) ctx.getSystemService + (Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.invitation_bluetooth_invitation_code, this); + + // current step + // TODO this could go into the ActionBar eventually + TextView step = (TextView) view.findViewById(R.id.stepView); + step.setText(String.format(ctx.getString(R.string.step), 2, 3)); + + // local invitation code + TextView code = (TextView) view.findViewById(R.id.codeView); int localCode = container.getLocalInvitationCode(); code.setText(String.format("%06d", localCode)); - addView(code); - CodeEntryView codeEntry = new CodeEntryView(ctx); - String enter = container.getString(R.string.enter_invitation_code); - codeEntry.init(this, enter); - addView(codeEntry); + if (waiting) { + // hide views we no longer need + view.findViewById(R.id.enterCodeTextView).setVisibility(View.GONE); + view.findViewById(R.id.codeEntryView).setVisibility(View.GONE); + view.findViewById(R.id.continueButton).setVisibility(View.GONE); + + // show progress indicator + view.findViewById(R.id.progressBar).setVisibility(View.VISIBLE); + + // show which code we are waiting for + TextView connecting = (TextView) view.findViewById(R.id.waitingView); + int remoteCode = container.getRemoteInvitationCode(); + String format = container.getString(R.string.searching_format); + connecting.setText(String.format(format, remoteCode)); + connecting.setVisibility(View.VISIBLE); + } + else { + // handle click on continue button + final EditText codeEntry = (EditText) view.findViewById(R.id.codeEntryView); + final Button continueButton = (Button) view.findViewById(R.id.continueButton); + continueButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + send(codeEntry); + } + }); + + // activate continue button only when we have a 6 digit (CODE_LEN) code + codeEntry.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + continueButton.setEnabled(codeEntry.getText().length() == CODE_LEN); + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + + codeEntry.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_GO && v.getText().length() == CODE_LEN) { + send(v); + return true; + } + return false; + } + }); + } } - public void codeEntered(int remoteCode) { - container.remoteInvitationCodeEntered(remoteCode); + private void send(TextView codeEntry) { + int code = Integer.parseInt(codeEntry.getText().toString()); + container.remoteInvitationCodeEntered(code); + + // Hide the soft keyboard + Object o = getContext().getSystemService(INPUT_METHOD_SERVICE); + ((InputMethodManager) o).hideSoftInputFromWindow(codeEntry.getWindowToken(), 0); } + } diff --git a/briar-android/src/org/briarproject/android/invitation/WaitForContactView.java b/briar-android/src/org/briarproject/android/invitation/WaitForContactView.java deleted file mode 100644 index f417dd1139..0000000000 --- a/briar-android/src/org/briarproject/android/invitation/WaitForContactView.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.briarproject.android.invitation; - -import static android.view.Gravity.CENTER; -import static android.view.Gravity.CENTER_HORIZONTAL; - -import org.briarproject.R; - -import android.content.Context; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; - -class WaitForContactView extends AddContactView { - - WaitForContactView(Context ctx) { - super(ctx); - } - - void populate() { - removeAllViews(); - Context ctx = getContext(); - LinearLayout innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - ImageView icon = new ImageView(ctx); - icon.setImageResource(R.drawable.navigation_accept); - innerLayout.addView(icon); - - TextView connected = new TextView(ctx); - connected.setTextSize(22); - connected.setPadding(pad, pad, pad, pad); - connected.setText(R.string.connected_to_contact); - innerLayout.addView(connected); - addView(innerLayout); - - TextView yourCode = new TextView(ctx); - yourCode.setGravity(CENTER_HORIZONTAL); - yourCode.setPadding(pad, 0, pad, pad); - yourCode.setText(R.string.your_confirmation_code); - addView(yourCode); - - TextView code = new TextView(ctx); - code.setGravity(CENTER_HORIZONTAL); - code.setTextSize(50); - code.setPadding(pad, 0, pad, pad); - int localCode = container.getLocalConfirmationCode(); - code.setText(String.format("%06d", localCode)); - addView(code); - - innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(HORIZONTAL); - innerLayout.setGravity(CENTER); - - ProgressBar progress = new ProgressBar(ctx); - progress.setIndeterminate(true); - progress.setPadding(pad, pad, pad, pad); - innerLayout.addView(progress); - - TextView connecting = new TextView(ctx); - connecting.setText(R.string.waiting_for_contact); - innerLayout.addView(connecting); - addView(innerLayout); - } -} -- GitLab