plectrum

Plectrum: instrument tuner for Android
Log | Files | Refs | README | LICENSE

MainActivity.java (13145B)


      1 package com.github.cythara;
      2 
      3 import android.Manifest;
      4 import android.content.Intent;
      5 import android.content.SharedPreferences;
      6 import android.content.pm.PackageManager;
      7 import android.graphics.Insets;
      8 import android.net.Uri;
      9 import android.os.Build;
     10 import android.os.Bundle;
     11 import android.view.ContextThemeWrapper;
     12 import android.view.Menu;
     13 import android.view.MenuItem;
     14 import android.view.ViewGroup;
     15 import android.view.WindowManager.LayoutParams;
     16 
     17 import com.github.cythara.ListenerFragment.TaskCallbacks;
     18 import com.jaredrummler.materialspinner.MaterialSpinner;
     19 import com.jaredrummler.materialspinner.MaterialSpinner.OnItemSelectedListener;
     20 import com.jaredrummler.materialspinner.MaterialSpinnerAdapter;
     21 import com.shawnlin.numberpicker.NumberPicker;
     22 import com.shawnlin.numberpicker.NumberPicker.OnValueChangeListener;
     23 
     24 import java.util.Arrays;
     25 
     26 import androidx.annotation.NonNull;
     27 import androidx.appcompat.app.AlertDialog;
     28 import androidx.appcompat.app.AlertDialog.Builder;
     29 import androidx.appcompat.app.AppCompatActivity;
     30 import androidx.appcompat.app.AppCompatDelegate;
     31 import androidx.appcompat.widget.Toolbar;
     32 import androidx.core.app.ActivityCompat;
     33 import androidx.core.content.ContextCompat;
     34 import androidx.core.view.ViewCompat;
     35 import androidx.core.view.WindowInsetsCompat;
     36 import androidx.fragment.app.FragmentManager;
     37 
     38 public class MainActivity extends AppCompatActivity implements TaskCallbacks,
     39         OnItemSelectedListener, OnValueChangeListener {
     40 
     41     public static final int RECORD_AUDIO_PERMISSION = 0;
     42     public static final String PREFS_FILE = "prefs_file";
     43     public static final String USE_SCIENTIFIC_NOTATION = "use_scientific_notation";
     44     public static final String CURRENT_TUNING = "current_tuning";
     45     protected static final String REFERENCE_PITCH = "reference_pitch";
     46     private static final String TAG_LISTENER_FRAGMENT = "listener_fragment";
     47     private static final String USE_DARK_MODE = "use_dark_mode";
     48     private static int tuningPosition = 0;
     49     private static boolean isDarkModeEnabled;
     50     private static int referencePitch;
     51     private static int referencePosition;
     52     private static boolean isAutoModeEnabled = true;
     53 
     54     public static Tuning getCurrentTuning() {
     55         return TuningMapper.getTuningFromPosition(tuningPosition);
     56     }
     57 
     58     public static boolean isDarkModeEnabled() {
     59         return isDarkModeEnabled;
     60     }
     61 
     62     public static int getReferencePitch() {
     63         return referencePitch;
     64     }
     65 
     66     public static boolean isAutoModeEnabled() {
     67         return isAutoModeEnabled;
     68     }
     69 
     70     public static int getReferencePosition() {
     71         return referencePosition - 1; //to account for the position of the AUTO option
     72     }
     73 
     74     @Override
     75     protected void onCreate(Bundle savedInstanceState) {
     76         super.onCreate(savedInstanceState);
     77         AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
     78 
     79         int permissionCheck = ContextCompat.checkSelfPermission(this,
     80                 Manifest.permission.RECORD_AUDIO);
     81 
     82         if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
     83             requestRecordAudioPermission();
     84         } else {
     85             startRecording();
     86         }
     87 
     88         enableTheme();
     89 
     90         setContentView(R.layout.activity_main);
     91 
     92         setTuning();
     93         setReferencePitch();
     94 
     95         getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
     96 
     97         Toolbar myToolbar = findViewById(R.id.my_toolbar);
     98         myToolbar.setTitle(R.string.app_name);
     99         myToolbar.showOverflowMenu();
    100         setSupportActionBar(myToolbar);
    101 
    102 
    103         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    104             ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main_layout), (v, windowInsets) -> {
    105                 Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).toPlatformInsets();
    106                 // Apply the insets as a margin to the view. This solution sets only the
    107                 // bottom, left, and right dimensions, but you can apply whichever insets are
    108                 // appropriate to your layout. You can also update the view padding if that's
    109                 // more appropriate.
    110                 ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
    111                 mlp.leftMargin = insets.left;
    112                 mlp.bottomMargin = insets.bottom;
    113                 mlp.rightMargin = insets.right;
    114                 mlp.topMargin = insets.top;
    115                 v.setLayoutParams(mlp);
    116 
    117                 // Return CONSUMED if you don't want the window insets to keep passing
    118                 // down to descendant views.
    119                 return WindowInsetsCompat.CONSUMED;
    120             });
    121         }
    122     }
    123 
    124     @Override
    125     public boolean onCreateOptionsMenu(Menu menu) {
    126         getMenuInflater().inflate(R.menu.toolbar_menu, menu);
    127         return true;
    128     }
    129 
    130     @Override
    131     public boolean onOptionsItemSelected(MenuItem item) {
    132         int itemId = item.getItemId();
    133         if (itemId == R.id.show_privacy_policy) {
    134             Intent browserIntent = new Intent(Intent.ACTION_VIEW,
    135                     Uri.parse(getString(R.string.privacy_policy_link)));
    136             startActivity(browserIntent);
    137         } else if (itemId == R.id.set_notation) {
    138             final SharedPreferences preferences = getSharedPreferences(PREFS_FILE,
    139                     MODE_PRIVATE);
    140             final boolean useScientificNotation =
    141                     preferences.getBoolean(USE_SCIENTIFIC_NOTATION, true);
    142 
    143             int checkedItem = useScientificNotation ? 0 : 1;
    144 
    145             Builder builder = new Builder(new ContextThemeWrapper(this,
    146                     R.style.AppTheme));
    147             builder.setTitle(R.string.choose_notation);
    148             builder.setSingleChoiceItems(R.array.notations, checkedItem,
    149                     (dialog, which) -> {
    150                         SharedPreferences.Editor editor = preferences.edit();
    151                         editor.putBoolean(USE_SCIENTIFIC_NOTATION, which == 0);
    152                         editor.apply();
    153 
    154                         dialog.dismiss();
    155 
    156                         TunerView tunerView = findViewById(R.id.pitch);
    157                         tunerView.invalidate();
    158                     });
    159             builder.show();
    160         } else if (itemId == R.id.toggle_dark_mode) {
    161             final SharedPreferences preferences = getSharedPreferences(PREFS_FILE,
    162                     MODE_PRIVATE);
    163             boolean currentlyUsingDarkMode = preferences.getBoolean(USE_DARK_MODE, true);
    164 
    165             SharedPreferences.Editor editor = preferences.edit();
    166             editor.putBoolean(USE_DARK_MODE, !currentlyUsingDarkMode);
    167             editor.apply();
    168 
    169             recreate();
    170         } else if (itemId == R.id.set_reference_pitch) {
    171             final SharedPreferences preferences = getSharedPreferences(PREFS_FILE,
    172                     MODE_PRIVATE);
    173             int referencePitch = preferences.getInt(REFERENCE_PITCH, 440);
    174 
    175             NumberPickerDialog dialog = new NumberPickerDialog();
    176 
    177             Bundle bundle = new Bundle();
    178             bundle.putInt("current_value", referencePitch);
    179             dialog.setArguments(bundle);
    180 
    181             dialog.setValueChangeListener(this);
    182             dialog.show(getSupportFragmentManager(), "reference_pitch_picker");
    183         } else if (itemId == R.id.choose_tuning_mode) {
    184             final SharedPreferences preferences = getSharedPreferences(PREFS_FILE,
    185                     MODE_PRIVATE);
    186             NotePickerDialog dialog = new NotePickerDialog();
    187 
    188             Bundle bundle = new Bundle();
    189             bundle.putBoolean("use_scientific_notation", preferences.getBoolean(
    190                     MainActivity.USE_SCIENTIFIC_NOTATION, true));
    191             bundle.putInt("current_value", referencePosition);
    192             dialog.setArguments(bundle);
    193 
    194             dialog.setValueChangeListener(this);
    195             dialog.show(getSupportFragmentManager(), "note_picker");
    196         }
    197 
    198         return false;
    199     }
    200 
    201     @Override
    202     public void onProgressUpdate(PitchDifference pitchDifference) {
    203         TunerView tunerView = this.findViewById(R.id.pitch);
    204 
    205         tunerView.setPitchDifference(pitchDifference);
    206         tunerView.invalidate();
    207     }
    208 
    209     @Override
    210     public void onBackPressed() {
    211         moveTaskToBack(true);
    212     }
    213 
    214     @Override
    215     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
    216                                            @NonNull int[] grantResults) {
    217         if (requestCode == RECORD_AUDIO_PERMISSION) {
    218             if (grantResults.length > 0) {
    219                 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    220                     startRecording();
    221                 } else {
    222                     AlertDialog alertDialog = new Builder(MainActivity.this).create();
    223                     alertDialog.setTitle(R.string.permission_required);
    224                     alertDialog.setMessage(getString(R.string.microphone_permission_required));
    225                     alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, getString(R.string.ok),
    226                             (dialog, which) -> {
    227                                 dialog.dismiss();
    228                                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    229                                     finishAffinity();
    230                                 } else {
    231                                     finish();
    232                                 }
    233                             });
    234                     alertDialog.show();
    235                 }
    236             }
    237         }
    238     }
    239 
    240     @Override
    241     public void onItemSelected(MaterialSpinner view, int position, long id, Object item) {
    242         final SharedPreferences preferences = getSharedPreferences(PREFS_FILE,
    243                 MODE_PRIVATE);
    244 
    245         SharedPreferences.Editor editor = preferences.edit();
    246         editor.putInt(CURRENT_TUNING, position);
    247         editor.apply();
    248 
    249         tuningPosition = position;
    250 
    251         isAutoModeEnabled = true;
    252         referencePosition = 0;
    253 
    254         recreate();
    255     }
    256 
    257     @Override
    258     public void onValueChange(NumberPicker picker, int oldValue, int newValue) {
    259         String tag = String.valueOf(picker.getTag());
    260         if ("reference_pitch_picker".equalsIgnoreCase(tag)) {
    261             final SharedPreferences preferences = getSharedPreferences(PREFS_FILE,
    262                     MODE_PRIVATE);
    263 
    264             SharedPreferences.Editor editor = preferences.edit();
    265             editor.putInt(REFERENCE_PITCH, newValue);
    266             editor.apply();
    267 
    268             setReferencePitch();
    269 
    270             TunerView tunerView = this.findViewById(R.id.pitch);
    271             tunerView.invalidate();
    272         } else if ("note_picker".equalsIgnoreCase(tag)) {
    273             isAutoModeEnabled = newValue == 0;
    274 
    275             referencePosition = newValue;
    276 
    277             recreate();
    278         }
    279     }
    280 
    281     private void startRecording() {
    282         FragmentManager fragmentManager = getSupportFragmentManager();
    283         ListenerFragment listenerFragment = (ListenerFragment)
    284                 fragmentManager.findFragmentByTag(TAG_LISTENER_FRAGMENT);
    285 
    286         if (listenerFragment == null) {
    287             listenerFragment = new ListenerFragment();
    288             fragmentManager
    289                     .beginTransaction()
    290                     .add(listenerFragment, TAG_LISTENER_FRAGMENT)
    291                     .commit();
    292         }
    293     }
    294 
    295     private void setTuning() {
    296         final SharedPreferences preferences = getSharedPreferences(PREFS_FILE,
    297                 MODE_PRIVATE);
    298         tuningPosition = preferences.getInt(CURRENT_TUNING, 0);
    299 
    300         int textColorDark = getResources().getColor(R.color.colorTextDark);
    301 
    302         MaterialSpinner spinner = findViewById(R.id.tuning);
    303         MaterialSpinnerAdapter<String> adapter = new MaterialSpinnerAdapter<>(this,
    304                 Arrays.asList(getResources().getStringArray(R.array.tunings)));
    305 
    306         if (isDarkModeEnabled) {
    307             spinner.setTextColor(textColorDark);
    308             spinner.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
    309             spinner.setTextColor(textColorDark);
    310             spinner.setArrowColor(textColorDark);
    311         }
    312 
    313         spinner.setAdapter(adapter);
    314         spinner.setOnItemSelectedListener(this);
    315         spinner.setSelectedIndex(tuningPosition);
    316     }
    317 
    318     private void enableTheme() {
    319         final SharedPreferences preferences = getSharedPreferences(PREFS_FILE,
    320                 MODE_PRIVATE);
    321         isDarkModeEnabled = preferences.getBoolean(USE_DARK_MODE, true);
    322 
    323         int mode = AppCompatDelegate.MODE_NIGHT_NO;
    324         if (isDarkModeEnabled) {
    325             mode = AppCompatDelegate.MODE_NIGHT_YES;
    326         }
    327 
    328         AppCompatDelegate.setDefaultNightMode(mode);
    329     }
    330 
    331     private void setReferencePitch() {
    332         final SharedPreferences preferences = getSharedPreferences(PREFS_FILE,
    333                 MODE_PRIVATE);
    334         referencePitch = preferences.getInt(REFERENCE_PITCH, 440);
    335     }
    336 
    337     private void requestRecordAudioPermission() {
    338         ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO},
    339                 RECORD_AUDIO_PERMISSION);
    340     }
    341 }