diff --git a/app/build.gradle b/app/build.gradle index 89655e7f6..df03a0c71 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,11 +8,11 @@ android { } defaultConfig { applicationId "net.programmierecke.radiodroid2" - minSdkVersion 9 + minSdkVersion 14 targetSdkVersion 23 - versionCode 42 - versionName "0.32" + versionCode 46 + versionName "0.36" } buildTypes { release { @@ -27,10 +27,13 @@ android { } dependencies { - compile 'com.android.support:support-v4:25.3.1' - compile 'com.android.support:appcompat-v7:25.3.1' - compile 'com.android.support:design:25.3.1' - compile 'com.android.support:preference-v7:25.3.1' + compile 'com.android.support:support-v4:25.2.0' + compile 'com.android.support:appcompat-v7:25.2.0' + compile 'com.android.support:design:25.2.0' + compile 'com.android.support:preference-v7:25.2.0' + compile 'com.android.support:mediarouter-v7:25.2.0' compile 'com.google.code.gson:gson:2.7' compile 'com.google.android.exoplayer:exoplayer:r2.3.1' + compile 'com.google.android.gms:play-services-cast:11.0.1' + compile 'com.google.android.gms:play-services-cast-framework:11.0.1' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a2d4062d5..13436527a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -45,5 +45,11 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/net/programmierecke/radiodroid2/ActivityMain.java b/app/src/main/java/net/programmierecke/radiodroid2/ActivityMain.java index f6c337fcd..1f4e36e6a 100644 --- a/app/src/main/java/net/programmierecke/radiodroid2/ActivityMain.java +++ b/app/src/main/java/net/programmierecke/radiodroid2/ActivityMain.java @@ -4,6 +4,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; +import android.os.Handler; import android.support.design.widget.NavigationView; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; @@ -21,6 +22,12 @@ import android.view.MenuItem; import android.widget.Toast; +import com.google.android.gms.cast.framework.CastButtonFactory; +import com.google.android.gms.cast.framework.CastContext; +import com.google.android.gms.cast.framework.Session; +import com.google.android.gms.cast.framework.SessionManager; +import com.google.android.gms.cast.framework.SessionManagerListener; + import net.programmierecke.radiodroid2.interfaces.IFragmentRefreshable; import net.programmierecke.radiodroid2.interfaces.IFragmentSearchable; @@ -28,7 +35,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -public class ActivityMain extends AppCompatActivity implements SearchView.OnQueryTextListener { +public class ActivityMain extends AppCompatActivity implements SearchView.OnQueryTextListener, IMPDClientStatusChange { private SearchView mSearchView; private static final String TAG = "RadioDroid"; @@ -45,13 +52,82 @@ public class ActivityMain extends AppCompatActivity implements SearchView.OnQuer MenuItem menuItemRefresh; private SharedPreferences sharedPref; - - @Override + private CastContext mCastContext; + private MenuItem mediaRouteMenuItem; + private SessionManager mSessionManager; + + private final SessionManagerListener mSessionManagerListener = + new SessionManagerListenerImpl(); + private MenuItem menuItemMPDOK; + private MenuItem menuItemMPDNok; + + @Override + public void changed() { + Handler mainHandler = new Handler(getMainLooper()); + Runnable myRunnable = new Runnable() { + @Override + public void run() { + invalidateOptionsMenu(); + } + }; + mainHandler.post(myRunnable); + } + + private class SessionManagerListenerImpl implements SessionManagerListener { + @Override + public void onSessionStarting(Session session) { + + } + + @Override + public void onSessionStarted(Session session, String sessionId) { + invalidateOptionsMenu(); + Utils.mCastSession = mSessionManager.getCurrentCastSession(); + } + + @Override + public void onSessionStartFailed(Session session, int i) { + + } + + @Override + public void onSessionEnding(Session session) { + } + + @Override + public void onSessionResumed(Session session, boolean wasSuspended) { + invalidateOptionsMenu(); + Utils.mCastSession = mSessionManager.getCurrentCastSession(); + } + + @Override + public void onSessionResumeFailed(Session session, int i) { + Utils.mCastSession = null; + } + + @Override + public void onSessionSuspended(Session session, int i) { + Utils.mCastSession = null; + } + + @Override + public void onSessionEnded(Session session, int error) { + Utils.mCastSession = null; + } + + @Override + public void onSessionResuming(Session session, String s) { + + } + } + + + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_main); - try { + try { File dir = new File(getFilesDir().getAbsolutePath()); if (dir.isDirectory()) { String[] children = dir.list(); @@ -159,7 +235,12 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { ActionBarDrawerToggle mDrawerToggle = new ActionBarDrawerToggle(this,mDrawerLayout, R.string.app_name,R.string.app_name); mDrawerLayout.addDrawerListener(mDrawerToggle); mDrawerToggle.syncState(); - } + + mCastContext = CastContext.getSharedInstance(this); + mSessionManager = mCastContext.getSessionManager(); + + MPDClient.StartDiscovery(this, this); + } @Override public void onRequestPermissionsResult(int requestCode, @@ -186,15 +267,21 @@ public void onRequestPermissionsResult(int requestCode, public void onDestroy() { super.onDestroy(); PlayerServiceUtil.unBind(this); + MPDClient.StopDiscovery(); } @Override protected void onPause() { + Log.i(TAG,"PAUSED"); super.onPause(); - } + mSessionManager.removeSessionManagerListener(mSessionManagerListener); + Utils.mCastSession = null; + MPDClient.StopDiscovery(); + } @Override public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); @@ -203,6 +290,8 @@ public boolean onCreateOptionsMenu(Menu menu) { mSearchView.setOnQueryTextListener(this); menuItemRefresh = menu.findItem(R.id.action_refresh); + menuItemMPDNok = menu.findItem(R.id.action_mpd_nok); + menuItemMPDOK = menu.findItem(R.id.action_mpd_ok); if (fragSearchable == null) { menuItemSearch.setVisible(false); @@ -212,7 +301,14 @@ public boolean onCreateOptionsMenu(Menu menu) { menuItemRefresh.setVisible(false); } - return true; + menuItemMPDOK.setVisible(MPDClient.Discovered() && MPDClient.Connected()); + menuItemMPDNok.setVisible(MPDClient.Discovered() && !MPDClient.Connected()); + + mediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), + menu, + R.id.media_route_menu_item); + + return true; } @Override @@ -226,6 +322,12 @@ public boolean onOptionsItemSelected(MenuItem item) { fragRefreshable.Refresh(); } return true; + case R.id.action_mpd_nok: + MPDClient.Connect(this); + return true; + case R.id.action_mpd_ok: + MPDClient.Disconnect(this, this); + return true; } return super.onOptionsItemSelected(item); } @@ -255,7 +357,13 @@ protected void onResume() { mFragmentTransaction = mFragmentManager.beginTransaction(); mFragmentTransaction.replace(R.id.containerView,first).commit(); - } + + Utils.mCastSession = mSessionManager.getCurrentCastSession(); + mSessionManager.addSessionManagerListener(mSessionManagerListener); + + Log.i(TAG,"RESUMED"); + MPDClient.StartDiscovery(this, this); + } public void Search(String query){ if (fragSearchable != null) { diff --git a/app/src/main/java/net/programmierecke/radiodroid2/CastOptionsProvider.java b/app/src/main/java/net/programmierecke/radiodroid2/CastOptionsProvider.java new file mode 100644 index 000000000..ab07d0bf5 --- /dev/null +++ b/app/src/main/java/net/programmierecke/radiodroid2/CastOptionsProvider.java @@ -0,0 +1,24 @@ +package net.programmierecke.radiodroid2; + +import android.content.Context; + +import com.google.android.gms.cast.framework.CastOptions; +import com.google.android.gms.cast.framework.OptionsProvider; +import com.google.android.gms.cast.framework.SessionProvider; + +import java.util.List; + +public class CastOptionsProvider implements OptionsProvider { + + @Override + public CastOptions getCastOptions(Context context) { + return new CastOptions.Builder() + .setReceiverApplicationId(context.getString(R.string.app_id)) + .build(); + } + + @Override + public List getAdditionalSessionProviders(Context context) { + return null; + } +} diff --git a/app/src/main/java/net/programmierecke/radiodroid2/MPDClient.java b/app/src/main/java/net/programmierecke/radiodroid2/MPDClient.java new file mode 100644 index 000000000..06b09423d --- /dev/null +++ b/app/src/main/java/net/programmierecke/radiodroid2/MPDClient.java @@ -0,0 +1,214 @@ +package net.programmierecke.radiodroid2; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.support.v7.preference.PreferenceManager; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.Socket; + +interface IMPDClientStatusChange{ + void changed(); +} + +public class MPDClient { + static final String TAG = "MPD"; + private static boolean connected; + + public static int StringToInt(String str, int defaultValue){ + try{ + return Integer.parseInt(str); + } + catch (Exception e){ + } + return defaultValue; + } + + public static void Connect(IMPDClientStatusChange listener){ + connected = true; + listener.changed(); + } + + public static void Disconnect(Context context, IMPDClientStatusChange listener){ + connected = false; + listener.changed(); + Stop(context); + } + + public static void Play(final String url, final Context context) { + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + final String mpd_hostname = sharedPref.getString("mpd_hostname", null); + final int mpd_port = StringToInt(sharedPref.getString("mpd_port", "6600"), 6600); + + new AsyncTask() { + @Override + protected Boolean doInBackground(Void... params) { + return PlayRemoteMPD(mpd_hostname, mpd_port, url); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + } + }.execute(); + } + + static boolean discovered = false; + static Thread t = null; + static boolean discoveryActive = false; + + public static boolean Discovered(){ + return discovered; + } + + public static void StartDiscovery(final Context context, final IMPDClientStatusChange listener){ + final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + + if (t == null) { + discoveryActive = true; + t = new Thread(new Runnable() { + @Override + public void run() { + while(discoveryActive){ + try { + final String mpd_hostname = sharedPref.getString("mpd_hostname", "").trim(); + final int mpd_port = StringToInt(sharedPref.getString("mpd_port", "6600"), 6600); + + if (mpd_hostname != ""){ + SetDiscoveredStatus(CheckConnection(mpd_hostname, mpd_port), listener); + } + // check every 5 seconds + Thread.sleep(5*1000); + } catch (Exception e) { + SetDiscoveredStatus(false, listener); + } + } + SetDiscoveredStatus(false, listener); + t = null; + } + }); + t.start(); + } + } + + private static void SetDiscoveredStatus(boolean status, IMPDClientStatusChange listener){ + if (status != discovered){ + discovered = status; + listener.changed(); + } + } + + private static boolean CheckConnection(String mpd_hostname, int mpd_port) { + Boolean result = false; + + try { + Log.i(TAG, "Check connection..."); + Socket s = new Socket(mpd_hostname, mpd_port); + BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream())); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); + String info = reader.readLine(); + Log.i(TAG, info); + if (info.startsWith("OK")){ + result = true; + } + reader.close(); + writer.close(); + s.close(); + } catch (Exception e) { + Log.e(TAG,e.toString()); + } + Log.i(TAG, "Connection status:"+result); + return result; + } + + + public static void Stop(Context context) { + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + final String mpd_hostname = sharedPref.getString("mpd_hostname", "").trim(); + final int mpd_port = StringToInt(sharedPref.getString("mpd_port", "6600"), 6600); + new Thread(new Runnable() { + @Override + public void run() { + StopInternal(mpd_hostname, mpd_port); + } + }).start(); + } + + private static boolean StopInternal(String mpd_hostname, int mpd_port) { + Boolean result = false; + + try { + Log.i(TAG, "Check connection..."); + Socket s = new Socket(mpd_hostname, mpd_port); + BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream())); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); + String info = reader.readLine(); + Log.i(TAG, info); + if (info.startsWith("OK")) { + String cmd = "stop"; + writer.write(cmd); + writer.newLine(); + writer.flush(); + result = true; + } + reader.close(); + writer.close(); + s.close(); + } catch (Exception e) { + Log.e(TAG,e.toString()); + } + Log.i(TAG, "Connection status:"+result); + return result; + } + + public static void StopDiscovery(){ + discoveryActive = false; + discovered = false; + t = null; + } + + private static Boolean PlayRemoteMPD(String mpd_hostname, int mpd_port, String url){ + Boolean result = false; + try { + Log.i("MPD", "Start"); + Socket s = new Socket(mpd_hostname, mpd_port); + BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream())); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); + String info = reader.readLine(); + Log.i("MPD", info); + if (info.startsWith("OK")){ + String cmd = "addid "+url; + writer.write(cmd); + writer.newLine(); + writer.flush(); + + info = reader.readLine(); + Log.i("MPD", info); + if (info.startsWith("Id:")){ + int songid = Integer.parseInt(info.substring(3).trim()); + cmd = "playid "+songid; + writer.write(cmd); + writer.newLine(); + writer.flush(); + Log.i("MPD", "OK"); + result = true; + } + } + reader.close(); + writer.close(); + s.close(); + } catch (Exception e) { + Log.e("MPD",e.toString()); + } + return result; + } + + public static boolean Connected() { + return connected; + } +} diff --git a/app/src/main/java/net/programmierecke/radiodroid2/PlayerService.java b/app/src/main/java/net/programmierecke/radiodroid2/PlayerService.java index 3875b98bd..2d6641599 100644 --- a/app/src/main/java/net/programmierecke/radiodroid2/PlayerService.java +++ b/app/src/main/java/net/programmierecke/radiodroid2/PlayerService.java @@ -80,6 +80,7 @@ public class PlayerService extends Service implements IStreamProxyEventReceiver private WifiManager.WifiLock wifiLock; private boolean isHls = false; boolean useExo = false; + private boolean isAlarm = false; enum PlayStatus{ Idle, @@ -384,6 +385,7 @@ public void onAudioFocusChange(int focusChange) { public void ReplayCurrent(final boolean isAlarm) { liveInfo = null; streamInfo = null; + this.isAlarm = isAlarm; SetPlayStatus(PlayStatus.Idle); if (wakeLock == null) { @@ -417,9 +419,9 @@ public void ReplayCurrent(final boolean isAlarm) { useExo = false; } - new Thread(new Runnable() { + /*new Thread(new Runnable() { @Override - public void run() { + public void run() {*/ if (proxy != null){ Log.i(TAG,"stop old proxy"); proxy.stop(); @@ -428,77 +430,10 @@ public void run() { SetPlayStatus(PlayStatus.CreateProxy); proxy = new StreamProxy(PlayerService.this, itsStationURL, PlayerService.this); - String proxyConnection = proxy.getLocalAdress(); - Log.v(TAG, "Stream url:" + proxyConnection); - SetPlayStatus(PlayStatus.ClearOld); - - if (useExo){ - if (player != null){ - player.stop(); - } - - Looper.prepare(); - - if (player == null){ - // 1. Create a default TrackSelector - //Handler mainHandler = new Handler(); - DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); - TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); - TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); - - // 2. Create a default LoadControl - LoadControl loadControl = new DefaultLoadControl(); - - // 3. Create the player - player = ExoPlayerFactory.newSimpleInstance(itsContext, trackSelector, loadControl); - player.setAudioStreamType(isAlarm ? AudioManager.STREAM_ALARM : AudioManager.STREAM_MUSIC); - } - // Produces DataSource instances through which media data is loaded. - DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(itsContext, Util.getUserAgent(itsContext, "yourApplicationName"), null); - // Produces Extractor instances for parsing the media data. - ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory(); - // This is the MediaSource representing the media to be played. - MediaSource videoSource = null; - videoSource = new ExtractorMediaSource(Uri.parse(proxyConnection), dataSourceFactory, extractorsFactory, null, null); - player.prepare(videoSource); - player.setPlayWhenReady(true); - - SetPlayStatus(PlayStatus.Playing); - - Looper.loop(); - }else - { - if (itsMediaPlayer == null) { - itsMediaPlayer = new MediaPlayer(); - } - if (itsMediaPlayer.isPlaying()) { - itsMediaPlayer.stop(); - itsMediaPlayer.reset(); - } - try { - SetPlayStatus(PlayStatus.PrepareStream); - itsMediaPlayer.setAudioStreamType(isAlarm ? AudioManager.STREAM_ALARM : AudioManager.STREAM_MUSIC); - itsMediaPlayer.setDataSource(proxyConnection); - itsMediaPlayer.prepare(); - SetPlayStatus(PlayStatus.PrePlaying); - itsMediaPlayer.start(); - SetPlayStatus(PlayStatus.Playing); - } catch (IllegalArgumentException e) { - Log.e(TAG, "" + e); - ToastOnUi(R.string.error_stream_url); - Stop(); - } catch (IOException e) { - Log.e(TAG, "" + e); - ToastOnUi(R.string.error_caching_stream); - Stop(); - } catch (Exception e) { - Log.e(TAG, "" + e); - ToastOnUi(R.string.error_play_stream); - Stop(); - } - } + /* } }).start(); + */ } void ToastOnUi(final int messageId){ @@ -581,6 +516,83 @@ public void foundLiveStreamInfo(Map liveInfo) { UpdateNotification(); } + @Override + public void streamCreated(final String proxyConnection) { + new Thread(new Runnable() { + @Override + public void run() { + Log.v(TAG, "Stream url:" + proxyConnection); + SetPlayStatus(PlayStatus.ClearOld); + + if (useExo){ + if (player != null){ + player.stop(); + } + + Looper.prepare(); + + if (player == null){ + // 1. Create a default TrackSelector + //Handler mainHandler = new Handler(); + DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); + TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); + TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); + + // 2. Create a default LoadControl + LoadControl loadControl = new DefaultLoadControl(); + + // 3. Create the player + player = ExoPlayerFactory.newSimpleInstance(itsContext, trackSelector, loadControl); + player.setAudioStreamType(isAlarm ? AudioManager.STREAM_ALARM : AudioManager.STREAM_MUSIC); + } + // Produces DataSource instances through which media data is loaded. + DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(itsContext, Util.getUserAgent(itsContext, "yourApplicationName"), null); + // Produces Extractor instances for parsing the media data. + ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory(); + // This is the MediaSource representing the media to be played. + MediaSource videoSource = null; + videoSource = new ExtractorMediaSource(Uri.parse(proxyConnection), dataSourceFactory, extractorsFactory, null, null); + player.prepare(videoSource); + player.setPlayWhenReady(true); + + SetPlayStatus(PlayStatus.Playing); + + Looper.loop(); + }else + { + if (itsMediaPlayer == null) { + itsMediaPlayer = new MediaPlayer(); + } + if (itsMediaPlayer.isPlaying()) { + itsMediaPlayer.stop(); + itsMediaPlayer.reset(); + } + try { + SetPlayStatus(PlayStatus.PrepareStream); + itsMediaPlayer.setAudioStreamType(isAlarm ? AudioManager.STREAM_ALARM : AudioManager.STREAM_MUSIC); + itsMediaPlayer.setDataSource(proxyConnection); + itsMediaPlayer.prepare(); + SetPlayStatus(PlayStatus.PrePlaying); + itsMediaPlayer.start(); + SetPlayStatus(PlayStatus.Playing); + } catch (IllegalArgumentException e) { + Log.e(TAG, "" + e); + ToastOnUi(R.string.error_stream_url); + Stop(); + } catch (IOException e) { + Log.e(TAG, "" + e); + ToastOnUi(R.string.error_caching_stream); + Stop(); + } catch (Exception e) { + Log.e(TAG, "" + e); + ToastOnUi(R.string.error_play_stream); + Stop(); + } + } + } + }).start(); + } + @Override public void streamStopped() { Stop(); diff --git a/app/src/main/java/net/programmierecke/radiodroid2/StreamProxy.java b/app/src/main/java/net/programmierecke/radiodroid2/StreamProxy.java index 14d36a42a..59f767545 100644 --- a/app/src/main/java/net/programmierecke/radiodroid2/StreamProxy.java +++ b/app/src/main/java/net/programmierecke/radiodroid2/StreamProxy.java @@ -49,6 +49,7 @@ public StreamProxy(Context context, String uri, IStreamProxyEventReceiver callba private void createProxy() { Log.i(TAG,"thread started"); + /* ServerSocket proxyServer = null; try { proxyServer = new ServerSocket(0, 1, InetAddress.getLocalHost()); @@ -57,34 +58,22 @@ private void createProxy() { } catch (IOException e) { Log.e(TAG,"createProxy() create server socket: "+e); } + */ - if (proxyServer != null) { - final ServerSocket finalProxyServer = proxyServer; + //if (proxyServer != null) { +// final ServerSocket finalProxyServer = proxyServer; new Thread(new Runnable() { @Override public void run() { try { - Log.i(TAG, "waiting.."); - socketProxy = finalProxyServer.accept(); - finalProxyServer.close(); - doConnectToStream(); - Log.i(TAG, "createProxy() ended"); - } catch (IOException e) { + } catch (Exception e) { Log.e(TAG, "" + e); } } }).start(); - - while (localAdress == null) { - try { - Log.i(TAG, "starting serversock..."); - Thread.sleep(100); - } catch (Exception e) { - } - } - } + //} } InputStream in; @@ -272,19 +261,29 @@ private void hlsStream(URL path, int size, InputStream inM3U) throws Exception{ private void doConnectToStream() { try{ - final int MaxRetries = 30; + final int MaxRetries = 100; int retry = MaxRetries; while (!isStopped && retry > 0) { try { // connect to stream - Log.i(TAG,"doConnectToStream:"+uri); + Log.i(TAG,"doConnectToStream (try="+retry+"):"+uri); URL u = new URL(uri); URLConnection connection = u.openConnection(); - connection.setConnectTimeout(5000); - connection.setReadTimeout(10000); + connection.setConnectTimeout(2000); + connection.setReadTimeout(2000); connection.setRequestProperty("Icy-MetaData", "1"); connection.connect(); + Log.i(TAG, "create serversocket.."); + ServerSocket proxyServer = null; + proxyServer = new ServerSocket(0, 1, InetAddress.getLocalHost()); + int port = proxyServer.getLocalPort(); + localAdress = String.format(Locale.US,"http://localhost:%d",port); + Log.i(TAG, "waiting.."); + callback.streamCreated(localAdress); + socketProxy = proxyServer.accept(); + proxyServer.close(); + // send ok message to local mediaplayer out = socketProxy.getOutputStream(); out.write(("HTTP/1.0 200 OK\r\n" + diff --git a/app/src/main/java/net/programmierecke/radiodroid2/Utils.java b/app/src/main/java/net/programmierecke/radiodroid2/Utils.java index 8074fd4b0..7d35f3b67 100644 --- a/app/src/main/java/net/programmierecke/radiodroid2/Utils.java +++ b/app/src/main/java/net/programmierecke/radiodroid2/Utils.java @@ -12,13 +12,17 @@ import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.preference.PreferenceManager; -import android.util.Base64; import android.util.Log; import android.widget.Toast; +import com.google.android.gms.cast.MediaInfo; +import com.google.android.gms.cast.MediaMetadata; +import com.google.android.gms.cast.framework.CastSession; +import com.google.android.gms.cast.framework.media.RemoteMediaClient; +import com.google.android.gms.common.images.WebImage; + import net.programmierecke.radiodroid2.data.DataRadioStation; -import org.json.JSONArray; import org.json.JSONObject; import java.io.BufferedReader; @@ -31,13 +35,13 @@ import java.net.HttpURLConnection; import java.net.URL; import java.util.Date; -import java.util.Dictionary; -import java.util.Enumeration; import java.util.Locale; import java.util.Map; public class Utils { - public static String getCacheFile(Context ctx, String theURI) { + public static CastSession mCastSession; + + public static String getCacheFile(Context ctx, String theURI) { StringBuffer chaine = new StringBuffer(""); try{ String aFileName = theURI.toLowerCase().replace("http://",""); @@ -180,12 +184,24 @@ protected void onPostExecute(String result) { itsProgressLoading.dismiss(); if (result != null) { - if (external){ - Intent share = new Intent(Intent.ACTION_VIEW); - share.setDataAndType(Uri.parse(result), "audio/*"); - context.startActivity(share); - }else { - PlayerServiceUtil.play(result, station.Name, station.ID, true); + boolean externalActive = false; + if (MPDClient.Connected() && MPDClient.Discovered()){ + MPDClient.Play(result, context); + externalActive = true; + } + if (mCastSession != null){ + PlayRemote(station.Name, result, station.IconUrl); + externalActive = true; + } + + if (!externalActive){ + if (external){ + Intent share = new Intent(Intent.ACTION_VIEW); + share.setDataAndType(Uri.parse(result), "audio/*"); + context.startActivity(share); + }else { + PlayerServiceUtil.play(result, station.Name, station.ID, true); + } } } else { Toast toast = Toast.makeText(context.getApplicationContext(), context.getResources().getText(R.string.error_station_load), Toast.LENGTH_SHORT); @@ -196,6 +212,25 @@ protected void onPostExecute(String result) { }.execute(); } + private static void PlayRemote(String title, String url, String iconurl){ + MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MUSIC_TRACK); + + movieMetadata.putString(MediaMetadata.KEY_TITLE, title); + //movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, "MySubTitle"); + movieMetadata.addImage(new WebImage(Uri.parse(iconurl))); + //movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1)))); + + + MediaInfo mediaInfo = new MediaInfo.Builder(url) + .setStreamType(MediaInfo.STREAM_TYPE_LIVE) + .setContentType("audio/ogg") + .setMetadata(movieMetadata) + //.setStreamDuration(mSelectedMedia.getDuration() * 1000) + .build(); + RemoteMediaClient remoteMediaClient = Utils.mCastSession.getRemoteMediaClient(); + remoteMediaClient.load(mediaInfo, true); + } + // Storage Permissions public static final int REQUEST_EXTERNAL_STORAGE = 1; private static String[] PERMISSIONS_STORAGE = { diff --git a/app/src/main/java/net/programmierecke/radiodroid2/interfaces/IStreamProxyEventReceiver.java b/app/src/main/java/net/programmierecke/radiodroid2/interfaces/IStreamProxyEventReceiver.java index aee42c1c2..8330d708d 100644 --- a/app/src/main/java/net/programmierecke/radiodroid2/interfaces/IStreamProxyEventReceiver.java +++ b/app/src/main/java/net/programmierecke/radiodroid2/interfaces/IStreamProxyEventReceiver.java @@ -7,5 +7,6 @@ public interface IStreamProxyEventReceiver { void foundShoutcastStream(ShoutcastInfo bitrate, boolean isHls); void foundLiveStreamInfo(Map liveInfo); + void streamCreated(String proxyConnection); void streamStopped(); } diff --git a/app/src/main/res/drawable/mpd_nok.png b/app/src/main/res/drawable/mpd_nok.png new file mode 100644 index 000000000..25c9e6e49 Binary files /dev/null and b/app/src/main/res/drawable/mpd_nok.png differ diff --git a/app/src/main/res/drawable/mpd_ok.png b/app/src/main/res/drawable/mpd_ok.png new file mode 100644 index 000000000..e2b0a44bb Binary files /dev/null and b/app/src/main/res/drawable/mpd_ok.png differ diff --git a/app/src/main/res/layout/layout_main.xml b/app/src/main/res/layout/layout_main.xml index 3fa60247b..207843ee6 100644 --- a/app/src/main/res/layout/layout_main.xml +++ b/app/src/main/res/layout/layout_main.xml @@ -19,6 +19,14 @@ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> + + + + app:showAsAction="always" /> + + + + + + + app:showAsAction="always" /> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 168076fa3..ef3597eeb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -90,6 +90,16 @@ Delete More + GoogleCast + 5A97BAE4 + + Music Player Daemon + Hostname + Port + MPD not connected + MPD connected + Refresh + S M diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 37f24df9f..90907358d 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -58,4 +58,15 @@ android:title="@string/settings_alarm_sleep_timer" /> + + + + + \ No newline at end of file