From 4e0edc62801a9eb19e40965e5be31deedafd327d Mon Sep 17 00:00:00 2001
From: Aaron Huttner
Date: Tue, 23 Aug 2016 17:46:26 -0400
Subject: [PATCH] add voter declaration add disclosure agreement version bump
---
app/build.gradle | 2 +-
.../grommet/data/api/RegistrationService.java | 18 ++++-
.../data/api/model/ApiVoterRegistration.java | 1 +
.../grommet/data/db/model/RockyRequest.java | 1 +
.../registration/AdditionalInfoFragment.java | 2 +-
.../DisclosureAgreementDialogFragment.java | 73 ++++++++++++++++++
.../ui/registration/PersonalInfoFragment.java | 24 +++---
.../ReviewAndConfirmFragment.java | 28 ++++++-
.../layout/dialog_disclosure_agreement.xml | 52 +++++++++++++
.../res/layout/fragment_additional_info.xml | 3 +
.../res/layout/fragment_assistant_info.xml | 12 ++-
.../res/layout/fragment_personal_info.xml | 8 ++
app/src/main/res/values/strings.xml | 74 ++++++++++++++++++-
13 files changed, 276 insertions(+), 22 deletions(-)
create mode 100644 app/src/main/java/com/rockthevote/grommet/ui/registration/DisclosureAgreementDialogFragment.java
create mode 100644 app/src/main/res/layout/dialog_disclosure_agreement.xml
diff --git a/app/build.gradle b/app/build.gradle
index b90c92d8..6fab2dc4 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,7 +1,7 @@
// Manifest version information!
def versionMajor = 1
def versionMinor = 0
-def versionPatch = 3
+def versionPatch = 4
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
apply plugin: 'com.android.application'
diff --git a/app/src/main/java/com/rockthevote/grommet/data/api/RegistrationService.java b/app/src/main/java/com/rockthevote/grommet/data/api/RegistrationService.java
index 39a3c4a6..f1cb879e 100644
--- a/app/src/main/java/com/rockthevote/grommet/data/api/RegistrationService.java
+++ b/app/src/main/java/com/rockthevote/grommet/data/api/RegistrationService.java
@@ -63,6 +63,7 @@
import static com.rockthevote.grommet.data.db.model.RockyRequest.Status.ABANDONED;
import static com.rockthevote.grommet.data.db.model.RockyRequest.Status.FORM_COMPLETE;
import static com.rockthevote.grommet.data.db.model.RockyRequest.Status.IN_PROGRESS;
+import static com.rockthevote.grommet.data.db.model.RockyRequest.Status.REGISTER_CLIENT_FAILURE;
import static com.rockthevote.grommet.data.db.model.RockyRequest.Status.REGISTER_SERVER_FAILURE;
import static com.rockthevote.grommet.data.db.model.RockyRequest.Status.REGISTER_SUCCESS;
import static java.util.Map.Entry;
@@ -163,6 +164,14 @@ private void doWork(final RockyRequest rockyRequest) {
.status(status)
.build(),
RockyRequest._ID + " = ? ", String.valueOf(rockyRequest.id()));
+ },
+ throwable -> {
+ // mark the row for removal if the data is corrupt
+ db.update(RockyRequest.TABLE,
+ new RockyRequest.Builder()
+ .status(REGISTER_CLIENT_FAILURE)
+ .build(),
+ RockyRequest._ID + " = ? ", String.valueOf(rockyRequest.id()));
}
);
}
@@ -181,6 +190,7 @@ private void cleanup() {
+ STATUS + " IN ("
+ "'" + ABANDONED + "', "
+ "'" + REGISTER_SERVER_FAILURE + "',"
+ + "'" + REGISTER_CLIENT_FAILURE + "',"
+ "'" + REGISTER_SUCCESS + "'"
+ ")";
@@ -270,8 +280,11 @@ private ApiRockyRequestWrapper zipRockyRequest(RockyRequest rockyRequest,
EnumMap voterIds,
EnumMap additionalInfo) {
-
- // Get address info objects
+ /*
+ * Get address info objects, this is a bit "mucky" because of the way I collect the data
+ * there will still be an address object even if the user didn't check the box for
+ * that type
+ */
ApiAddress apiRegAddress = ApiAddress.fromAddress(addresses.get(REGISTRATION_ADDRESS));
ApiAddress apiMailAddress = rockyRequest.hasMailingAddress() ?
ApiAddress.fromAddress(addresses.get(MAILING_ADDRESS)) : null;
@@ -345,7 +358,6 @@ private ApiRockyRequestWrapper zipRockyRequest(RockyRequest rockyRequest,
return ApiRockyRequestWrapper.builder().apiRockyRequest(apiRockyRequest).build();
}
-
@Nullable
@Override
public IBinder onBind(Intent intent) {
diff --git a/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiVoterRegistration.java b/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiVoterRegistration.java
index c4993c82..df8fa2c2 100644
--- a/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiVoterRegistration.java
+++ b/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiVoterRegistration.java
@@ -123,6 +123,7 @@ public static ApiVoterRegistration fromDb(RockyRequest rockyRequest,
List contactMethods,
List additionalInfo,
ApiRegistrationHelper registrationHelper) {
+
return builder()
.dateOfBirth(Dates.formatAsISO8601_ShortDate(rockyRequest.dateOfBirth()))
.mailingAddress(mailingAddress)
diff --git a/app/src/main/java/com/rockthevote/grommet/data/db/model/RockyRequest.java b/app/src/main/java/com/rockthevote/grommet/data/db/model/RockyRequest.java
index 5a5bdd32..0cec063f 100644
--- a/app/src/main/java/com/rockthevote/grommet/data/db/model/RockyRequest.java
+++ b/app/src/main/java/com/rockthevote/grommet/data/db/model/RockyRequest.java
@@ -16,6 +16,7 @@
import rx.functions.Func1;
@AutoValue
+@SuppressWarnings("mutable")
public abstract class RockyRequest implements Parcelable, BaseColumns {
public static final String TABLE = "rocky_request";
diff --git a/app/src/main/java/com/rockthevote/grommet/ui/registration/AdditionalInfoFragment.java b/app/src/main/java/com/rockthevote/grommet/ui/registration/AdditionalInfoFragment.java
index 2b7dc95f..f9131114 100644
--- a/app/src/main/java/com/rockthevote/grommet/ui/registration/AdditionalInfoFragment.java
+++ b/app/src/main/java/com/rockthevote/grommet/ui/registration/AdditionalInfoFragment.java
@@ -61,7 +61,7 @@ public class AdditionalInfoFragment extends BaseRegistrationFragment {
@BindView(R.id.does_not_have_penn_dot_checkbox) CheckBox noPennDOTCheckbox;
- @NotEmpty(messageResId = R.string.error_penn_dot)
+ @Length(min = 8, max = 8, messageResId = R.string.error_penn_dot)
@BindView(R.id.til_penn_dot) TextInputLayout pennDOTTIL;
@BindView(R.id.penn_dot_edit_text) EditText pennDOTEditText;
diff --git a/app/src/main/java/com/rockthevote/grommet/ui/registration/DisclosureAgreementDialogFragment.java b/app/src/main/java/com/rockthevote/grommet/ui/registration/DisclosureAgreementDialogFragment.java
new file mode 100644
index 00000000..a9eb5462
--- /dev/null
+++ b/app/src/main/java/com/rockthevote/grommet/ui/registration/DisclosureAgreementDialogFragment.java
@@ -0,0 +1,73 @@
+package com.rockthevote.grommet.ui.registration;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.DialogFragment;
+import android.text.Html;
+import android.text.Spanned;
+import android.text.method.LinkMovementMethod;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.rockthevote.grommet.R;
+import com.rockthevote.grommet.util.ListTagHandler;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+
+public class DisclosureAgreementDialogFragment extends DialogFragment {
+
+ @BindView(R.id.text_content) TextView content;
+
+ private DisclosureListener listener;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ setCancelable(false);
+ View v = inflater.inflate(R.layout.dialog_disclosure_agreement, container);
+ ButterKnife.bind(this, v);
+
+ Spanned result;
+
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ result = Html.fromHtml(getString(R.string.disclosure_agreement),
+ Html.FROM_HTML_MODE_LEGACY, null, new ListTagHandler());
+ } else {
+ result = Html.fromHtml(getString(R.string.disclosure_agreement),
+ null, new ListTagHandler());
+ }
+
+ content.setText(result);
+ content.setMovementMethod(LinkMovementMethod.getInstance());
+ return v;
+ }
+
+ public void setListener(DisclosureListener listener) {
+ this.listener = listener;
+ }
+
+ @OnClick(R.id.disclosure_decline_button)
+ public void onDeclineClick(View v) {
+ if (null != listener) {
+ listener.onDeclineClick();
+ }
+ }
+
+ @OnClick(R.id.disclosure_accept_button)
+ public void onAcceptClick(View v) {
+ if (null != listener) {
+ listener.onAcceptClick();
+ }
+ }
+
+ public interface DisclosureListener {
+
+ void onDeclineClick();
+
+ void onAcceptClick();
+ }
+}
diff --git a/app/src/main/java/com/rockthevote/grommet/ui/registration/PersonalInfoFragment.java b/app/src/main/java/com/rockthevote/grommet/ui/registration/PersonalInfoFragment.java
index f4056ace..ba86b2e0 100644
--- a/app/src/main/java/com/rockthevote/grommet/ui/registration/PersonalInfoFragment.java
+++ b/app/src/main/java/com/rockthevote/grommet/ui/registration/PersonalInfoFragment.java
@@ -9,10 +9,12 @@
import com.f2prateek.rx.preferences.Preference;
import com.jakewharton.rxbinding.widget.RxCompoundButton;
+import com.mobsandgeeks.saripaar.annotation.Checked;
import com.rockthevote.grommet.R;
import com.rockthevote.grommet.data.db.model.RockyRequest;
import com.rockthevote.grommet.data.db.model.VoterClassification;
import com.rockthevote.grommet.data.prefs.CurrentRockyRequestId;
+import com.rockthevote.grommet.ui.misc.ObservableValidator;
import com.rockthevote.grommet.ui.views.AddressView;
import com.squareup.sqlbrite.BriteDatabase;
@@ -33,6 +35,9 @@
public class PersonalInfoFragment extends BaseRegistrationFragment {
+ @Checked(value = false, messageResId = R.string.error_no_address)
+ @BindView(R.id.no_address_checkbox) CheckBox noAddress;
+
@BindView(R.id.home_address) AddressView homeAddress;
@BindView(R.id.mailing_address) AddressView mailingAddress;
@@ -55,6 +60,8 @@ public class PersonalInfoFragment extends BaseRegistrationFragment {
private CompositeSubscription subscriptions;
+ private ObservableValidator validator;
+
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@@ -66,6 +73,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
+ validator = new ObservableValidator(this, getActivity());
}
@Override
@@ -125,17 +133,9 @@ public Observable verify() {
Observable changedAddObs = previousAddress.verify()
.flatMap(val -> Observable.just(addressChanged.isChecked() ? val : true));
- return homeAddress.verify()
- .concatWith(mailingAddressObs)
- .concatWith(changedAddObs)
- .toList()
- .flatMap(list -> {
- Boolean ret = true;
- for (Boolean val : list) {
- ret = ret && val;
- }
-
- return Observable.just(ret);
- });
+ return Observable.zip(homeAddress.verify(), mailingAddressObs,
+ changedAddObs, validator.validate(),
+ (home, mail, change, other) -> home && mail && change && other);
+
}
}
diff --git a/app/src/main/java/com/rockthevote/grommet/ui/registration/ReviewAndConfirmFragment.java b/app/src/main/java/com/rockthevote/grommet/ui/registration/ReviewAndConfirmFragment.java
index e50c5af0..2dc6d8ed 100644
--- a/app/src/main/java/com/rockthevote/grommet/ui/registration/ReviewAndConfirmFragment.java
+++ b/app/src/main/java/com/rockthevote/grommet/ui/registration/ReviewAndConfirmFragment.java
@@ -8,6 +8,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.CheckBox;
import android.widget.TextView;
import com.f2prateek.rx.preferences.Preference;
@@ -65,6 +66,8 @@ public class ReviewAndConfirmFragment extends BaseRegistrationFragment implement
@BindView(R.id.signature_pad_error) TextView signaturePadError;
@BindView(R.id.button_register) Button buttonRegister;
+ @BindView(R.id.checkbox_agreement) CheckBox confirmCheckbox;
+
@Inject @EventRegTotal Preference eventRegTotal;
@Inject @AppRegTotal Preference appRegTotal;
@@ -73,6 +76,7 @@ public class ReviewAndConfirmFragment extends BaseRegistrationFragment implement
@Inject BriteDatabase db;
private CompositeSubscription subscriptions;
+ private DisclosureAgreementDialogFragment dialog;
@Nullable
@Override
@@ -85,11 +89,28 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
+
+ dialog = new DisclosureAgreementDialogFragment();
+ dialog.setCancelable(false);
}
@Override
public void onResume() {
super.onResume();
+ dialog.setListener(new DisclosureAgreementDialogFragment.DisclosureListener() {
+ @Override
+ public void onDeclineClick() {
+ confirmCheckbox.setChecked(false);
+ dialog.dismiss();
+ }
+
+ @Override
+ public void onAcceptClick() {
+ buttonRegister.setEnabled(true);
+ dialog.dismiss();
+ }
+ });
+
subscriptions = new CompositeSubscription();
signaturePad.setOnSignedListener(this);
@@ -155,6 +176,7 @@ public void onPause() {
super.onPause();
subscriptions.unsubscribe();
signaturePad.setOnSignedListener(null);
+ dialog.setListener(null);
}
@OnClick(R.id.clear_signature)
@@ -164,7 +186,9 @@ public void onClearSignatureClick(View v) {
@OnCheckedChanged(R.id.checkbox_agreement)
public void onCheckChanged(boolean checked) {
- buttonRegister.setEnabled(checked);
+ if (checked) {
+ dialog.show(getFragmentManager(), "disclosure_dialog");
+ }
}
@Override
@@ -192,7 +216,7 @@ public void onClear() {
RockyRequest._ID + " = ? ", String.valueOf(rockyRequestRowId.get()));
}
- // the default value is set in the datamodule
+ // the default value is set in the data module
@SuppressWarnings("ConstantConditions")
@OnClick(R.id.button_register)
public void onRegisterClick(View v) {
diff --git a/app/src/main/res/layout/dialog_disclosure_agreement.xml b/app/src/main/res/layout/dialog_disclosure_agreement.xml
new file mode 100644
index 00000000..10e26b07
--- /dev/null
+++ b/app/src/main/res/layout/dialog_disclosure_agreement.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_additional_info.xml b/app/src/main/res/layout/fragment_additional_info.xml
index d3975b75..67820bfa 100644
--- a/app/src/main/res/layout/fragment_additional_info.xml
+++ b/app/src/main/res/layout/fragment_additional_info.xml
@@ -79,6 +79,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/label_drivers_license_id"
+ android:digits="0123456789"
+ android:inputType="number"
+ android:maxLength="8"
android:maxLines="1"/>
diff --git a/app/src/main/res/layout/fragment_assistant_info.xml b/app/src/main/res/layout/fragment_assistant_info.xml
index b9da992b..e69fd3ab 100644
--- a/app/src/main/res/layout/fragment_assistant_info.xml
+++ b/app/src/main/res/layout/fragment_assistant_info.xml
@@ -29,7 +29,7 @@
android:columnCount="2"
android:useDefaultMargins="true"
android:visibility="gone"
- tools:visibility="gone"
+ tools:visibility="visible"
>
+
+
+
+
I agree that %s/Pennsylvania Voice/State Voices may call and text me with important updates and reminders."
County RequiredInvalid phone number
+ I have no permanent or mailing address
+ Complete registration using paper formPersonal
@@ -103,13 +105,28 @@
I do not know the last four of my SSNPreferred Language to Receive Election MaterialsElection Info
- Check "I do not know" if you don\'t know your PennDOT number
+ Check /"I do not know/" if you don\'t know your PennDOT numberCheck "I do not know" if you don\'t know your SSN last fourDid someone helped you with this form?Helper NameHelper AddressHelper Contact Info
- I have reviewed the above information, and it is true and accurate to the best of my knowledge.
+
+\n
+\n
+If you helped a voter complete this voter registration application, you must also sign the application.
+By checking the box, you are signing the application electronically.
+\n
+\n
+In doing so:
+\n
+\n
+\u2022 You understand that your electronic signature on this application will constitute the legal equivalent of your signature
+\n
+\n
+\u2022 You agree to sign this application by electronic means and that all laws of the Commonwealth of Pennsylvania will apply.
+
+ I CONFIRM THAT I HAVE READ AND AGREE TO THE TERMS ABOVEReview
@@ -123,6 +140,7 @@
Signature is requiredCancel RegistrationRegistration Complete
+ Voter Declaration / Penalty
\n
\u2022 Your application has been sent to your county voter registration office.
@@ -136,6 +154,56 @@
\n
\u2022 If you do not receive your Voter Registration Card within 14 days, contact your county voter registration office.
+
+
+I declare that
+
+
+
+I am a United States citizen and will have been a citizen for at least 1 month on the day of the next election.
+
+
+I will be at least 18 years old on the day of the next election.
+
+
+I will have lived at the address in section 5 for at least 30 days before the election.
+
+
+I am legally qualified to vote.
+
+
+
+I affirm that this information is true. I understand that this declaration is the same as an affidavit, and, if this information is not true, I can be convicted of perjury, and fined up to $15,000, jailed for up to 7 years, or both.
+
+By clicking \'accept\' below, you are signing the application electronically. In doing so:
+
+
+
+You agree you have read and accept the terms of the declaration above.
+
+
+You understand that your electronic signature on this application will constitute the legal equivalent of your signature for this voter registration application.
+
+
+You agree to conduct this voter registration transaction by electronic means and that all laws of the Commonwealth of Pennsylvania will apply to this transaction.
+
+
+
+If you provided your PA driver\'s license or PennDOT ID number, you understand that the signature from the PennDOT record will constitute your signature on your voter registration record. If you upload an image of your signature, you understand that the signature you upload will constitute your signature on your voter registration record. You understand that you do not have to register electronically, and may use a paper or other non-electronic form of this voter registration application.
+
+
+PENALTY FOR FALSIFYING DECLARATION
+
+
+WARNING: If a person signs an official registration application knowing a statement declared in the application to be false, makes a false registration, or furnishes false information, the person commits perjury. Perjury is punishable, upon conviction, by a term of imprisonment not exceeding seven years, or a fine not exceeding $15,000, or both, at the discretion of the court.
+
+
+Submitting an application containing false information may also subject a person to other penalties, including loss of the right of suffrage, under state or federal law.
+