The application structure

A key part of the development is the application structure. In this section we take you through the steps in building it out with code examples.

  1. Declare the UI of the application by modifying activity_main.xml, which is under app/src/main/res/layout.
  2. As shown in the following code, use one linear layout to align the controls vertically. The additional linear layout is used here to position the Truncate and Convolution buttons next to each other. The ScrollView wraps all controls. This means that the user can scroll down if the screen is too small to display everything at once.

    <?xml version="1.0" encoding="utf-8"?>
    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="wrap_content">

    <LinearLayout 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"

    android:orientation="vertical"

    tools:context=".MainActivity">

    <Button

    android:id="@+id/buttonGenerateSignal"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="center_horizontal"

    android:onClick="buttonGenerateSignalClicked"

    android:text="Generate Signal"

    android:layout_margin="5dp"/>

    <CheckBox

    android:id="@+id/checkboxUseNeon"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="center_horizontal"

    android:text="Use Neon?" />

    <LinearLayout

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="center_horizontal">

    <Button

    android:id="@+id/buttonTruncate"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="center_horizontal"

    android:onClick="buttonTruncateClicked"

    android:text="Truncate"

    android:layout_margin="5dp"/>

    <Button

    android:id="@+id/buttonConvolution"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="center_horizontal"

    android:onClick="buttonConvolutionClicked"

    android:text="Convolution"

    android:layout_margin="5dp"/>
    </LinearLayout>

    <com.jjoe64.graphview.GraphView

    android:id="@+id/graph"

    android:layout_width="match_parent"

    android:layout_height="350dp"

    android:layout_margin="10dp"/>

    <TextView

    android:id="@+id/textViewProcessingTime"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="center_horizontal"

    android:textSize="18sp" />
    </LinearLayout>
    </ScrollView>

  3. To plot the signal before and after processing, use the GraphView library. Install this library by including the following line under the dependency node of your build.gradle:

    implementation 'com.jjoe64:graphview:4.2.2'

     

  4. Then, include the GraphView in the UI by using the <com.jjoe64.graphview.GraphView> attribute. When GraphView is included, you get a reference to the GraphView instance like you would get with any other Android control.

In the configureGraph method that we invoke under the onCreate activity method in the following code. This can then use a reference to the GraphView to format the plot and add data series. We use a three-line series with instances of the LineGraphSeries from the GraphView library. One displays the input signal, one is for the truncated signal, and the last one is for convolution.

GraphView graph;
 
private LineGraphSeries signalSeries = new LineGraphSeries<>(); 

private void configureGraph(){
    // Initialize graph
    graph = (GraphView) findViewById(R.id.graph);

    // Set bounds
    graph.getViewport().setXAxisBoundsManual(true);
    graph.getViewport().setMaxX(getSignalLength());

    graph.getViewport().setYAxisBoundsManual(true);
    graph.getViewport().setMinY(-150);
    graph.getViewport().setMaxY(150);

    // Configure series
    int thickness = 4;
    signalSeries.setTitle("Signal");
    signalSeries.setThickness(thickness);
    signalSeries.setColor(Color.BLUE);

    // Truncate, and convolution series are configured in the same way
    // Add series
    graph.addSeries(signalSeries);

    // Add legend
    graph.getLegendRenderer().setVisible(true);
    graph.getLegendRenderer().setAlign(LegendRenderer.LegendAlign.TOP);
}

 

After setting up the graph, implement event handlers for buttons. The structure of each event handler is similar. The event handlers use the resetData method of the appropriate LineGraphSeries instance to display the data in the chart. For example, the following code displays input signal and data after convolution:

private LineGraphSeries signalSeries = new LineGraphSeries<>();
private LineGraphSeries signalAfterConvolutionSeries = 
    new LineGraphSeries<>();
 
public void buttonGenerateSignalClicked(View v) {
    signalSeries.resetData(getDataPoints(generateSignal()));
}
 
public void buttonConvolutionClicked(View v) {
signalAfterConvolutionSeries.resetData(getDataPoints(convolution(useNeon())));

    displayProcessingTime();
}

Event handlers that are related to truncation and convolution also take the information, whether the processing is done using Neon intrinsics or native C++. Check the state of the Use neon checkbox that is provided in the application interface, as you can see here:

private boolean useNeon() {
    return checkBoxUseNeon.isChecked();
}

getDataPoints is the helper that converts the byte array, from native-lib, to a collection of DataPoints that the GraphView requires for plotting. Refer to the code repository in the related information tab for more information.

Another helper, displayProcessingTime, is used to depict the code execution time in the label that is located below the chart. Use this to compare the processing performance between Neon and non-Neon code.

Previous Next