Skip to content

Commit 6630e28

Browse files
author
Chris Bellew
committed
Added support for audio-only Chromecast devices.
Started fixing the playing of all songs by an artist (WIP). Fixed display of notifications (background color). Fixed bug that caused inconsistent connection to non-Chromecast Plex clients. Version 2.0.5b2
1 parent 2d70263 commit 6630e28

18 files changed

+325
-268
lines changed

mobile/src/main/AndroidManifest.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools"
44
package="com.atomjack.vcfp"
5-
android:versionCode="48"
6-
android:versionName="2.0.4b8" >
5+
android:versionCode="51"
6+
android:versionName="2.0.5b2" >
77

88
<uses-permission android:name="com.mohammadag.googlesearchapi.permission.ACCESS_GGOGLE_SEARCH_API" />
99
<uses-permission android:name="android.permission.INTERNET" />

mobile/src/main/java/com/atomjack/vcfp/CastPlayerManager.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.atomjack.shared.PlayerState;
88
import com.atomjack.shared.Preferences;
99
import com.atomjack.vcfp.interfaces.ActiveConnectionHandler;
10+
import com.atomjack.vcfp.model.Capabilities;
1011
import com.atomjack.vcfp.model.Connection;
1112
import com.atomjack.vcfp.model.PlexClient;
1213
import com.atomjack.vcfp.model.PlexMedia;
@@ -73,6 +74,14 @@ public static final class PARAMS {
7374

7475
};
7576

77+
public static final class RECEIVER_EVENTS {
78+
public static final String PLAYLIST_ADVANCE = "playlistAdvance";
79+
public static final String PLAYER_STATUS_CHANGED = "playerStatusChanged";
80+
public static final String GET_PLAYBACK_STATE = "getPlaybackState";
81+
public static final String TIME_UPDATE = "timeUpdate";
82+
public static final String DEVICE_CAPABILITIES = "deviceCapabilities";
83+
}
84+
7685
private Context mContext;
7786

7887
private CastListener listener;
@@ -182,6 +191,7 @@ public interface CastListener {
182191
void onCastPlayerState(PlayerState state, PlexMedia media);
183192
void onCastConnectionFailed();
184193
void onCastSeek();
194+
void onGetDeviceCapabilities(Capabilities capabilities);
185195
PlexMedia getNowPlayingMedia();
186196
};
187197

@@ -313,21 +323,21 @@ public void onDataMessageReceived(String message) {
313323
try {
314324
JSONObject obj = new JSONObject(message);
315325
if(obj.has("event") && obj.has("status")) {
316-
if(obj.getString("event").equals("playerStatusChanged")) {
326+
if(obj.getString("event").equals(RECEIVER_EVENTS.PLAYER_STATUS_CHANGED)) {
317327
Logger.d("playerStatusChanged: %s", obj.getString("status"));
318328
currentState = PlayerState.getState(obj.getString("status"));
319329
listener.onCastPlayerStateChanged(currentState);
320330
}
321-
} else if(obj.has("event") && obj.getString("event").equals("timeUpdate") && obj.has("currentTime")) {
331+
} else if(obj.has("event") && obj.getString("event").equals(RECEIVER_EVENTS.TIME_UPDATE) && obj.has("currentTime")) {
322332
listener.onCastPlayerTimeUpdate(obj.getInt("currentTime"));
323-
} else if(obj.has("event") && obj.getString("event").equals("playlistAdvance") && obj.has("media") && obj.has("type")) {
333+
} else if(obj.has("event") && obj.getString("event").equals(RECEIVER_EVENTS.PLAYLIST_ADVANCE) && obj.has("media") && obj.has("type")) {
324334
Logger.d("[CastPlayerManager] playlistAdvance");
325335
if(obj.getString("type").equals(PARAMS.MEDIA_TYPE_VIDEO))
326336
nowPlayingMedia = VoiceControlForPlexApplication.gsonRead.fromJson(obj.getString("media"), PlexVideo.class);
327337
else
328338
nowPlayingMedia = VoiceControlForPlexApplication.gsonRead.fromJson(obj.getString("media"), PlexTrack.class);
329339
listener.onCastPlayerPlaylistAdvance(nowPlayingMedia);
330-
} else if(obj.has("event") && obj.getString("event").equals("getPlaybackState") && obj.has("state")) {
340+
} else if(obj.has("event") && obj.getString("event").equals(RECEIVER_EVENTS.GET_PLAYBACK_STATE) && obj.has("state")) {
331341
currentState = PlayerState.getState(obj.getString("state"));
332342
PlexMedia media = null;
333343
if(obj.has("media") && obj.has("type") && obj.has("client")) {
@@ -338,6 +348,9 @@ public void onDataMessageReceived(String message) {
338348
mClient = VoiceControlForPlexApplication.gsonRead.fromJson(obj.getString("client"), PlexClient.class);
339349
}
340350
listener.onCastPlayerState(PlayerState.getState(obj.getString("state")), media);
351+
} else if(obj.has("event") && obj.getString("event").equals(RECEIVER_EVENTS.DEVICE_CAPABILITIES) && obj.has("capabilities")) {
352+
Capabilities capabilities = VoiceControlForPlexApplication.gsonRead.fromJson(obj.getString("capabilities"), Capabilities.class);
353+
listener.onGetDeviceCapabilities(capabilities);
341354
}
342355
} catch (Exception ex) {
343356
ex.printStackTrace();
@@ -376,18 +389,13 @@ public void onApplicationConnected(ApplicationMetadata appMetadata,
376389
Logger.d("sessionid: %s", sessionId);
377390
Logger.d("was launched: %s", wasLaunched);
378391
mSessionId = sessionId;
379-
// if(!launched) {
380-
// launched = true;
381-
if(onConnectedRunnable != null)
382-
onConnectedRunnable.run();
383-
// }
384-
392+
if(onConnectedRunnable != null)
393+
onConnectedRunnable.run();
385394
}
386395

387396
@Override
388397
public void onConnectivityRecovered() {
389-
// com.google.sample.cast.refplayer.utils.Utils.
390-
// showToast(VideoBrowserActivity.this, R.string.connection_recovered);
398+
391399
}
392400

393401
@Override

mobile/src/main/java/com/atomjack/vcfp/activities/MainActivity.java

Lines changed: 87 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.atomjack.vcfp.activities;
22

3+
import android.Manifest;
34
import android.app.Activity;
45
import android.app.AlertDialog;
56
import android.app.Dialog;
@@ -16,6 +17,8 @@
1617
import android.os.Environment;
1718
import android.os.Handler;
1819
import android.speech.tts.TextToSpeech;
20+
import android.support.v4.app.ActivityCompat;
21+
import android.support.v4.content.ContextCompat;
1922
import android.support.v7.media.MediaRouteSelector;
2023
import android.support.v7.media.MediaRouter;
2124
import android.view.LayoutInflater;
@@ -174,74 +177,102 @@ public void emailDeviceLogs(final String wearLog) {
174177
@Override
175178
protected Void doInBackground(Void... params) {
176179
try {
177-
Logger.d("Emailing device logs");
178-
Intent emailIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
179-
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Voice Control for Plex Android Logs");
180-
181-
// Build the body of the email
182-
StringBuilder body = new StringBuilder();
183-
body.append(String.format("Manufacturer: %s\n", Build.MANUFACTURER));
184-
body.append(String.format("Device: %s\n", Build.DEVICE));
185-
body.append(String.format("Model: %s\n", Build.MODEL));
186-
body.append(String.format("Product: %s\n", Build.PRODUCT));
187-
body.append(String.format("Version: %s\n\n", Build.VERSION.RELEASE));
188-
189-
body.append(String.format("Logged in: %s\n\n", VoiceControlForPlexApplication.getInstance().prefs.getString(Preferences.PLEX_USERNAME) != null ? "yes" : "no"));
190-
191-
body.append("Description of the issue:\n\n");
192-
193-
emailIntent.setType("application/octet-stream");
194-
195-
emailIntent.putExtra(Intent.EXTRA_TEXT, body.toString());
196-
197-
File tempDirectory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmp");
198-
if (!tempDirectory.exists())
199-
tempDirectory.mkdirs();
200-
201-
File tempFile = new File(tempDirectory, "/vcfp-log.txt");
202-
FileOutputStream fos = new FileOutputStream(tempFile);
203-
Writer out = new OutputStreamWriter(fos, "UTF-8");
204-
205-
Process process = Runtime.getRuntime().exec("logcat -d *:V");
206-
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
207-
StringBuilder log = new StringBuilder();
208-
String line;
209-
while ((line = bufferedReader.readLine()) != null)
210-
{
211-
log.append(line);
212-
log.append(System.getProperty("line.separator"));
213-
}
180+
boolean hasPermission = (ContextCompat.checkSelfPermission(MainActivity.this,
181+
Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
182+
if(!hasPermission) {
183+
ActivityCompat.requestPermissions(MainActivity.this,
184+
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
185+
REQUEST_WRITE_STORAGE);
186+
} else {
187+
Logger.d("Emailing device logs");
188+
Intent emailIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
189+
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Voice Control for Plex Android Logs");
190+
191+
// Build the body of the email
192+
StringBuilder body = new StringBuilder();
193+
body.append(String.format("Manufacturer: %s\n", Build.MANUFACTURER));
194+
body.append(String.format("Device: %s\n", Build.DEVICE));
195+
body.append(String.format("Model: %s\n", Build.MODEL));
196+
body.append(String.format("Product: %s\n", Build.PRODUCT));
197+
body.append(String.format("Version: %s\n\n", Build.VERSION.RELEASE));
198+
199+
body.append(String.format("Logged in: %s\n\n", VoiceControlForPlexApplication.getInstance().prefs.getString(Preferences.PLEX_USERNAME) != null ? "yes" : "no"));
200+
201+
body.append("Description of the issue:\n\n");
202+
203+
emailIntent.setType("application/octet-stream");
214204

215-
bufferedReader.close();
205+
emailIntent.putExtra(Intent.EXTRA_TEXT, body.toString());
216206

217-
out.write(log.toString());
218-
out.flush();
219-
out.close();
207+
File tempDirectory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmp");
208+
if (!tempDirectory.exists())
209+
tempDirectory.mkdirs();
220210

221-
ArrayList<Uri> uris = new ArrayList<Uri>();
222-
uris.add(Uri.parse("file://" + tempFile.getAbsolutePath()));
211+
File tempFile = new File(tempDirectory, "/vcfp-log.txt");
212+
FileOutputStream fos = new FileOutputStream(tempFile);
213+
Writer out = new OutputStreamWriter(fos, "UTF-8");
223214

224-
if(!wearLog.equals("")) {
225-
tempFile = new File(tempDirectory, "/vcfp-wear-log.txt");
226-
fos = new FileOutputStream(tempFile);
227-
out = new OutputStreamWriter(fos, "UTF-8");
228-
out.write(wearLog);
215+
Process process = Runtime.getRuntime().exec("logcat -d *:V");
216+
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
217+
StringBuilder log = new StringBuilder();
218+
String line;
219+
while ((line = bufferedReader.readLine()) != null) {
220+
log.append(line);
221+
log.append(System.getProperty("line.separator"));
222+
}
223+
224+
bufferedReader.close();
225+
226+
out.write(log.toString());
229227
out.flush();
230228
out.close();
229+
230+
ArrayList<Uri> uris = new ArrayList<Uri>();
231231
uris.add(Uri.parse("file://" + tempFile.getAbsolutePath()));
232+
233+
if (!wearLog.equals("")) {
234+
tempFile = new File(tempDirectory, "/vcfp-wear-log.txt");
235+
fos = new FileOutputStream(tempFile);
236+
out = new OutputStreamWriter(fos, "UTF-8");
237+
out.write(wearLog);
238+
out.flush();
239+
out.close();
240+
uris.add(Uri.parse("file://" + tempFile.getAbsolutePath()));
241+
}
242+
emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
243+
startActivity(emailIntent);
232244
}
233-
emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
234-
startActivity(emailIntent);
235-
} catch (Exception ex) {
236-
Logger.e("Exception emailing device logs: %s", ex);
237-
feedback.e("Error emailing device logs: %s", ex.getMessage());
238-
}
239-
return null;
245+
} catch (final Exception ex) {
246+
Logger.e("Exception emailing device logs: %s", ex);
247+
runOnUiThread(new Runnable() {
248+
@Override
249+
public void run() {
250+
feedback.e("Error emailing device logs: %s", ex.getMessage());
251+
}
252+
});
253+
}
254+
return null;
240255
}
241256
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
242257
}
243258

244-
public void showChangelog(MenuItem item) {
259+
@Override
260+
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
261+
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
262+
switch (requestCode) {
263+
case REQUEST_WRITE_STORAGE: {
264+
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
265+
{
266+
emailDeviceLogs("");
267+
} else
268+
{
269+
feedback.e(R.string.email_device_logs_write_storage_denied);
270+
}
271+
}
272+
}
273+
}
274+
275+
public void showChangelog(MenuItem item) {
245276
final WhatsNewDialog whatsNewDialog = new WhatsNewDialog(this);
246277
whatsNewDialog.forceShow();
247278
}

mobile/src/main/java/com/atomjack/vcfp/activities/NowPlayingActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ public boolean onCreateOptionsMenu(Menu _menu) {
330330
getMenuInflater().inflate(R.menu.menu_playing, _menu);
331331
menu = _menu;
332332
if(plexSubscription.isSubscribed()) {
333-
if(plexSubscription.mClient.machineIdentifier != mClient.machineIdentifier) {
333+
if(!plexSubscription.mClient.machineIdentifier.equals(mClient.machineIdentifier)) {
334334
// We're already subscribed to another client, so unsubscribe from that one and subscribe to the new one
335335
plexSubscription.unsubscribe(false, new Runnable() {
336336
@Override

mobile/src/main/java/com/atomjack/vcfp/activities/VCFPActivity.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import android.os.Build;
1616
import android.os.Bundle;
1717
import android.os.Handler;
18-
import android.support.v7.app.ActionBarActivity;
18+
import android.support.v7.app.AppCompatActivity;
1919
import android.view.LayoutInflater;
2020
import android.view.Menu;
2121
import android.view.MenuItem;
@@ -47,6 +47,7 @@
4747
import com.atomjack.shared.UriSerializer;
4848
import com.atomjack.vcfp.VoiceControlForPlexApplication;
4949
import com.atomjack.vcfp.adapters.PlexListAdapter;
50+
import com.atomjack.vcfp.model.Capabilities;
5051
import com.atomjack.vcfp.model.Connection;
5152
import com.atomjack.vcfp.model.MediaContainer;
5253
import com.atomjack.vcfp.model.PlexClient;
@@ -75,11 +76,13 @@
7576

7677
import cz.fhucho.android.util.SimpleDiskCache;
7778

78-
public abstract class VCFPActivity extends ActionBarActivity implements PlexSubscription.PlexListener, CastPlayerManager.CastListener, VoiceControlForPlexApplication.NetworkChangeListener {
79+
public abstract class VCFPActivity extends AppCompatActivity implements PlexSubscription.PlexListener, CastPlayerManager.CastListener, VoiceControlForPlexApplication.NetworkChangeListener {
7980
protected PlexMedia nowPlayingMedia;
8081
protected boolean subscribing = false;
8182
protected PlexClient mClient;
8283

84+
protected static final int REQUEST_WRITE_STORAGE = 112;
85+
8386
protected VoiceControlForPlexApplication app;
8487

8588
public final static String BUGSENSE_APIKEY = "879458d0";
@@ -522,6 +525,14 @@ public void onCastPlayerState(PlayerState state, PlexMedia media) {
522525
sendWearPlaybackChange();
523526
}
524527

528+
@Override
529+
public void onGetDeviceCapabilities(Capabilities capabilities) {
530+
if(mClient.isCastClient) {
531+
mClient.isAudioOnly = !capabilities.displaySupported;
532+
setClient(mClient);
533+
}
534+
}
535+
525536
@Override
526537
public void onCastDisconnected() {
527538
Logger.d("[VCFPActivity] onCastDisconnected");
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.atomjack.vcfp.model;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
5+
public class Capabilities {
6+
@SerializedName("display_supported")
7+
public boolean displaySupported;
8+
}

mobile/src/main/java/com/atomjack/vcfp/model/PlexClient.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
public class PlexClient extends PlexDevice {
2121
public boolean isCastClient = false;
2222
public CastDevice castDevice;
23+
public boolean isAudioOnly = false;
2324

2425
public PlexClient() {
2526

@@ -49,6 +50,7 @@ public void writeToParcel(Parcel parcel, int i) {
4950
parcel.writeString(address);
5051
parcel.writeString(machineIdentifier);
5152
parcel.writeInt(isCastClient ? 1 : 0);
53+
parcel.writeInt(isAudioOnly ? 1 : 0);
5254
parcel.writeParcelable(castDevice, i);
5355
}
5456

@@ -60,6 +62,7 @@ public PlexClient(Parcel in) {
6062
address = in.readString();
6163
machineIdentifier = in.readString();
6264
isCastClient = in.readInt() == 1;
65+
isAudioOnly = in.readInt() == 1;
6366
castDevice = in.readParcelable(CastDevice.class.getClassLoader());
6467
Logger.d("set cast device from parcel");
6568
}

0 commit comments

Comments
 (0)