Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guidance hint solution that resolves #2007 #2105

Merged
merged 30 commits into from
May 10, 2018
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b5fb693
created a layout and view binding mechanisms for all help/hint relate…
jd-alexander Apr 7, 2018
a729e9a
added list preference for guidance setting
jd-alexander Apr 7, 2018
0257eeb
fetched guidance hint from the associated FormEntryCaption based on t…
jd-alexander Apr 8, 2018
c29f833
added animations to collapse guidance setup.
jd-alexander Apr 8, 2018
f6b42dc
wrote integration tests to verify that guidance hint works.
jd-alexander Apr 8, 2018
6fc91cf
performed cleanup of test.
jd-alexander Apr 8, 2018
fa67b0f
added styling and configurations for rendering to guidance text.
jd-alexander Apr 8, 2018
9449e31
re-enabled D8.
jd-alexander Apr 8, 2018
66b5cd2
removed this visibility check on help related layouts because if the …
jd-alexander Apr 8, 2018
eadb0ad
resolved pmd issues.
jd-alexander Apr 8, 2018
c7b8f7d
fixed checkstyle issues
jd-alexander Apr 8, 2018
39df324
performed several fixes to adhere to codacy styles
jd-alexander Apr 8, 2018
2254319
changed assertTrue to assertFalse for string null check.
jd-alexander Apr 9, 2018
368ea98
added view id matcher static import.
jd-alexander Apr 9, 2018
5ccab73
performed style and drawable click fixes
jd-alexander Apr 12, 2018
8c77dc9
added a background to guidance hint's layout so that a noticeable out…
jd-alexander Apr 25, 2018
c7062fc
Merge branch 'master' into guidance_hint
jd-alexander Apr 25, 2018
6218717
several minor style and project fixes
jd-alexander Apr 25, 2018
f0a5061
Update colors.xml
jd-alexander Apr 25, 2018
dc25767
Merge branch 'guidance_hint' of https://github.com/jd-alexander/colle…
jd-alexander Apr 26, 2018
b0fdd53
removed unused resources
jd-alexander Apr 26, 2018
3f7a0d4
minor fix to trigger rebuild.
jd-alexander Apr 27, 2018
80e417c
added the ability to store expanded/collapsed state of guidance hint.…
jd-alexander May 1, 2018
c1f5ab7
renamed collapsed key to expanded to match property it's refernencing…
jd-alexander May 1, 2018
32d00a6
integrated guidance hint with dark/light theme functionality
jd-alexander May 1, 2018
d3b8278
Merge branch 'master' into guidance_hint
jd-alexander May 1, 2018
48164cf
Refactored hint guidance layout approach
jd-alexander May 8, 2018
10ca320
Merge branch 'master' into guidance_hint
jd-alexander May 8, 2018
de39e4c
Help layout hidden for child widgets of DateTimeWidget
jd-alexander May 9, 2018
64b99b0
Merge remote-tracking branch 'origin/guidance_hint' into guidance_hint
jd-alexander May 9, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package org.odk.collect.android;


import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
import android.os.Environment;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.intent.rule.IntentsTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;

import org.apache.commons.io.IOUtils;
import org.javarosa.form.api.FormEntryPrompt;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.odk.collect.android.activities.FormEntryActivity;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.preferences.GeneralSharedPreferences;
import org.odk.collect.android.preferences.GuidanceHint;
import org.odk.collect.android.preferences.PreferenceKeys;
import org.odk.collect.android.utilities.ActivityAvailability;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import tools.fastlane.screengrab.Screengrab;
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static junit.framework.Assert.assertFalse;
import static org.odk.collect.android.activities.FormEntryActivity.EXTRA_TESTING_PATH;

@RunWith(AndroidJUnit4.class)
public class GuidanceHintFormTest {

private static final String GUIDANCE_SAMPLE_FORM = "guidance_hint_form.xml";
private static final String FORMS_DIRECTORY = "/odk/forms/";

@Rule
public FormEntryActivityTestRule activityTestRule = new FormEntryActivityTestRule();

@Mock
private ActivityAvailability activityAvailability;

//region Test prep.
@BeforeClass
public static void copyFormToSdCard() throws IOException {
String pathname = formPath();
if (new File(pathname).exists()) {
return;
}

AssetManager assetManager = InstrumentationRegistry.getContext().getAssets();
InputStream inputStream = assetManager.open(GUIDANCE_SAMPLE_FORM);

File outFile = new File(pathname);
OutputStream outputStream = new FileOutputStream(outFile);

IOUtils.copy(inputStream, outputStream);
}

@BeforeClass
public static void beforeAll() {
Screengrab.setDefaultScreenshotStrategy(new UiAutomatorScreenshotStrategy());
}

@Before
public void prepareDependencies() {
FormEntryActivity activity = activityTestRule.getActivity();
activity.setActivityAvailability(activityAvailability);
activity.setShouldOverrideAnimations(true);
}

@Test
public void guidanceVisibilityContentTest() {
GeneralSharedPreferences.getInstance().save(PreferenceKeys.KEY_GUIDANCE_HINT, GuidanceHint.YesCollapsed.toString());

FormEntryPrompt prompt = Collect.getInstance().getFormController().getQuestionPrompt();

String guidance = prompt.getSpecialFormQuestionText(prompt.getQuestion().getHelpTextID(), "guidance");

assertFalse(TextUtils.isEmpty(guidance));

onView(withId(R.id.help_text_view)).perform(click());

Screengrab.screenshot("guidance_hint");

onView(withId(R.id.guidance_text_view)).check(matches(withText(guidance)));

}

//region Helper methods.
private static String formPath() {
return Environment.getExternalStorageDirectory().getPath()
+ FORMS_DIRECTORY
+ GUIDANCE_SAMPLE_FORM;
}



//region Custom TestRule.
private class FormEntryActivityTestRule extends IntentsTestRule<FormEntryActivity> {

FormEntryActivityTestRule() {
super(FormEntryActivity.class);
}

@Override
protected Intent getActivityIntent() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
Intent intent = new Intent(context, FormEntryActivity.class);

intent.putExtra(EXTRA_TESTING_PATH, formPath());

return intent;
}
}
//endregion


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.odk.collect.android.listeners;

/**
* Generic Result interface that returns a result of type T
* from another thread or function.
*
*/
public interface Result<T> {
void onComplete(T result);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static org.odk.collect.android.preferences.AdminKeys.ALLOW_OTHER_WAYS_OF_EDITING_FORM;
import static org.odk.collect.android.preferences.PreferenceKeys.KEY_AUTOSEND;
import static org.odk.collect.android.preferences.PreferenceKeys.KEY_CONSTRAINT_BEHAVIOR;
import static org.odk.collect.android.preferences.PreferenceKeys.KEY_GUIDANCE_HINT;
import static org.odk.collect.android.preferences.PreferenceKeys.KEY_IMAGE_SIZE;

public class FormManagementPreferences extends BasePreferenceFragment {
Expand All @@ -37,6 +38,7 @@ public void onCreate(Bundle savedInstanceState) {
initConstraintBehaviorPref();
initAutoSendPrefs();
initImageSizePrefs();
initGuidancePrefs();
}

@Override
Expand Down Expand Up @@ -112,4 +114,25 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
}
});
}


private void initGuidancePrefs() {
final ListPreference guidance = (ListPreference) findPreference(KEY_GUIDANCE_HINT);

if (guidance == null) {
return;
}

guidance.setSummary(guidance.getEntry());
guidance.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
int index = ((ListPreference) preference).findIndexOfValue(newValue.toString());
String entry = (String) ((ListPreference) preference).getEntries()[index];
preference.setSummary(entry);
return true;
}
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.odk.collect.android.preferences;

public enum GuidanceHint {
Yes("yes"),
YesCollapsed("yes_collapsed"),
No("no");

private final String name;

GuidanceHint(String s) {
name = s;
}

public static GuidanceHint get(String name) {
for (GuidanceHint hint : GuidanceHint.values()) {
if (hint.name.equals(name)) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove extra blank line.

return hint;
}
}

return null;
}

public String toString() {
return this.name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public final class PreferenceKeys {
public static final String KEY_CONSTRAINT_BEHAVIOR = "constraint_behavior";
public static final String KEY_HIGH_RESOLUTION = "high_resolution";
public static final String KEY_IMAGE_SIZE = "image_size";
public static final String KEY_GUIDANCE_HINT = "guidance_hint";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation here should match that of other values.

public static final String KEY_INSTANCE_SYNC = "instance_sync";

// form_metadata_preferences.xml
Expand Down Expand Up @@ -66,6 +67,7 @@ public final class PreferenceKeys {
public static final String NAVIGATION_BUTTONS = "buttons";
private static final String GOOGLE_MAPS = "google_maps";
private static final String AUTOSEND_OFF = "off";
private static final String GUIDANCE_HINT_OFF = "no";
static final String GOOGLE_MAPS_BASEMAP_DEFAULT = "streets";
static final String OSM_BASEMAP_KEY = "osmdroid";
static final String OSM_MAPS_BASEMAP_DEFAULT = "openmap_streets";
Expand All @@ -77,6 +79,7 @@ private static HashMap<String, Object> getHashMap() {
hashMap.put(KEY_USERNAME, "");
// form_management_preferences.xml
hashMap.put(KEY_AUTOSEND, AUTOSEND_OFF);
hashMap.put(KEY_GUIDANCE_HINT, GUIDANCE_HINT_OFF);
hashMap.put(KEY_DELETE_AFTER_SEND, false);
hashMap.put(KEY_COMPLETED_DEFAULT, true);
hashMap.put(KEY_CONSTRAINT_BEHAVIOR, CONSTRAINT_BEHAVIOR_ON_SWIPE);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package org.odk.collect.android.utilities;

import android.support.v4.view.animation.PathInterpolatorCompat;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

import org.odk.collect.android.listeners.Result;

/**
* Created by Ing. Oscar G. Medina Cruz on 18/06/2016.
*/
public class AnimateUtils {


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove extra blank line

private static final Interpolator easeInOutQuart = PathInterpolatorCompat.create(0.77f, 0f, 0.175f, 1f);

private AnimateUtils() {

}
Expand Down Expand Up @@ -39,4 +47,117 @@ public void onAnimationRepeat(Animation animation) {
});
view.startAnimation(scaleInAnimation);
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove extra blank line

// Added animation related code and inspiration from this Stack Overflow Question
// https://stackoverflow.com/questions/4946295/android-expand-collapse-animation

public static Animation expand(final View view, Result<Boolean> result) {
int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) view.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
final int targetHeight = view.getMeasuredHeight();

// Older versions of android (pre API 21) cancel animations for views with a height of 0 so use 1 instead.
view.getLayoutParams().height = 1;
view.setVisibility(View.VISIBLE);

Animation animation = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {

view.getLayoutParams().height = interpolatedTime == 1
? ViewGroup.LayoutParams.WRAP_CONTENT
: (int) (targetHeight * interpolatedTime);

view.requestLayout();
}

@Override
public boolean willChangeBounds() {
return true;
}
};

animation.setInterpolator(easeInOutQuart);
animation.setDuration(computeDurationFromHeight(view));
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove extra blank line

//triggered when animation starts.
}

@Override
public void onAnimationEnd(Animation animation) {
result.onComplete(true);
}

@Override
public void onAnimationRepeat(Animation animation) {
//triggered when animation repeats.
}
});
view.startAnimation(animation);

return animation;
}

public static Animation collapse(final View view, Result<Boolean> result) {
final int initialHeight = view.getMeasuredHeight();

Animation a = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (interpolatedTime == 1) {
view.setVisibility(View.GONE);
} else {
view.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime);
view.requestLayout();
}
}

@Override
public boolean willChangeBounds() {
return true;
}
};

a.setInterpolator(easeInOutQuart);

int durationMillis = computeDurationFromHeight(view);
a.setDuration(durationMillis);

a.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//triggered when animation starts.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove extra blank line

}

@Override
public void onAnimationEnd(Animation animation) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove extra blank line

result.onComplete(true);
}

@Override
public void onAnimationRepeat(Animation animation) {
//triggered when animation repeats.

}
});

view.startAnimation(a);


return a;
}

private static int computeDurationFromHeight(View view) {
// 1dp/ms * multiplier
return (int) (view.getMeasuredHeight() / view.getContext().getResources().getDisplayMetrics().density);
}


}
Loading