Skip to content

Commit 7b2d196

Browse files
author
Chris Bellew
committed
[Wear] Only push intents to activities if the app is visible.
[Wear] Updated mic button. [Wear] Retry getting playback state from handheld until the handheld has had a chance to check with google whether Wear Support has been purchased or not. [Wear] If voice input is canceled, finish the activity and go back to the home screen.
1 parent 4f6033e commit 7b2d196

File tree

10 files changed

+106
-102
lines changed

10 files changed

+106
-102
lines changed

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.atomjack.shared.UriSerializer;
3737
import com.atomjack.shared.WearConstants;
3838
import com.atomjack.vcfp.activities.CastActivity;
39+
import com.atomjack.vcfp.activities.MainActivity;
3940
import com.atomjack.vcfp.activities.NowPlayingActivity;
4041
import com.atomjack.vcfp.interfaces.BitmapHandler;
4142
import com.atomjack.vcfp.interfaces.ServerFindHandler;
@@ -122,6 +123,7 @@ public static enum NOTIFICATION_STATUS {
122123
private IabHelper mIabHelper;
123124
// TODO: Obfuscate this somehow:
124125
String base64EncodedPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlgV+Gdi4nBVn2rRqi+oVLhenzbWcEVyUf1ulhvAElEf6c8iuX3OB4JZRYVhCE690mFaYUdEb8OG8p8wT7IrQmlZ0DRfP2X9csBJKd3qB+l9y11Ggujivythvoiz+uvDPhz54O6wGmUB8+oZXN+jk9MT5Eia3BZxJDvgFcmDe/KQTTKZoIk1Qs/4PSYFP8jaS/lc71yDyRmvAM+l1lv7Ld8h69hVvKFUr9BT/20lHQGohCIc91CJvKIP5DaptbE98DAlrTxjZRRpbi+wrLGKVbJpUOBgPC78qo3zPITn6M6N0tHkv1tHkGOeyLUbxOC0wFdXj33mUldV/rp3tHnld1wIDAQAB";
126+
private boolean inventoryQueried = false;
125127

126128
// Has the user purchased chromecast/wear support?
127129
// This is the default value.
@@ -138,6 +140,9 @@ public static enum NOTIFICATION_STATUS {
138140

139141
GoogleApiClient googleApiClient;
140142

143+
// This is needed so that we can let the main activity know that wear support is enabled, after querying the inventory from Google
144+
private MainActivity mainActivity;
145+
141146
@Override
142147
public void onCreate() {
143148
super.onCreate();
@@ -627,7 +632,7 @@ public void onQueryInventoryFinished(IabResult result, final Inventory inventory
627632

628633
Logger.d("Query inventory was successful.");
629634

630-
// Get the price for chromecast support
635+
// Get the price for chromecast & wear support
631636
mIabHelper.queryInventoryAsync(true, new ArrayList<String>(Arrays.asList(SKU_CHROMECAST, SKU_WEAR)), new IabHelper.QueryInventoryFinishedListener() {
632637
@Override
633638
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
@@ -655,6 +660,7 @@ public void onQueryInventoryFinished(IabResult result, Inventory inv) {
655660
Logger.d("Has Chromecast: %s", mHasChromecast);
656661
Logger.d("Has Wear: %s", mHasWear);
657662
Logger.d("Initial inventory query finished.");
663+
inventoryQueried = true;
658664
onInventoryQueryFinished();
659665
}
660666
});
@@ -663,13 +669,26 @@ public void onQueryInventoryFinished(IabResult result, Inventory inv) {
663669
}
664670
};
665671

672+
public boolean getInventoryQueried() {
673+
return inventoryQueried;
674+
}
675+
666676
// This runs once we have queried the play store to see if chromecast or wear support have been purchased.
667677
// If Wear support has not been purchased, we can attempt to contact a connected Wear device, and if we hear back,
668678
// we can throw a popup alerting the user that the app supports Wear
669679
private void onInventoryQueryFinished() {
670-
Logger.d("[VoiceControlForPlexApplication] Sending ping");
671-
if(!mHasWear)
680+
if(!mHasWear) {
681+
Logger.d("[VoiceControlForPlexApplication] Sending ping");
672682
new SendToDataLayerThread(WearConstants.PING, this).start();
683+
} else {
684+
if(mainActivity != null) {
685+
mainActivity.hidePurchaseWearMenuItem();
686+
}
687+
}
688+
}
689+
690+
public void setOnHasWearActivity(MainActivity activity) {
691+
mainActivity = activity;
673692
}
674693

675694
boolean verifyDeveloperPayload(Purchase p) {
@@ -778,13 +797,11 @@ public static Asset createAssetFromBitmap(Bitmap bitmap) {
778797
public static void SetWearMediaTitles(DataMap dataMap, PlexMedia media) {
779798
if(media.isShow()) {
780799
dataMap.putString(WearConstants.MEDIA_TITLE, media.getTitle());
781-
Logger.d("[VCFPActivity] got show title: %s", media.getTitle());
782800
dataMap.putString(WearConstants.MEDIA_SUBTITLE, media.getEpisodeTitle());
783801
} else if(media.isMovie()) {
784802
dataMap.putString(WearConstants.MEDIA_TITLE, media.title);
785803
dataMap.remove(WearConstants.MEDIA_SUBTITLE);
786804
} else if(media.isMusic()) {
787-
Logger.d("[VCFPActivity] got music: %s by %s", media.title, media.grandparentTitle);
788805
dataMap.putString(WearConstants.MEDIA_TITLE, media.grandparentTitle);
789806
dataMap.putString(WearConstants.MEDIA_SUBTITLE, media.title);
790807
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ protected void onPause() {
212212
castManager.decrementUiCounter();
213213
castManager.removeVideoCastConsumer(castConsumer);
214214
}
215+
VoiceControlForPlexApplication.applicationPaused();
215216
super.onPause();
216217
}
217218

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ public class MainActivity extends VCFPActivity implements TextToSpeech.OnInitLis
106106
@Override
107107
protected void onCreate(Bundle savedInstanceState) {
108108
super.onCreate(savedInstanceState);
109+
Logger.d("[MainActivity] onCreate");
110+
111+
// This will enable the UI to be updated (Wear Support hidden/Wear Options shown)
112+
// once inventory is queried via Google, if wear support has been purchased
113+
VoiceControlForPlexApplication.getInstance().setOnHasWearActivity(this);
109114

110115
final WhatsNewDialog whatsNewDialog = new WhatsNewDialog(this);
111116
whatsNewDialog.show();
@@ -1059,7 +1064,7 @@ public void onDisconnected() {
10591064
}
10601065

10611066
@Override
1062-
protected void hidePurchaseWearMenuItem() {
1067+
public void hidePurchaseWearMenuItem() {
10631068
MenuItem wearItem = menu.findItem(R.id.menu_purchase_wear);
10641069
wearItem.setVisible(false);
10651070
MenuItem wearOptionsItem = menu.findItem(R.id.menu_wear_options);
@@ -1089,6 +1094,8 @@ public void onClick(DialogInterface dialog, int id) {
10891094
});
10901095
chooserDialog.show();
10911096
}
1097+
1098+
10921099
}
10931100

10941101

mobile/src/main/java/com/atomjack/vcfp/services/WearListenerService.java

Lines changed: 46 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,61 +3,37 @@
33

44
import android.content.Intent;
55
import android.graphics.Bitmap;
6-
import android.net.Uri;
6+
import android.os.Handler;
77
import android.speech.RecognizerIntent;
88

99
import com.atomjack.shared.Logger;
1010
import com.atomjack.shared.SendToDataLayerThread;
11-
import com.atomjack.shared.UriSerializer;
1211
import com.atomjack.shared.WearConstants;
13-
import com.atomjack.shared.UriDeserializer;
1412
import com.atomjack.vcfp.CastPlayerManager;
1513
import com.atomjack.shared.PlayerState;
16-
import com.atomjack.vcfp.PlexHeaders;
1714
import com.atomjack.vcfp.PlexSubscription;
1815
import com.atomjack.vcfp.VoiceControlForPlexApplication;
16+
import com.atomjack.vcfp.activities.CastActivity;
1917
import com.atomjack.vcfp.activities.MainActivity;
2018
import com.atomjack.vcfp.activities.NowPlayingActivity;
2119
import com.atomjack.vcfp.activities.VCFPActivity;
2220
import com.atomjack.vcfp.interfaces.BitmapHandler;
23-
import com.atomjack.vcfp.model.MediaContainer;
2421
import com.atomjack.vcfp.model.PlexClient;
25-
import com.atomjack.shared.model.Timeline;
2622
import com.atomjack.vcfp.model.PlexMedia;
27-
import com.atomjack.vcfp.net.PlexHttpClient;
2823
import com.google.android.gms.common.api.GoogleApiClient;
29-
import com.google.android.gms.wearable.Asset;
30-
import com.google.android.gms.wearable.DataEvent;
31-
import com.google.android.gms.wearable.DataEventBuffer;
3224
import com.google.android.gms.wearable.DataMap;
33-
import com.google.android.gms.wearable.DataMapItem;
3425
import com.google.android.gms.wearable.MessageEvent;
3526
import com.google.android.gms.wearable.Wearable;
3627
import com.google.android.gms.wearable.WearableListenerService;
37-
import com.google.gson.Gson;
38-
import com.google.gson.GsonBuilder;
39-
import com.google.gson.reflect.TypeToken;
40-
41-
import org.apache.http.Header;
42-
import org.apache.http.message.BasicHeader;
43-
44-
import java.io.ByteArrayOutputStream;
45-
import java.lang.reflect.Type;
46-
import java.util.ArrayList;
47-
import java.util.Hashtable;
4828

4929
public class WearListenerService extends WearableListenerService {
5030

51-
protected Gson gsonRead = new GsonBuilder()
52-
.registerTypeAdapter(Uri.class, new UriDeserializer())
53-
.create();
54-
55-
protected Gson gsonWrite = new GsonBuilder()
56-
.registerTypeAdapter(Uri.class, new UriSerializer())
57-
.create();
58-
5931
GoogleApiClient googleApiClient;
6032

33+
Handler handler = new Handler();
34+
// How many times we've had the wear request playback state.
35+
int playbackStateRetries = 0;
36+
6137
@Override
6238
public void onCreate() {
6339
super.onCreate();
@@ -67,39 +43,38 @@ public void onCreate() {
6743
googleApiClient.connect();
6844
}
6945

70-
// @Override
71-
// public void onDataChanged(DataEventBuffer dataEvents) {
72-
// Logger.d("[WearListenerService] onDataChanged");
73-
// DataMap dataMap;
74-
// for (DataEvent event : dataEvents) {
75-
// dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap();
76-
// // Check the data type
77-
// if (event.getType() == DataEvent.TYPE_CHANGED) {
78-
// // Check the data path
79-
// String path = event.getDataItem().getUri().getPath();
80-
// if (path.equals(WearConstants.GET_PLAYBACK_STATE)) {
81-
// Logger.d("GET_PLAYBACK_STATE");
82-
// }
83-
// }
84-
// }
85-
//
86-
// }
87-
8846
@Override
8947
public void onMessageReceived(MessageEvent messageEvent) {
9048
String message = messageEvent.getPath() == null ? "" : messageEvent.getPath();
9149
Logger.d("[WearListenerService] onMessageReceived: %s", message);
50+
if(!VoiceControlForPlexApplication.getInstance().getInventoryQueried() && message.equals(WearConstants.GET_PLAYBACK_STATE)) {
51+
// This message was received before we've had a chance to check with Google on whether or not Wear support
52+
// has been purchased. After a delay of 500ms, send a message back to the Wearable to get playback state again.
53+
// By then, it should have had time to see if Wear Support has been purchased.
54+
playbackStateRetries++;
55+
if(playbackStateRetries < 4) {
56+
handler.postDelayed(new Runnable() {
57+
@Override
58+
public void run() {
59+
new SendToDataLayerThread(WearConstants.RETRY_GET_PLAYBACK_STATE, WearListenerService.this).start();
60+
}
61+
}, 500);
62+
return;
63+
}
64+
}
9265
if(!VoiceControlForPlexApplication.getInstance().hasWear() && !message.equals(WearConstants.PONG)) {
9366
// Wear support has not been purchased, so send a message back to the wear device, and show the purchase required
9467
// popup on the handheld. However, if the message is 'pong', a response to a 'ping', skip since we want to react to a pong
9568
// even if wear support has not been purchased (so we can alert the user to the option to purchase)
9669
new SendToDataLayerThread(WearConstants.WEAR_UNAUTHORIZED, this).start();
97-
Intent intent = new Intent(this, MainActivity.class);
98-
intent.setAction(com.atomjack.shared.Intent.SHOW_WEAR_PURCHASE_REQUIRED);
99-
intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
100-
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
101-
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
102-
startActivity(intent);
70+
if(VoiceControlForPlexApplication.isApplicationVisible()) {
71+
Intent intent = new Intent(this, MainActivity.class);
72+
intent.setAction(com.atomjack.shared.Intent.SHOW_WEAR_PURCHASE_REQUIRED);
73+
intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
74+
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
75+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
76+
startActivity(intent);
77+
}
10378
return;
10479
}
10580
if(messageEvent.getPath() != null) {
@@ -176,22 +151,26 @@ public void onSuccess(Bitmap bitmap) {
176151

177152
} else if(message.equals(WearConstants.GET_PLAYING_MEDIA)) {
178153
// Send an intent to NowPlayingActivity to tell it to forward on information about the currently playing media back to the wear device
179-
Intent intent = new Intent(this, NowPlayingActivity.class);
180-
intent.setAction(com.atomjack.shared.Intent.GET_PLAYING_MEDIA);
181-
intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
182-
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
183-
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
184-
startActivity(intent);
154+
if(VoiceControlForPlexApplication.isApplicationVisible()) {
155+
Class theClass = castPlayerManager.isSubscribed() ? CastActivity.class : NowPlayingActivity.class;
156+
Intent intent = new Intent(this, theClass);
157+
intent.setAction(com.atomjack.shared.Intent.GET_PLAYING_MEDIA);
158+
intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
159+
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
160+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
161+
startActivity(intent);
162+
}
185163
} else if(message.equals(WearConstants.PONG)) {
186164
// Received a pong back from the user, so show a popup allowing the user to purchase wear support.
187165
Logger.d("[WearListenerService] Received pong");
188-
189-
Intent intent = new Intent(this, MainActivity.class);
190-
intent.setAction(com.atomjack.shared.Intent.SHOW_WEAR_PURCHASE);
191-
intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
192-
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
193-
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
194-
startActivity(intent);
166+
if(VoiceControlForPlexApplication.isApplicationVisible()) {
167+
Intent intent = new Intent(this, MainActivity.class);
168+
intent.setAction(com.atomjack.shared.Intent.SHOW_WEAR_PURCHASE);
169+
intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
170+
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
171+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
172+
startActivity(intent);
173+
}
195174
} else if(message.equals(WearConstants.ACTION_PAUSE) || message.equals(WearConstants.ACTION_PLAY) || message.equals(WearConstants.ACTION_STOP)) {
196175
Intent intent = new android.content.Intent(this, PlexControlService.class);
197176
intent.setAction(message);

shared/src/main/java/com/atomjack/shared/SendToDataLayerThread.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public SendToDataLayerThread(String path, Context context) {
4747

4848
public void sendDataItem() {
4949
sendDataItem = true;
50-
this.start();
50+
start();
5151
}
5252

5353
public void run() {

shared/src/main/java/com/atomjack/shared/WearConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public class WearConstants {
2727

2828
// This is used to know whether or not the voice input was triggered from an initial launch of the app
2929
public final static String LAUNCHED = "com.atomjack.vcfp.launched";
30+
public final static String RETRY_GET_PLAYBACK_STATE = "com.atomjack.vcfp.retry_get_playback_state";
31+
3032

3133
public final static String FINISH = "com.atomjack.vcfp.finish";
3234

wear/src/main/java/com/atomjack/vcfp/MainActivity.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,14 +158,16 @@ protected void onStart() {
158158

159159
@Override
160160
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
161-
Logger.d("[VoiceInputActivity] onActivityResult: %s", requestCode);
161+
Logger.d("[MainActivity] onActivityResult. requestCode: %d, resultCode: %d", requestCode, resultCode);
162162
if (requestCode == SPEECH_RECOGNIZER_REQUEST_CODE) {
163163
// When the speech recognizer finishes its work, Android invokes this callback with requestCode equal to SPEECH_RECOGNIZER_REQUEST_CODE
164164
if (resultCode == RESULT_OK) {
165165
DataMap dataMap = new DataMap();
166166
dataMap.putStringArrayList(WearConstants.SPEECH_QUERY, data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS));
167167
new SendToDataLayerThread(WearConstants.SPEECH_QUERY, dataMap, MainActivity.this).start();
168168
finish();
169+
} else if(resultCode == RESULT_CANCELED) {
170+
finish();
169171
}
170172
}
171173
super.onActivityResult(requestCode, resultCode, data);

wear/src/main/java/com/atomjack/vcfp/WearListenerService.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ public void onMessageReceived(MessageEvent messageEvent) {
6363
DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
6464
Logger.d("[WearListenerService] data: %s", dataMap);
6565

66-
if(message.equals(WearConstants.GET_PLAYBACK_STATE)) {
66+
if(message.equals(WearConstants.RETRY_GET_PLAYBACK_STATE)) {
67+
DataMap dataMap1 = new DataMap();
68+
dataMap1.putBoolean(WearConstants.LAUNCHED, true);
69+
new SendToDataLayerThread(WearConstants.GET_PLAYBACK_STATE, dataMap1, this).start();
70+
} else if(message.equals(WearConstants.GET_PLAYBACK_STATE)) {
6771

6872
PlayerState state = PlayerState.getState(dataMap.getString(WearConstants.PLAYBACK_STATE));
6973
Logger.d("[WearListenerService] state: %s", state);
-743 Bytes
Loading

0 commit comments

Comments
 (0)