1 |
iliev |
2302 |
/* |
2 |
|
|
* JSampler - a java front-end for LinuxSampler |
3 |
|
|
* |
4 |
|
|
* Copyright (C) 2011 Grigor Iliev <grigor@grigoriliev.com> |
5 |
|
|
* |
6 |
|
|
* This file is part of JSampler. |
7 |
|
|
* |
8 |
|
|
* JSampler is free software; you can redistribute it and/or modify |
9 |
|
|
* it under the terms of the GNU General Public License version 2 |
10 |
|
|
* as published by the Free Software Foundation. |
11 |
|
|
* |
12 |
|
|
* JSampler is distributed in the hope that it will be useful, |
13 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
|
|
* GNU General Public License for more details. |
16 |
|
|
* |
17 |
|
|
* You should have received a copy of the GNU General Public License |
18 |
|
|
* along with JSampler; if not, write to the Free Software |
19 |
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
20 |
|
|
* MA 02111-1307 USA |
21 |
|
|
*/ |
22 |
|
|
|
23 |
|
|
package org.jsampler.android.view.classic; |
24 |
|
|
|
25 |
|
|
import java.beans.PropertyChangeEvent; |
26 |
|
|
import java.beans.PropertyChangeListener; |
27 |
|
|
|
28 |
|
|
import org.jsampler.CC; |
29 |
|
|
import org.jsampler.HF; |
30 |
|
|
import org.jsampler.JSPrefs; |
31 |
|
|
import org.jsampler.Server; |
32 |
|
|
import org.jsampler.android.AHF; |
33 |
|
|
import org.jsampler.android.JSamplerActivity; |
34 |
|
|
import org.jsampler.android.R; |
35 |
|
|
import org.jsampler.android.view.AndroidChannelsPane; |
36 |
|
|
import org.jsampler.android.view.AndroidMainFrame; |
37 |
|
|
import org.jsampler.event.SamplerAdapter; |
38 |
|
|
import org.jsampler.event.SamplerEvent; |
39 |
|
|
|
40 |
|
|
import android.content.Context; |
41 |
|
|
import android.os.Parcelable; |
42 |
|
|
import android.support.v4.view.PagerAdapter; |
43 |
|
|
import android.support.v4.view.ViewPager; |
44 |
|
|
import android.util.Log; |
45 |
|
|
import android.view.LayoutInflater; |
46 |
|
|
import android.view.MotionEvent; |
47 |
|
|
import android.view.View; |
48 |
|
|
import android.widget.ImageButton; |
49 |
|
|
import android.widget.SeekBar; |
50 |
|
|
import android.widget.TextView; |
51 |
|
|
import android.widget.ViewFlipper; |
52 |
|
|
|
53 |
|
|
import static org.jsampler.JSPrefs.MAXIMUM_MASTER_VOLUME; |
54 |
|
|
import static org.jsampler.JSPrefs.VOL_MEASUREMENT_UNIT_DECIBEL; |
55 |
|
|
import static org.jsampler.android.view.AndroidI18n.i18n; |
56 |
|
|
import static org.jsampler.android.view.classic.ClassicPrefs.preferences; |
57 |
|
|
import static org.jsampler.android.JSamplerActivity.*; |
58 |
|
|
|
59 |
|
|
public class MainFrame extends AndroidMainFrame<AndroidChannelsPane> { |
60 |
|
|
private static final int FLING_MIN_DISTANCE = 100; |
61 |
|
|
|
62 |
|
|
private View view = null; |
63 |
|
|
private ViewFlipper panesFlipper; |
64 |
|
|
private ViewPager lanesPager; |
65 |
|
|
|
66 |
|
|
public MainFrame() { |
67 |
|
|
|
68 |
|
|
} |
69 |
|
|
|
70 |
|
|
@Override |
71 |
|
|
public void |
72 |
|
|
onCreate() { |
73 |
|
|
LayoutInflater inflater = |
74 |
|
|
(LayoutInflater)AHF.getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
75 |
|
|
view = inflater.inflate(R.layout.classic_main, null); |
76 |
|
|
|
77 |
|
|
ChannelLanesPagerAdapter lanesPagerAdapter = new ChannelLanesPagerAdapter(); |
78 |
|
|
lanesPager = (ViewPager)view.findViewById(R.id.main_channel_lane_pager); |
79 |
|
|
lanesPager.setAdapter(lanesPagerAdapter); |
80 |
|
|
|
81 |
|
|
panesFlipper = (ViewFlipper)view.findViewById(R.id.main_flipper); |
82 |
|
|
panesFlipper.setDisplayedChild(1); |
83 |
|
|
updateMainTitle(); |
84 |
|
|
|
85 |
|
|
installListeners(); |
86 |
|
|
updateVolume(); |
87 |
|
|
|
88 |
|
|
ChannelsPane cp = new ChannelsPane(); |
89 |
|
|
cp.setTitle("Channel Lane 1"); |
90 |
|
|
addChannelsPane(cp); |
91 |
|
|
|
92 |
|
|
addChannelsPane(new ChannelsPane("Channel Lane 2")); |
93 |
|
|
setSelectedChannelsPane(cp); |
94 |
|
|
} |
95 |
|
|
|
96 |
|
|
public void |
97 |
|
|
uninstall() { |
98 |
|
|
uninstallListeners(); |
99 |
|
|
} |
100 |
|
|
|
101 |
|
|
@Override |
102 |
|
|
public View |
103 |
|
|
getView() { return view; } |
104 |
|
|
|
105 |
|
|
@Override |
106 |
|
|
public boolean |
107 |
|
|
onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { |
108 |
|
|
/*ViewFlipper flipper = getFlipper(); |
109 |
|
|
|
110 |
|
|
if(Math.abs(e1.getX() - e2.getX()) > Math.abs(e1.getY() - e2.getY())) { |
111 |
|
|
if(Math.abs(e1.getX() - e2.getX()) < FLING_MIN_DISTANCE) return false; |
112 |
|
|
if(e1.getX() - e2.getX() > 0) { // right to left |
113 |
|
|
flipper.setInAnimation(AHF.inFromRightAnimation()); |
114 |
|
|
flipper.setOutAnimation(AHF.outToLeftAnimation()); |
115 |
|
|
flipper.showNext(); |
116 |
|
|
} else { // left to right |
117 |
|
|
flipper.setInAnimation(AHF.inFromLeftAnimation()); |
118 |
|
|
flipper.setOutAnimation(AHF.outToRightAnimation()); |
119 |
|
|
flipper.showPrevious(); |
120 |
|
|
} |
121 |
|
|
return true; |
122 |
|
|
} else { |
123 |
|
|
if(e1.getY() - e2.getY() > 0) { // up |
124 |
|
|
|
125 |
|
|
} else { // down |
126 |
|
|
|
127 |
|
|
} |
128 |
|
|
}*/ |
129 |
|
|
return false; |
130 |
|
|
} |
131 |
|
|
|
132 |
|
|
public boolean |
133 |
|
|
onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { |
134 |
|
|
|
135 |
|
|
return false; |
136 |
|
|
} |
137 |
|
|
|
138 |
|
|
@Override |
139 |
|
|
public void setVisible(boolean b) { |
140 |
|
|
|
141 |
|
|
} |
142 |
|
|
|
143 |
|
|
private void |
144 |
|
|
installListeners() { |
145 |
|
|
SeekBar sliderVolume = (SeekBar)view.findViewById(R.id.main_slider_volume); |
146 |
|
|
int i = CC.preferences().getIntProperty(MAXIMUM_MASTER_VOLUME); |
147 |
|
|
sliderVolume.setMax(i); |
148 |
|
|
CC.preferences().addPropertyChangeListener(MAXIMUM_MASTER_VOLUME, getHandler()); |
149 |
|
|
|
150 |
|
|
sliderVolume.setOnSeekBarChangeListener(getHandler()); |
151 |
|
|
|
152 |
|
|
CC.getSamplerModel().addSamplerListener(getHandler()); |
153 |
|
|
|
154 |
|
|
CC.preferences().addPropertyChangeListener(VOL_MEASUREMENT_UNIT_DECIBEL, getHandler()); |
155 |
|
|
|
156 |
|
|
ImageButton btn = (ImageButton)view.findViewById(R.id.classic_main_flip_back); |
157 |
|
|
btn.setOnClickListener(new View.OnClickListener() { |
158 |
|
|
public void onClick(View v) { |
159 |
|
|
panesFlipper.setInAnimation(AHF.inFromLeftAnimation()); |
160 |
|
|
panesFlipper.setOutAnimation(AHF.outToRightAnimation()); |
161 |
|
|
panesFlipper.showPrevious(); |
162 |
|
|
updateMainTitle(); |
163 |
|
|
} |
164 |
|
|
}); |
165 |
|
|
|
166 |
|
|
btn = (ImageButton)view.findViewById(R.id.classic_main_flip_forward); |
167 |
|
|
btn.setOnClickListener(new View.OnClickListener() { |
168 |
|
|
public void onClick(View v) { |
169 |
|
|
panesFlipper.setInAnimation(AHF.inFromRightAnimation()); |
170 |
|
|
panesFlipper.setOutAnimation(AHF.outToLeftAnimation()); |
171 |
|
|
panesFlipper.showNext(); |
172 |
|
|
updateMainTitle(); |
173 |
|
|
} |
174 |
|
|
}); |
175 |
|
|
} |
176 |
|
|
|
177 |
|
|
private void |
178 |
|
|
uninstallListeners() { |
179 |
|
|
CC.preferences().removePropertyChangeListener(MAXIMUM_MASTER_VOLUME, getHandler()); |
180 |
|
|
CC.getSamplerModel().removeSamplerListener(getHandler()); |
181 |
|
|
CC.preferences().removePropertyChangeListener(VOL_MEASUREMENT_UNIT_DECIBEL, getHandler()); |
182 |
|
|
|
183 |
|
|
ImageButton btn = (ImageButton)view.findViewById(R.id.classic_main_flip_back); |
184 |
|
|
btn.setOnClickListener(null); |
185 |
|
|
|
186 |
|
|
btn = (ImageButton)view.findViewById(R.id.classic_main_flip_forward); |
187 |
|
|
btn.setOnClickListener(null); |
188 |
|
|
|
189 |
|
|
SeekBar sliderVolume = (SeekBar)view.findViewById(R.id.main_slider_volume); |
190 |
|
|
sliderVolume.setOnSeekBarChangeListener(null); |
191 |
|
|
} |
192 |
|
|
|
193 |
|
|
private void |
194 |
|
|
updateMainTitle() { |
195 |
|
|
TextView text = (TextView)view.findViewById(R.id.main_title); |
196 |
|
|
switch(panesFlipper.getDisplayedChild()) { |
197 |
|
|
case 0: |
198 |
|
|
text.setText(i18n.getLabel("MainFrame.titleMidiDevices")); |
199 |
|
|
break; |
200 |
|
|
case 1: |
201 |
|
|
text.setText(i18n.getLabel("MainFrame.titleSamplerChannels")); |
202 |
|
|
break; |
203 |
|
|
case 2: |
204 |
|
|
text.setText(i18n.getLabel("MainFrame.titleAudioDevices")); |
205 |
|
|
break; |
206 |
|
|
default: |
207 |
|
|
text.setText(""); |
208 |
|
|
} |
209 |
|
|
} |
210 |
|
|
|
211 |
|
|
private void |
212 |
|
|
updateVolume() { |
213 |
|
|
SeekBar sliderVolume = (SeekBar)view.findViewById(R.id.main_slider_volume); |
214 |
|
|
sliderVolume.setProgress((int)(CC.getSamplerModel().getVolume() * 100)); |
215 |
|
|
} |
216 |
|
|
|
217 |
|
|
private void |
218 |
|
|
setVolume(boolean isAdjusting) { |
219 |
|
|
SeekBar sliderVolume = (SeekBar)view.findViewById(R.id.main_slider_volume); |
220 |
|
|
TextView tvVolume = (TextView)view.findViewById(R.id.main_text_volume); |
221 |
|
|
int volume = sliderVolume.getProgress(); |
222 |
|
|
|
223 |
|
|
tvVolume.setText(HF.getVolumeString(volume)); |
224 |
|
|
|
225 |
|
|
if(isAdjusting) return; |
226 |
|
|
|
227 |
|
|
int vol = (int)(CC.getSamplerModel().getVolume() * 100); |
228 |
|
|
|
229 |
|
|
if(vol == sliderVolume.getProgress()) return; |
230 |
|
|
|
231 |
|
|
/* |
232 |
|
|
* If the model's volume is not equal to the slider |
233 |
|
|
* value we assume that the change is due to user input. |
234 |
|
|
* So we must update the volume at the backend too. |
235 |
|
|
*/ |
236 |
|
|
float v = sliderVolume.getProgress(); |
237 |
|
|
v /= 100; |
238 |
|
|
CC.getSamplerModel().setBackendVolume(v); |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
EventHandler handler = new EventHandler(); |
242 |
|
|
|
243 |
|
|
private EventHandler |
244 |
|
|
getHandler() { return handler; } |
245 |
|
|
|
246 |
|
|
private class EventHandler extends SamplerAdapter |
247 |
|
|
implements SeekBar.OnSeekBarChangeListener, PropertyChangeListener { |
248 |
|
|
|
249 |
|
|
@Override |
250 |
|
|
public void |
251 |
|
|
volumeChanged(SamplerEvent e) { updateVolume(); } |
252 |
|
|
|
253 |
|
|
@Override |
254 |
|
|
public void |
255 |
|
|
onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { |
256 |
|
|
setVolume(true); |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
@Override |
260 |
|
|
public void |
261 |
|
|
onStopTrackingTouch(SeekBar seekBar) { setVolume(false); } |
262 |
|
|
|
263 |
|
|
@Override |
264 |
|
|
public void |
265 |
|
|
onStartTrackingTouch(SeekBar seekBar) { } |
266 |
|
|
|
267 |
|
|
@Override |
268 |
|
|
public void |
269 |
|
|
propertyChange(PropertyChangeEvent e) { |
270 |
|
|
if (VOL_MEASUREMENT_UNIT_DECIBEL.equals(e.getPropertyName())) { |
271 |
|
|
setVolume(true); |
272 |
|
|
} else if (MAXIMUM_MASTER_VOLUME.equals(e.getPropertyName())) { |
273 |
|
|
int j = CC.preferences().getIntProperty(MAXIMUM_MASTER_VOLUME); |
274 |
|
|
SeekBar sliderVolume = (SeekBar)view.findViewById(R.id.main_slider_volume); |
275 |
|
|
sliderVolume.setMax(j); |
276 |
|
|
} |
277 |
|
|
|
278 |
|
|
} |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
/** |
282 |
|
|
* Gets the channels' pane that is currently shown, |
283 |
|
|
* or has the focus if more than one channels' panes are shown. |
284 |
|
|
* If the GUI implementation has only one pane containing sampler channels, |
285 |
|
|
* than this method should always return that pane (the channels' pane |
286 |
|
|
* with index 0). |
287 |
|
|
* @return The selected channels' pane. |
288 |
|
|
*/ |
289 |
|
|
@Override |
290 |
|
|
public AndroidChannelsPane |
291 |
|
|
getSelectedChannelsPane() { |
292 |
|
|
return getChannelsPane(lanesPager.getCurrentItem()); |
293 |
|
|
} |
294 |
|
|
|
295 |
|
|
/** |
296 |
|
|
* Sets the channels' pane to be selected. |
297 |
|
|
* Note that all registered listeners should be notified |
298 |
|
|
* when the selection is changed. |
299 |
|
|
* @param pane The channels' pane to be shown. |
300 |
|
|
* @see #fireChannelsPaneSelectionChanged |
301 |
|
|
*/ |
302 |
|
|
@Override |
303 |
|
|
public void |
304 |
|
|
setSelectedChannelsPane(AndroidChannelsPane pane) { |
305 |
|
|
int idx = getChannelsPaneIndex(pane); |
306 |
|
|
if(idx == -1) { |
307 |
|
|
Log.w("setSelectedChannelsPane", "Channel lane not found!"); |
308 |
|
|
return; |
309 |
|
|
} |
310 |
|
|
lanesPager.setCurrentItem(idx, true); |
311 |
|
|
} |
312 |
|
|
|
313 |
|
|
/** |
314 |
|
|
* Inserts the specified channels' pane at the specified position |
315 |
|
|
* in the view and in the channels' pane list. |
316 |
|
|
* Where and how this pane will be shown depends on the view/GUI implementation. |
317 |
|
|
* Note that some GUI implementation may have only one pane containing sampler channels. |
318 |
|
|
* @param pane The channels' pane to be inserted. |
319 |
|
|
* @param idx Specifies the position of the channels' pane. |
320 |
|
|
* @see #getChannelsPaneList |
321 |
|
|
*/ |
322 |
|
|
@Override |
323 |
|
|
public void insertChannelsPane(AndroidChannelsPane pane, int idx) { |
324 |
|
|
chnPaneList.insertElementAt(pane, idx); |
325 |
|
|
lanesPager.getAdapter().notifyDataSetChanged(); |
326 |
|
|
lanesPager.setCurrentItem(idx, true); |
327 |
|
|
|
328 |
|
|
/*ViewFlipper flipper = getFlipper(); |
329 |
|
|
|
330 |
|
|
if(idx < 0 || flipper.getChildCount() < idx) { |
331 |
|
|
Log.w("classic.MainFrame.insertChannelsPane", "Incorrect lane position: " + idx); |
332 |
|
|
flipper.addView(pane.getView()); |
333 |
|
|
} else { |
334 |
|
|
flipper.addView(pane.getView(), idx); |
335 |
|
|
} |
336 |
|
|
|
337 |
|
|
flipper.setDisplayedChild(idx);*/ |
338 |
|
|
firePropertyChange("channelLaneAdded", null, pane); |
339 |
|
|
} |
340 |
|
|
|
341 |
|
|
/** |
342 |
|
|
* Invoked on startup when no JSampler home directory is specified |
343 |
|
|
* or the specified JSampler home directory doesn't exist. |
344 |
|
|
* This method should ask the user to specify a JSampler |
345 |
|
|
* home directory and then set the specified JSampler home directory using |
346 |
|
|
* {@link org.jsampler.CC#setJSamplerHome} method. |
347 |
|
|
* @see org.jsampler.CC#getJSamplerHome |
348 |
|
|
* @see org.jsampler.CC#setJSamplerHome |
349 |
|
|
*/ |
350 |
|
|
@Override |
351 |
|
|
public void installJSamplerHome() { |
352 |
|
|
AHF.getActivity().showDialog(JSamplerActivity.DLG_CHOOSE_HOME_DIR); |
353 |
|
|
} |
354 |
|
|
|
355 |
|
|
/** Shows a detailed error information about the specified exception. */ |
356 |
|
|
@Override |
357 |
|
|
public void showDetailedErrorMessage(String err, String details) { |
358 |
|
|
|
359 |
|
|
} |
360 |
|
|
|
361 |
|
|
@Override |
362 |
|
|
public void handleConnectionFailure() { |
363 |
|
|
|
364 |
|
|
} |
365 |
|
|
|
366 |
|
|
/** |
367 |
|
|
* Gets the server address to which to connect. If the server should be |
368 |
|
|
* manually selected, a dialog asking the user to choose a server is displayed. |
369 |
|
|
* @param manualSelect Determines whether the server should be manually selected. |
370 |
|
|
*/ |
371 |
|
|
@Override |
372 |
|
|
public void getServer(final CC.Run<Server> r, boolean manualSelect) { |
373 |
|
|
if(!manualSelect) { |
374 |
|
|
int i = preferences().getIntProperty(JSPrefs.SERVER_INDEX); |
375 |
|
|
int size = CC.getServerList().getServerCount(); |
376 |
|
|
if(size == 0) r.run(null); |
377 |
|
|
else if(i >= size) r.run(CC.getServerList().getServer(0)); |
378 |
|
|
else r.run(CC.getServerList().getServer(i)); |
379 |
|
|
return; |
380 |
|
|
} |
381 |
|
|
|
382 |
|
|
AHF.getActivity().startLocalActivity(new Requestor<Server>(CHOOSE_BACKEND_REQUEST) { |
383 |
|
|
public void |
384 |
|
|
onResult(Server res) { r.run(res); } |
385 |
|
|
}); |
386 |
|
|
} |
387 |
|
|
|
388 |
|
|
/** |
389 |
|
|
* Sends the specified script to the backend. |
390 |
|
|
* @param script The file name of the script to run. |
391 |
|
|
*/ |
392 |
|
|
@Override |
393 |
|
|
public void runScript(String script) { |
394 |
|
|
|
395 |
|
|
} |
396 |
|
|
|
397 |
|
|
private class ChannelLanesPagerAdapter extends PagerAdapter { |
398 |
|
|
public int |
399 |
|
|
getCount() { return getChannelsPaneCount(); } |
400 |
|
|
|
401 |
|
|
public void startUpdate(View container) { } |
402 |
|
|
public void finishUpdate(View container) { } |
403 |
|
|
|
404 |
|
|
public Object |
405 |
|
|
instantiateItem(View container, int position) { |
406 |
|
|
View view = getChannelsPane(position).getView(); |
407 |
|
|
((ViewPager)container).addView(view, 0); |
408 |
|
|
return view; |
409 |
|
|
} |
410 |
|
|
|
411 |
|
|
public void |
412 |
|
|
destroyItem(View container, int position, Object object) { |
413 |
|
|
((ViewPager)container).removeView((View)object); |
414 |
|
|
} |
415 |
|
|
|
416 |
|
|
public void |
417 |
|
|
setPrimaryItem(View container, int position, Object object) { |
418 |
|
|
setSelectedChannelsPane(getChannelsPane(position)); |
419 |
|
|
} |
420 |
|
|
|
421 |
|
|
public boolean |
422 |
|
|
isViewFromObject(View view, Object object) { return view == object; } |
423 |
|
|
|
424 |
|
|
public Parcelable |
425 |
|
|
saveState() { return null; } |
426 |
|
|
|
427 |
|
|
public void |
428 |
|
|
restoreState(Parcelable state, ClassLoader loader) { } |
429 |
|
|
} |
430 |
|
|
} |