com.retroteam.studio.retrostudio.EditorLandscape.java Source code

Java tutorial

Introduction

Here is the source code for com.retroteam.studio.retrostudio.EditorLandscape.java

Source

/*
 * Retro Studio Copyright 2015 Retro Team
 * This file is part of Retro Studio.
 *
 * Retro Studio is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Retro Studio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Retro Studio.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.retroteam.studio.retrostudio;

import com.getbase.floatingactionbutton.FloatingActionButton;
import com.getbase.floatingactionbutton.FloatingActionsMenu;

import android.annotation.TargetApi;

import android.app.ActionBar;
import android.app.Activity;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;

import android.os.Environment;
import android.support.v4.content.ContextCompat;
import android.util.Log;

import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;

import android.view.View;
import android.view.MenuItem;
import android.support.v4.app.NavUtils;
import android.widget.EditText;
import android.widget.GridLayout;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import com.retroteam.studio.retrostudio.pcm.*;

public class EditorLandscape extends Activity {

    /**
     * The info that gets passed into the MeasureEditor activity.
     */
    public final static String MEASURE_INFO = "com.retroteam.studio.MEASUREINFO";

    public final static String MEASURE_TITLE = "com.retroteam.studio.MEASURETITLE";

    public final static String MEASURE = "com.retroteam.studio.MEASURE";

    public final static String MEASURE_TRACK = "com.retroteam.studio.MEASURE_TRACK";

    private double PROJECT_TEMPO = 60.0;

    private String PROJECT_TITLE = "";

    private int TS_BEATS = 4;

    private int TS_NOTES = 4;

    private Project theproject = new Project(new TimeSignature(TS_BEATS, TS_NOTES));

    private Point displaysize = new Point();

    private ThreadedPlayback audioplayer;

    private int trackReloadCounter = 0;

    private Menu actionMenu;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_editor_landscape);

        //get display size
        Display display = getWindowManager().getDefaultDisplay();
        display.getSize(displaysize);

        //check where we're coming from, if we have an intent
        if (getIntent().getStringExtra("SourceActivity") != null && getIntent() != null) {
            Intent intent = getIntent();
            if (intent.getStringExtra("SourceActivity").equals("MainActivity")) {
                ProjectWrapper projwrapper = (ProjectWrapper) intent.getSerializableExtra("project");
                if (projwrapper != null) {
                    // We are loading the project from a file.
                    PROJECT_TITLE = projwrapper.title;
                    PROJECT_TEMPO = projwrapper.tempo;
                    TS_BEATS = projwrapper.tSig.beats();
                    TS_NOTES = projwrapper.tSig.notes();
                    theproject = projwrapper.project;
                    ArrayList<MeasureTagGroup> mTagList = projwrapper.mTagList;
                    //redraw project
                    redrawMe(theproject);
                    //re-apply measure tags
                    for (int m = 0; m < mTagList.size(); m++) {
                        ImageView measure = (ImageView) findMeasureByCoords(mTagList.get(m).row,
                                mTagList.get(m).column);
                        measure.setTag(R.id.TAG_ROW, mTagList.get(m).row);
                        measure.setTag(R.id.TAG_COLUMN, mTagList.get(m).column);
                        measure.setTag(R.bool.TAG_HASNOTES, mTagList.get(m).hasNotes);
                        measure.setTag(R.id.TAG_GUISNAP, mTagList.get(m).guiSNAP);
                        measure.setTag(R.id.TAG_FILLED_NOTES, mTagList.get(m).filledNotes);
                        if (mTagList.get(m).hasNotes) {
                            measure.setImageResource(R.drawable.measure_new_filled);
                        }
                    }
                    Toast.makeText(this, "Opened project " + PROJECT_TITLE, Toast.LENGTH_SHORT).show();
                } else {
                    // We're making a new project or coming from the main menu
                    Toast.makeText(this, "Created new project.", Toast.LENGTH_SHORT).show();
                    TS_BEATS = intent.getIntExtra("TimeSigBeats", 4);
                    TS_NOTES = intent.getIntExtra("TimeSigNotes", 4);
                    theproject = new Project(new TimeSignature(TS_BEATS, TS_NOTES));
                }
            }
        }

        // Put the project title in the actionbar.
        if (PROJECT_TITLE != "") {
            setupActionBar("'" + PROJECT_TITLE + "' Project Editor (" + Integer.toString(TS_BEATS) + "/"
                    + Integer.toString(TS_NOTES) + " Time)");
        } else {
            setupActionBar(
                    "Project Editor (" + Integer.toString(TS_BEATS) + "/" + Integer.toString(TS_NOTES) + " Time)");
        }

        //add buttons to the floating action menu
        FloatingActionsMenu menuAddTrack = (FloatingActionsMenu) findViewById(R.id.add_track_menu);
        FloatingActionButton addSineTrack = new FloatingActionButton(getBaseContext());
        addSineTrack.setTitle("Add Sine Wave Track");
        addSineTrack.setIcon(R.drawable.sine_wave);
        addSineTrack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SineWave wave = new SineWave();
                theproject.addTrack(wave);
                addTrack(R.drawable.sine_wave, false);
            }
        });
        FloatingActionButton addSquareTrack = new FloatingActionButton(getBaseContext());
        addSquareTrack.setTitle("Add Square Wave Track");
        addSquareTrack.setIcon(R.drawable.square_wave);
        addSquareTrack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SquareWave wave = new SquareWave();
                theproject.addTrack(wave);
                addTrack(R.drawable.square_wave, false);
            }
        });
        FloatingActionButton addNoiseTrack = new FloatingActionButton(getBaseContext());
        addNoiseTrack.setTitle("Add Noise Wave Track");
        addNoiseTrack.setIcon(R.drawable.noise_wave);
        addNoiseTrack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                NoiseWave wave = new NoiseWave();
                theproject.addTrack(wave);
                addTrack(R.drawable.noise_wave, false);
            }
        });

        FloatingActionButton addSawTrack = new FloatingActionButton(getBaseContext());
        addSawTrack.setTitle("Add Saw Wave Track");
        addSawTrack.setIcon(R.drawable.saw_wave);
        addSawTrack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SawtoothWave wave = new SawtoothWave();
                theproject.addTrack(wave);
                addTrack(R.drawable.saw_wave, false);
            }
        });

        FloatingActionButton addInvSawTrack = new FloatingActionButton(getBaseContext());
        addInvSawTrack.setTitle("Add Inverse Saw Wave Track");
        addInvSawTrack.setIcon(R.drawable.inv_saw_wave);
        addInvSawTrack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                InverseSawtoothWave wave = new InverseSawtoothWave();
                theproject.addTrack(wave);
                addTrack(R.drawable.inv_saw_wave, false);
            }
        });

        FloatingActionButton addTriangleTrack = new FloatingActionButton(getBaseContext());
        addTriangleTrack.setTitle("Add Triangle Wave Track");
        addTriangleTrack.setIcon(R.drawable.triangle_wave);
        addTriangleTrack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TriangleWave wave = new TriangleWave();
                theproject.addTrack(wave);
                addTrack(R.drawable.triangle_wave, false);
            }
        });

        menuAddTrack.addButton(addSineTrack);
        menuAddTrack.addButton(addSquareTrack);
        menuAddTrack.addButton(addNoiseTrack);
        menuAddTrack.addButton(addSawTrack);
        menuAddTrack.addButton(addInvSawTrack);
        menuAddTrack.addButton(addTriangleTrack);

        // Set the project tempo.
        theproject.tempo(PROJECT_TEMPO);
    }

    /**
     * Set up the {@link android.app.ActionBar}, if the API is available.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void setupActionBar(String title) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            // Show the Up button in the action bar.
            ActionBar editorbar = getActionBar();
            editorbar.setDisplayHomeAsUpEnabled(true);
            editorbar.setTitle(title);

        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu items for use in the action bar
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.editor_activity_actions, menu);
        actionMenu = menu; //save menu reference to modify it later
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == android.R.id.home) {
            NavUtils.navigateUpFromSameTask(this);
            return true;
        }

        if (id == R.id.action_play) {
            if (!(theproject.size() == 0) && theproject.track(0).measure(0) != null) {

                theproject.tempo(PROJECT_TEMPO);

                new Thread(new Runnable() {
                    public void run() {
                        ThreadedPlayback audioplayer2 = new ThreadedPlayback(theproject.export());
                        audioplayer = audioplayer2;
                        audioplayer.play();
                    }
                }).start();
                Toast.makeText(this, "Playing...", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Add a track and measure first!", Toast.LENGTH_SHORT).show();
            }
        }

        if (id == R.id.action_exportaswav) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            LayoutInflater inflater = this.getLayoutInflater();

            final View save_dialog = inflater.inflate(R.layout.save_dialog, null);
            final EditText sd = (EditText) save_dialog.findViewById(R.id.projectTitleField);
            sd.setText(PROJECT_TITLE);

            builder.setTitle("Export as Unsigned 8-bit WAV").setMessage("Enter the filename.").setView(save_dialog)
                    .setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {

                            exportAsWav(theproject, sd.getText().toString());

                        }
                    }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });

            AlertDialog dialog = builder.create();

            dialog.show();

        }

        if (id == R.id.action_togglefs) {
            toggleHideyBar();
        }

        if (id == R.id.action_tempo) {
            tempoPrompt();
        }

        if (id == R.id.action_save) {
            if (PROJECT_TITLE == "") {
                saveProject();
            } else {
                saveProject(PROJECT_TITLE);
            }
        }

        if (id == R.id.action_saveas) {
            saveProject();
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * How to handle a result when we come back from the measure.
     * @param requestCode
     * @param resultCode
     * @param data
     */

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // Check which request we're responding to
        if (requestCode == 123) {
            // Make sure the request was successful
            if (resultCode == RESULT_OK) {
                //Uri thedata = data.getData();
                theproject = (Project) data.getSerializableExtra("Project");
                //redrawMe(theproject);
                updateMe(data.getBooleanExtra("HasNotes", false), data.getIntExtra("trackNum", 0),
                        data.getIntExtra("measureNum", 0), data.getIntExtra("guiSNAP", 0),
                        (ArrayList<int[]>) data.getSerializableExtra("filledNotes"));
            }
        }
    }

    /**
     * Write the 8bit PCM audio to the wav file format to the public music dir.
     * http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
     * @param proj
     * @param filename
     */

    private void exportAsWav(Project proj, String filename) {
        try {
            File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC),
                    filename + ".wav");
            DataOutputStream os = new DataOutputStream(new FileOutputStream(file));

            // get project length
            int pLength = proj.export().getBytes().length;

            os.writeBytes("RIFF"); // Chunk ID 'RIFF'
            os.write(intToByteArray(36 + pLength), 0, 4); //file size
            os.writeBytes("WAVE"); // Wave ID
            os.writeBytes("fmt "); // Chunk ID 'fmt '
            os.write(intToByteArray(16), 0, 4); // chunk size
            os.write(intToByteArray(1), 0, 2); // pcm audio
            os.write(intToByteArray(1), 0, 2); // mono
            os.write(intToByteArray(8000), 0, 4); // samples/sec
            os.write(intToByteArray(pLength), 0, 4); // bytes/sec
            os.write(intToByteArray(1), 0, 2); // 1 byte/sample
            os.write(intToByteArray(8), 0, 2); // 8 bits/sample
            os.writeBytes("data"); // Chunk ID 'data'
            os.write(intToByteArray(pLength), 0, 4); // size of data chunk
            os.write(proj.export().getBytes()); // write the data
            os.close();
            Toast.makeText(this, "Saved file to: " + file.getPath(), Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            Toast.makeText(this, "IOException while saving project.", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Convert int to a byte array for making the wav header.
     * @param i
     * @return
     */

    private byte[] intToByteArray(int i) {
        byte[] b = new byte[4];
        b[0] = (byte) (i & 0x00FF);
        b[1] = (byte) ((i >> 8) & 0x000000FF);
        b[2] = (byte) ((i >> 16) & 0x000000FF);
        b[3] = (byte) ((i >> 24) & 0x000000FF);
        return b;
    }

    /**
     * Save the project with a title.
     * @param title
     */

    private void saveProject(String title) {
        String fTitle = title + ".retro";
        try {
            FileOutputStream fos = getApplicationContext().openFileOutput(fTitle, Context.MODE_PRIVATE);
            ObjectOutputStream os = new ObjectOutputStream(fos);
            os.writeObject(new ProjectWrapper(theproject, new TimeSignature(TS_BEATS, TS_NOTES), PROJECT_TITLE,
                    PROJECT_TEMPO, getMeasureTags()));
            os.close();
            fos.close();
            Toast.makeText(this, "Saved as " + fTitle, Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            Toast.makeText(this, "IOException while saving project.", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Prompt the user for a title and call saveProject(title).
     */

    private void saveProject() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        LayoutInflater inflater = this.getLayoutInflater();

        final View save_dialog = inflater.inflate(R.layout.save_dialog, null);
        final EditText sd = (EditText) save_dialog.findViewById(R.id.projectTitleField);
        sd.setText(PROJECT_TITLE);

        builder.setTitle("Project Name").setView(save_dialog)
                .setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {

                        String title = sd.getText().toString();
                        PROJECT_TITLE = title;
                        saveProject(PROJECT_TITLE);

                    }
                }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.cancel();
                    }
                });

        AlertDialog dialog = builder.create();

        dialog.show();

    }

    /**
     * Iterate through all the measures and return a list of measure tag group objects.
     * The list is serialized out to the .retro project file.
     * @return ArrayList<MeasureTagGroup>
     */

    private ArrayList<MeasureTagGroup> getMeasureTags() {

        ArrayList<MeasureTagGroup> mtaglist = new ArrayList<>();

        LinearLayout track_layout = (LinearLayout) findViewById(R.id.track_layout);

        //iterate through all the measures
        for (int t = 0; t < track_layout.getChildCount(); t++) {
            //get hscrollview
            HorizontalScrollView hchild = (HorizontalScrollView) track_layout.getChildAt(t);
            for (int h = 0; h < hchild.getChildCount(); h++) {
                //get gridlayout
                GridLayout gchild = (GridLayout) hchild.getChildAt(h);
                for (int g = 0; g < gchild.getChildCount(); g++) {
                    //get measure
                    if (gchild.getChildAt(g) instanceof ImageView) {
                        ImageView ivchild = (ImageView) gchild.getChildAt(g);
                        Drawable measureFill = ivchild.getDrawable();
                        if (measureFill.getConstantState()
                                .equals(ContextCompat
                                        .getDrawable(getApplicationContext(), R.drawable.measure_new_empty)
                                        .getConstantState())) {
                            mtaglist.add(new MeasureTagGroup((int) ivchild.getTag(R.id.TAG_ROW),
                                    (int) ivchild.getTag(R.id.TAG_COLUMN), false,
                                    (int) ivchild.getTag(R.id.TAG_GUISNAP),
                                    (ArrayList<int[]>) ivchild.getTag(R.id.TAG_FILLED_NOTES)));
                        } else if (measureFill.getConstantState()
                                .equals(ContextCompat
                                        .getDrawable(getApplicationContext(), R.drawable.measure_new_filled)
                                        .getConstantState())) {
                            mtaglist.add(new MeasureTagGroup((int) ivchild.getTag(R.id.TAG_ROW),
                                    (int) ivchild.getTag(R.id.TAG_COLUMN), true,
                                    (int) ivchild.getTag(R.id.TAG_GUISNAP),
                                    (ArrayList<int[]>) ivchild.getTag(R.id.TAG_FILLED_NOTES)));
                        }
                    }

                }
            }
        }

        return mtaglist;
    }

    /**
     * Show the user the tempo setting alert dialog.
     */

    private void tempoPrompt() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        LayoutInflater inflater = this.getLayoutInflater();

        final View tempo_picker = inflater.inflate(R.layout.tempo_picker, null);
        final EditText tv = (EditText) tempo_picker.findViewById(R.id.tempoPickerField);
        tv.setText(Double.toString(PROJECT_TEMPO));

        builder.setTitle("Set Project Tempo").setMessage("Type a value.").setView(tempo_picker)
                .setPositiveButton(R.string.set, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {

                        String et = tv.getText().toString();
                        PROJECT_TEMPO = Double.parseDouble(et);
                        theproject.tempo(PROJECT_TEMPO);

                    }
                }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        // do nothing
                    }
                });

        AlertDialog dialog = builder.create();

        dialog.show();
    }

    /**
     * Redraw the project when loading from a file.
     * @param p (Project)
     */

    private void redrawMe(Project p) {
        int numtracks = p.size();
        ImageView t = null;
        for (int i = 0; i < numtracks; i++) {
            SoundWave tracktype = p.track(i).soundWave();
            if (tracktype instanceof SineWave) {
                t = addTrack(R.drawable.sine_wave, true);
            } else if (tracktype instanceof SquareWave) {
                t = addTrack(R.drawable.square_wave, true);
            } else if (tracktype instanceof NoiseWave) {
                t = addTrack(R.drawable.noise_wave, true);
            } else if (tracktype instanceof TriangleWave) {
                t = addTrack(R.drawable.triangle_wave, true);
            } else if (tracktype instanceof SawtoothWave) {
                t = addTrack(R.drawable.saw_wave, true);
            } else if (tracktype instanceof InverseSawtoothWave) {
                t = addTrack(R.drawable.inv_saw_wave, true);
            }
            int nummeasures = p.track(i).size();
            for (int m = 0; m < nummeasures; m++) {
                addMeasure(t, true);
            }
        }
    }

    /**
     * Toggle Immersive Mode for more room on small devices.
     */

    private void toggleHideyBar() {

        // The UI options currently enabled are represented by a bitfield.
        // getSystemUiVisibility() gives us that bitfield.
        int uiOptions = this.getWindow().getDecorView().getSystemUiVisibility();
        int newUiOptions = uiOptions;
        boolean isImmersiveModeEnabled = ((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions);
        if (isImmersiveModeEnabled) {
            Log.i("EditorLandscape", "Turning immersive mode mode off. ");
        } else {
            Log.i("EditorLandscape", "Turning immersive mode mode on.");
        }

        // Navigation bar hiding:  Backwards compatible to ICS.
        if (Build.VERSION.SDK_INT >= 14) {
            newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
        }

        // Status bar hiding: Backwards compatible to Jellybean
        if (Build.VERSION.SDK_INT >= 16) {
            newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
        }

        // Immersive mode: Backward compatible to KitKat.
        // Note that this flag doesn't do anything by itself, it only augments the behavior
        // of HIDE_NAVIGATION and FLAG_FULLSCREEN.  For the purposes of this sample
        // all three flags are being toggled together.
        // Note that there are two immersive mode UI flags, one of which is referred to as "sticky".
        // Sticky immersive mode differs in that it makes the navigation and status bars
        // semi-transparent, and the UI flag does not get cleared when the user interacts with
        // the screen.
        if (Build.VERSION.SDK_INT >= 18) {
            newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        }

        this.getWindow().getDecorView().setSystemUiVisibility(newUiOptions);
    }

    /**
     * Start MeasureEditor with an intent with all the data.
     * @param view
     */

    private void editMeasure(View view) {

        Intent intent = new Intent(this, MeasureEditor.class);

        intent.putExtra("Project", theproject);
        ImageView viewi = (ImageView) view;
        intent.putExtra(MEASURE_TRACK, (int) viewi.getTag(R.id.TAG_ROW));
        intent.putExtra(MEASURE, (int) viewi.getTag(R.id.TAG_COLUMN));
        intent.putExtra(MEASURE_TITLE, Integer.toString((int) viewi.getTag(R.id.TAG_COLUMN)) + ","
                + Integer.toString((int) viewi.getTag(R.id.TAG_ROW)));
        intent.putExtra("measureID", viewi.getId());
        intent.putExtra("SourceActivity", "EditorLandscape");
        intent.putExtra("guiSNAP", (int) viewi.getTag(R.id.TAG_GUISNAP));
        intent.putExtra("filledNotes", (ArrayList<int[]>) viewi.getTag(R.id.TAG_FILLED_NOTES));
        intent.putExtra("tsBeats", TS_BEATS);
        intent.putExtra("tsNotes", TS_NOTES);
        startActivityForResult(intent, 123);
    }

    /**
     * Process information returned from the MeasureEditor.
     * @param hasNotes
     * @param row
     * @param column
     * @param guiSNAP
     * @param filledNotes
     */

    private void updateMe(boolean hasNotes, int row, int column, int guiSNAP, ArrayList<int[]> filledNotes) {
        //mark the views that contain notes and remember the guiSNAP
        ImageView clickedmeasure = (ImageView) findMeasureByCoords(row, column);
        if (hasNotes) {
            clickedmeasure.setImageResource(R.drawable.measure_new_filled);
        }
        clickedmeasure.setTag(R.id.TAG_GUISNAP, guiSNAP);
        clickedmeasure.setTag(R.id.TAG_FILLED_NOTES, filledNotes);
    }

    /**
     * Create all the views associated with a track.
     * @param waveDrawableID
     * @param projectLoad
     * @return
     */

    private ImageView addTrack(int waveDrawableID, boolean projectLoad) {
        //add the track with the measure adder to the view
        //get layout
        LinearLayout track_layout = (LinearLayout) findViewById(R.id.track_layout);

        //create track container
        HorizontalScrollView track_container = new HorizontalScrollView(getApplicationContext());
        track_container.setLayoutParams(new HorizontalScrollView.LayoutParams(
                HorizontalScrollView.LayoutParams.MATCH_PARENT, displaysize.y / 4));
        track_container.setBackground(getResources().getDrawable(R.color.track_container_bg));

        //create grid layout
        GridLayout track_grid = new GridLayout(getApplicationContext());
        track_grid.setColumnCount(100);
        track_grid.setRowCount(1);
        track_grid.setOrientation(GridLayout.HORIZONTAL);
        track_grid.setId(R.id.track_grid);

        //create linear layout for track id and wave
        LinearLayout track_identifier = new LinearLayout(getApplicationContext());
        track_identifier.setLayoutParams(new LinearLayout.LayoutParams(displaysize.x / 14, displaysize.y / 4));
        track_identifier.setOrientation(LinearLayout.VERTICAL);
        track_identifier.setBackgroundColor(getResources().getColor(R.color.black_overlay));

        //create textview for linear layout
        TextView track_num = new TextView(getApplicationContext());
        track_num.setText("1");
        track_num.setTextSize(45);
        track_num.setGravity(Gravity.CENTER | Gravity.CENTER_VERTICAL);

        //create imageview for linear layout
        ImageView track_type = new ImageView(getApplicationContext());
        track_type.setImageResource(waveDrawableID);
        track_type.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT));

        //create "add measure" for grid layout
        ImageView add_measure = new ImageView(getApplicationContext());
        add_measure.setImageResource(R.drawable.measure_new);
        add_measure.setLayoutParams(new LinearLayout.LayoutParams((int) (displaysize.x / 3.32),
                LinearLayout.LayoutParams.MATCH_PARENT));
        if (projectLoad) {
            add_measure.setTag(R.id.TAG_ROW, trackReloadCounter);
            add_measure.setId(trackReloadCounter + 4200);
        } else {
            add_measure.setTag(R.id.TAG_ROW, theproject.size() - 1);
            add_measure.setId(theproject.size() - 1 + 4200);
        }

        add_measure.setTag(R.id.TAG_COLUMN, 0);
        add_measure.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addMeasure(v, false);

            }
        });

        track_identifier.addView(track_num);
        if (projectLoad) {
            track_num.setText(Integer.toString(trackReloadCounter + 1));
            trackReloadCounter += 1;
        } else {
            track_num.setText(Integer.toString(theproject.size()));
        }
        track_num.setTextSize(45);
        track_identifier.addView(track_type);

        track_grid.addView(track_identifier);
        track_grid.addView(add_measure);

        track_container.addView(track_grid);

        track_layout.addView(track_container);

        return add_measure;

    }

    /**
     * Create all the views associated with a measure.
     * @param v
     * @param projectLoad
     */

    private void addMeasure(View v, boolean projectLoad) {
        int whichtrack = (int) v.getTag(R.id.TAG_ROW);
        int whichmeasure = (int) v.getTag(R.id.TAG_COLUMN);

        if (!projectLoad) {
            theproject.track(whichtrack).addMeasure();
        }
        GridLayout myparent = (GridLayout) v.getParent();

        ImageView measure = new ImageView(getApplicationContext());
        measure.setImageResource(R.drawable.measure_new_empty);
        measure.setLayoutParams(new LinearLayout.LayoutParams((int) (displaysize.x / 3.32),
                LinearLayout.LayoutParams.MATCH_PARENT));
        measure.setTag(R.id.TAG_ROW, whichtrack);
        measure.setTag(R.id.TAG_COLUMN, whichmeasure);
        measure.setTag(R.bool.TAG_HASNOTES, false);
        measure.setTag(R.id.TAG_GUISNAP, 0);
        measure.setTag(R.id.TAG_FILLED_NOTES, new ArrayList<int[]>());
        measure.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ///
                int whichtrack = (int) v.getTag(R.id.TAG_ROW);
                int whichmeasure = (int) v.getTag(R.id.TAG_COLUMN);
                editMeasure(v);
            }
        });
        myparent.addView(measure, whichmeasure + 1);

        v.setTag(R.id.TAG_COLUMN, whichmeasure + 1);
    }

    /**
     * Find a measure view object by its coordinates.
     * @param row
     * @param column
     * @return
     */

    private View findMeasureByCoords(int row, int column) {

        LinearLayout track_layout = (LinearLayout) findViewById(R.id.track_layout);
        //
        HorizontalScrollView track_container = (HorizontalScrollView) track_layout.getChildAt(row + 1);
        GridLayout track = (GridLayout) track_container.getChildAt(0);

        int numChildren = track.getChildCount();
        for (int i = 0; i < numChildren; i++) {
            View child = track.getChildAt(i);
            if (child.getTag(R.id.TAG_ROW) == row && child.getTag(R.id.TAG_COLUMN) == column) {
                return child;
            }
        }
        return null;
    }

}