diff --git a/src/android/res/drawable/album.png b/src/android/res/drawable/album.png index 706e4e2a3..5c35542a3 100644 Binary files a/src/android/res/drawable/album.png and b/src/android/res/drawable/album.png differ diff --git a/src/android/res/layout-land/play_control.xml b/src/android/res/layout-land/play_control.xml index 86f2febe8..2b0fc3025 100644 --- a/src/android/res/layout-land/play_control.xml +++ b/src/android/res/layout-land/play_control.xml @@ -9,9 +9,9 @@ - - - + + + @@ -28,5 +28,5 @@ - + \ No newline at end of file diff --git a/src/android/res/layout/category_list.xml b/src/android/res/layout/category_list.xml index cef9bef61..e2c3f0214 100644 --- a/src/android/res/layout/category_list.xml +++ b/src/android/res/layout/category_list.xml @@ -11,9 +11,13 @@ android:layout_height="fill_parent" android:layout_weight="1" android:drawSelectorOnTop="false"/> - - - \ No newline at end of file + + + + + + + diff --git a/src/android/res/layout/main.xml b/src/android/res/layout/main.xml index 6c4855188..80c2b1a5f 100644 --- a/src/android/res/layout/main.xml +++ b/src/android/res/layout/main.xml @@ -13,6 +13,7 @@ + diff --git a/src/android/res/layout/play_control.xml b/src/android/res/layout/play_control.xml index f28727065..5975f1e47 100644 --- a/src/android/res/layout/play_control.xml +++ b/src/android/res/layout/play_control.xml @@ -8,10 +8,10 @@ - + - - + + @@ -27,5 +27,5 @@ - + \ No newline at end of file diff --git a/src/android/res/layout/track_list.xml b/src/android/res/layout/track_list.xml index 2a7fcee05..b5cec97a5 100644 --- a/src/android/res/layout/track_list.xml +++ b/src/android/res/layout/track_list.xml @@ -12,8 +12,12 @@ android:layout_weight="1" android:drawSelectorOnTop="false"/> - + + + + + \ No newline at end of file diff --git a/src/android/src/org/musikcube/CategoryList.java b/src/android/src/org/musikcube/CategoryList.java index aa5fd3a9a..89aaffabb 100644 --- a/src/android/src/org/musikcube/CategoryList.java +++ b/src/android/src/org/musikcube/CategoryList.java @@ -36,7 +36,7 @@ public class CategoryList extends ListActivity implements OnQueryResultListener private ArrayList selectedCategory; private ArrayList selectedCategoryIds; - private ProgressDialog loadingDialog; +// private ProgressDialog loadingDialog; // Need handler for callbacks to the UI thread final Handler callbackHandler = new Handler(); @@ -60,15 +60,28 @@ public class CategoryList extends ListActivity implements OnQueryResultListener } public int getCount() { - return this.query.resultsInts.size(); + int size = this.query.resultsInts.size(); + if(size==0){ + return 0; + }else{ + return size+1; + } } public Object getItem(int position) { - return this.query.resultsInts.get(position); + if(position==0){ + return 0; + }else{ + return this.query.resultsInts.get(position-1); + } } public long getItemId(int position) { - return this.query.resultsInts.get(position); + if(position==0){ + return 0; + }else{ + return this.query.resultsInts.get(position-1); + } } public View getView(int position, View view, ViewGroup parent) { @@ -76,32 +89,17 @@ public class CategoryList extends ListActivity implements OnQueryResultListener view = inflator.inflate(R.layout.category_list_item, null); } - ((TextView) view.findViewById(R.id.text)).setText(this.query.resultsStrings.get(position)); + // + if(position==0){ + ((TextView) view.findViewById(R.id.text)).setText("- All -"); + }else{ + ((TextView) view.findViewById(R.id.text)).setText(this.query.resultsStrings.get(position-1)); + } return view; } } -/* private class CategoryItemView extends LinearLayout { - public CategoryItemView(Context context, String title) { - super(context); - this.setOrientation(VERTICAL); - - mTitle = new TextView(context); - mTitle.setTextSize(22); - mTitle.setText(title); - addView(mTitle, new LinearLayout.LayoutParams( - LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); - - } - - public void SetTitle(String title) { - mTitle.setText(title); - } - - private TextView mTitle; - } - */ private ResultAdapter listAdapter; @Override @@ -151,7 +149,7 @@ public class CategoryList extends ListActivity implements OnQueryResultListener org.musikcube.core.Library library = org.musikcube.core.Library.GetInstance(); - this.loadingDialog = ProgressDialog.show(this, "", "Loading "+this.category+"...", true); + //this.loadingDialog = ProgressDialog.show(this, "", "Loading "+this.category+"...", true); library.AddQuery(this.query); }else{ @@ -165,10 +163,10 @@ public class CategoryList extends ListActivity implements OnQueryResultListener public void OnResults(){ //Log.i("CategoryList::OnResults","In right thread "+this.query.resultsStrings.size()); - if(this.loadingDialog!=null){ + /*if(this.loadingDialog!=null){ this.loadingDialog.dismiss(); this.loadingDialog = null; - } + }*/ this.listAdapter.notifyDataSetChanged(); } @@ -192,8 +190,10 @@ public class CategoryList extends ListActivity implements OnQueryResultListener ArrayList selectedCategory = (ArrayList)this.selectedCategory.clone(); ArrayList selectedCategoryIds = (ArrayList)this.selectedCategoryIds.clone(); - selectedCategory.add(this.category); - selectedCategoryIds.add((int)id); + if(id!=0){ + selectedCategory.add(this.category); + selectedCategoryIds.add((int)id); + } if(this.nextCategoryList.equals("")){ // List tracks diff --git a/src/android/src/org/musikcube/PlayerControl.java b/src/android/src/org/musikcube/PlayerControl.java index fb061e624..46cfecaaa 100644 --- a/src/android/src/org/musikcube/PlayerControl.java +++ b/src/android/src/org/musikcube/PlayerControl.java @@ -28,11 +28,13 @@ import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; -public class PlayerControl extends Activity implements OnTrackUpdateListener, OnQueryResultListener { +public class PlayerControl 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) { @@ -74,56 +76,24 @@ public class PlayerControl extends Activity implements OnTrackUpdateListener, On this.callbackTrackPositionsUpdateHandler.post(this.callbackTrackPositionsUpdateRunnable); } public void OnTrackUpdate() { -/* - synchronized(lock){ - int newTrackId = Player.GetInstance().GetCurrentTrackId(); - if(newTrackId!=this.trackId){ - this.trackId = newTrackId; - this.track = new Track(); - - if(this.trackId!=0){ - MetadataQuery query = new MetadataQuery(); - query.requestedMetakeys.add("title"); - query.requestedMetakeys.add("track"); - query.requestedMetakeys.add("visual_artist"); - query.requestedMetakeys.add("album"); - query.requestedMetakeys.add("year"); - query.requestedMetakeys.add("thumbnail_id"); - query.requestedMetakeys.add("duration"); - query.requestedTracks.add(this.trackId); - query.SetResultListener(this); - Library.GetInstance().AddQuery(query); - } - } - }*/ this.callbackTrackUpdateHandler.post(this.callbackTrackUpdateRunnable); } - - public void OnQueryResults(IQuery query) { -/* MetadataQuery mdQuery = (MetadataQuery)query; - if(!mdQuery.resultTracks.isEmpty()){ - synchronized(lock){ - Track newTrack = mdQuery.resultTracks.get(0); - if(this.trackId==newTrack.id){ - this.track = newTrack; - 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 @@ -189,13 +159,16 @@ public class PlayerControl extends Activity implements OnTrackUpdateListener, On } // 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); + } } } @@ -238,24 +211,26 @@ public class PlayerControl extends Activity implements OnTrackUpdateListener, On }; 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 diff --git a/src/android/src/org/musikcube/Service.java b/src/android/src/org/musikcube/Service.java index ed965c14e..8e96adf71 100644 --- a/src/android/src/org/musikcube/Service.java +++ b/src/android/src/org/musikcube/Service.java @@ -68,6 +68,16 @@ public class Service extends android.app.Service { Player player = Player.GetInstance(); player.Play(intent.getIntegerArrayListExtra("org.musikcube.Service.tracklist"), intent.getIntExtra("org.musikcube.Service.position", 0)); } + if(action.equals("appendlist")){ + Player player = Player.GetInstance(); + player.Append(intent.getIntegerArrayListExtra("org.musikcube.Service.tracklist")); + } + + if(action.equals("playlist_prepare")){ + Player player = Player.GetInstance(); + player.PlayWhenPrepared(intent.getIntegerArrayListExtra("org.musikcube.Service.tracklist"), intent.getIntExtra("org.musikcube.Service.position", 0)); + } + if(action.equals("next")){ Player player = Player.GetInstance(); player.Next(); @@ -90,6 +100,11 @@ public class Service extends android.app.Service { this.paceDetector.StartSensor(this); } } + if(action.equals("bpmstop")){ + if(this.paceDetector!=null){ + this.paceDetector.StopSensor(this); + } + } if(action.equals("player_start")){ Track track = Player.GetInstance().GetCurrentTrack(); diff --git a/src/android/src/org/musikcube/TrackList.java b/src/android/src/org/musikcube/TrackList.java index 884e4c417..0706feddc 100644 --- a/src/android/src/org/musikcube/TrackList.java +++ b/src/android/src/org/musikcube/TrackList.java @@ -16,15 +16,18 @@ import android.app.ListActivity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; +import android.view.ContextMenu; 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.view.ContextMenu.ContextMenuInfo; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; +import android.widget.AdapterView.AdapterContextMenuInfo; /** * @author doy @@ -41,6 +44,11 @@ public class TrackList extends ListActivity implements OnQueryResultListener { private java.lang.Object lock = new java.lang.Object(); + final static public int PLAY_THIS_ID = 0; + final static public int ADD_THIS_ID = 1; + final static public int ADD_ALL_ID = 2; + + // Need handler for callbacks to the UI thread final Handler callbackHandler = new Handler(); @@ -164,6 +172,8 @@ public class TrackList extends ListActivity implements OnQueryResultListener { this.query.listTracks = true; library.AddQuery(this.query); + + this.registerForContextMenu(this.getListView()); } public void OnResults(){ @@ -294,5 +304,46 @@ public class TrackList extends ListActivity implements OnQueryResultListener { return super.onContextItemSelected(item); } } + + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); + java.util.ArrayList trackList = new java.util.ArrayList(); + Intent intent = new Intent(this, org.musikcube.Service.class); + intent.putExtra("org.musikcube.Service.position", 0); + switch (item.getItemId()) { + case PLAY_THIS_ID: + trackList.add((int)info.id); + intent.putExtra("org.musikcube.Service.tracklist", trackList); + intent.putExtra("org.musikcube.Service.action", "playlist"); + startService(intent); + Intent intent2 = new Intent(this, PlayerControl.class); + startActivity(intent2); + return true; + case ADD_THIS_ID: + trackList.add((int)info.id); + intent.putExtra("org.musikcube.Service.tracklist", trackList); + intent.putExtra("org.musikcube.Service.action", "appendlist"); + startService(intent); + return true; + case ADD_ALL_ID: + trackList.add((int)info.id); + intent.putExtra("org.musikcube.Service.tracklist", this.trackList); + intent.putExtra("org.musikcube.Service.action", "appendlist"); + startService(intent); + return true; + default: + return super.onContextItemSelected(item); + } + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + 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"); + } } diff --git a/src/android/src/org/musikcube/core/BPMQuery.java b/src/android/src/org/musikcube/core/BPMQuery.java new file mode 100644 index 000000000..0e368ba49 --- /dev/null +++ b/src/android/src/org/musikcube/core/BPMQuery.java @@ -0,0 +1,47 @@ +package org.musikcube.core; + +import doep.xml.WriterNode; + +public class BPMQuery extends ListQuery { + + public float queryForBPM = 0; + + public BPMQuery() { + super(); + this.type = "BPMQuery"; + this.listTracks = true; + } + + + @Override + public void SendQuery(WriterNode node) + throws Exception + { + WriterNode queryNode = this.SendQueryNode(node); + + // List selections + WriterNode selectionsNode = queryNode.ChildNode("selections"); + int selectionCount = this.selectionStrings.size(); + for(int i=0;i selectionStrings = new java.util.ArrayList(); - private java.util.ArrayList selectionInts = new java.util.ArrayList(); + protected java.util.ArrayList selectionStrings = new java.util.ArrayList(); + protected java.util.ArrayList selectionInts = new java.util.ArrayList(); public java.util.ArrayList resultsStrings = new java.util.ArrayList(); public java.util.ArrayList resultsInts = new java.util.ArrayList(); public java.util.ArrayList trackList = new java.util.ArrayList(); diff --git a/src/android/src/org/musikcube/core/PaceDetector.java b/src/android/src/org/musikcube/core/PaceDetector.java index 208b9d7d8..59814f8e2 100644 --- a/src/android/src/org/musikcube/core/PaceDetector.java +++ b/src/android/src/org/musikcube/core/PaceDetector.java @@ -1,6 +1,11 @@ package org.musikcube.core; +import java.util.Collections; + +import org.musikcube.core.IQuery.OnQueryResultListener; + import android.content.Context; +import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -8,18 +13,23 @@ import android.hardware.SensorManager; import android.util.Log; -public class PaceDetector implements Runnable, SensorEventListener { +public class PaceDetector implements Runnable, SensorEventListener, OnQueryResultListener { - static public float MAX_BPM = 165; - static public float MIN_BPM = 80; - static public int WAVE_MEMORY = 12; - static public int WAVE_MIN_CALC = 6; - static public float WAVE_MIN_BPM_DIFF = 80; // This is in miliseconds - static public int WAVE_COMPARE = 3; + 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 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 + static public float BPM_THRESHOLD = 10; // if BPM is off by more than 10 bpm, switch track private float currentBPM = 0; private float currentAccuracy = 0; - + private long currentBPMstart = 0; + + private Context context; + private class PaceDimension{ public java.util.ArrayList beatTimes = new java.util.ArrayList(); public java.util.ArrayList amplitude = new java.util.ArrayList(); @@ -59,26 +69,30 @@ public class PaceDetector implements Runnable, SensorEventListener { // Lets calculate BPM long bpmSum = 0; - java.util.TreeSet bpms = new java.util.TreeSet(); + //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.first(); - Long last = bpms.last(); + Long first = bpms.get(0); + Long last = bpms.get(bpms.size()-1); bpmDiff = last-first; int bpmSize = bpms.size(); @@ -92,11 +106,11 @@ public class PaceDetector implements Runnable, SensorEventListener { if(avg-first>last-avg){ // Remove first bpmSum -= first; - bpms.remove(first); + bpms.remove(0); }else{ // Remove last bpmSum -= last; - bpms.remove(last); + bpms.remove(bpms.size()-1); } } } @@ -110,7 +124,7 @@ public class PaceDetector implements Runnable, SensorEventListener { amplitude /= this.amplitude.size(); - this.currentBPM = (60000*bpms.size())/bpmSum; + this.currentBPM = ((float)60000*bpms.size())/((float)bpmSum); this.currentAccuracy = (100-bpmDiff)+bpms.size()*13+amplitude*5; PaceDetector.this.ChangeBPM(this.currentBPM,this.currentAccuracy); } @@ -134,20 +148,35 @@ public class PaceDetector implements Runnable, SensorEventListener { this.xAxis.NextValue(values[0]); this.yAxis.NextValue(values[1]); this.zAxis.NextValue(values[2]); -// Log.v("MC2::TJOOOO",""+values[0]); - -// Log.v("mC2::ACC","x="+values[0]+" y="+values[1]+" z="+values[2]); } public void ChangeBPM(float bpm,float accuracy){ - if(accuracy>=this.xAxis.currentAccuracy || accuracy>=this.yAxis.currentAccuracy || accuracy>=this.zAxis.currentAccuracy){ - this.currentBPM = bpm; - this.currentAccuracy = accuracy; - Log.v("MC2::BPM","bpm="+bpm+" "+accuracy); + bpm *= 2; // BPM should be the double + + if(accuracy>=this.xAxis.currentAccuracy && accuracy>=this.yAxis.currentAccuracy && accuracy>=this.zAxis.currentAccuracy && accuracy>150){ + // The BPM has changed + + long currentTime = android.os.SystemClock.elapsedRealtime(); + if(currentTime>this.currentBPMstart+MIN_PLAYTIME){ + // We have played more than minimum time + + if(bpm>this.currentBPM+BPM_THRESHOLD || bpm playlist){ + + synchronized(this.lock){ + this.nowPlaying.addAll(playlist); + } + } + + public void PlayWhenPrepared(java.util.ArrayList playlist,int position){ + synchronized(this.lock){ + this.nowPlaying = playlist; + this.position = position; + if(this.nextPlayer!=null){ + this.nextPlayer.SetListener(null); + this.nextPlayer.Stop(); + } + this.playWhenPrepared = true; + this.nextPlayer = this.PrepareTrack(this.position); + this.nextPlayer.SetListener(this); + } + } private TrackPlayer PrepareTrack(int position){ synchronized(this.lock){ @@ -70,7 +93,7 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL this.nextPlayer = null; }else{ // Something wrong here, not the prepared track - this.nextPlayer.listener = null; + this.nextPlayer.SetListener(null); this.nextPlayer.Stop(); this.nextPlayer = null; } @@ -80,7 +103,7 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL } this.playingTracks.add(newPlayer); this.currentPlayer = newPlayer; - newPlayer.listener = this; + newPlayer.SetListener(this); newPlayer.Play(); if(this.listener!=null){ @@ -114,7 +137,9 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL public void OnTrackBufferUpdate(int percent); public void OnTrackPositionUpdate(int secondsPlayed); } + protected OnTrackUpdateListener listener = null; + public void SetUpdateListener(OnTrackUpdateListener listener){ synchronized(this.lock){ this.listener = listener; @@ -144,7 +169,7 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL synchronized(this.lock){ this.StopAllTracks(); if(this.nextPlayer!=null){ - this.nextPlayer.listener = null; + this.nextPlayer.SetListener(null); this.nextPlayer.Stop(); this.nextPlayer = null; } @@ -157,7 +182,7 @@ public class Player implements TrackPlayer.OnTrackStatusListener, OnQueryResultL this.currentPlayer = null; int trackCount = this.playingTracks.size(); for(int i=0;i