From 522e3c917b93c490ffdc5577ef6ffb558612782c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96nnerby?= Date: Sat, 29 Aug 2009 21:20:13 +0000 Subject: [PATCH] More on the Workout mode. Shuffle and repeat working. Highlighting the playing track in "now playing" list. More icons. Added help dialog. --- src/android/AndroidManifest.xml | 4 +- src/android/res/drawable/ic_playing.png | Bin 0 -> 935 bytes src/android/res/drawable/mc_notify.png | Bin 1042 -> 1124 bytes src/android/res/drawable/running.png | Bin 0 -> 1953 bytes src/android/res/layout-land/play_control.xml | 6 + src/android/res/layout/category_list_item.xml | 4 +- src/android/res/layout/category_select.xml | 23 ++ .../res/layout/category_select_item.xml | 7 + src/android/res/layout/help.xml | 10 + .../res/layout/now_playing_list_item.xml | 8 +- src/android/res/layout/play_bpm_control.xml | 45 ++++ src/android/res/layout/play_control.xml | 2 - src/android/res/menu/default_menu.xml | 1 + src/android/res/values/strings.xml | 2 + src/android/src/doep/xml/Reader.java | 10 +- src/android/src/doep/xml/ReaderNode.java | 12 +- src/android/src/doep/xml/Writer.java | 10 +- src/android/src/doep/xml/WriterNode.java | 10 +- .../src/org/musikcube/CategoryList.java | 38 +-- .../src/org/musikcube/CategorySelect.java | 252 ++++++++++++++++++ src/android/src/org/musikcube/Helper.java | 44 +++ .../src/org/musikcube/NowPlayingList.java | 13 +- ...{BPMControl.java => PlayerBPMControl.java} | 106 ++++---- .../src/org/musikcube/PlayerControl.java | 22 +- src/android/src/org/musikcube/Service.java | 10 +- .../src/org/musikcube/TrackListBase.java | 40 +-- .../src/org/musikcube/core/IQuery.java | 2 +- .../src/org/musikcube/core/Library.java | 34 +-- .../src/org/musikcube/core/ListQuery.java | 11 +- .../src/org/musikcube/core/MetadataQuery.java | 6 +- .../src/org/musikcube/core/PaceDetector.java | 150 ++++++----- .../src/org/musikcube/core/Player.java | 75 +++++- .../src/org/musikcube/core/TrackPlayer.java | 23 +- src/android/src/org/musikcube/main.java | 35 +-- 34 files changed, 745 insertions(+), 270 deletions(-) create mode 100644 src/android/res/drawable/ic_playing.png create mode 100644 src/android/res/drawable/running.png create mode 100644 src/android/res/layout/category_select.xml create mode 100644 src/android/res/layout/category_select_item.xml create mode 100644 src/android/res/layout/help.xml create mode 100644 src/android/res/layout/play_bpm_control.xml create mode 100644 src/android/src/org/musikcube/CategorySelect.java create mode 100644 src/android/src/org/musikcube/Helper.java rename src/android/src/org/musikcube/{BPMControl.java => PlayerBPMControl.java} (71%) diff --git a/src/android/AndroidManifest.xml b/src/android/AndroidManifest.xml index 4094faa35..1036bd4d7 100644 --- a/src/android/AndroidManifest.xml +++ b/src/android/AndroidManifest.xml @@ -3,7 +3,7 @@ package="org.musikcube" android:versionCode="1" android:versionName="1.0"> - + @@ -17,6 +17,8 @@ + + diff --git a/src/android/res/drawable/ic_playing.png b/src/android/res/drawable/ic_playing.png new file mode 100644 index 0000000000000000000000000000000000000000..48d82a812e5a0e721079fdf5d40502e0b26fa083 GIT binary patch literal 935 zcmV;Y16cftP)X*dhvzu-b>o|eee7IdA~S{ z#Ujzl)<(3rwVVL?rx&rwSPyi4a690BmzYaz*+IaupAP`|QJSXX%m{N;Qz?_ph{Bri z9L6FL`EnR0a8eZIq&lf?)QL{{F8hYr2{w*gXdKA(ABqVSRndE-BE5HaxqD;L*vaM9 zWg18vgGU>n#!uUv5Rm|^0ui!AL>m)`poRvAZZHAn9#EeKB3^q4s1*ftU`-LY5M0nZ zJZC%?0GxHZ-D9B26&SZQm4F6TJuib-dKaRZ>&&=+(0>=m1|rc&Pg4~zRL}r%MRf48 zUx1lNZ=ZKyDmoRXkJGm!6Ol{JaY7tWRjkAeh17~t9i1KBY={k|SJPd=J3$wa7P*Jq zZsR6UmAa(_HIDcOe8b_1a2!O725$xX8zvyDvT26u-Qy;zd8g{(dTw@RHsm?$xfvV{ z`k7J2)vynkY8c^IKH+HY6eDNsR958t!jlC)vzb|f=j*9ts!$gJeKGRy;e(qAA4}WP zd+q_Zk}u>_0Q|P0M5G-=;JtO_0ob(+CuLH2B|L>yMF9WWa3-Wr&;;s0wPqo)9&0j$zQ`T300BU^pebseT1yZMInBr z)>1FB+3X^`-sI-EAGXzmX1te6GjOyI{31A7A-@8^>$Vo9uwxm3$L002ov JPDHLkV1oEip(OwS literal 0 HcmV?d00001 diff --git a/src/android/res/drawable/mc_notify.png b/src/android/res/drawable/mc_notify.png index 6ceff637fc75299b8598b1ea3f5cceabc0a50c97..394ba8efc15b6e9b79935883e0b018285863afa2 100644 GIT binary patch delta 1067 zcmV+`1l0SI2;>NmBn<>-K}|sb0I$e51&ZmBAt-7f+gdx&rS0Y3HI8cIV*9Qpxv ztyspfWLcJk^|9X1>Aa&|S!; zZ*Tw~jruDz8^<`T?qbmI;Zt_!5!7lg4{+JHV%3e2ciAg6pv;3A+Sl!3}k)!gEy;0>ifLsYgeLv8)xkkyfTS)X zX(~u+3Xa<*u4yVV={P3`T>hSEc_M$27$2ye41?9dYBN9&F^1gX)p`1@6%c~#)n*+aW_R%vf ze(k}JF&uSHNT1)bH(VL*ny87!)(tN%+v` zB%M}6WZt)27`h5e$i&3z7BUi<=ZmT=K40|B;qH!@zYr&sZE8+PE?V79KKaJNZ7k0l zylkGfG+Og4kkAc+2>DNp?Y@85hA&<2#NqZdb6X)>UJUzUOy<|5yEL(lcq{-9D@bOQL^k`JSkR^cNBdi~CSy34iST2y(rWOKR|u{6EqH@@{=*niL?0yn5H>?mv)U(sf2dBJuTPz`a1Ua_aV#jYn$~# zQERn|u4d!LDq5J0xG@{IZ9#A)RX6Htx)_+vK-~<2qKPO$sn}?tiX$}Dj#UwoHcDQ7 z$-AC=-@D06`jWaCZ*s}I_nvdU`<=fR#yQ9TJv4lW5%ezhAKZ_rf5|DH$z+mr67Aj9 z8#iIfvj~0EbLG0)*_~T=mNMzx-OJlnw~5E?G-HYJ#K@D0Cq|{gY5}<<1pBya$){M!XW9PB?(13G=AOw>? z9{zPM_Vn+;?Mg=Ee~XI=#+|jFE31NNS^&-KcVO@Fi)dQE4SGa}9@Km2XyWPE;LwYk z7YxQq@?5ZE4v+#^mWgtS77AfwcRx1uokm?_6Cx2(4Mql$$3*Jp$ZuX1$%>Q|gJqe( z=Fa4{SU_)V!G;58(A>2TMxB93U8J4dI5ra-iM_jjwOzkf zyGEMU?nLvh<7jB^5I}-e53!nl|KR$Nl2`8XG70)JAqB>@Vt|zk#YMGX7^{9djQqk| z6z0YO%SJk%9$a|z=ShSxKo;ZQlwGjJt(*yW@&Wee9q!x3n$>(FMjA+l1s zFh<&A#ISwYf0zU`<{G2POTQA7d%%fi(AbRnjeTfrSc6uhRV0T2V_ceXZ2z;T(J ziitF8xyC&gAes5ki!d9v0D1tjV^D2TYZ|E;lfsupC;7q4a00b9su|K=dS()zvNMkHyex(rbn|FS=b@BS?R-fG zt_}i`e?eL?x(gp?@&4aOvhv-LBF%vxyN<@06td-4$bQrNq*UfMEHj6f&u*jeIqTIJ z-ui3*ki*h0^*YzJ-WOO+ki2DK?o|@0mk&jMQj&3M_XAsZ_ouj<#>KQ_<@%Lud_nI0 za}8lFqym@{e*dNK_Zww3!^+cICJ6lBCrdy&P}(~%otlOjEC?2tNO-ujt22|GO_V8C zxKS-9L9HtNkPK+pA=t##aFb0324;Rg&7oJOBUy07*qo IM6N<$g6qBMaR2}S diff --git a/src/android/res/drawable/running.png b/src/android/res/drawable/running.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec010aaee011a1ba007e547084bf7a9541fa484 GIT binary patch literal 1953 zcmb`IX*e6`7RQ5_hA|PNr6n{O1eGmp9V>wI|MbI$YopY!dUf3o#0i?77x!~p=n zS2!%%_9$|XALxstzrvxi=qLb5NGl`&P{l=v-dj4V&xM#fgy1ROAz|)8L;%u@;z5Mr z0^CVNTcW#Hcp#5x1OS}+28Tx4QO8%_i&3O4&c;oN-4Twr{w>T$KuB1revc>Jp%?n^h|#p>7+12_Re@FR`_awb9z;!Wg}sK*Vy_|r00F{4*CBB* z01`k00EIxp5a5aQ1Ur%bM+O7^(eWqtKhuuce+QqCrFuV9&s7J@5?N~9<_5NmgB{CC z0;8eW|MVQr#@_A>(mNG0W1n4G(6Arzvsy~|Jy=cWqhfxaa8!C#x#fAUH~PjuLtk2q znA06+sL7rPJK3WSk@7g$<1)8FTNhxSUe^Z=k6J;gx-wUDox%ejxDqgQ$7!O?sw8#A zX}m~Q2p*4I}#Y>qfUzHj6Lo$uDwdUFaj(Ad-W`(=;IUD%iP-FCBwrQElyITBsg$Ynq@5c z+VYwH!p?9}T-2{^fdjM^_3r42$(|te{Az~yg6pF68yBsvhT&T-$ucYc>WE1cN;Il!)qOVQ3|a+*>1;Du>ks4i5Kxn@L0i&Ov~DCF=c$+TkF*6k zsbzWx(TIBDV^I0Q;A3+4qbw8hQ`tGBO&aqs{hGqfcUegUYF5AW5*$8MXILD6o$Wds zp1HHjb|8=phec%)s+Gh0ppLpLgrjdPg$xHN<*E7>PSX;;ojtr0- zMZN3E?XN61q3OD$b+ZcHon=b!Kynk56m%}J6@;Rz*5oF>n}`2sq%Iu`Bx`2mQlq*3 zo%Q=76{nE-JuKaQ-xde!Elb}rwzCwLj`?WZM=-L`jN8@Y42|JXbc~e1c;4^YD5v;a ztC8$Q8Z6bvCh__&sb#^gq0O&u_^!QZxtn?ceJ$V36V3?R z>9^cDK>n>@lZd4!GfGzQrS>(@LInW_?EajZLv-Yh2KptoOj>U+yOrAWYX_E5JgrA> zznh9tM=aMCiQYT?DaVkzK5}u8cJpg?-+oB)jOS)TBx{`R-wL1WRF*Z;OoB-d1+@=7 zs65)AA^zIqYi{iNA7C)9!^O7+=K74o7SnB^d3*y&wDQAZu-#5`#5(VyGPfRd=}NZ5 z!w9Kpnkl|n{>FCX#BO3Yh8_qhv8}^zZ9yr)r2T;2R<~B9Y`>5a_VN^u6_phb%I8LI zAcEVs7x0{nR%}$HgC|zc$Ia|Z6y3=w>4~yY><5w6S-P)Xlqv{`>EOxrvGw5VS6X9w z??S8A^=USeoX&oSV}=|<|8`qJr>P%tm{Z%xnp@j#qR14&ej39XlA2nUea@N8&%gr; zJDhkeVjvwz(%!H+|GSJ9cK*yB&W&1|7dyFn)iPfUsV-%h8lq(r&FI#x=ydMa2@`+a zC29t9WGmPgE)E9WEPxs&I0BWp1OC4xg;ZxU!xXl2t|-mhM$3;#d3~`2u9#phO&A>b z?S>AkKfh!=Z7W@x7W1}E_kCmBieNo9Um%5{!9dMQpkH5lu(0n0QpyX>1V533|<5djz(mUZ>rkMgU`m6g#Q3GOz^NY zXm85ets=oeavhts&E2?o8}0HljsT0W`cv`asHb_}kIZt_HXZMA;VIuIOY%N91BJSZ z$)d*&a^lb*d&QsJyiM4#>u>hR;Ep#R7@3h|mFA7^5 literal 0 HcmV?d00001 diff --git a/src/android/res/layout-land/play_control.xml b/src/android/res/layout-land/play_control.xml index fa61c6a2e..69ef3bc37 100644 --- a/src/android/res/layout-land/play_control.xml +++ b/src/android/res/layout-land/play_control.xml @@ -7,9 +7,15 @@ android:paddingRight="8dp" android:gravity="center_vertical"> + + + + + + diff --git a/src/android/res/layout/category_list_item.xml b/src/android/res/layout/category_list_item.xml index 21fdb1477..0968dbc3f 100644 --- a/src/android/res/layout/category_list_item.xml +++ b/src/android/res/layout/category_list_item.xml @@ -5,8 +5,6 @@ android:layout_height="fill_parent"> + android:layout_height="wrap_content" android:textSize="28sp" android:fadingEdge="horizontal" android:maxLines="1" android:fadingEdgeLength="10sp" android:layout_width="wrap_content"/> \ No newline at end of file diff --git a/src/android/res/layout/category_select.xml b/src/android/res/layout/category_select.xml new file mode 100644 index 000000000..e2c3f0214 --- /dev/null +++ b/src/android/res/layout/category_select.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/src/android/res/layout/category_select_item.xml b/src/android/res/layout/category_select_item.xml new file mode 100644 index 000000000..e439023d6 --- /dev/null +++ b/src/android/res/layout/category_select_item.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/src/android/res/layout/help.xml b/src/android/res/layout/help.xml new file mode 100644 index 000000000..94c6f576e --- /dev/null +++ b/src/android/res/layout/help.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/android/res/layout/now_playing_list_item.xml b/src/android/res/layout/now_playing_list_item.xml index a39e24dbf..053bd5fa4 100644 --- a/src/android/res/layout/now_playing_list_item.xml +++ b/src/android/res/layout/now_playing_list_item.xml @@ -3,17 +3,17 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal"> + + android:layout_height="wrap_content" android:textSize="24sp" android:fadingEdge="horizontal" android:maxLines="1" android:fadingEdgeLength="10sp" android:hint="...." android:layout_width="wrap_content"/> + android:layout_height="wrap_content" android:fadingEdge="horizontal" android:maxLines="1" android:fadingEdgeLength="10sp" android:textColor="#999999" android:textSize="16sp" android:layout_width="wrap_content"/> + \ No newline at end of file diff --git a/src/android/res/layout/play_bpm_control.xml b/src/android/res/layout/play_bpm_control.xml new file mode 100644 index 000000000..50c57109c --- /dev/null +++ b/src/android/res/layout/play_bpm_control.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/android/res/layout/play_control.xml b/src/android/res/layout/play_control.xml index 9b106867e..e8b66bb66 100644 --- a/src/android/res/layout/play_control.xml +++ b/src/android/res/layout/play_control.xml @@ -14,8 +14,6 @@ - - diff --git a/src/android/res/menu/default_menu.xml b/src/android/res/menu/default_menu.xml index a1199a045..93251af18 100644 --- a/src/android/res/menu/default_menu.xml +++ b/src/android/res/menu/default_menu.xml @@ -3,4 +3,5 @@ + diff --git a/src/android/res/values/strings.xml b/src/android/res/values/strings.xml index 2b78dd45a..c9a38ce26 100644 --- a/src/android/res/values/strings.xml +++ b/src/android/res/values/strings.xml @@ -19,4 +19,6 @@ Player controls Browse Now playing +musikCube is a application that connects to you musikServer that you have installed on your desktop/server computer. Once musikCube is connected you are able to browse and play all your music directly on your phone without having to copy them to you SD card. + diff --git a/src/android/src/doep/xml/Reader.java b/src/android/src/doep/xml/Reader.java index 9acbb582e..411defd35 100644 --- a/src/android/src/doep/xml/Reader.java +++ b/src/android/src/doep/xml/Reader.java @@ -6,10 +6,10 @@ import java.io.InputStream; import java.lang.Exception; -public class Reader extends ReaderNode { - private InputStream stream; - private org.xmlpull.v1.XmlPullParser parser; - private java.util.LinkedList nodeLevels = new java.util.LinkedList(); +public final class Reader extends ReaderNode { + private final InputStream stream; + private final org.xmlpull.v1.XmlPullParser parser; + private final java.util.LinkedList nodeLevels = new java.util.LinkedList(); public ReaderNode currentNode; private boolean firstParse = true; @@ -33,7 +33,7 @@ public class Reader extends ReaderNode { } - public void Parse() + public final void Parse() throws Exception { int eventType = 0; diff --git a/src/android/src/doep/xml/ReaderNode.java b/src/android/src/doep/xml/ReaderNode.java index c69d944bc..d0fc711c0 100644 --- a/src/android/src/doep/xml/ReaderNode.java +++ b/src/android/src/doep/xml/ReaderNode.java @@ -3,10 +3,10 @@ import doep.xml.Reader; public class ReaderNode { - public String name = ""; + public final String name; public boolean ended = false; public String content = ""; - public ReaderNode parent; + public final ReaderNode parent; public Reader reader; public int level = 1; @@ -25,7 +25,7 @@ public class ReaderNode { * @param attribute * @return String or null */ - public String Attribute(String attribute){ + public final String Attribute(String attribute){ return this.attributes.get(attribute); } @@ -34,7 +34,7 @@ public class ReaderNode { * @param name Name of node to be found * @return ReaderNode if found, or null if this goes out of scope */ - public ReaderNode ChildNode(String name) + public final ReaderNode ChildNode(String name) throws Exception { //Log.v("doep::ReaderNode::ChildNode","Node: "+name+" ended: "+this.ended); @@ -59,7 +59,7 @@ public class ReaderNode { return null; } - public void End() + public final void End() throws Exception { while(!this.ended){ @@ -73,7 +73,7 @@ public class ReaderNode { * Wait for a childnode * @return ReaderNode if found, or null if this goes out of scope */ - public ReaderNode ChildNode() + public final ReaderNode ChildNode() throws Exception { while(!this.ended){ diff --git a/src/android/src/doep/xml/Writer.java b/src/android/src/doep/xml/Writer.java index ac9f81900..0a87b1d43 100644 --- a/src/android/src/doep/xml/Writer.java +++ b/src/android/src/doep/xml/Writer.java @@ -1,10 +1,10 @@ package doep.xml; -public class Writer extends WriterNode { +public final class Writer extends WriterNode { public String buffer; - private java.io.OutputStream stream; + private final java.io.OutputStream stream; public Writer(java.io.OutputStream stream){ super("",null); @@ -14,7 +14,7 @@ public class Writer extends WriterNode { this.state = 1; } - public void Write(String content) + public final void Write(String content) throws java.io.IOException { //Log.v("doep.xml.Writer","Write "+content); @@ -22,13 +22,13 @@ public class Writer extends WriterNode { // this.buffer += content; } - public void Flush() + public final void Flush() throws java.io.IOException { this.Flush(false); } - public void Flush(boolean writeNull) + public final void Flush(boolean writeNull) throws java.io.IOException { //Log.v("doep.xml.Writer","Flush "+writeNull); diff --git a/src/android/src/doep/xml/WriterNode.java b/src/android/src/doep/xml/WriterNode.java index 5409b5ff0..e2bf6c9f3 100644 --- a/src/android/src/doep/xml/WriterNode.java +++ b/src/android/src/doep/xml/WriterNode.java @@ -4,12 +4,12 @@ import java.util.ListIterator; import java.util.Map; public class WriterNode { - public String name = ""; + public final String name; public String content = ""; protected WriterNode parent; protected Writer writer; - private java.util.List children = new java.util.LinkedList(); - public java.util.SortedMap attributes = new java.util.TreeMap(); + private final java.util.List children = new java.util.LinkedList(); + public final java.util.SortedMap attributes = new java.util.TreeMap(); protected int state = 0; @@ -27,7 +27,7 @@ public class WriterNode { return newNode; } - public void End() + public final void End() throws java.io.IOException { if(this.parent.state==0){ @@ -61,7 +61,7 @@ public class WriterNode { } } - protected void WriteStartTag() + protected final void WriteStartTag() throws java.io.IOException { if(this.state==0){ diff --git a/src/android/src/org/musikcube/CategoryList.java b/src/android/src/org/musikcube/CategoryList.java index 312fe0dfe..db5a62aae 100644 --- a/src/android/src/org/musikcube/CategoryList.java +++ b/src/android/src/org/musikcube/CategoryList.java @@ -5,6 +5,7 @@ package org.musikcube; import java.util.ArrayList; +import org.musikcube.TrackListBase.TrackViewHolder; import org.musikcube.core.IQuery; import org.musikcube.core.ListQuery; import org.musikcube.core.IQuery.OnQueryResultListener; @@ -20,6 +21,7 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; +import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; @@ -46,6 +48,10 @@ public class CategoryList extends ListActivity implements OnQueryResultListener OnResults(); } }; + + static class CategoryViewHolder{ + TextView title; + } public class ResultAdapter extends BaseAdapter{ @@ -84,15 +90,21 @@ public class CategoryList extends ListActivity implements OnQueryResultListener } public View getView(int position, View view, ViewGroup parent) { + final CategoryViewHolder holder; if(view==null){ view = inflator.inflate(R.layout.category_list_item, null); + holder = new CategoryViewHolder(); + holder.title = (TextView) view.findViewById(R.id.text); + view.setTag(holder); + }else{ + holder = (CategoryViewHolder)view.getTag(); } // if(position==0){ - ((TextView) view.findViewById(R.id.text)).setText("- All -"); + holder.title.setText("- All -"); }else{ - ((TextView) view.findViewById(R.id.text)).setText(this.query.resultsStrings.get(position-1)); + holder.title.setText(this.query.resultsStrings.get(position-1)); } return view; } @@ -230,22 +242,10 @@ public class CategoryList extends ListActivity implements OnQueryResultListener } public boolean onOptionsItemSelected(MenuItem item) { - //Log.i("MC2.onContextItemSelected","item "+item.getItemId()+" "+R.id.context_settings); - switch (item.getItemId()) { - case R.id.context_settings: - startActivity(new Intent(this, org.musikcube.Preferences.class)); - return true; - case R.id.context_browse: - startActivity(new Intent(this, org.musikcube.main.class)); - return true; - case R.id.context_controls: - startActivity(new Intent(this, org.musikcube.PlayerControl.class)); - return true; - case R.id.context_nowplaying: - startActivity(new Intent(this, org.musikcube.NowPlayingList.class)); - return true; - default: - return super.onContextItemSelected(item); - } + if(Helper.DefaultOptionsItemSelected(item,this)){ + return true; + }else{ + return super.onContextItemSelected(item); + } } } diff --git a/src/android/src/org/musikcube/CategorySelect.java b/src/android/src/org/musikcube/CategorySelect.java new file mode 100644 index 000000000..5d114a24f --- /dev/null +++ b/src/android/src/org/musikcube/CategorySelect.java @@ -0,0 +1,252 @@ +/** + * + */ +package org.musikcube; + +import java.util.ArrayList; + +import org.musikcube.TrackListBase.TrackViewHolder; +import org.musikcube.core.IQuery; +import org.musikcube.core.ListQuery; +import org.musikcube.core.IQuery.OnQueryResultListener; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +/** + * @author doy + * + */ +public class CategorySelect extends ListActivity implements OnQueryResultListener { + + private String category = null; + private String nextCategoryList = ""; + private ListQuery query = new ListQuery(); + + private ArrayList selectedCategory; + private ArrayList selectedCategoryIds; +// private ProgressDialog loadingDialog; + + // Need handler for callbacks to the UI thread + final Handler callbackHandler = new Handler(); + + // Create runnable for posting + final Runnable callbackRunnable = new Runnable() { + public void run() { + OnResults(); + } + }; + + static class CategoryViewHolder{ + CheckBox title; + } + + public class ResultAdapter extends BaseAdapter{ + + protected ListQuery query; + protected ListActivity context; + private LayoutInflater inflator; + + public ResultAdapter(ListActivity context){ + this.context = context; + this.inflator = context.getLayoutInflater(); + } + + public int getCount() { + int size = this.query.resultsInts.size(); + if(size==0){ + return 0; + }else{ + return size+1; + } + } + + public Object getItem(int position) { + if(position==0){ + return 0; + }else{ + return this.query.resultsInts.get(position-1); + } + } + + public long getItemId(int position) { + if(position==0){ + return 0; + }else{ + return this.query.resultsInts.get(position-1); + } + } + + public View getView(int position, View view, ViewGroup parent) { + final CategoryViewHolder holder; + if(view==null){ + view = inflator.inflate(R.layout.category_select_item, null); + holder = new CategoryViewHolder(); + holder.title = (CheckBox) view.findViewById(R.id.text); + view.setTag(holder); + }else{ + holder = (CategoryViewHolder)view.getTag(); + } + + // + if(position==0){ + holder.title.setText("- All -"); + }else{ + holder.title.setText(this.query.resultsStrings.get(position-1)); + } + return view; + } + + } + + private ResultAdapter listAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + //Log.v("musikcube.CategoryList", "start"); + this.setContentView(R.layout.category_select); + + Intent intent = this.getIntent(); + + this.query.SetResultListener(this); + + this.listAdapter = new ResultAdapter(this); + this.listAdapter.query = this.query; + setListAdapter(this.listAdapter); + + + // Extract the category order + String categoryString = intent.getStringExtra("org.musikcube.CategorySelect.listCategory"); + String[] categories = categoryString.split(","); + this.category = categories[0]; + + // Save the next category lists + for(int i=1;i1){ + this.nextCategoryList += ","; + } + this.nextCategoryList += categories[i]; + } + + this.setTitle("musikCube: "+this.category); + + + if(this.category!=null){ + //Log.v("musikcube.CategoryList", "category="+this.category); + // Query for data + this.query.category = this.category; + + // check for selection + this.selectedCategory = intent.getStringArrayListExtra("org.musikcube.CategoryList.selectedCategory"); + this.selectedCategoryIds = intent.getIntegerArrayListExtra("org.musikcube.CategoryList.selectedCategoryId"); + if(this.selectedCategory!=null){ + for(int i=0;i(); + } + if(this.selectedCategoryIds==null){ + this.selectedCategoryIds = new ArrayList(); + } + ArrayList selectedCategory = (ArrayList)this.selectedCategory.clone(); + ArrayList selectedCategoryIds = (ArrayList)this.selectedCategoryIds.clone(); + + if(id!=0){ + selectedCategory.add(this.category); + selectedCategoryIds.add((int)id); + } + + if(this.nextCategoryList.equals("")){ + // List tracks + Intent intent = new Intent(this, TrackList.class); + intent.putExtra("org.musikcube.CategoryList.listCategory", this.nextCategoryList); + intent.putExtra("org.musikcube.CategoryList.selectedCategory", selectedCategory); + intent.putExtra("org.musikcube.CategoryList.selectedCategoryId", selectedCategoryIds); + startActivity(intent); + }else{ + Intent intent = new Intent(this, CategoryList.class); + intent.putExtra("org.musikcube.CategoryList.listCategory", this.nextCategoryList); + intent.putExtra("org.musikcube.CategoryList.selectedCategory", selectedCategory); + intent.putExtra("org.musikcube.CategoryList.selectedCategoryId", selectedCategoryIds); + startActivity(intent); + } + } + + @Override + protected void onPause() { + super.onPause(); + org.musikcube.core.Library.GetInstance().RemovePointer(); + } + + @Override + protected void onResume() { + super.onResume(); + startService(new Intent(this, org.musikcube.Service.class)); + org.musikcube.core.Library.GetInstance().AddPointer(); + } + + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.default_menu, menu); + return true; + } + + public boolean onOptionsItemSelected(MenuItem item) { + if(Helper.DefaultOptionsItemSelected(item,this)){ + return true; + }else{ + return super.onContextItemSelected(item); + } + } +} diff --git a/src/android/src/org/musikcube/Helper.java b/src/android/src/org/musikcube/Helper.java new file mode 100644 index 000000000..cf1e6c8b1 --- /dev/null +++ b/src/android/src/org/musikcube/Helper.java @@ -0,0 +1,44 @@ +package org.musikcube; + +import org.musikcube.core.Player; + +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.view.MenuItem; + +public class Helper { + + + public static boolean DefaultOptionsItemSelected(MenuItem item,Context context) { + //Log.i("MC2.onContextItemSelected","item "+item.getItemId()+" "+R.id.context_settings); + switch (item.getItemId()) { + case R.id.context_settings: + context.startActivity(new Intent(context, org.musikcube.Preferences.class)); + return true; + case R.id.context_browse: + context.startActivity(new Intent(context, org.musikcube.main.class)); + return true; + case R.id.context_controls: + if(Player.GetInstance().GetBPMMode()){ + context.startActivity(new Intent(context, org.musikcube.PlayerBPMControl.class)); + }else{ + context.startActivity(new Intent(context, org.musikcube.PlayerControl.class)); + } + return true; + case R.id.context_nowplaying: + context.startActivity(new Intent(context, org.musikcube.NowPlayingList.class)); + return true; + case R.id.context_help: + Dialog dialog = new Dialog(context); + dialog.setContentView(R.layout.help); + dialog.setTitle("Help"); + + dialog.show(); + return true; + default: + return false; + } + } + +} diff --git a/src/android/src/org/musikcube/NowPlayingList.java b/src/android/src/org/musikcube/NowPlayingList.java index a6188a7b8..f03a5c574 100644 --- a/src/android/src/org/musikcube/NowPlayingList.java +++ b/src/android/src/org/musikcube/NowPlayingList.java @@ -28,11 +28,11 @@ public class NowPlayingList extends TrackListBase implements OnTrackListUpdateLi final static public int ADD_ALL_ID = 2; final static public int REMOVE_ID = 3; - public int position = 0; - @Override protected void onCreate(Bundle savedInstanceState){ + this.markPosition = true; + this.trackListViewId = R.layout.now_playing_list; this.trackListItemViewId = R.layout.now_playing_list_item; @@ -74,6 +74,11 @@ public class NowPlayingList extends TrackListBase implements OnTrackListUpdateLi intent.putExtra("org.musikcube.Service.action", "appendlist"); startService(intent); return true; + case REMOVE_ID: + intent.putExtra("org.musikcube.Service.action", "remove_from_list"); + intent.putExtra("org.musikcube.Service.position", info.position); + startService(intent); + return true; default: return super.onContextItemSelected(item); } @@ -86,6 +91,7 @@ public class NowPlayingList extends TrackListBase implements OnTrackListUpdateLi menu.add(0, PLAY_THIS_ID, 0, "Play this track"); menu.add(0, ADD_THIS_ID, 0, "Add this to current playlist"); menu.add(0, ADD_ALL_ID, 0, "Add all to current playlist"); + menu.add(0, REMOVE_ID, 0, "Remove from playlist"); } public void OnTrackListPositionUpdate() { @@ -106,7 +112,6 @@ public class NowPlayingList extends TrackListBase implements OnTrackListUpdateLi } - @Override protected void onPause() { super.onPause(); @@ -117,6 +122,8 @@ public class NowPlayingList extends TrackListBase implements OnTrackListUpdateLi protected void onResume() { super.onResume(); Player.GetInstance().SetListUpdateListener(this); + this.position = Player.GetInstance().GetPosition(); + this.setSelection(this.position); } diff --git a/src/android/src/org/musikcube/BPMControl.java b/src/android/src/org/musikcube/PlayerBPMControl.java similarity index 71% rename from src/android/src/org/musikcube/BPMControl.java rename to src/android/src/org/musikcube/PlayerBPMControl.java index a021d2582..c5c575aab 100644 --- a/src/android/src/org/musikcube/BPMControl.java +++ b/src/android/src/org/musikcube/PlayerBPMControl.java @@ -19,71 +19,79 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; -public class BPMControl extends Activity implements OnTrackUpdateListener { +public class PlayerBPMControl extends Activity implements OnTrackUpdateListener { private Track track = new Track(); private int duration = 0; private Object lock = new Object(); + private boolean enable = false; + private int currentAlbumCoverId = 0; @Override public void onCreate(Bundle savedInstanceState) { Log.v("MC2::PC","OnCreate"); super.onCreate(savedInstanceState); - setContentView(R.layout.play_control); -/* + setContentView(R.layout.play_bpm_control); + ImageButton nextButton = (ImageButton)findViewById(R.id.MediaNext); nextButton.setOnClickListener(this.onNextClick); ImageButton pauseButton = (ImageButton)findViewById(R.id.MediaPause); pauseButton.setOnClickListener(this.onPauseClick); - */ + this.callbackTrackPositionsUpdateHandler.postDelayed(callbackTrackPositionsUpdateRunnable,500); + + Intent intent = new Intent(PlayerBPMControl.this, org.musikcube.Service.class); + intent.putExtra("org.musikcube.Service.action", "bpmstart"); + startService(intent); } -/* + private OnClickListener onNextClick = new OnClickListener() { public void onClick(View v){ - Intent intent = new Intent(PlayerControl.this, org.musikcube.Service.class); + Intent intent = new Intent(PlayerBPMControl.this, org.musikcube.Service.class); intent.putExtra("org.musikcube.Service.action", "next"); startService(intent); } }; private OnClickListener onPauseClick = new OnClickListener() { public void onClick(View v){ - Intent intent = new Intent(PlayerControl.this, org.musikcube.Service.class); + Intent intent = new Intent(PlayerBPMControl.this, org.musikcube.Service.class); intent.putExtra("org.musikcube.Service.action", "stop"); startService(intent); } }; -*/ + public void OnTrackBufferUpdate(int percent) { - synchronized(lock){ - } this.callbackTrackPositionsUpdateHandler.post(this.callbackTrackPositionsUpdateRunnable); } public void OnTrackPositionUpdate(int secondsPlayed) { - synchronized(lock){ - } this.callbackTrackPositionsUpdateHandler.post(this.callbackTrackPositionsUpdateRunnable); } public void OnTrackUpdate() { this.callbackTrackUpdateHandler.post(this.callbackTrackUpdateRunnable); } - @Override protected void onPause() { + this.enable = false; Log.v("MC2::PC","OnPause"); Player.GetInstance().SetUpdateListener(null); super.onPause(); } @Override protected void onResume() { + this.enable = true; Log.v("MC2::PC","OnResume"); Player.GetInstance().SetUpdateListener(this); super.onResume(); + this.OnUpdateTrackPositionsUI(); + this.OnUpdateTrackUI(); } // Need handler for callbacks to the UI thread @@ -149,13 +157,16 @@ public class BPMControl extends Activity implements OnTrackUpdateListener { } // clear image - ImageView cover = (ImageView)findViewById(R.id.AlbumCover); - cover.setImageResource(R.drawable.album); - - if(thumbnailId!=0){ - // Load image - Library library = Library.GetInstance(); - new DownloadAlbumCoverTask().execute("http://"+library.host+":"+library.httpPort+"/cover/?cover_id="+thumbnailId); + if(this.currentAlbumCoverId!=thumbnailId){ + this.currentAlbumCoverId=thumbnailId; + ImageView cover = (ImageView)findViewById(R.id.AlbumCover); + cover.setImageResource(R.drawable.album); + + if(thumbnailId!=0){ + // Load image + Library library = Library.GetInstance(); + new DownloadAlbumCoverTask().execute("http://"+library.host+":"+library.httpPort+"/cover/?cover_id="+thumbnailId); + } } } @@ -198,24 +209,26 @@ public class BPMControl extends Activity implements OnTrackUpdateListener { }; public void OnUpdateTrackPositionsUI() { - int msPosition = Player.GetInstance().GetTrackPosition(); - int position = msPosition/1000; - int minutes = (int)Math.floor(position/60); - int seconds = position-minutes*60; - String positionText = Integer.toString(minutes)+":"; - if(seconds<10){ positionText += "0"; } - positionText += Integer.toString(seconds); - TextView positionView = (TextView)findViewById(R.id.TrackPosition); - positionView.setText(positionText); - - SeekBar seekBar = (SeekBar)findViewById(R.id.TrackProgress); - synchronized (this.lock) { - if(this.duration==0){ - seekBar.setProgress(0); - }else{ - seekBar.setProgress(msPosition/this.duration); + if(this.enable){ + int msPosition = Player.GetInstance().GetTrackPosition(); + int position = msPosition/1000; + int minutes = (int)Math.floor(position/60); + int seconds = position-minutes*60; + String positionText = Integer.toString(minutes)+":"; + if(seconds<10){ positionText += "0"; } + positionText += Integer.toString(seconds); + TextView positionView = (TextView)findViewById(R.id.TrackPosition); + positionView.setText(positionText); + + SeekBar seekBar = (SeekBar)findViewById(R.id.TrackProgress); + synchronized (this.lock) { + if(this.duration==0){ + seekBar.setProgress(0); + }else{ + seekBar.setProgress(msPosition/this.duration); + } + seekBar.setSecondaryProgress(10*Player.GetInstance().GetTrackBuffer()); } - seekBar.setSecondaryProgress(10*Player.GetInstance().GetTrackBuffer()); } // Next callback in 0.5 seconds @@ -230,20 +243,11 @@ public class BPMControl extends Activity implements OnTrackUpdateListener { } public boolean onOptionsItemSelected(MenuItem item) { - //Log.i("MC2.onContextItemSelected","item "+item.getItemId()+" "+R.id.context_settings); - switch (item.getItemId()) { - case R.id.context_settings: - startActivity(new Intent(this, org.musikcube.Preferences.class)); - return true; - case R.id.context_browse: - startActivity(new Intent(this, org.musikcube.main.class)); - return true; - case R.id.context_controls: - startActivity(new Intent(this, org.musikcube.PlayerControl.class)); - return true; - default: - return super.onContextItemSelected(item); - } + if(Helper.DefaultOptionsItemSelected(item,this)){ + return true; + }else{ + return super.onContextItemSelected(item); + } } } diff --git a/src/android/src/org/musikcube/PlayerControl.java b/src/android/src/org/musikcube/PlayerControl.java index 46f33e8d9..96aa3c8b8 100644 --- a/src/android/src/org/musikcube/PlayerControl.java +++ b/src/android/src/org/musikcube/PlayerControl.java @@ -287,23 +287,11 @@ public class PlayerControl extends Activity implements OnTrackUpdateListener { } public boolean onOptionsItemSelected(MenuItem item) { - //Log.i("MC2.onContextItemSelected","item "+item.getItemId()+" "+R.id.context_settings); - switch (item.getItemId()) { - case R.id.context_settings: - startActivity(new Intent(this, org.musikcube.Preferences.class)); - return true; - case R.id.context_browse: - startActivity(new Intent(this, org.musikcube.main.class)); - return true; - case R.id.context_controls: - startActivity(new Intent(this, org.musikcube.PlayerControl.class)); - return true; - case R.id.context_nowplaying: - startActivity(new Intent(this, org.musikcube.NowPlayingList.class)); - return true; - default: - return super.onContextItemSelected(item); - } + if(Helper.DefaultOptionsItemSelected(item,this)){ + return true; + }else{ + return super.onContextItemSelected(item); + } } } diff --git a/src/android/src/org/musikcube/Service.java b/src/android/src/org/musikcube/Service.java index 987b3f354..f789d6bee 100644 --- a/src/android/src/org/musikcube/Service.java +++ b/src/android/src/org/musikcube/Service.java @@ -92,6 +92,10 @@ public class Service extends android.app.Service { Player player = Player.GetInstance(); player.Append(intent.getIntegerArrayListExtra("org.musikcube.Service.tracklist")); } + if(action.equals("remove_from_list")){ + Player player = Player.GetInstance(); + player.RemoveFromList(intent.getIntExtra("org.musikcube.Service.position", -1)); + } if(action.equals("playlist_prepare")){ Player player = Player.GetInstance(); @@ -109,6 +113,9 @@ public class Service extends android.app.Service { if(action.equals("stop")){ Player player = Player.GetInstance(); player.Stop(); + if(this.paceDetector!=null){ + this.paceDetector.StopSensor(this); + } } if(action.equals("play")){ Player player = Player.GetInstance(); @@ -119,6 +126,7 @@ public class Service extends android.app.Service { this.stopSelf(); } if(action.equals("bpmstart")){ + Player.GetInstance().SetBPMMode(true); if(this.paceDetector==null){ this.paceDetector = new PaceDetector(); this.paceDetector.StartSensor(this); @@ -157,7 +165,7 @@ public class Service extends android.app.Service { contentText += trackArtist; } } - Intent notificationIntent = new Intent(this, PlayerControl.class); + Intent notificationIntent = new Intent(this, Player.GetInstance().GetBPMMode()?PlayerBPMControl.class:PlayerControl.class); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.flags |= Notification.FLAG_ONGOING_EVENT|Notification.FLAG_NO_CLEAR; diff --git a/src/android/src/org/musikcube/TrackListBase.java b/src/android/src/org/musikcube/TrackListBase.java index 79cf79f27..a0970b8d0 100644 --- a/src/android/src/org/musikcube/TrackListBase.java +++ b/src/android/src/org/musikcube/TrackListBase.java @@ -20,6 +20,7 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; +import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; @@ -39,6 +40,9 @@ public class TrackListBase extends ListActivity implements OnQueryResultListener protected java.lang.Object lock = new java.lang.Object(); + protected int position = -1; + protected boolean markPosition = false; + // Need handler for callbacks to the UI thread final Handler callbackHandler = new Handler(); @@ -53,6 +57,7 @@ public class TrackListBase extends ListActivity implements OnQueryResultListener TextView track; TextView title; TextView artist; + ImageView marker; } public class ResultAdapter extends BaseAdapter{ @@ -82,18 +87,29 @@ public class TrackListBase extends ListActivity implements OnQueryResultListener public View getView(int position, View view, ViewGroup parent) { TrackViewHolder holder; if(view==null){ - view = inflator.inflate(R.layout.track_list_item, null); + view = inflator.inflate(TrackListBase.this.trackListItemViewId, null); holder = new TrackViewHolder(); holder.title = (TextView) view.findViewById(R.id.title); holder.track = (TextView) view.findViewById(R.id.track); holder.artist = (TextView) view.findViewById(R.id.artist); + if(TrackListBase.this.markPosition){ + holder.marker = (ImageView) view.findViewById(R.id.PlayingImage); + } view.setTag(holder); }else{ holder = (TrackViewHolder)view.getTag(); } + if(holder.marker!=null){ + if(position==this.trackList.position){ + holder.marker.setImageResource(R.drawable.ic_playing); + }else{ + holder.marker.setImageBitmap(null); + } + } + Track track = this.trackList.GetTrack(position); if(track==null){ holder.track.setText(""); @@ -248,23 +264,11 @@ public class TrackListBase extends ListActivity implements OnQueryResultListener } public boolean onOptionsItemSelected(MenuItem item) { - //Log.i("MC2.onContextItemSelected","item "+item.getItemId()+" "+R.id.context_settings); - switch (item.getItemId()) { - case R.id.context_settings: - startActivity(new Intent(this, org.musikcube.Preferences.class)); - return true; - case R.id.context_browse: - startActivity(new Intent(this, org.musikcube.main.class)); - return true; - case R.id.context_controls: - startActivity(new Intent(this, org.musikcube.PlayerControl.class)); - return true; - case R.id.context_nowplaying: - startActivity(new Intent(this, org.musikcube.NowPlayingList.class)); - return true; - default: - return super.onContextItemSelected(item); - } + if(Helper.DefaultOptionsItemSelected(item,this)){ + return true; + }else{ + return super.onContextItemSelected(item); + } } } diff --git a/src/android/src/org/musikcube/core/IQuery.java b/src/android/src/org/musikcube/core/IQuery.java index 4b49ea9df..862de499c 100644 --- a/src/android/src/org/musikcube/core/IQuery.java +++ b/src/android/src/org/musikcube/core/IQuery.java @@ -24,7 +24,7 @@ public class IQuery extends Object{ protected WriterNode SendQueryNode(WriterNode parentNode) { - WriterNode queryNode = parentNode.ChildNode("query"); + final WriterNode queryNode = parentNode.ChildNode("query"); queryNode.attributes.put("id",Integer.toString(this.id)); queryNode.attributes.put("uid",Integer.toString(this.uid)); queryNode.attributes.put("type",this.type); diff --git a/src/android/src/org/musikcube/core/Library.java b/src/android/src/org/musikcube/core/Library.java index da795c24b..797c89fd2 100644 --- a/src/android/src/org/musikcube/core/Library.java +++ b/src/android/src/org/musikcube/core/Library.java @@ -18,7 +18,7 @@ import org.musikcube.core.IQuery; * @author doy * */ -public class Library implements Runnable{ +public final class Library implements Runnable{ // private String username; // private String password; @@ -67,7 +67,7 @@ public class Library implements Runnable{ public void OnLibraryStatusChange(int status); } - public void SetStatusListener(OnLibraryStatusListener statusListener){ + public final void SetStatusListener(OnLibraryStatusListener statusListener){ synchronized(this.status){ this.statusListener = statusListener; if(this.statusListener!=null){ @@ -76,7 +76,7 @@ public class Library implements Runnable{ } } - private void SetStatus(int status){ + private final void SetStatus(int status){ synchronized(this.status){ //Log.v("mC2::Lib","STATUS "+this.status); this.status = status; @@ -86,19 +86,19 @@ public class Library implements Runnable{ } } - public int GetStatus(){ + public final int GetStatus(){ synchronized(this.status){ return this.status.intValue(); } } - public String GetHost(){ + public final String GetHost(){ synchronized(this.notifier){ return this.host; } } - public void AddPointer(){ + public final void AddPointer(){ synchronized(this.notifier){ this.connections++; @@ -114,7 +114,7 @@ public class Library implements Runnable{ this.notifier.notifyAll(); } } - public void RemovePointer(){ + public final void RemovePointer(){ synchronized(this.notifier){ this.connections--; if(this.connections==0){ @@ -127,7 +127,7 @@ public class Library implements Runnable{ } - public void Startup(Context context){ + public final void Startup(Context context){ this.context = context; // Startup thread when the application sends the context for the first time @@ -136,7 +136,7 @@ public class Library implements Runnable{ this.thread.start(); } - public void Restart(){ + public final void Restart(){ synchronized(this.notifier){ this.running = false; this.restart = true; @@ -152,7 +152,7 @@ public class Library implements Runnable{ } } - public boolean Running(){ + public final boolean Running(){ synchronized(this.notifier){ if(this.running==true){ return true; @@ -161,7 +161,7 @@ public class Library implements Runnable{ return false; } - private class WriterThreadHelper implements Runnable{ + private final class WriterThreadHelper implements Runnable{ private Thread thread; private Library library; public WriterThreadHelper(Library library){ @@ -169,7 +169,7 @@ public class Library implements Runnable{ this.thread = new Thread(this); } - public void Start(){ + public final void Start(){ this.thread.start(); } @@ -183,7 +183,7 @@ public class Library implements Runnable{ protected Library(){ } - public void WaitForAuthroization(){ + public final void WaitForAuthroization(){ // Log.v("Library::WaitForAuthroization","start"); synchronized (notifier) { if(this.authorization.equals("")){ @@ -340,7 +340,7 @@ public class Library implements Runnable{ } } - public void WriteThread(WriterThreadHelper thread){ + public final void WriteThread(WriterThreadHelper thread){ //Log.v("Library::WriteThread","Started"); this.SetStatus(STATUS_AUTHENTICATING); @@ -426,7 +426,7 @@ public class Library implements Runnable{ //Log.v("Library::WriteThread","Ended"); } - public void Exit(){ + public final void Exit(){ synchronized(this.notifier){ this.exit = true; this.running = false; @@ -451,14 +451,14 @@ public class Library implements Runnable{ } } - public void AddQuery(IQuery query){ + public final void AddQuery(IQuery query){ synchronized(this.sendQueryQueue){ this.sendQueryQueue.addLast(query); this.sendQueryQueue.notifyAll(); } } - public String GetTrackURL(int trackId){ + public final String GetTrackURL(int trackId){ synchronized (notifier) { if(this.status==STATUS_CONNECTED){ String trackURL = "http://"+this.host+":"+this.httpPort+"/track/?track_id="+trackId+"&auth_key="+this.authorization; diff --git a/src/android/src/org/musikcube/core/ListQuery.java b/src/android/src/org/musikcube/core/ListQuery.java index 17c2fb95f..a39ff8237 100644 --- a/src/android/src/org/musikcube/core/ListQuery.java +++ b/src/android/src/org/musikcube/core/ListQuery.java @@ -23,7 +23,7 @@ public class ListQuery extends IQuery { public void SendQuery(WriterNode node) throws Exception { - WriterNode queryNode = this.SendQueryNode(node); + final WriterNode queryNode = this.SendQueryNode(node); // List selections WriterNode selectionsNode = queryNode.ChildNode("selections"); @@ -60,8 +60,11 @@ public class ListQuery extends IQuery { ReaderNode mdNode = null; while( (mdNode=childNode.ChildNode("md"))!=null ){ mdNode.End(); - this.resultsInts.add(Integer.parseInt(mdNode.attributes.get("id"))); - this.resultsStrings.add(mdNode.content); + int resultId = Integer.parseInt(mdNode.attributes.get("id")); + if(resultId!=0){ + this.resultsInts.add(resultId); + this.resultsStrings.add(mdNode.content); + } } } }else if(childNode.name.equals("tracklist")){ @@ -85,7 +88,7 @@ public class ListQuery extends IQuery { } } - public void SelectData(String category,int selection){ + public final void SelectData(String category,int selection){ this.selectionStrings.add(category); this.selectionInts.add(selection); } diff --git a/src/android/src/org/musikcube/core/MetadataQuery.java b/src/android/src/org/musikcube/core/MetadataQuery.java index 74ab00fe3..6e8632aa0 100644 --- a/src/android/src/org/musikcube/core/MetadataQuery.java +++ b/src/android/src/org/musikcube/core/MetadataQuery.java @@ -5,9 +5,9 @@ import doep.xml.WriterNode; public class MetadataQuery extends IQuery { - public java.util.ArrayList requestedTracks = new java.util.ArrayList(); - public java.util.ArrayList requestedMetakeys = new java.util.ArrayList(); - public java.util.ArrayList resultTracks = new java.util.ArrayList(); + public final java.util.ArrayList requestedTracks = new java.util.ArrayList(); + public final java.util.ArrayList requestedMetakeys = new java.util.ArrayList(); + public final java.util.ArrayList resultTracks = new java.util.ArrayList(); public MetadataQuery() { super(); diff --git a/src/android/src/org/musikcube/core/PaceDetector.java b/src/android/src/org/musikcube/core/PaceDetector.java index 59814f8e2..b47f2c76d 100644 --- a/src/android/src/org/musikcube/core/PaceDetector.java +++ b/src/android/src/org/musikcube/core/PaceDetector.java @@ -18,7 +18,7 @@ public class PaceDetector implements Runnable, SensorEventListener, OnQueryResul static public float MAX_BPM = 85; static public float MIN_BPM = 40; static public int WAVE_MEMORY = 20; - static public int WAVE_MIN_CALC = 8; + static public int WAVE_MIN_CALC = 10; static public float WAVE_MIN_BPM_DIFF = 120; // This is in miliseconds static public int WAVE_COMPARE = 4; static public int MIN_PLAYTIME = 20000; // Play at leased 20 seconds of a track @@ -53,80 +53,88 @@ public class PaceDetector implements Runnable, SensorEventListener, OnQueryResul } if(this.lastDiff>=0 && diff<0){ - // this is a top on the curve - this.beatTimes.add(android.os.SystemClock.elapsedRealtime()); - this.amplitude.add(this.currentMax-this.currentMin); - - // Reset the amplitude - this.currentMin = value; - this.currentMax = value; - - // only keep the last 10 waves - while(this.beatTimes.size()>WAVE_MEMORY){ - this.beatTimes.remove(0); - this.amplitude.remove(0); - } - - // Lets calculate BPM - long bpmSum = 0; - //java.util.TreeSet bpms = new java.util.TreeSet(); - java.util.ArrayList bpms = new java.util.ArrayList(); - for(int i=0;i(60000/MAX_BPM) && bpmSample<(60000/MIN_BPM)){ - bpms.add(bpmSample); - bpmSum += bpmSample; + + // Amplitude must be at leased 5 + if(this.currentMax-this.currentMin<7){ + this.currentMin = value; + this.currentMax = value; +// Log.v("APM","-- "+(this.currentMax-this.currentMin)); + }else{ +// Log.v("APM","B "+(this.currentMax-this.currentMin)); + // this is a top on the curve + this.beatTimes.add(android.os.SystemClock.elapsedRealtime()); + this.amplitude.add(this.currentMax-this.currentMin); + + // Reset the amplitude + this.currentMin = value; + this.currentMax = value; + // only keep the last 10 waves + while(this.beatTimes.size()>WAVE_MEMORY){ + this.beatTimes.remove(0); + this.amplitude.remove(0); + } + + // Lets calculate BPM + long bpmSum = 0; + //java.util.TreeSet bpms = new java.util.TreeSet(); + java.util.ArrayList bpms = new java.util.ArrayList(); + for(int i=0;i(60000/MAX_BPM) && bpmSample<(60000/MIN_BPM)){ + bpms.add(bpmSample); + bpmSum += bpmSample; + } } } - } - Collections.sort(bpms); - //Log.v("MC2::BEAT","B "+(bpms.size())); - - // Lets remove the most "off" samples and correct the AVG until we are down to the desired "diff" - boolean qualified = false; - long bpmDiff = 0; - - while(!qualified && bpms.size()>=WAVE_MIN_CALC){ - Long first = bpms.get(0); - Long last = bpms.get(bpms.size()-1); - bpmDiff = last-first; - int bpmSize = bpms.size(); - -// Log.v("MC2::DIFF","diff "+bpmSize+" "+first+"-"+last+" diff="+bpmDiff); + Collections.sort(bpms); + //Log.v("MC2::BEAT","B "+(bpms.size())); - if(bpmDifflast-avg){ - // Remove first - bpmSum -= first; - bpms.remove(0); + // Lets remove the most "off" samples and correct the AVG until we are down to the desired "diff" + boolean qualified = false; + long bpmDiff = 0; + + while(!qualified && bpms.size()>=WAVE_MIN_CALC){ + Long first = bpms.get(0); + Long last = bpms.get(bpms.size()-1); + bpmDiff = last-first; + int bpmSize = bpms.size(); + + // Log.v("MC2::DIFF","diff "+bpmSize+" "+first+"-"+last+" diff="+bpmDiff); + + if(bpmDifflast-avg){ + // Remove first + bpmSum -= first; + bpms.remove(0); + }else{ + // Remove last + bpmSum -= last; + bpms.remove(bpms.size()-1); + } } } - } - - if(qualified){ - // Get avg amplitude - float amplitude = this.amplitude.get(0); - for(int i=1;ithis.currentBPMstart+MIN_PLAYTIME){ + //Log.v("BPM","3 "+bpm); // We have played more than minimum time if(bpm>this.currentBPM+BPM_THRESHOLD || bpm nowPlaying = new ArrayList(); private int position = 0; private boolean repeat = false; private boolean shuffle = false; + private int nextShufflePosition = -1; private Library library; @@ -27,6 +30,8 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL public android.app.Service service; + private boolean bpmMode = false; + public void run() { } @@ -44,6 +49,9 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL synchronized(this.lock){ this.nowPlaying = playlist; this.position = position; + if(this.listListener!=null){ + this.listListener.OnTrackListUpdate(); + } } this.Play(); @@ -64,12 +72,13 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL this.nowPlaying = playlist; this.position = position; if(this.nextPlayer!=null){ + this.nextPlayer.SetPrepareListener(null); this.nextPlayer.SetListener(null); this.nextPlayer.Stop(); } this.playWhenPrepared = true; this.nextPlayer = this.PrepareTrack(this.position); - this.nextPlayer.SetListener(this); + this.nextPlayer.SetPrepareListener(this); if(this.listListener!=null){ this.listListener.OnTrackListUpdate(); @@ -182,7 +191,18 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL public void Next(){ synchronized(this.lock){ this.currentTrack = new Track(); - this.position++; + + if(this.shuffle){ + if(this.nextShufflePosition==-1){ + Random rand = new Random(); + this.position = rand.nextInt(this.nowPlaying.size()); + }else{ + this.position = this.nextShufflePosition; + this.nextShufflePosition = -1; + } + }else{ + this.position++; + } if(this.position>=this.nowPlaying.size()){ if(this.repeat){ @@ -205,8 +225,20 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL public void Prev(){ synchronized(this.lock){ - this.currentTrack = new Track(); - this.position--; + this.currentTrack = new Track(); + + if(this.shuffle){ + if(this.nextShufflePosition==-1){ + Random rand = new Random(); + this.position = rand.nextInt(this.nowPlaying.size()); + }else{ + this.position = this.nextShufflePosition; + this.nextShufflePosition = -1; + } + }else{ + this.position--; + } + if(this.position<0){ this.position = 0; } @@ -322,7 +354,17 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL public void OnTrackAlmostDone(TrackPlayer trackPlayer) { synchronized(this.lock){ if(this.nextPlayer==null){ - this.nextPlayer = this.PrepareTrack(this.position+1); + if(this.shuffle){ + if(this.nextShufflePosition==-1){ + Random rand = new Random(); + this.nextShufflePosition = rand.nextInt(this.nowPlaying.size()); + this.nextPlayer = this.PrepareTrack(this.nextShufflePosition); + }else{ + this.nextPlayer = this.PrepareTrack(this.nextShufflePosition); + } + }else{ + this.nextPlayer = this.PrepareTrack(this.position+1); + } } } } @@ -390,4 +432,25 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL } } + public void SetBPMMode(boolean bpmMode){ + synchronized(this.lock){ + this.bpmMode = bpmMode; + } + } + public boolean GetBPMMode(){ + synchronized(this.lock){ + return this.bpmMode; + } + } + + public void RemoveFromList(int position){ + synchronized(this.lock){ + this.nowPlaying.remove(position); + if(this.listListener!=null){ + this.listListener.OnTrackListUpdate(); + } + } + } + + } diff --git a/src/android/src/org/musikcube/core/TrackPlayer.java b/src/android/src/org/musikcube/core/TrackPlayer.java index b04851e7a..d7edca74d 100644 --- a/src/android/src/org/musikcube/core/TrackPlayer.java +++ b/src/android/src/org/musikcube/core/TrackPlayer.java @@ -31,12 +31,12 @@ public class TrackPlayer implements Runnable, MediaPlayer.OnCompletionListener, this.mediaPlayer.setOnBufferingUpdateListener(this); String url = Library.GetInstance().GetTrackURL(this.trackId); - while(url==null && (this.status==STATUS_PREPARED || this.status==STATUS_PLAYING)){ - Log.v("mC2::TrackPlayer","Retrying "+this.trackId); - synchronized(this.lock){ + synchronized(this.lock){ + while(url==null && (this.status==STATUS_PREPARED || this.status==STATUS_PLAYING)){ + Log.v("mC2::TrackPlayer","Retrying "+this.trackId); this.lock.wait(250); + url = Library.GetInstance().GetTrackURL(this.trackId); } - url = Library.GetInstance().GetTrackURL(this.trackId); } if(url==null){ @@ -49,8 +49,8 @@ public class TrackPlayer implements Runnable, MediaPlayer.OnCompletionListener, } synchronized(this.lock){ - if(this.listener!=null){ - this.listener.OnTrackPrepared(this); + if(this.listenerPrepare!=null){ + this.listenerPrepare.OnTrackPrepared(this); } while(this.status==STATUS_PREPARED) @@ -157,18 +157,25 @@ public class TrackPlayer implements Runnable, MediaPlayer.OnCompletionListener, } public interface OnTrackStatusListener{ - public void OnTrackPrepared(TrackPlayer trackPlayer); public void OnTrackStatusUpdate(TrackPlayer trackPlayer,int status); public void OnTrackAlmostDone(TrackPlayer trackPlayer); } + public interface OnTrackPrepareListener{ + public void OnTrackPrepared(TrackPlayer trackPlayer); + } private OnTrackStatusListener listener = null; - public void SetListener(OnTrackStatusListener listener){ synchronized(this.lock){ this.listener = listener; } } + private OnTrackPrepareListener listenerPrepare = null; + public void SetPrepareListener(OnTrackPrepareListener listener){ + synchronized(this.lock){ + this.listenerPrepare = listener; + } + } public void onCompletion(MediaPlayer mp) { synchronized(this.lock){ diff --git a/src/android/src/org/musikcube/main.java b/src/android/src/org/musikcube/main.java index 1ce2636bb..fe6689c9e 100644 --- a/src/android/src/org/musikcube/main.java +++ b/src/android/src/org/musikcube/main.java @@ -70,12 +70,13 @@ public class main extends Activity implements OnLibraryStatusListener { private OnClickListener onBPMClick = new OnClickListener() { public void onClick(View v){ - Intent intent = new Intent(main.this, org.musikcube.Service.class); - intent.putExtra("org.musikcube.Service.action", "bpmstart"); - startService(intent); - - Intent intent2 = new Intent(main.this, PlayerControl.class); +/* Log.v("mC2::main","onBPMClick 1"); + Intent intent2 = new Intent(main.this, PlayerBPMControl.class); startActivity(intent2); + Log.v("mC2::main","onBPMClick 2");*/ + Intent intent = new Intent(main.this, CategorySelect.class); + intent.putExtra("org.musikcube.CategorySelect.listCategory", "genre"); + startActivity(intent); } }; @@ -86,23 +87,11 @@ public class main extends Activity implements OnLibraryStatusListener { } public boolean onOptionsItemSelected(MenuItem item) { - //Log.i("MC2.onContextItemSelected","item "+item.getItemId()+" "+R.id.context_settings); - switch (item.getItemId()) { - case R.id.context_settings: - startActivity(new Intent(this, org.musikcube.Preferences.class)); - return true; - case R.id.context_browse: - startActivity(new Intent(this, org.musikcube.main.class)); - return true; - case R.id.context_controls: - startActivity(new Intent(this, org.musikcube.PlayerControl.class)); - return true; - case R.id.context_nowplaying: - startActivity(new Intent(this, org.musikcube.NowPlayingList.class)); - return true; - default: - return super.onContextItemSelected(item); - } + if(Helper.DefaultOptionsItemSelected(item,this)){ + return true; + }else{ + return super.onContextItemSelected(item); + } } @Override @@ -146,7 +135,7 @@ public class main extends Activity implements OnLibraryStatusListener { genreButton.setEnabled(true); artistsButton.setEnabled(true); yearButton.setEnabled(true); -// bpmButton.setEnabled(true); + bpmButton.setEnabled(false); }else{ genreButton.setEnabled(false); artistsButton.setEnabled(false);