/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.common.midi; import android.app.Activity; import android.media.midi.MidiDeviceInfo; import android.media.midi.MidiDeviceStatus; import android.media.midi.MidiManager; import android.media.midi.MidiManager.DeviceCallback; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; import java.util.HashSet; /** * Base class that uses a Spinner to select available MIDI ports. */ public abstract class MidiPortSelector extends DeviceCallback { private int mType = MidiDeviceInfo.PortInfo.TYPE_INPUT; protected ArrayAdapter<MidiPortWrapper> mAdapter; protected HashSet<MidiPortWrapper> mBusyPorts = new HashSet<MidiPortWrapper>(); private Spinner mSpinner; protected MidiManager mMidiManager; protected Activity mActivity; private MidiPortWrapper mCurrentWrapper; /** * @param midiManager * @param activity * @param spinnerId * ID from the layout resource * @param type * TYPE_INPUT or TYPE_OUTPUT */ public MidiPortSelector(MidiManager midiManager, Activity activity, int spinnerId, int type) { mMidiManager = midiManager; mActivity = activity; mType = type; mAdapter = new ArrayAdapter<MidiPortWrapper>(activity, android.R.layout.simple_spinner_item); mAdapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item); mAdapter.add(new MidiPortWrapper(null, 0, 0)); mSpinner = (Spinner) activity.findViewById(spinnerId); mSpinner.setOnItemSelectedListener( new AdapterView.OnItemSelectedListener() { public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { mCurrentWrapper = mAdapter.getItem(pos); onPortSelected(mCurrentWrapper); } public void onNothingSelected(AdapterView<?> parent) { onPortSelected(null); mCurrentWrapper = null; } }); mSpinner.setAdapter(mAdapter); mMidiManager.registerDeviceCallback(this, new Handler(Looper.getMainLooper())); MidiDeviceInfo[] infos = mMidiManager.getDevices(); for (MidiDeviceInfo info : infos) { onDeviceAdded(info); } } /** * Set to no port selected. */ public void clearSelection() { mSpinner.setSelection(0); } private int getInfoPortCount(final MidiDeviceInfo info) { int portCount = (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT) ? info.getInputPortCount() : info.getOutputPortCount(); return portCount; } @Override public void onDeviceAdded(final MidiDeviceInfo info) { int portCount = getInfoPortCount(info); for (int i = 0; i < portCount; ++i) { MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i); mAdapter.add(wrapper); Log.i(MidiConstants.TAG, wrapper + " was added"); mAdapter.notifyDataSetChanged(); } } @Override public void onDeviceRemoved(final MidiDeviceInfo info) { int portCount = getInfoPortCount(info); for (int i = 0; i < portCount; ++i) { MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i); MidiPortWrapper currentWrapper = mCurrentWrapper; mAdapter.remove(wrapper); // If the currently selected port was removed then select no port. if (wrapper.equals(currentWrapper)) { clearSelection(); } mAdapter.notifyDataSetChanged(); Log.i(MidiConstants.TAG, wrapper + " was removed"); } } @Override public void onDeviceStatusChanged(final MidiDeviceStatus status) { // If an input port becomes busy then remove it from the menu. // If it becomes free then add it back to the menu. if (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT) { MidiDeviceInfo info = status.getDeviceInfo(); Log.i(MidiConstants.TAG, "MidiPortSelector.onDeviceStatusChanged status = " + status + ", mType = " + mType + ", activity = " + mActivity.getPackageName() + ", info = " + info); // Look for transitions from free to busy. int portCount = info.getInputPortCount(); for (int i = 0; i < portCount; ++i) { MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i); if (!wrapper.equals(mCurrentWrapper)) { if (status.isInputPortOpen(i)) { // busy? if (!mBusyPorts.contains(wrapper)) { // was free, now busy mBusyPorts.add(wrapper); mAdapter.remove(wrapper); mAdapter.notifyDataSetChanged(); } } else { if (mBusyPorts.remove(wrapper)) { // was busy, now free mAdapter.add(wrapper); mAdapter.notifyDataSetChanged(); } } } } } } /** * Implement this method to handle the user selecting a port on a device. * * @param wrapper */ public abstract void onPortSelected(MidiPortWrapper wrapper); /** * Implement this method to clean up any open resources. */ public abstract void onClose(); /** * */ public void close() { onClose(); } }