Home | History | Annotate | Download | only in com.example.android.common.midi
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.common.midi;
     18 
     19 import android.app.Activity;
     20 import android.media.midi.MidiDeviceInfo;
     21 import android.media.midi.MidiDeviceStatus;
     22 import android.media.midi.MidiManager;
     23 import android.media.midi.MidiManager.DeviceCallback;
     24 import android.os.Handler;
     25 import android.os.Looper;
     26 import android.util.Log;
     27 import android.view.View;
     28 import android.widget.AdapterView;
     29 import android.widget.ArrayAdapter;
     30 import android.widget.Spinner;
     31 
     32 import java.util.HashSet;
     33 
     34 /**
     35  * Base class that uses a Spinner to select available MIDI ports.
     36  */
     37 public abstract class MidiPortSelector extends DeviceCallback {
     38     private int mType = MidiDeviceInfo.PortInfo.TYPE_INPUT;
     39     protected ArrayAdapter<MidiPortWrapper> mAdapter;
     40     protected HashSet<MidiPortWrapper> mBusyPorts = new HashSet<MidiPortWrapper>();
     41     private Spinner mSpinner;
     42     protected MidiManager mMidiManager;
     43     protected Activity mActivity;
     44     private MidiPortWrapper mCurrentWrapper;
     45 
     46     /**
     47      * @param midiManager
     48      * @param activity
     49      * @param spinnerId
     50      *            ID from the layout resource
     51      * @param type
     52      *            TYPE_INPUT or TYPE_OUTPUT
     53      */
     54     public MidiPortSelector(MidiManager midiManager, Activity activity,
     55             int spinnerId, int type) {
     56         mMidiManager = midiManager;
     57         mActivity = activity;
     58         mType = type;
     59         mAdapter = new ArrayAdapter<MidiPortWrapper>(activity,
     60                 android.R.layout.simple_spinner_item);
     61         mAdapter.setDropDownViewResource(
     62                 android.R.layout.simple_spinner_dropdown_item);
     63         mAdapter.add(new MidiPortWrapper(null, 0, 0));
     64 
     65         mSpinner = (Spinner) activity.findViewById(spinnerId);
     66         mSpinner.setOnItemSelectedListener(
     67                 new AdapterView.OnItemSelectedListener() {
     68 
     69                     public void onItemSelected(AdapterView<?> parent, View view,
     70                             int pos, long id) {
     71                         mCurrentWrapper = mAdapter.getItem(pos);
     72                         onPortSelected(mCurrentWrapper);
     73                     }
     74 
     75                     public void onNothingSelected(AdapterView<?> parent) {
     76                         onPortSelected(null);
     77                         mCurrentWrapper = null;
     78                     }
     79                 });
     80         mSpinner.setAdapter(mAdapter);
     81 
     82         mMidiManager.registerDeviceCallback(this,
     83                 new Handler(Looper.getMainLooper()));
     84 
     85         MidiDeviceInfo[] infos = mMidiManager.getDevices();
     86         for (MidiDeviceInfo info : infos) {
     87             onDeviceAdded(info);
     88         }
     89     }
     90 
     91     /**
     92      * Set to no port selected.
     93      */
     94     public void clearSelection() {
     95         mSpinner.setSelection(0);
     96     }
     97 
     98     private int getInfoPortCount(final MidiDeviceInfo info) {
     99         int portCount = (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT)
    100                 ? info.getInputPortCount() : info.getOutputPortCount();
    101         return portCount;
    102     }
    103 
    104     @Override
    105     public void onDeviceAdded(final MidiDeviceInfo info) {
    106         int portCount = getInfoPortCount(info);
    107         for (int i = 0; i < portCount; ++i) {
    108             MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
    109             mAdapter.add(wrapper);
    110             Log.i(MidiConstants.TAG, wrapper + " was added");
    111             mAdapter.notifyDataSetChanged();
    112         }
    113     }
    114 
    115     @Override
    116     public void onDeviceRemoved(final MidiDeviceInfo info) {
    117         int portCount = getInfoPortCount(info);
    118         for (int i = 0; i < portCount; ++i) {
    119             MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
    120             MidiPortWrapper currentWrapper = mCurrentWrapper;
    121             mAdapter.remove(wrapper);
    122             // If the currently selected port was removed then select no port.
    123             if (wrapper.equals(currentWrapper)) {
    124                 clearSelection();
    125             }
    126             mAdapter.notifyDataSetChanged();
    127             Log.i(MidiConstants.TAG, wrapper + " was removed");
    128         }
    129     }
    130 
    131     @Override
    132     public void onDeviceStatusChanged(final MidiDeviceStatus status) {
    133         // If an input port becomes busy then remove it from the menu.
    134         // If it becomes free then add it back to the menu.
    135         if (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT) {
    136             MidiDeviceInfo info = status.getDeviceInfo();
    137             Log.i(MidiConstants.TAG, "MidiPortSelector.onDeviceStatusChanged status = " + status
    138                     + ", mType = " + mType
    139                     + ", activity = " + mActivity.getPackageName()
    140                     + ", info = " + info);
    141             // Look for transitions from free to busy.
    142             int portCount = info.getInputPortCount();
    143             for (int i = 0; i < portCount; ++i) {
    144                 MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
    145                 if (!wrapper.equals(mCurrentWrapper)) {
    146                     if (status.isInputPortOpen(i)) { // busy?
    147                         if (!mBusyPorts.contains(wrapper)) {
    148                             // was free, now busy
    149                             mBusyPorts.add(wrapper);
    150                             mAdapter.remove(wrapper);
    151                             mAdapter.notifyDataSetChanged();
    152                         }
    153                     } else {
    154                         if (mBusyPorts.remove(wrapper)) {
    155                             // was busy, now free
    156                             mAdapter.add(wrapper);
    157                             mAdapter.notifyDataSetChanged();
    158                         }
    159                     }
    160                 }
    161             }
    162         }
    163     }
    164 
    165     /**
    166      * Implement this method to handle the user selecting a port on a device.
    167      *
    168      * @param wrapper
    169      */
    170     public abstract void onPortSelected(MidiPortWrapper wrapper);
    171 
    172     /**
    173      * Implement this method to clean up any open resources.
    174      */
    175     public abstract void onClose();
    176 
    177     /**
    178      *
    179      */
    180     public void close() {
    181         onClose();
    182     }
    183 }
    184