Wednesday, July 29, 2015

Summer Project

I am a little delayed in getting my next post together.  We have been busy working on our summer project: building a shed and doing some landscaping work. Today we put in the last piece of siding and are finally done building the shed. 

20150729_202032

Next up is a paved deck/patio in front of the shed and putting in a lot more plants.  Unluckily, I sprained my ankle last week so I should have more time to work on this blog/project instead of doing manual labor.

Stay tuned for the next update to the project which should involve some UI improvements and the Observer pattern…

Wednesday, July 22, 2015

MyNotes: Unit Test Round 2


Recently, Android updated the unit test framework to include Junit4 and mockito.  The testing framework now comes in two flavors: Local Tests and Instrumentation Tests.  The local tests are true units that do not run on an emulator or device.   As a result, everything has to mocked.   The instrumentation tests are pretty much the same as the earlier tests: They require an emulator or device and run the full android system.  

Android provides a handy write up on the Android Developers site: Building Local Unit Tests

I followed their instructions fairly closely with the big deviation that I brought in PowerMockito as well.

I followed the same pattern in both the MyNotes and CommonLibrary projects.  For simplicity, I am only going to detail the changes to one of the projects.

Step 1: Update Gradle

Here are the changes I had to make in order for the new unit test framework to work:


android {
...

defaultConfig {
...
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
...
}


dependencies {
...
//Unit test dependencies
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
testCompile 'org.powermock:powermock-module-junit4:1.6.2'
testCompile 'org.powermock:powermock-api-mockito:1.6.2'

androidTestCompile 'org.hamcrest:hamcrest-library:1.1'
androidTestCompile 'com.android.support.test:runner:0.3'
androidTestCompile 'com.android.support.test:rules:0.3'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2'
androidTestCompile "com.google.dexmaker:dexmaker:1.2"
androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
}


Step 2: Create The Local Unit Test Packages


So the instrumentation tests (those that require an emulator) are in the androidTest folder that is a sister to the main folder.  The local unit tests get their own folder, test, at the same level as the main folder also:


Untitled


The “test” folder must contain a folder named “java”.  Under the java folder is the basic package layout for the classes that will be tested.



Step 3: Creating a Local Unit Test


@RunWith(PowerMockRunner.class)
@PrepareForTest({LocalBroadcastManager.class, ThreadUtils.class, MyNotesApplication.class, Intent.class,
NoteFilterBroadcast.class})
public class NoteFilterCacheTest {

@Mock
private Context mMockContext;

@Mock
private SharedPreferences mMockSharedPreferences;

@Mock
private SharedPreferences.Editor mMockSharedEditorPreferences;

@Mock
private LocalBroadcastManager mMockLocalBroadcastManager;

@Mock
private Intent mMockIntent;

@Before
public void prepareForTest() throws Exception {
when(mMockContext.getSharedPreferences(NoteFilterCache.CACHE_NAME, Context.MODE_PRIVATE))
.thenReturn(mMockSharedPreferences);

PowerMockito.mockStatic(LocalBroadcastManager.class);
when(LocalBroadcastManager.getInstance(mMockContext)).thenReturn(mMockLocalBroadcastManager);

when(mMockSharedPreferences.edit()).thenReturn(mMockSharedEditorPreferences);
when(mMockSharedEditorPreferences.clear()).thenReturn(mMockSharedEditorPreferences);
doNothing().when(mMockSharedEditorPreferences).apply();
when(mMockSharedEditorPreferences.putBoolean(anyString(), anyBoolean())).thenReturn(mMockSharedEditorPreferences);

PowerMockito.whenNew(Intent.class).withAnyArguments().thenReturn(mMockIntent);
}

/**
* Test constructor
*/
@Test
public void testConstructorFailure() {
try {
new NoteFilterCache(null);
assert false;
}
catch (NullPointerException e) {
}
}


/**
* Tests method {@link NoteFilterCache#isColorEnabled(NoteColor)} and
* method {@link NoteFilterCache#getEnabledColors()}
*/
@Test
public void testRetrievingTheDefaultValuesForEachFilter() {
NoteFilterCache cache = new NoteFilterCache(mMockContext);

when(mMockSharedPreferences.getBoolean(anyString(), eq(true))).thenReturn(true);
Set < NoteColor > actualEnabledColors = cache.getEnabledColors();
for (NoteColor color : NoteColor.values()) {
assertThat(cache.isColorEnabled(color), is(true));
assertThat(actualEnabledColors.contains(color), is(true));
}
}


/**
* Tests method {@link NoteFilterCache#isColorEnabled(NoteColor)}
*/
@Test
public void testCheckingEnabledColorForNullValue() {
try {
new NoteFilterCache(mMockContext).isColorEnabled(null);
assert false;
}
catch (NullPointerException e) {
}
}


/**
* Tests method {@link NoteFilterCache#enableColor(NoteColor,
* boolean)}.
*/
@Test
public void testDisablingEachColor() {
final NoteFilterCache cache = new NoteFilterCache(mMockContext);

when(mMockIntent.putExtra(eq(NoteFilterBroadcast.Extras.COLOR), anyInt())).thenReturn(
mMockIntent);
when(mMockIntent.putExtra(NoteFilterBroadcast.Extras.ENABLED, false)).thenReturn(
mMockIntent);

for (final NoteColor color : NoteColor.values()) {
cache.enableColor(color, false);
}

verify(mMockSharedEditorPreferences, times(NoteColor.values().length)).putBoolean(
anyString(), eq(false));
verify(mMockLocalBroadcastManager, times(NoteColor.values().length)).sendBroadcast((Intent) anyObject());
}


/**
* Tests method {@link NoteFilterCache#enableColor(NoteColor,
* boolean)}.
*/
public void testEnablingEachColor() {
final NoteFilterCache cache = new NoteFilterCache(mMockContext);

for (final NoteColor color : NoteColor.values()) {
cache.enableColor(color, true);
verify(mMockSharedEditorPreferences.putBoolean(color.name(), true));
}

verify(mMockLocalBroadcastManager, times(NoteColor.values().length)).sendBroadcast((Intent) anyObject());
}
}

This is your basic Junit 4 test.  The important thing to remember is that you have to mock all Android classes that are called from the unit under test.  For most part, this will go smoothly, but there are a few classes that just do not mock well (Point, Rect, etc).  This is because the classes use final public attributes instead of accessor methods which make them incredibly hard to mock.  Also be warned, that you will have to mock everything from the framework, this includes things some very basic and commonly used classes like TextUtils.  Android does provide a way to use default mock values, but I typically don’t use it. If you are interested, just add the following statement to your gradle file:


android {
// ...
testOptions {
unitTests.returnDefaultValues = true
}
}

The local unit tests work really well for most of the business logic in the app.  It does not work well for UI components, the Database, Loaders, Broadcast Receivers, etc.  Basically, anything that is highly dependent on an actual Android framework. 


Step 4: How to run



  • Open the Build Variants tool in the Android Studio (located in the lower left corner:

Untitled



  • From the Test Artifacts menu, select Unit Tests
  • Navigate to a unit test and do a right-click:

Untitled



  • Select the Run or Debug option of your choice.
  • After the tests run, the results are displayed in the junit results:

Untitled


Instrumentation Tests


These are very similar to the old instrumentation tests.  The biggest change is that they are now JUnit4 (Yeah!) and also support Mockito.  It will support PowerMockito as well, but you can’t mock any statics (unfortunately).   You also have to add a Test Runner to the top of each test:



/**
* Test the {@link Note class}.
*/
@RunWith(AndroidJUnit4.class)
public class NoteContentValuesTest {

Running these is very similar to the local unit test, except you choose the “Android Instrumentation Tests”  in the Build Variant menu:


Untitled


My Final Note


This is more of an FYI.  The Test Artifact selected in the Build Variant are the only ones that will compile and run.  So if you are working on Local Unit Tests, make sure to select the correct build variant first so that you can see the syntax issues while you writing the tests.  The same goes for the Instrumentation Tests.


It is also important to compile each variant before considering the task complete since compilation issue won’t show up for the unselected variant.


Commits


As always, the commits to support this entry can be found on GitHub at https://github.com/fsk-software/mynotes/commit/ef1dd5389c7783deb05cbacf43c5e5f5628398f6.

Wednesday, July 15, 2015

Useful Tips: How to Record Screen Videos

I have been recording videos to show the screen animations.  The easiest and best way to do this is via the adb and a terminal window.  I have a preference for Linux so I use Cygwin for my terminal, but it will work in the windows terminal for the DOS fans out there.

  1. Attach a debuggable device or start an emulator
  2. Open your favorite terminal (You will need your environment variables setup to find the adb commands).
  3. In the terminal:, enter “adb shell screenrecord /sdcard/movie.mp4”. You can use any movie name that is Linux compliant.
  4. You are recording at this point so go to the device/emulator and perform the actions You want recorded.
  5. Back in the terminal:
    •  crtl-c to stop the recording.
    • change directory to the location where you want the movie.
    • enter “adb pull /sdcard/movie.mp4

Tada! Your video is now on the computer and available for viewing.

Wednesday, July 8, 2015

MyNotes: Color Shifting

In my last two entries I covered the Shared Element Transition and the Translation Animations.  In this entry I am going to cover the last of the main edit note animations: The color shift.


This animation happens when the user changes the color of the note.  It is a subtle animation, but adds a nice polish to the UI.
This is a very simple animation that slowly changes from color A to color B over a time interval.  This animation can be found in the BackgroundAnimator class inside the common library.
BackgroundAnimator
/**
 * A helper class to help with background drawable animators
 */
public class BackgroundAnimatorHelper {

    /**
     * Animate a transition from one color to the next.
     *
     * @param view
     *         The view to animate.
     * @param fromColor
     *         the initial color for the animation.
     * @param toColor
     *         the final color for the animation.
     * @param duration
     *         The time in milliseconds for the animation.
     * @param animatorListener
     *         The listener for the animation.
     */
    public static void crossBlendColors(@NonNull View view, int fromColor, int toColor,
                                        int duration, Animator.AnimatorListener animatorListener) {

        view.animate().cancel();

        ObjectAnimator animator = ObjectAnimator
                .ofObject(view, "backgroundColor", new ArgbEvaluator(), fromColor, toColor);
        if (animatorListener != null) {
            animator.addListener(animatorListener);
        }
        animator.setDuration(duration);
        animator.start();
    }


    /**
     * Animate a transition from one color resource to the next.
     *
     * @param view
     *         The view to animate.
     * @param fromColorResource
     *         the initial color resource for the animation.
     * @param toColorResource
     *         the final color resource for the animation.
     * @param duration
     *         The time in milliseconds for the animation.
     * @param animatorListener
     *         The listener for the animation.
     */
    public static void crossBlendColorResource(@NonNull View view, int fromColorResource,
                                               int toColorResource, int duration,
                                               Animator.AnimatorListener animatorListener) {

        Resources resources = view.getResources();
        int fromRgb = resources.getColor(fromColorResource);
        int toRgb = resources.getColor(toColorResource);
        crossBlendColors(view, fromRgb, toRgb, duration, animatorListener);
    }


There are two methods here, but the meat of the animation is here:

    public static void crossBlendColors(@NonNull View view, int fromColor, int toColor,
                                        int duration, Animator.AnimatorListener animatorListener) {

        view.animate().cancel();

        ObjectAnimator animator = ObjectAnimator
                .ofObject(view, "backgroundColor", new ArgbEvaluator(), fromColor, toColor);
        if (animatorListener != null) {
            animator.addListener(animatorListener);
        }
        animator.setDuration(duration);
        animator.start();
    }

All this method does is create an ObjectAnimator with the ArgbEvaluator. The ArgEvaluator is a special interpolator that does the work of the color shift.  All the animator does is use the ArgbEvalauator to change the background color.

The other method, crossBlendColorResources, is a convenience method that figures out the argb value for  a color resource and then called the crossBlendColors method to perform the color shift.

Commits


These changes can be found at commits https://github.com/fsk-software/mynotes/commit/a0632503c5fabf74a31cca091d7975419d002328 and https://github.com/fsk-software/mynotes/commit/ec427c6a5684220836f823824d58a61cd2aea47b.

Wednesday, July 1, 2015

MyNotes: Edit Note Toolbar Slide


The EditNoteActivity toolbar comes in two flavors, the main toolbar and the color picker toolbar.  These are implemented as separate fragments, NoteEditMainToolbarFragment and the NoteEditColorPickerFragment respectively.

Here are the pictures:

Main Toolbar:
Screenshot_2015-06-17-22-42-51

Color Picker:
Screenshot_2015-06-17-22-42-47

For the phone layout this toolbar sets right under the notification bar.  I am still undecided about the edit note tablet layout, so I will cover that under another entry.
Anyway, for the phone I want the toolbar to slide down when entering and slide up when exiting.
In practice it looks like this:

FractionalFrameLayout

I am making these animations work by specifying a custom animation on the fragment transactions:
    private void showFragment(@NonNull Fragment fragment, @NonNull String tag) {
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.setCustomAnimations(R.animator.slide_down, R.animator.slide_up);
        transaction.replace(R.id.activity_single_note_header_container, fragment, tag);
        transaction.commit();
    }

In this case the enter animation in the R.animator.slide_down and the exit animation is R.animator.slide_up.

R.animator.slide_down:

<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_longAnimTime"
    android:interpolator="@android:anim/anticipate_overshoot_interpolator"
    android:propertyName="yFraction"
    android:valueFrom="-1.0"
    android:valueTo="0.0"
    android:valueType="floatType"/>

The R.animator.slide_up is nearly identical, except the it swaps the valueFrom and the valueTo.  It also uses the overshoot interpolator to give it a little wobble at the end.

This animator translates it’s target view up or down based on a position proportional to its target view height.  So a valueFrom of –1 translates the target view –100% of its height up.  This will move the toolbar fully off the screen.  Translating to back to 0 moves it back to its original position.

Unfortunately, the “yFraction” property name does not exist in the default android views.  I had to write a special view in the common library, FractionalFrameLayout, that handles that. 

FractionalFrameLayout:

public class FractionalFrameLayout extends FrameLayout {


    /**
     * The last requested y-fraction.
     */
    private float mRequestedYFraction;


    /**
     * The last requested x-fraction.
     */
    private float mRequestedXFraction;
    ...

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (getHeight() != 0) {
            setYFraction(mRequestedYFraction);
        }

        if (getWidth() != 0) {
            setXFraction(mRequestedXFraction);
        }
    }


    /**
     * Get the current horizontal percentage of the view that is visible.
     *
     * @return the current horizontal percentage of the view that is visible.
     */
    public float getXFraction() {
        int width = getWidth();
        return (width == 0) ? 0 : getX() / (float) width;
    }


    /**
     * Set the current horizontal percentage of the view that is visible.
     *
     * @return the current horizontal percentage of the view that is visible.
     */
    public void setXFraction(float xFraction) {
        mRequestedXFraction = xFraction;
        int width = getWidth();
        if (width != 0) {
            setX(xFraction * width);
        }
    }


    public float getYFraction() {
        int height = getHeight();
        return (height == 0) ? 0 : getY() / (float) height;
    }


    /**
     * Set the current vertical percentage of the view that is visible.
     *
     * @return the current vertical percentage of the view that is visible.
     */
    public void setYFraction(float yFraction) {
        mRequestedYFraction = yFraction;
        int height = getHeight();

        if (height != 0) {
            setY(yFraction * height);
        }
    }
}

The animator’s propertyValue=”yFraction” corresponds to the FractionalFrameLayout’s setyFraction(float) method.  That method changes the y position of the view based on the view height and the yFraction value. 

This class also overwrites the onMeasure to try handle a case where the view blinks briefly when drawn for the first time on the animation start, but that code isn’t working quite right right.  I have to go back to the drawing board on that areas.

This Layout is the parent layout for the toolbar fragments.  Here is the xml for the main toolbar to show it in action:

layouts/fragment_note_edit_main_toolbar_fragment.xml:

<?xml version="1.0" encoding="utf-8"?>
<com.fsk.common.presentation.components.FractionalFrameLayout
    style="@style/NoteEditToolbar"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/fragment_note_edit_color_picker_done"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/save_note_background"/>

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:layout_marginBottom="4dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="4dp"
            android:alpha="0.5"
            android:background="@android:color/black"/>

        <include
            layout="@layout/include_color_picker_radio_group"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>
</com.fsk.common.presentation.components.FractionalFrameLayout>

Commits


These changes can be found at commits https://github.com/fsk-software/mynotes/commit/a0632503c5fabf74a31cca091d7975419d002328 and https://github.com/fsk-software/mynotes/commit/ec427c6a5684220836f823824d58a61cd2aea47b.