-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 0e23f8ed..7ac24c77 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -3,9 +3,8 @@
-
+
-
diff --git a/app/build.gradle b/app/build.gradle
index 43f93761..f4c7d629 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -161,9 +161,9 @@ dependencies {
internalDebugCompile 'com.jakewharton.scalpel:scalpel:1.1.2'
internalDebugCompile 'com.jakewharton:process-phoenix:1.0.2'
- debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-SNAPSHOT'
- releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-SNAPSHOT'
- testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-SNAPSHOT'
+ debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4'
+ releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4'
+// testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4'
internalCompile 'com.mattprecious.telescope:telescope:2.1.0'
diff --git a/app/src/main/java/com/rockthevote/grommet/data/DataModule.java b/app/src/main/java/com/rockthevote/grommet/data/DataModule.java
index 685787ab..83aa4cd4 100644
--- a/app/src/main/java/com/rockthevote/grommet/data/DataModule.java
+++ b/app/src/main/java/com/rockthevote/grommet/data/DataModule.java
@@ -9,8 +9,8 @@
import com.rockthevote.grommet.data.api.ApiModule;
import com.rockthevote.grommet.data.api.RegistrationService;
import com.rockthevote.grommet.data.api.RockyAdapterFactory;
+import com.rockthevote.grommet.data.api.StringNormalizerFactory;
import com.rockthevote.grommet.data.db.DbModule;
-import com.rockthevote.grommet.data.prefs.AppRegTotal;
import com.rockthevote.grommet.data.prefs.CanvasserName;
import com.rockthevote.grommet.data.prefs.CurrentRockyRequestId;
import com.rockthevote.grommet.data.prefs.EventName;
@@ -73,13 +73,6 @@ Preference provideEventRegTotal(RxSharedPreferences prefs, Application
return prefs.getInteger(app.getResources().getString(R.string.pref_key_event_reg_total), 0);
}
- @Provides
- @Singleton
- @AppRegTotal
- Preference provideAppRegTotal(RxSharedPreferences prefs, Application app){
- return prefs.getInteger(app.getResources().getString(R.string.pref_key_app_reg_total), 0);
- }
-
@Provides
@Singleton
@PartnerId
@@ -126,6 +119,7 @@ Preference provideCurrentRockyRequestId(RxSharedPreferences prefs) {
@Singleton
Moshi provideMoshi() {
return new Moshi.Builder()
+ .add(new StringNormalizerFactory())
.add(RockyAdapterFactory.create())
.build();
}
diff --git a/app/src/main/java/com/rockthevote/grommet/data/api/Normalize.java b/app/src/main/java/com/rockthevote/grommet/data/api/Normalize.java
new file mode 100644
index 00000000..152efd55
--- /dev/null
+++ b/app/src/main/java/com/rockthevote/grommet/data/api/Normalize.java
@@ -0,0 +1,15 @@
+package com.rockthevote.grommet.data.api;
+
+import com.squareup.moshi.JsonQualifier;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Created by Aaron Huttner on 9/23/16. Grommet
+ */
+
+@Retention(RetentionPolicy.RUNTIME)
+@JsonQualifier
+public @interface Normalize {
+}
diff --git a/app/src/main/java/com/rockthevote/grommet/data/api/StringNormalizerFactory.java b/app/src/main/java/com/rockthevote/grommet/data/api/StringNormalizerFactory.java
new file mode 100644
index 00000000..fa805710
--- /dev/null
+++ b/app/src/main/java/com/rockthevote/grommet/data/api/StringNormalizerFactory.java
@@ -0,0 +1,55 @@
+package com.rockthevote.grommet.data.api;
+
+import com.rockthevote.grommet.util.MoshiUtils;
+import com.squareup.moshi.JsonAdapter;
+import com.squareup.moshi.JsonReader;
+import com.squareup.moshi.JsonWriter;
+import com.squareup.moshi.Moshi;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.text.Normalizer;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+
+/**
+ * Created by Aaron Huttner on 9/23/16. Grommet
+ */
+
+public class StringNormalizerFactory implements JsonAdapter.Factory {
+
+ private static final String regex = Pattern.quote("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+");
+ private static final Pattern DIACRITICS_AND_FRIENDS = Pattern.compile(regex);
+
+ private static String stripDiacritics(String str) {
+ str = Normalizer.normalize(str, Normalizer.Form.NFD);
+ str = DIACRITICS_AND_FRIENDS.matcher(str).replaceAll("");
+ return str;
+ }
+
+ @Override
+ public JsonAdapter> create(
+ Type type, Set extends Annotation> annotations, Moshi moshi) {
+ if (!type.equals(String.class)) return null;
+ if (!MoshiUtils.isAnnotationPresent(annotations, Normalize.class)) return null;
+
+ final JsonAdapter stringAdapter
+ = moshi.nextAdapter(this, String.class, MoshiUtils.NO_ANNOTATIONS);
+ return new JsonAdapter() {
+ @Override
+ public String fromJson(JsonReader reader) throws IOException {
+ String s = stringAdapter.fromJson(reader);
+ return stripDiacritics(s);
+ }
+
+ @Override
+ public void toJson(JsonWriter writer, String value) throws IOException {
+ stringAdapter.toJson(writer, stripDiacritics(value));
+ }
+ };
+ }
+
+
+}
diff --git a/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiAdditionalInfo.java b/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiAdditionalInfo.java
index 4a4c5e0a..9e7ff7f2 100644
--- a/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiAdditionalInfo.java
+++ b/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiAdditionalInfo.java
@@ -3,6 +3,7 @@
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
+import com.rockthevote.grommet.data.api.Normalize;
import com.rockthevote.grommet.data.db.model.AdditionalInfo;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
@@ -13,6 +14,7 @@ public abstract class ApiAdditionalInfo {
abstract String name();
+ @Normalize
@Json(name = "string_value")
abstract String stringValue();
diff --git a/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiAddress.java b/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiAddress.java
index b54c8945..e682de77 100644
--- a/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiAddress.java
+++ b/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiAddress.java
@@ -4,6 +4,7 @@
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
+import com.rockthevote.grommet.data.api.Normalize;
import com.rockthevote.grommet.data.db.model.Address;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
@@ -15,10 +16,13 @@
@AutoValue
public abstract class ApiAddress {
+ @Normalize
abstract String streetName();
+ @Normalize
abstract String subAddress();
+ @Normalize
abstract String municipalJurisdiction();
abstract String county();
diff --git a/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiName.java b/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiName.java
index 1b2d6870..e931af70 100644
--- a/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiName.java
+++ b/app/src/main/java/com/rockthevote/grommet/data/api/model/ApiName.java
@@ -3,6 +3,8 @@
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
+import com.rockthevote.grommet.data.api.Normalize;
+import com.rockthevote.grommet.data.api.StringNormalizerFactory;
import com.rockthevote.grommet.data.db.model.Name;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
@@ -10,12 +12,16 @@
@AutoValue
public abstract class ApiName {
+
+ @Normalize
@Json(name = "first_name")
abstract String firstName();
+ @Normalize
@Json(name = "last_name")
abstract String lastName();
+ @Normalize
@Json(name = "middle_name")
abstract String middleName();
diff --git a/app/src/main/java/com/rockthevote/grommet/ui/MainActivity.java b/app/src/main/java/com/rockthevote/grommet/ui/MainActivity.java
index 253bda74..49ffa5e2 100644
--- a/app/src/main/java/com/rockthevote/grommet/ui/MainActivity.java
+++ b/app/src/main/java/com/rockthevote/grommet/ui/MainActivity.java
@@ -16,7 +16,6 @@
import com.f2prateek.rx.preferences.Preference;
import com.rockthevote.grommet.R;
import com.rockthevote.grommet.data.db.model.RockyRequest;
-import com.rockthevote.grommet.data.prefs.AppRegTotal;
import com.rockthevote.grommet.data.prefs.CanvasserName;
import com.rockthevote.grommet.data.prefs.CurrentRockyRequestId;
import com.rockthevote.grommet.data.prefs.EventName;
@@ -47,7 +46,6 @@ public final class MainActivity extends BaseActivity {
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.event_details_toolbar) Toolbar eventToolbar;
- @BindView(R.id.total_registered) TextView totalRegistered;
@BindView(R.id.registered_for_event) TextView registeredEvent;
@BindView(R.id.event_details) EventDetails eventDetails;
@BindView(R.id.editable_action_view) EditableActionView editableActionView;
@@ -64,8 +62,6 @@ public final class MainActivity extends BaseActivity {
@Inject @EventRegTotal Preference eventRegTotal;
- @Inject @AppRegTotal Preference appRegtotal;
-
@Inject ViewContainer viewContainer;
@Inject BriteDatabase db;
@@ -112,9 +108,6 @@ protected void onResume() {
subscriptions.add(eventRegTotal.asObservable()
.subscribe(eventTotal -> registeredEvent.setText(String.valueOf(eventTotal))));
-
- subscriptions.add(appRegtotal.asObservable()
- .subscribe(appTotal -> totalRegistered.setText(String.valueOf(appTotal))));
}
@Override
@@ -155,6 +148,7 @@ private void createNewVoterRecord() {
.partnerTrackingId(eventZipPref.get())
.sourceTrackingId(canvasserNamePref.get())
.openTrackingId(eventNamePref.get())
+ .partnerOptInSMS(true) // override database default so we don't have to perform a migration
.generateDate();
if (null != location) {
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 0af5971a..9cd0a20b 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
@@ -19,7 +19,6 @@
import com.rockthevote.grommet.data.db.model.ContactMethod;
import com.rockthevote.grommet.data.db.model.Name;
import com.rockthevote.grommet.data.db.model.RockyRequest;
-import com.rockthevote.grommet.data.prefs.AppRegTotal;
import com.rockthevote.grommet.data.prefs.CurrentRockyRequestId;
import com.rockthevote.grommet.data.prefs.EventRegTotal;
import com.rockthevote.grommet.util.Dates;
@@ -72,7 +71,6 @@ public class ReviewAndConfirmFragment extends BaseRegistrationFragment implement
@BindView(R.id.checkbox_agreement) CheckBox confirmCheckbox;
@Inject @EventRegTotal Preference eventRegTotal;
- @Inject @AppRegTotal Preference appRegTotal;
@Inject @CurrentRockyRequestId Preference rockyRequestRowId;
@@ -248,9 +246,6 @@ public void onRegisterClick(View v) {
int totalEvent = eventRegTotal.get();
eventRegTotal.set(++totalEvent);
- int totalApp = appRegTotal.get();
- appRegTotal.set(++totalApp);
-
RegistrationCompleteDialogFragment dialog = new RegistrationCompleteDialogFragment();
dialog.setCancelable(false);
dialog.show(getFragmentManager(), "complete_dialog");
diff --git a/app/src/main/java/com/rockthevote/grommet/util/MoshiUtils.java b/app/src/main/java/com/rockthevote/grommet/util/MoshiUtils.java
new file mode 100644
index 00000000..0a02e67a
--- /dev/null
+++ b/app/src/main/java/com/rockthevote/grommet/util/MoshiUtils.java
@@ -0,0 +1,61 @@
+package com.rockthevote.grommet.util;
+
+import com.squareup.moshi.JsonQualifier;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+
+/**
+ * Created by Aaron Huttner on 9/23/16. Grommet
+ */
+
+public final class MoshiUtils {
+ public static final Set NO_ANNOTATIONS = Collections.emptySet();
+
+ public static boolean typesMatch(Type pattern, Type candidate) {
+ // TODO: permit raw types (like Set.class) to match non-raw candidates (like Set).
+ return pattern.equals(candidate);
+ }
+
+ public static Set extends Annotation> jsonAnnotations(AnnotatedElement annotatedElement) {
+ return jsonAnnotations(annotatedElement.getAnnotations());
+ }
+
+ public static Set extends Annotation> jsonAnnotations(Annotation[] annotations) {
+ Set result = null;
+ for (Annotation annotation : annotations) {
+ if (annotation.annotationType().isAnnotationPresent(JsonQualifier.class)) {
+ if (result == null) result = new LinkedHashSet<>();
+ result.add(annotation);
+ }
+ }
+ return result != null ? Collections.unmodifiableSet(result) : MoshiUtils.NO_ANNOTATIONS;
+ }
+
+ public static boolean isAnnotationPresent(
+ Set extends Annotation> annotations, Class extends Annotation> annotationClass) {
+ if (annotations.isEmpty()) return false; // Save an iterator in the common case.
+ for (Annotation annotation : annotations) {
+ if (annotation.annotationType() == annotationClass) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if {@code annotations} has any annotation whose simple name is Nullable.
+ */
+ public static boolean hasNullable(Annotation[] annotations) {
+ for (Annotation annotation : annotations) {
+ if (annotation.annotationType().getSimpleName().equals("Nullable")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/app/src/main/res/drawable/register_a_voter.xml b/app/src/main/res/drawable/register_a_voter.xml
new file mode 100644
index 00000000..e7fb0475
--- /dev/null
+++ b/app/src/main/res/drawable/register_a_voter.xml
@@ -0,0 +1,357 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/register_to_vote.xml b/app/src/main/res/drawable/register_to_vote.xml
deleted file mode 100644
index 473ef10b..00000000
--- a/app/src/main/res/drawable/register_to_vote.xml
+++ /dev/null
@@ -1,365 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index fa595c1a..81a3aae5 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,7 +1,6 @@
@@ -71,47 +70,6 @@
android:layout_marginStart="@dimen/content_margin"
android:background="@drawable/rounded_bottom"
android:elevation="2dp"/>
-
-
-
-
-
-
-
-
@@ -86,6 +87,7 @@
android:layout_height="wrap_content"
android:hint="@string/label_other_party"
android:inputType="textCapWords"
+ android:maxLength="50"
android:maxLines="1"/>
@@ -218,6 +220,7 @@
android:layout_marginBottom="@dimen/content_area_padding"
android:hint="@string/label_email"
android:inputType="textEmailAddress"
+ android:maxLength="50"
android:maxLines="1"/>
diff --git a/app/src/main/res/layout/view_address.xml b/app/src/main/res/layout/view_address.xml
index 05485f2f..a16baf1f 100644
--- a/app/src/main/res/layout/view_address.xml
+++ b/app/src/main/res/layout/view_address.xml
@@ -35,6 +35,7 @@
android:layout_height="@dimen/list_item_height"
android:hint="@string/label_street"
android:inputType="textPostalAddress|textCapWords"
+ android:maxLength="40"
android:maxLines="1"/>
@@ -52,6 +53,7 @@
android:layout_height="@dimen/list_item_height"
android:hint="@string/label_unit_num"
android:inputType="textPostalAddress|textCapWords"
+ android:maxLength="15"
android:maxLines="1"/>
@@ -81,6 +83,7 @@
android:layout_height="@dimen/list_item_height"
android:hint="@string/label_city"
android:inputType="textPostalAddress|textCapWords"
+ android:maxLength="35"
android:maxLines="1"/>
diff --git a/app/src/main/res/layout/view_name.xml b/app/src/main/res/layout/view_name.xml
index d150140e..c56897e1 100644
--- a/app/src/main/res/layout/view_name.xml
+++ b/app/src/main/res/layout/view_name.xml
@@ -33,6 +33,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/list_item_height"
android:freezesText="true"
+ android:maxLength="30"
android:hint="@string/label_first_name"
android:inputType="textPersonName|textCapWords"
android:maxLines="1"/>
@@ -59,6 +60,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/list_item_height"
android:hint="@string/label_middle_name"
+ android:maxLength="30"
android:inputType="textPersonName|textCapWords"
android:maxLines="1"/>
@@ -78,6 +80,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/list_item_height"
android:hint="@string/label_last_name"
+ android:maxLength="30"
android:inputType="textPersonName|textCapWords"
android:maxLines="1"/>
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index cbc9d921..52412a02 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -52,7 +52,7 @@
Consigo mi correo desde una dirección diferente a la de arriba.He cambiado mi dirección.He cambiado mi nombre.
- Estoy de acuerdo que puede llamar a %s y texto con actualizaciones importantes y recordatorios.
+ Estoy de acuerdo que puede llamar a %s/Pennsylvania Voice/State Voices y texto con actualizaciones importantes y recordatorios.Condado donde vive es obligatorioNo tengo dirección normal ni dirección permanenteCompleta registro mediante formulario en papel
diff --git a/app/src/main/res/values/open_source_licenses.xml b/app/src/main/res/values/open_source_licenses.xml
index c84a68cb..7179856b 100644
--- a/app/src/main/res/values/open_source_licenses.xml
+++ b/app/src/main/res/values/open_source_licenses.xml
@@ -1,6 +1,6 @@
- Open Source Licenses
+ Open Source Licenses
Copyright 2013 Square, Inc.\n
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9019c6c1..65579fea 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -18,7 +18,6 @@
Enter Info
- Total RegisteredRegisteredNew VoterEvent Details
@@ -97,11 +96,11 @@
PennDOT numberOther Political PartyI do not know my PennDOT number
- SSN Last Four
- I do not know the last four of my SSN
+ Last Four Digitals of social security number
+ I do not know the last four of my social security numberPreferred Language to Receive Election Materials
- Election Info
- If you have a PA driver\'s license or PennDOT ID card number, you must use it
+ Personal Info
+ If you have a PA driver\'s license or PennDOT ID card number, you must use it. Your PennDOT is the 8 digit number found on the top right of a PA driver\'s license OR non-driver\'s state ID cardCheck \"I do not know\" if you don\'t know your PennDOT numberIf you do not have a PennDOT number please enter the last 4 digits of your Social Security number Check \"I do not know\" if you don\'t know your SSN last four
@@ -224,7 +223,7 @@ Submitting an application containing false information may also subject a person
event_reg_totalapp_reg_total
- Partner Name
+ OrganizationPartner IDCanvaser NameEvent Zip Code
@@ -245,7 +244,14 @@ Submitting an application containing false information may also subject a person
+     The Pennsylvania Voter Registration App is the first of its kind in the country
+– and only available in Pennsylvania – allowing canvassers to electronically register voters using
+the Pennsylvania Department of State’s Online Web API. Pennsylvania Voice worked with Rock the Vote
+and OSET’s TrustTheVote Project to build this first-of-its-kind app that feeds registrations
+directly into the innovative PA online voter registration system, developed by the Pennsylvania
+Department of State.
+
     This mobile application “app” was built by Rock the Vote and OSET’s TrustTheVote Project
to facilitate the submission of electronic voter registration applications in the Commonwealth of
@@ -257,6 +263,12 @@ and the
outlined by the Pennsylvania Secretary of State’s office
+     Pennsylvania Voice, Rock the Vote and their partners are deploying a pilot
+project in the fall of 2016 to see how this technology works in practice and measure its cost
+savings. To learn more about the 2016 Pennsylvania Voter Registration App,
+contact ovrtool@rockthevote.com.
+
+
     Rock the Vote is the largest nonpartisan, nonprofit organization in the country driving the
youth vote to the polls. Since 1990, Rock the Vote has fused pop culture, music, art and technology
to fulfill its mission of building long-term youth political power. During the past six Presidential