com.billooms.harppedals.keysignature.KeySignatureFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.billooms.harppedals.keysignature.KeySignatureFragment.java

Source

package com.billooms.harppedals.keysignature;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;

import com.billooms.harppedals.R;
import com.billooms.harppedals.notes.Note;
import com.billooms.harppedals.notes.NotePlayer;
import com.billooms.harppedals.notes.SharpFlat;
import com.billooms.harppedals.pedals.PedalPosition;
import com.billooms.harppedals.pedals.Pedals;

import java.util.ArrayList;
import java.util.List;

import static com.billooms.harppedals.notes.SharpFlat.DOUBLESHARP;

/**
 * Fragment for setting the Key Signature.
 *
 * @author Bill Ooms. Copyright 2017 Studio of Bill Ooms. All rights reserved.
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class KeySignatureFragment extends Fragment {

    /** Pointer to the PedalChangeListener. */
    private OnKeySigChangeListener keySigChangeListener;

    /** Pointers to all of the controls that are displayed. */
    private Spinner keySpinner, scaleSpinner;
    private TextView scaleText, keyText;
    private ImageView keySigIcon;
    private ArrayAdapter<String> keySpinnerAdapter, scaleSpinnerAdapter;

    /** NotePlayer for playing notes. */
    private NotePlayer player;

    /** Key. */
    private Key key;

    /**
     * Required empty public constructor.
     */
    public KeySignatureFragment() {
    }

    /**
     * Container Activity must implement this interface.
     */
    public interface OnKeySigChangeListener {
        /**
         * Change the pedals based on the given PedalPosition and save the given Key.
         *
         * @param pedPos PedalPosition
         * @param key Key
         * @param firstNote first note to play on the scale
         */
        void onKeySigChange(PedalPosition pedPos, Key key, Note firstNote);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        //      Log.d(TAG, "KeySignatureFragment.onCreate " + this.toString());
        super.onCreate(savedInstanceState);

        key = new Key(); // default is C major
        player = new NotePlayer(this.getContext());
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //      Log.d(TAG, "KeySignatureFragment.onCreateView " + this.toString());
        View view = inflater.inflate(R.layout.fragment_key_signature, container, false);

        keySpinner = (Spinner) view.findViewById(R.id.keySpinner);
        List<String> keySigList = new ArrayList<>();
        for (KeySignature k : KeySignature.values()) {
            keySigList.add(k.getText());
        }
        keySpinnerAdapter = new ArrayAdapter<>(this.getContext(), R.layout.spinner_item, keySigList);
        keySpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        keySpinner.setAdapter(keySpinnerAdapter);
        keySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if (view != null) {
                    key.setKeySignature(KeySignature.values()[keySpinner.getPositionForView(view)]);
                }
                updateForm();
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });

        scaleSpinner = (Spinner) view.findViewById(R.id.scaleSpinner);
        List<String> scaleList = new ArrayList<>();
        for (Scale s : Scale.values()) {
            scaleList.add(s.name());
        }
        scaleSpinnerAdapter = new ArrayAdapter<>(this.getContext(), R.layout.spinner_item, scaleList);
        scaleSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        scaleSpinner.setAdapter(scaleSpinnerAdapter);
        scaleSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if (view != null) {
                    key.setScale(Scale.values()[scaleSpinner.getPositionForView(view)]);
                }
                updateForm();
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });

        scaleText = (TextView) view.findViewById(R.id.scaleText);

        keyText = (TextView) view.findViewById(R.id.keyText);

        keySigIcon = (ImageView) view.findViewById(R.id.keySigIcon);

        Button playButton = (Button) view.findViewById(R.id.playScaleButton);
        playButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                playScale();
            }
        });

        Button setKeyButton = (Button) view.findViewById(R.id.setKeyButton);
        setKeyButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setPedalsForKey();
            }
        });

        Button setTonicButton = (Button) view.findViewById(R.id.setTonicButton);
        setTonicButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setPedalsForTonic();
            }
        });

        Button setV7Button = (Button) view.findViewById(R.id.setV7Button);
        setV7Button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setPedalsForV7();
            }
        });

        restoreInstanceState(savedInstanceState);

        return view;
    }

    @Override
    public void onStart() {
        //      Log.d(TAG, "KeySignatureFragment.onStart " + this.toString());
        super.onStart();

        // During startup, check if there are arguments passed to the fragment.
        // Note: arguments will over-ride any savedInstanceState that was given to onCreateView
        restoreInstanceState(getArguments());
    }

    @Override
    public void onResume() {
        //      Log.d(TAG, "KeySignatureFragment.onResume " + this.toString());
        super.onResume();

        updateForm();
    }

    //   @Override
    //   public void onPause() {
    //      super.onPause();
    //   }

    @Override
    public void onDestroy() {
        //      Log.d(TAG, "KeySignatureFragment.onDestroy " + this.toString());
        super.onDestroy();
        //      player.release();
        player = null;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        //      Log.d(TAG, "KeySignatureFragment.onSaveInstanceState " + this.toString());
        super.onSaveInstanceState(outState);
        if (outState == null) {
            return;
        }

        key.saveToBundle(outState);
        key.saveToBundle(this.getArguments()); // must update any arguments sent to this fragment as well
    }

    /**
     * Restore the state of this fragment from the given savedInstanceState.
     *
     * @param bundle savedInstanceState
     */
    private void restoreInstanceState(Bundle bundle) {
        if (bundle == null) {
            return;
        }

        key.restoreFromBundle(bundle);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnKeySigChangeListener) {
            keySigChangeListener = (OnKeySigChangeListener) context;
        } else {
            throw new RuntimeException(context.toString() + " must implement OnKeySigChangeListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        keySigChangeListener = null;
    }

    /**
     * Update all fields on the form from key signature data.
     */
    private void updateForm() {
        if (key.getKeySignature() != null) {
            keySpinner.setSelection(keySpinnerAdapter.getPosition(key.getKeySignature().getText()));
            scaleSpinner.setSelection(scaleSpinnerAdapter.getPosition(key.getScale().name()));
            keySigIcon.setImageDrawable(key.getKeySignature().getIcon(this.getContext()));
            scaleText.setText(key.getNotes().toString());
            keyText.setText(key.toString());
        }
    }

    /**
     * Play the current scale.
     */
    private void playScale() {
        ArrayList<Note> notes = key.getNotes();
        ArrayList<Note> notes2 = new ArrayList<>();
        for (Note note : notes) {
            notes2.add(new Note(note.getNumber() + 12)); // add a 2nd octave
        }
        for (Note note : notes) {
            notes2.add(new Note(note.getNumber() + 24)); // add a 3rd octave
        }
        notes2.add(new Note(notes.get(0).getNumber() + 36)); // and repeat the tonic
        notes.addAll(notes2);
        player.play(notes);
    }

    /**
     * Set the pedals for the current key signature.
     */
    private void setPedalsForKey() {
        ArrayList<Note> notes = key.getNotes();
        if (hasDoubleSharps(notes)) { // handle double sharps differently
            int pitchMask = key.getPitchMask();
            ArrayList<PedalPosition> pedalsForPitchMask = Pedals.pedalsForPitchMask(pitchMask);
            if (!pedalsForPitchMask.isEmpty()) {
                keySigChangeListener.onKeySigChange(pedalsForPitchMask.get(0), key, key.getFirstNote());
            }
        } else {
            keySigChangeListener.onKeySigChange(toPedalPosition(key.getNotes()), key, key.getFirstNote());
        }
    }

    /**
     * Determine if the given list of notes contains a double sharp.
     *
     * @param notes list of notes
     * @return true: contains a double sharp
     */
    private boolean hasDoubleSharps(ArrayList<Note> notes) {
        for (Note note : notes) {
            if (note.getSharpFlat() == DOUBLESHARP) {
                return true;
            }
        }
        return false;
    }

    /**
     * Convert an arrayList of notes to a PedalPosition object.
     * There must be exactly 7 notes in the list and each note must be represented.
     *
     * @param list ArrayList of notes
     * @return PedalPosition object
     */
    private PedalPosition toPedalPosition(ArrayList<Note> list) {
        if (list.size() != 7) {
            return null;
        }
        SharpFlat[] sf = new SharpFlat[7]; // array of SharpFlat for each note A,B,C,D,E,F,G
        for (Note n : list) {
            sf[n.getBaseNote().ordinal()] = n.getSharpFlat();
        }
        return new PedalPosition(sf[0], sf[1], sf[2], sf[3], sf[4], sf[5], sf[6]);
    }

    /**
     * Change the pedals for a tonic glissando.
     * This is done by replacing the 4th and 7th of the scale by other notes.
     */
    private void setPedalsForTonic() {
        key.setScale(Scale.MAJOR); // and use only the notes from the Major scale
        ArrayList<Note> noteList = key.getNotes();
        Note note4 = noteList.get(3); // get the 4th note of the scale
        Note note7 = noteList.get(6); // get the 7th note of the scale
        noteList.remove(note4); // and remove them
        noteList.remove(note7);
        Note alt4 = Pedals.findAlternate(note4, noteList); // find an alternate for the 4th
        Note alt7 = Pedals.findAlternate(note7, noteList); // find an alternate for the 7th
        if ((alt4 != null) && (alt7 != null)) {
            noteList.add(alt4);
            noteList.add(alt7);
            PedalPosition pedPos = toPedalPosition(noteList);
            keySigChangeListener.onKeySigChange(pedPos, key, key.getFirstNote());
        }
    }

    /**
     * Change the pedals for a V7 glissando.
     * This is done by replacing the 1st and 3rd of the scale by other notes.
     */
    private void setPedalsForV7() {
        key.setScale(Scale.MAJOR); // and use only the notes from the Major scale
        ArrayList<Note> noteList = key.getNotes();
        Note note1 = noteList.get(0); // get the 1st note of the scale
        Note note3 = noteList.get(2); // get the 3rd note of the scale
        noteList.remove(note1); // and remove them
        noteList.remove(note3);
        Note alt1 = Pedals.findAlternate(note1, noteList); // find an enharmonic for the 1st
        Note alt3 = Pedals.findAlternate(note3, noteList); // find an alternate for the 3rd
        if ((alt1 != null) && (alt3 != null)) {
            noteList.add(alt1);
            noteList.add(alt3);
            PedalPosition pedPos = toPedalPosition(noteList);
            keySigChangeListener.onKeySigChange(pedPos, key, new Note(key.getFirstNote().getNumber() + 7));
        }
    }

}