1 |
/* |
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 |
} |