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      */
MidiPortSelector(MidiManager midiManager, Activity activity, int spinnerId, int type)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      */
clearSelection()94     public void clearSelection() {
95         mSpinner.setSelection(0);
96     }
97 
getInfoPortCount(final MidiDeviceInfo info)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
onDeviceAdded(final MidiDeviceInfo info)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
onDeviceRemoved(final MidiDeviceInfo info)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
onDeviceStatusChanged(final MidiDeviceStatus status)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      */
onPortSelected(MidiPortWrapper wrapper)170     public abstract void onPortSelected(MidiPortWrapper wrapper);
171 
172     /**
173      * Implement this method to clean up any open resources.
174      */
onClose()175     public abstract void onClose();
176 
177     /**
178      *
179      */
close()180     public void close() {
181         onClose();
182     }
183 }
184