diff --git a/app/build.gradle b/app/build.gradle index 5a331b6..44df331 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,7 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + repositories { maven { url 'https://github.com/joytunes/USB-MIDI-Driver/raw/master/MIDIDriver/snapshots' diff --git a/app/src/main/java/com/midisheetmusic/MidiPlayer.java b/app/src/main/java/com/midisheetmusic/MidiPlayer.java index d43b761..9a09daa 100644 --- a/app/src/main/java/com/midisheetmusic/MidiPlayer.java +++ b/app/src/main/java/com/midisheetmusic/MidiPlayer.java @@ -193,7 +193,10 @@ void init() { stopButton.setOnClickListener(v -> Stop()); playButton.setOnClickListener(v -> Play()); fastFwdButton.setOnClickListener(v -> FastForward()); - settingsButton.setOnClickListener(v -> drawer.openDrawer()); + settingsButton.setOnClickListener(v -> { + drawer.deselect(); + drawer.openDrawer(); + }); midiButton.setOnClickListener(v -> toggleMidi()); leftHandButton.setOnClickListener(v -> toggleTrack(LEFT_TRACK)); rightHandButton.setOnClickListener(v -> toggleTrack(RIGHT_TRACK)); diff --git a/app/src/main/java/com/midisheetmusic/SettingsActivity.java b/app/src/main/java/com/midisheetmusic/SettingsActivity.java index dd143a7..10db2af 100644 --- a/app/src/main/java/com/midisheetmusic/SettingsActivity.java +++ b/app/src/main/java/com/midisheetmusic/SettingsActivity.java @@ -119,7 +119,6 @@ public static class SettingsFragment extends PreferenceFragmentCompat private Preference setAllToPiano; /** Set all instruments to piano */ private SwitchPreferenceCompat scrollVertically; /** Scroll vertically/horizontally */ private SwitchPreferenceCompat showPiano; /** Show the piano */ - private SwitchPreferenceCompat showMeasures; /** Show the measure numbers */ private SwitchPreferenceCompat showLyrics; /** Show the lyrics */ private SwitchPreferenceCompat twoStaffs; /** Combine tracks into two staffs */ private ListPreference showNoteLetters; /** Show the note letters */ @@ -135,11 +134,6 @@ public static class SettingsFragment extends PreferenceFragmentCompat private ColorPreference shade1Color; /** Right-hand color */ private ColorPreference shade2Color; /** Left-hand color */ - /** Play the measures from start to end in a loop */ - private SwitchPreferenceCompat playMeasuresInLoop; - private ListPreference loopStart; - private ListPreference loopEnd; - private Context context; @Override @@ -178,7 +172,6 @@ private void createView() { createTimeSignaturePrefs(root); createCombineIntervalPrefs(root); createColorPrefs(root); - createPlayMeasuresInLoopPrefs(root); setPreferenceScreen(root); } @@ -408,10 +401,6 @@ private void createColorPrefs(PreferenceScreen root) { PreferenceCategory localPreferenceCategory = new PreferenceCategory(context); localPreferenceCategory.setTitle("Select Colors"); root.addPreference(localPreferenceCategory); - useColors = new SwitchPreferenceCompat(context); - useColors.setTitle("Use Note Colors"); - useColors.setChecked(options.useColors); - root.addPreference(useColors); shade1Color = new ColorPreference(context); shade1Color.setColor(options.shade1Color); @@ -423,63 +412,28 @@ private void createColorPrefs(PreferenceScreen root) { shade2Color.setTitle(R.string.left_hand_color); root.addPreference(shade2Color); + useColors = new SwitchPreferenceCompat(context); + useColors.setTitle("Use Note Colors"); + useColors.setChecked(options.useColors); + useColors.setOnPreferenceChangeListener((preference, isChecked) -> { + for (ColorPreference noteColorPref : noteColors) { + noteColorPref.setVisible((boolean)isChecked); + } + return true; + }); + root.addPreference(useColors); + noteColors = new ColorPreference[options.noteColors.length]; for (int i = 0; i < 12; i++) { noteColors[i] = new ColorPreference(context); noteColors[i].setColor(options.noteColors[i]); noteColors[i].setTitle(new String[] {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"}[i]); + noteColors[i].setVisible(options.useColors); root.addPreference(noteColors[i]); } } - - /** Create the "Play Measures in a Loop" preference. - * - * Note that we display the measure numbers starting at 1, - * but the actual playMeasuresInLoopStart field starts at 0. - */ - private void createPlayMeasuresInLoopPrefs(PreferenceScreen root) { - String[] values = new String[options.lastMeasure + 1]; - for (int measure = 0; measure < values.length; measure++) { - values[measure] = "" + (measure+1); - } - - PreferenceCategory playLoopTitle = new PreferenceCategory(context); - playLoopTitle.setTitle(R.string.play_measures_in_loop_title); - root.addPreference(playLoopTitle); - - showMeasures = new SwitchPreferenceCompat(context); - showMeasures.setTitle(R.string.show_measures); - showMeasures.setChecked(options.showMeasures); - root.addPreference(showMeasures); - - playMeasuresInLoop = new SwitchPreferenceCompat(context); - playMeasuresInLoop.setTitle(R.string.play_measures_in_loop); - playMeasuresInLoop.setChecked(options.playMeasuresInLoop); - root.addPreference(playMeasuresInLoop); - - loopStart = new ListPreference(context); - loopStart.setKey("loop_start"); - loopStart.setOnPreferenceChangeListener(this); - loopStart.setTitle(R.string.play_measures_in_loop_start); - loopStart.setEntries(values); - loopStart.setEntryValues(values); - loopStart.setValueIndex(options.playMeasuresInLoopStart); - loopStart.setSummary(loopStart.getEntry() ); - root.addPreference(loopStart); - - loopEnd = new ListPreference(context); - loopEnd.setKey("loop_end"); - loopEnd.setOnPreferenceChangeListener(this); - loopEnd.setTitle(R.string.play_measures_in_loop_end); - loopEnd.setEntries(values); - loopEnd.setEntryValues(values); - loopEnd.setValueIndex(options.playMeasuresInLoopEnd); - loopEnd.setSummary(loopEnd.getEntry() ); - root.addPreference(loopEnd); - } - /** Create the "Restore Default Settings" preference */ private void createRestoreDefaultPrefs(PreferenceScreen root) { restoreDefaults = new Preference(context); @@ -532,10 +486,6 @@ private void updateOptions() { options.shade1Color = shade1Color.getColor(); options.shade2Color = shade2Color.getColor(); options.useColors = useColors.isChecked(); - options.showMeasures = showMeasures.isChecked(); - options.playMeasuresInLoop = playMeasuresInLoop.isChecked(); - options.playMeasuresInLoopStart = Integer.parseInt(loopStart.getValue()) - 1; - options.playMeasuresInLoopEnd = Integer.parseInt(loopEnd.getValue()) - 1; } @Override diff --git a/app/src/main/java/com/midisheetmusic/SheetMusicActivity.java b/app/src/main/java/com/midisheetmusic/SheetMusicActivity.java index f4d7965..4f84be3 100644 --- a/app/src/main/java/com/midisheetmusic/SheetMusicActivity.java +++ b/app/src/main/java/com/midisheetmusic/SheetMusicActivity.java @@ -31,9 +31,14 @@ import androidx.drawerlayout.widget.DrawerLayout; +import com.midisheetmusic.drawerItems.ExpandableSwitchDrawerItem; import com.midisheetmusic.sheets.ClefSymbol; import com.mikepenz.materialdrawer.Drawer; import com.mikepenz.materialdrawer.DrawerBuilder; +import com.mikepenz.materialdrawer.holder.StringHolder; +import com.mikepenz.materialdrawer.model.DividerDrawerItem; +import com.mikepenz.materialdrawer.model.SecondaryDrawerItem; +import com.mikepenz.materialdrawer.model.SecondarySwitchDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import java.io.File; @@ -55,7 +60,10 @@ public class SheetMusicActivity extends MidiHandlingActivity { public static final String MidiTitleID = "MidiTitleID"; public static final int settingsRequestCode = 1; - + public static final int ID_LOOP_ENABLE = 10; + public static final int ID_LOOP_START = 11; + public static final int ID_LOOP_END = 12; + private MidiPlayer player; /* The play/stop/rewind toolbar */ private Piano piano; /* The piano at the top */ private SheetMusic sheet; /* The sheet music */ @@ -128,13 +136,50 @@ public void onCreate(Bundle state) { void createViews() { layout = findViewById(R.id.sheet_content); + SecondarySwitchDrawerItem showMeasures = new SecondarySwitchDrawerItem() + .withName(R.string.show_measures) + .withLevel(2) + .withChecked(options.showMeasures) + .withOnCheckedChangeListener((iDrawerItem, compoundButton, isChecked) -> { + options.showMeasures = isChecked; + createSheetMusic(options); + }); + + SecondaryDrawerItem loopStart = new SecondaryDrawerItem() + .withIdentifier(ID_LOOP_START) + .withBadge(Integer.toString(options.playMeasuresInLoopStart + 1)) + .withName(R.string.play_measures_in_loop_start) + .withLevel(2); + + SecondaryDrawerItem loopEnd = new SecondaryDrawerItem() + .withIdentifier(ID_LOOP_END) + .withBadge(Integer.toString(options.playMeasuresInLoopEnd + 1)) + .withName(R.string.play_measures_in_loop_end) + .withLevel(2); + + + ExpandableSwitchDrawerItem loopSettings = new ExpandableSwitchDrawerItem() + .withIdentifier(ID_LOOP_ENABLE) + .withName("Loop on Measures") + .withChecked(options.playMeasuresInLoop) + .withOnCheckedChangeListener((iDrawerItem, compoundButton, isChecked) -> { + options.playMeasuresInLoop = isChecked; + }) + .withSubItems(showMeasures, loopStart, loopEnd); + // Drawer drawer = new DrawerBuilder() .withActivity(this) + .withInnerShadow(true) + .addDrawerItems( + loopSettings, + new DividerDrawerItem() + ) .inflateMenu(R.menu.sheet_menu) .withOnDrawerItemClickListener((view, i, item) -> drawerItemClickListener(item)) .withDrawerGravity(Gravity.RIGHT) .build(); + // Make sure that the view extends over the navigation buttons area drawer.getDrawerLayout().setFitsSystemWindows(false); // Lock the drawer so swiping doesn't open it @@ -181,18 +226,65 @@ public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } + + /** Create a string list of the numbers between listStart and listEnd (inclusive) */ + private String[] makeStringList(int listStart, int listEnd) { + String[] list = new String[listEnd]; + for (int i = 0; i < list.length; i++) { + list[i] = Integer.toString(i + listStart); + } + return list; + } + + /** Handle clicks on the drawer menu */ public boolean drawerItemClickListener(IDrawerItem item) { switch ((int)item.getIdentifier()) { case R.id.song_settings: changeSettings(); + drawer.closeDrawer(); break; case R.id.save_images: showSaveImagesDialog(); + drawer.closeDrawer(); + break; + case ID_LOOP_START: + // Note that we display the measure numbers starting at 1, + // but the actual playMeasuresInLoopStart field starts at 0. + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.play_measures_in_loop_start); + String[] items = makeStringList(1, options.lastMeasure + 1); + builder.setItems(items, (dialog, i) -> { + options.playMeasuresInLoopStart = Integer.parseInt(items[i]) - 1; + // Make sure End is not smaller than Start + if (options.playMeasuresInLoopStart > options.playMeasuresInLoopEnd) { + options.playMeasuresInLoopEnd = options.playMeasuresInLoopStart; + drawer.updateBadge(ID_LOOP_END, new StringHolder(items[i])); + } + ((SecondaryDrawerItem) item).withBadge(items[i]); + drawer.updateItem(item); + }); + builder.create().show(); + break; + case ID_LOOP_END: + // Note that we display the measure numbers starting at 1, + // but the actual playMeasuresInLoopEnd field starts at 0. + builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.play_measures_in_loop_end); + items = makeStringList(1, options.lastMeasure + 1); + builder.setItems(items, (dialog, i) -> { + options.playMeasuresInLoopEnd = Integer.parseInt(items[i]) - 1; + // Make sure End is not smaller than Start + if (options.playMeasuresInLoopStart > options.playMeasuresInLoopEnd) { + options.playMeasuresInLoopStart = options.playMeasuresInLoopEnd; + drawer.updateBadge(ID_LOOP_START, new StringHolder(items[i])); + } + ((SecondaryDrawerItem) item).withBadge(items[i]); + drawer.updateItem(item); + }); + builder.create().show(); break; } - item.setSelected(false); - drawer.closeDrawer(); return true; } diff --git a/app/src/main/java/com/midisheetmusic/drawerItems/AbstractExpandableSwitchDrawerItem.kt b/app/src/main/java/com/midisheetmusic/drawerItems/AbstractExpandableSwitchDrawerItem.kt new file mode 100644 index 0000000..4260b0c --- /dev/null +++ b/app/src/main/java/com/midisheetmusic/drawerItems/AbstractExpandableSwitchDrawerItem.kt @@ -0,0 +1,188 @@ +package com.midisheetmusic.drawerItems + +import com.mikepenz.materialdrawer.model.AbstractDrawerItem +import com.mikepenz.materialdrawer.model.BaseDescribeableDrawerItem +import com.mikepenz.materialdrawer.model.BaseViewHolder + +import com.midisheetmusic.R +import android.graphics.Color +import android.view.View +import android.widget.CompoundButton +import android.widget.ImageView +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.annotation.LayoutRes +import androidx.appcompat.widget.SwitchCompat +import androidx.core.view.ViewCompat +import com.mikepenz.iconics.IconicsColor +import com.mikepenz.iconics.IconicsDrawable +import com.mikepenz.iconics.IconicsSize +import com.mikepenz.materialdrawer.Drawer +import com.mikepenz.materialdrawer.holder.ColorHolder +import com.mikepenz.materialdrawer.icons.MaterialDrawerFont +import com.mikepenz.materialdrawer.interfaces.OnCheckedChangeListener +import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem + +/** + * Created by ditek on 09-Jul-19 + */ +abstract class AbstractExpandableSwitchDrawerItem> : BaseDescribeableDrawerItem() { + + override val type: Int + get() = R.id.drawer_item_expandable_switch + + override val layoutRes: Int + @LayoutRes + get() = R.layout.drawer_item_expandable_switch + + + /* *************************************/ + /* Switch Specific Part */ + /* *************************************/ + + var isSwitchEnabled = true + var isChecked = false + var onCheckedChangeListener: OnCheckedChangeListener? = null + + private val checkedChangeListener = object : CompoundButton.OnCheckedChangeListener { + override fun onCheckedChanged(buttonView: CompoundButton, ic: Boolean) { + if (isEnabled) { + isChecked = ic + onCheckedChangeListener?.onCheckedChanged(this@AbstractExpandableSwitchDrawerItem, buttonView, ic) + } else { + buttonView.setOnCheckedChangeListener(null) + buttonView.isChecked = !ic + buttonView.setOnCheckedChangeListener(this) + } + } + } + + fun withChecked(checked: Boolean): Item { + this.isChecked = checked + return this as Item + } + + fun withSwitchEnabled(switchEnabled: Boolean): Item { + this.isSwitchEnabled = switchEnabled + return this as Item + } + + fun withOnCheckedChangeListener(onCheckedChangeListener: OnCheckedChangeListener): Item { + this.onCheckedChangeListener = onCheckedChangeListener + return this as Item + } + + fun withCheckable(checkable: Boolean): Item { + return withSelectable(checkable) + } + + + /* *************************************/ + /* Expandable Specific Part */ + /* *************************************/ + + var mOnDrawerItemClickListener: Drawer.OnDrawerItemClickListener? = null + var arrowColor: ColorHolder? = null + var arrowRotationAngleStart = 0 + var arrowRotationAngleEnd = 180 + + /** + * our internal onDrawerItemClickListener which will handle the arrow animation + */ + override var onDrawerItemClickListener: Drawer.OnDrawerItemClickListener? = object : Drawer.OnDrawerItemClickListener { + override fun onItemClick(view: View?, position: Int, drawerItem: IDrawerItem<*>): Boolean { + if (drawerItem is AbstractDrawerItem<*, *> && drawerItem.isEnabled) { + view?.let { + if (drawerItem.subItems != null) { + if (drawerItem.isExpanded) { + ViewCompat.animate(view.findViewById(R.id.material_drawer_arrow)).rotation(this@AbstractExpandableSwitchDrawerItem.arrowRotationAngleEnd.toFloat()).start() + } else { + ViewCompat.animate(view.findViewById(R.id.material_drawer_arrow)).rotation(this@AbstractExpandableSwitchDrawerItem.arrowRotationAngleStart.toFloat()).start() + } + } + } + } + + return mOnDrawerItemClickListener?.onItemClick(view, position, drawerItem) ?: false + } + } + + fun withArrowColor(@ColorInt arrowColor: Int): Item { + this.arrowColor = ColorHolder.fromColor(arrowColor) + return this as Item + } + + fun withArrowColorRes(@ColorRes arrowColorRes: Int): Item { + this.arrowColor = ColorHolder.fromColorRes(arrowColorRes) + return this as Item + } + + fun withArrowRotationAngleStart(angle: Int): Item { + this.arrowRotationAngleStart = angle + return this as Item + } + + fun withArrowRotationAngleEnd(angle: Int): Item { + this.arrowRotationAngleEnd = angle + return this as Item + } + + override fun withOnDrawerItemClickListener(onDrawerItemClickListener: Drawer.OnDrawerItemClickListener): Item { + mOnDrawerItemClickListener = onDrawerItemClickListener + return this as Item + } + + override fun bindView(holder: ViewHolder, payloads: MutableList) { + super.bindView(holder, payloads) + + val ctx = holder.itemView.context + //bind the basic view parts + bindViewHelper(holder) + + //handle the switch + holder.switchView.setOnCheckedChangeListener(null) + holder.switchView.isChecked = isChecked + holder.switchView.setOnCheckedChangeListener(checkedChangeListener) + holder.switchView.isEnabled = isSwitchEnabled + + //add a onDrawerItemClickListener here to be able to check / uncheck if the drawerItem can't be selected + withOnDrawerItemClickListener(object : Drawer.OnDrawerItemClickListener { + override fun onItemClick(view: View?, position: Int, drawerItem: IDrawerItem<*>): Boolean { + if (!isSelectable) { + isChecked = !isChecked + holder.switchView.isChecked = isChecked + } + + return false + } + }) + + //make sure all animations are stopped + if (holder.arrow.drawable is IconicsDrawable) { + (holder.arrow.drawable as IconicsDrawable).color(IconicsColor.colorInt(this.arrowColor?.color(ctx) + ?: getIconColor(ctx))) + } + holder.arrow.clearAnimation() + if (!isExpanded) { + holder.arrow.rotation = this.arrowRotationAngleStart.toFloat() + } else { + holder.arrow.rotation = this.arrowRotationAngleEnd.toFloat() + } + + //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) + onPostBindView(this, holder.itemView) + } + + override fun getViewHolder(v: View): ViewHolder { + return ViewHolder(v) + } + + class ViewHolder(view: View) : BaseViewHolder(view) { + internal val switchView: SwitchCompat = view.findViewById(R.id.material_drawer_switch) as SwitchCompat + var arrow: ImageView = view.findViewById(R.id.material_drawer_arrow) + + init { + arrow.setImageDrawable(IconicsDrawable(view.context, MaterialDrawerFont.Icon.mdf_expand_more).size(IconicsSize.dp(16)).padding(IconicsSize.dp(2)).color(IconicsColor.colorInt(Color.BLACK))) + } + } +} diff --git a/app/src/main/java/com/midisheetmusic/drawerItems/ExpandableSwitchDrawerItem.kt b/app/src/main/java/com/midisheetmusic/drawerItems/ExpandableSwitchDrawerItem.kt new file mode 100644 index 0000000..0dc0628 --- /dev/null +++ b/app/src/main/java/com/midisheetmusic/drawerItems/ExpandableSwitchDrawerItem.kt @@ -0,0 +1,6 @@ +package com.midisheetmusic.drawerItems + +/** + * Created by ditek on 09-Jul-19 + */ +class ExpandableSwitchDrawerItem : AbstractExpandableSwitchDrawerItem() diff --git a/app/src/main/res/layout/drawer_item_expandable_switch.xml b/app/src/main/res/layout/drawer_item_expandable_switch.xml new file mode 100644 index 0000000..7a5db4b --- /dev/null +++ b/app/src/main/res/layout/drawer_item_expandable_switch.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/sheet_menu.xml b/app/src/main/res/menu/sheet_menu.xml index 2cd1bb5..5c8843c 100644 --- a/app/src/main/res/menu/sheet_menu.xml +++ b/app/src/main/res/menu/sheet_menu.xml @@ -1,10 +1,10 @@ - - + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..13d4e70 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,16 @@ + + + @color/md_teal_500 + @color/md_teal_800 + #82e9de + @color/md_teal_300 + #212121 + #757575 + #FFFFFF + #BDBDBD + + #2196F3 + #1976D2 + #BBDEFB + #FF4081 + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 161962f..e5d9323 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,4 +1,13 @@ - - - 40dp + + + 40dp + + + 48dp + 56dp + 12dp + 32dp + 14sp + 14sp + 12sp \ No newline at end of file diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml new file mode 100644 index 0000000..ae5921e --- /dev/null +++ b/app/src/main/res/values/ids.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e987614..252617c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ Enhanced MidiSheetMusic - Song Settings + More Settings Save As Images Save the sheet music as images, in the directory /Pictures/MidiSheetMusic, with the following filename Help diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index f881fe4..433ebc6 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -5,7 +5,10 @@ false true @null - @color/md_teal_300 + @color/accent + @color/primary + @color/primaryDark + @color/primary