Google Cast Remote Display Crash (Selector must not be null) - java

I am making a remote display for a gallery app and I am getting a very strange error that seems unrelated to the cast at all. At first, it crashed because of an invalid App ID, but after registering it, and getting an App ID, it crashes and says that the selector must not be null, but the Cast part is not in the same code that calls Null. Any help?
//Inside of OnCreate
MediaRouter = MediaRouter.getInstance(getApplicationContext());
MediaRouteSelector mMediaRouteSelector = new MediaRouteSelector.Builder()
.addControlCategory( CastMediaControlIntent.categoryForCast(getString(R.string.cast_sdk_id)))
.build();
//Inside of OnCreateOptionsMenu
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_viewer, menu);
if (mAdapter.getEntries().size() > 0) {
MediaEntry currentEntry = mAdapter.getEntries().get(mCurrentPosition);
if (currentEntry == null || currentEntry.isVideo()) {
menu.findItem(R.id.print).setVisible(false);
menu.findItem(R.id.edit).setVisible(false);
menu.findItem(R.id.set_as).setVisible(false);
} else {
menu.findItem(R.id.print).setVisible(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT);
menu.findItem(R.id.edit).setVisible(true);
menu.findItem(R.id.set_as).setVisible(true);
}
}
menu.findItem(R.id.slideshow).setVisible(!mAllVideos && mSlideshowTimer == null);
MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
MediaRouteActionProvider mediaRouteActionProvider =
(MediaRouteActionProvider) MenuItemCompat.getActionProvider(mediaRouteMenuItem);
mediaRouteActionProvider.setRouteSelector(mMediaRouteSelector);
return super.onCreateOptionsMenu(menu);;
java.lang.IllegalArgumentException: selector must not be null
at android.support.v7.app.MediaRouteActionProvider.setRouteSelector(MediaRouteActionProvider.java:169)
at com.afollestad.impression.viewer.ViewerActivity.onCreateOptionsMenu(ViewerActivity.java:804)

When you state that a certain line of code is throwing an exception, it is useful to indicate which line of the code it is since we don't have your full source. Regarding your issue, in your onCreate), as it is stated in your post, you have MediaRouteSelector mMediaRouteSelector = .... Since we don't have your source code, I am going to assume that it is really as you have copied here. Since you also use mMediaRouteSelector in onCreateOptionsMenu(), it sounds like you have an instance variable mMediaRouteSelector which is shadowed in your onCreate(); in other words, the assignment made in onCreate() is local to that method and not global, hence it is practically null in onCreateOptionsMenu(). Change the assignment in onCreate() to
mMediaRouteSelector = new MediaRouteSelector.Builder()
.addControlCategory( CastMediaControlIntent.categoryForCast(getString(R.string.cast_sdk_id)))
.build();

Related

Java Reflection getting message from WhatsApp by inner class on Android Notification

I´m looking for a way to get the messages from WhatsApp notifications when there is more than one line.
I´m trying to get the value from a private variable inside an inner class via Reflection in Android. I´m trying to get the 'mTexts' ArrayList of charsequence used to build an InboxStyle Notification. I´m looking for the messages from whatsapp since the last whats update there is no EXTRA on Notifications listened by notificationListener() that have the multiple lines notification (I can only get the first line).
Any way to get the lines is worth it.
This is my code.
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
Class[] declaredClasses = sbn.getNotification().getClass().getClasses();
for (Class c : declaredClasses){
if(c.getName().contains("Notification$InboxStyle")){
Class inboxStyleClass = c.getClass();
Field[] fields = inboxStyleClass.getDeclaredFields();
for(Field field : fields){
if(field.getName().contains("mText")){
Field fmText = null;
try {
fmText = inboxStyleClass.getDeclaredField("mTexts");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
ArrayList<CharSequence> mTextsArrayList = null;
fmText.setAccessible(true);
try{
mTextsArrayList = (ArrayList<CharSequence>) fmText.get(**WICH OBJECT MAY USE HERE**);
}catch(IllegalAccessException e){
e.printStackTrace();
}
for(CharSequence value : mTextsArrayList){
Log.i("XXX","Results are: "+value.toString());
}
}
}
}
}
}
I reach the mText field correctly but I can´t get the value from it.
I tried to use a new Notification.InboxStyle() object
Notification.InboxStyle iStyleObjectToGetTheValue = new Notification.InboxStyle();
to see if it works well, and it does
inboxStyle = (ArrayList<CharSequence>) fmText.get(iStyleObjectToGetTheValue);
but I need the values from the notification. Any idea on how can I achieve that?
I also tried to get the message lines inflating the RemoteViews that you can retrieve by StatusBarNotification.getNotification().THE_REMOTE_VIEW because using DeviceMonitor you can take an screenshoot of the device and see the IDs of the views... but had no lucky with that.
Any way to get the lines is worth it.
All the help is welcomed!!
Thanks!!
According to the docs for RemoteView.apply():
View apply(Context context, ViewGroup parent) - Inflates the view hierarchy represented by this object and applies all of the actions.
You can create a parent and provide the context to RemoteView.apply(). Inspect the resulting View to find the text:
#Nullable
public View getBigContentView (StatusBarNotification statusBarNotification) {
RemoteViews bigContentView = statusBarNotification.getNotification().bigContentView;
return bigContentView != null ? bigContentView.apply(ctx, null) : null;
}
This might not work in Nougat phones, as according to the documentation (reading this more carefully tells me that Google probably did not mean for you to read this parameter and that it should only be used when building the notification):
* As of N, this field may be null. The expanded notification view is determined by the
* inputs to {#link Notification.Builder}; a custom RemoteViews can optionally be
* supplied with {#link Notification.Builder#setCustomBigContentView(RemoteViews)}.

ListView unable to add and edit cell when empty

I'm trying to use a ListView as an Editor for Strings, that come out of a custom data model. I use TextFieldListCells with an appropriate StringConverter for the cells.
There is an add button next to the ListView that calls this method on action:
#FXML
private void addElement() {
WordListItem newItem = new WordListItem(-1, "");
wordListItems.add(newItem);
wordListView.setEditable(true);
wordListView.getSelectionModel().select(wordListItems.indexOf(newItem));
wordListView.edit(wordListItems.indexOf(newItem));
wordListView.setEditable(false);
}
Where wordListView is the ListView and wordListItems is the ObservableList containing the data for the wordListView.
This does work, except for when the list is empty (not null), and I couldn't quite explain why, so I inspected the Java source code for help.
Here's what I found out so far: the edit(int) call on ListView changes the ListViews internal editIndex value, which is supposed to call the EDIT_START Event. The editIndex is an ReadOnlyIntegerWrapper in which I found some weird code that I can't quite understand and I'm not sure if thats actually producing a bug or I just can't see why they did it:
#Override
protected void fireValueChangedEvent() {
super.fireValueChangedEvent();
if (readOnlyProperty != null) {
readOnlyProperty.fireValueChangedEvent();
}
}
This method is called whenever the editIndex property of ListView is changed. The problem: readOnlyProperty is null, because it's not set anywhere. The only place I could find where it got set is in the getter:
public ReadOnlyIntegerProperty getReadOnlyProperty() {
if (readOnlyProperty == null) {
readOnlyProperty = new ReadOnlyPropertyImpl();
}
return readOnlyProperty;
}
(ReadOnlyIntegerImpl is an inner private class and readOnlyProperty is it's type)
Now to my actual question: Is this a bug or am I overseeing something? Is there a reason why I can't add and edit a newly created Element in my list like that when it's empty, or is it really just this getter not being called yet?
The source code you found just is code for lazy initializing the property.
Unless new value is assigned to the property or the property itself is requested, null can be used as the property to avoid unnecessary creation of property objects. This is not an issue here.
The issue seems to be the ListView cells not being updated before edit is called. This happens during layout, so "manually" calling layout before starting the edit should work:
private void addElement() {
WordListItem newItem = new WordListItem(-1, "");
wordListItems.add(newItem);
wordListView.setEditable(true);
wordListView.layout();
wordListView.edit(wordListItems.size()-1);
wordListView.setEditable(false);
}

TextToSpeech setLanguage not working?

I am setting my TextToSpeech to use a particular language (English - UK), using the locale "en_GB". But it always uses my devices default language. Is there no way to set it programmatically? I have downloaded the files required for the language and when I change my TTS's default language to 'English - UK' it works but when the default is different the programmatic approach does not work. I have scoured the web to my best but am unable to resolve this issue.
String ttsEngine = "com.google.android.tts";
txt2Speech = new TextToSpeech(this, this, ttsEngine);
//Locale ttsLocale = new Locale("eng", "GBR");
txt2Speech.setLanguage(new Locale("en_GB"));
Tried several methods, but none are working.
Can I not set my TTS's language programmatically?
Thank You
EDIT: In response to 'A Honey Bustard'
Other Code:
public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener
My onInit()
public void onInit(int status) {
// TODO Auto-generated method stub
}
Also I'm calling .setLanguage() in my onCreate(), as soon as my TextToSpeech is initialized. Is that correct? Also I'm only calling it once. It is not required to call it every time right? Also I'm testing on a GS7
You need to set the language once the Text to Speech Engine has initialised correctly.
public void onInit(int status) {
switch (status) {
case SUCCESS:
// Set the language here
break;
case ERROR:
// Something went wrong. You can't set the language
break;
}
}
That should do it.
Try the second Constructor from Locale that takes two Strings like this :
txt2Speech.setLanguage(new Locale("en", "GB"));
EDIT :
Yes it is usually ok to do instantiate it in onCreate, and it usually only needs and should be done once.
All I can do is show you my working code, I am setting the default language after instantiating in onCreate() :
textToSpeech = new TextToSpeech(getApplicationContext(), this);
textToSpeech.setLanguage(Locale.getDefault());
In my app there are buttons in which you can change the language, which trigger this code (case British English) :
textToSpeech.setLanguage(new Locale("en", "GB"));
Maybe it is not available somehow , there are some checks you can validate if the language and country is available. You might find your error there.
if (textToSpeech.isLanguageAvailable(new Locale("en", "GB")) == TextToSpeech.LANG_COUNTRY_AVAILABLE
&& textToSpeech.isLanguageAvailable(new Locale("en", "GB")) == TextToSpeech.LANG_AVAILABLE)
should return true.

1 of the 3 callback weakreferences goes to null in the asynctask (Android)

Intro to me and my application school project
Hi,
iam pretty new with android and for some school project iam building an application where users can configure regions to recieve alerts from. The app need also make it posible to recieve alerts around the current location of the app user.
The app gets its info from a xml feed and sorts the data by the configured regions. The workflow is 1. to get the alerts which are in the configured regions. 2. When gps alerts are enabled the app need to get the location and when it is known it needs to do the first step again but this time the gps region is included. (i need to optimize this proces LATER)
(questions bellow)
intro to my app and problem
I'm using a asynctask in my application to download some xml feed. When the asynctask is ready i need to call 3 places for do something with the result.
1 class saves the result in the local database (alertmanager)
2 fragments (in a tabview) needs to show the results (1 in a map an one in a listview)
Now i use weakreferences for giving the call back "references" to the asynctask. in the onPostExecute() i use theWeakReference.get().updateMethod(result); for updating the class/fragments.
The alertmanager (the class who needs to recieve the updates) also calls a gps manager in the same method where it calls the asynctask to get the gps location. When i comment out (in my case with a if) the line what calls the gps manager the weak reference of the alertmanager will go to null in the asynctask between the constructor (all references are filled) and the doInBackground (the alertmanager reference is null, the other 2 still filled) which results in a crashing app.
When i dont comment out the if the app works fine.....
Alertmanager information
This is the method in the alertmanager who calls the async task. The references are filled on this place.
public void GetAlerts(List<WeakReference<e_Alerts>> callbackReferences, Context context) {
//Update the alerts in the listview and mapview with the local alerts.
List<Alert> localAlerts = internalDc.GetAllAlerts();
try {
for (WeakReference<e_Alerts> callback : callbackReferences) {
callback.get().UpdateAlerts(localAlerts);
}
} catch (Exception e) {
Log.e("AlertManager", e.getMessage());
}
//If connected to the internet then update the local db and the views
if (isConnectingToInternet(context)) {
WeakReference<e_Alerts> wr = new WeakReference<e_Alerts>(this);
callbackReferences.add(wr);
// Update the alerts where no location is needed for so the user has a quick result
externalDc.getAlerts(callbackReferences, areaManager.GetActiveAreas(false));
// If gps region is enabled then find the phones location and update the alerts
if (areaManager.GetGpsArea().IsActive()) {
new GpsManager(this.context, this, callbackReferences);
}
}
}
The GpsManager extends the LocationListener:
public class GpsManager extends Service implements LocationListener {
The listener is implemented by the Alertmanager
// This method is caled by the GPS Manager when the GPS location is changed
#Override
public void OnLocationChanged(Location location, List<WeakReference<e_Alerts>> references) {Area gpsArea = areaManager.GetGpsArea();
gpsArea.SetLocation(location);
areaManager.SaveArea(gpsArea);
externalDc.getAlerts(references, areaManager.GetActiveAreas(true));
}
Asynctask information
This are the asynctask methods:
Asynctask constructor:
Here the list callbackReferences contains 3 weakrefrences and all of them are filled (2x fragment reference 1x alertmanager reference)
public At_allAlerts(List<WeakReference<e_Alerts>> callbackReferences, List<Area> areas) {
this.mCallbackReferences = callbackReferences;
this.mAreas = areas;
}
doInBackground code:
The XmlDownloader: Downloads an xml feed an parses the xml to objects with a library
The AlertConverter: converts the xml object to the object i use in my app
Both classes can work without the asynctask class and don't use the references.
#Override
protected String doInBackground(String... inputUrl) {
Log.i("At_allAlerts", "Asynctask for downloading and parsing mAlerts is started");
try {
//Downloads the alert XMLs from the internet and parses it to xmlAlerts
this.mAlerts = new XmlDownloader().DownloadAlerts(inputUrl);
// Filters the mXml mAlerts so only the mAlerts where the enduser is interessed in will remain
this.mAlerts = filterAlerts(this.mAlerts);
// Converts the remaining xmlAlerts to Alerts;
this.mResult = new AlertConverter().Convert(this.mAlerts);
}catch (Exception e) {
Log.e("At_allAlerts",e.getMessage());
}
return null;
}
The onPostExecute method:
When the programm comes in this method the this.references.get(2) reference (alertmanager reference) = null, the other 2 references are still filed
#Override
protected void onPostExecute(String xml){
for (WeakReference<e_Alerts> reference : activityWeakReferences)
{
reference.get().UpdateAlerts(this.result);
}
}
filterAlerts Method:
private List<Item> filterAlerts(List<Item> alerts) {
List<Item> filteredXmlAlerts = new ArrayList<>();
for (Item alert : alerts)
{
Location alertLocation = new Location("");
alertLocation.setLatitude(alert.getGeometries().get(0).getLocations().get(0).getLat());
alertLocation.setLongitude(alert.getGeometries().get(0).getLocations().get(0).getLng());
for(Area area : this.mAreas)
{
if (area.IsOrganization() && alert.getCountryCode().toLowerCase().equals(area.getOrganizationcode().toLowerCase())){
filteredXmlAlerts.add(alert);
break;
}
else if(!area.IsOrganization() && isAlertInRegion(alertLocation, area)) {
filteredXmlAlerts.add(alert);
break;
}
}
}
return filteredXmlAlerts;
}
My Question(s)
I think Weakreference are the right way for giving references to asynctask is this correct or do i need to give it as an other object? (class or object or whatever?).
Why goes my reference to null? and only one of the 3? and only when i dont use the gps location class? and how to solve this?
I read something about the garbage collector what can be the cause of this problem, is this true and when yes how can i solve this?
It would be fine when the answere are simple to understand since android is pretty new for me.

How do I properly bind a .Text property to the Overridden .asString() method of a SimpleObjectProperty?

I'm creating a simple control to browse for and sample Audio files. I want to use an ObjectProperty<File> so that I can bind some properties of the button responsible for playing the file:
PlayButton.disableProperty.bind(this.BGMFile.isNull());
PlayButton.textProperty.bind(this.BGMFile.asString());
So then there will be three things that I will need to override, two of which I have successfully done, and so will not get into
The third is the asString method:
new SimpleObjectProperty<File>(this, "BGM File", null){
/*yadda yadda overrides*/
#Override public StringBinding asString(){
if (super.get() != null && super.get().exists())
return (StringBinding) Bindings.format(
super.get().getName(), this
);
else return (StringBinding) Bindings.format("[NONE]", this);
}
}
This feels correct to me, and I even ripped the code from grepCode here, but when I browse for a file using the FileChooser I have setup and select the file I want to use, and then set it to the SimpleProperty the button text stays as [NONE].
This is the code for browsing for the file:
this.btnBrowseBGM.setOnAction((ActionEvent E) -> {
FileChooser FC = new FileChooser();
FC.getExtensionFilters().add(Filters.AudioExtensions());
FC.setTitle("Browse for Background Audio File");
File F = FC.showOpenDialog(this.getScene().getWindow());
if (F != null && F.exists()) try {
this.BGMFile.set(Files.copy(
F.toPath(),
Paths.get("Settings/Sound/", F.getName()),
StandardCopyOption.REPLACE_EXISTING
).toFile());
} catch(IOException ex) {
Methods.Exception(
"Unable to copy file to Settings Sound Directory.",
"Failed to copy Sound File", ex);
this.BGMFile.set(F);
} else this.BGMFile.set(null);
E.consume();
});
Because the path doesn't exist it yells at me (which I expected) but it still should be setting the BGMFile property to F. I know it does because the toggle button becomes active and pressing it plays the sound file.
So what am I missing/doing wrong here?
EDIT:
I think I may have an idea:
One of the methods which I override is the set method:
#Override public void set(File newValue){
if (newValue != null && newValue.exists())
super.set(newValue);
else super.set(null);
}
Could it be that overriding the set method causes it to not trigger the overridden asString method?
The problem is that you create a new Binding every time the asString() method is called. Since you first call it when the file is null, you get the binding created by Bindings.format("[NONE]", this). So your binding on the button is equivalent to:
playButton.textProperty().bind(Bindings.format("[NONE]", bgmFile));
So even though the string value gets reevaluated when the file property changes, it still formats "[NONE]".
You can see the opposite problem if you do
ObjectProperty<File> fileProperty = new SimpleObjectProperty<File>() {
/* your previous implementation */
};
fileProperty.set(new File("/path/to/some/valid/file"));
// now bind when you get the filename:
playButton.textProperty().bind(fileProperty.asString());
// setting the fileProperty to null will now invoke the binding that was provided when it wasn't null
// and you'll see a nice bunch of null pointer exceptions:
fileProperty.set(null);
Another way to say this is that the logic that checks if there's a valid file whose name you want doesn't exist in the binding, it is in the asString() method. That method is not invoked just because the property changes.
So you need to create a single StringBinding that processes all the logic (check if the file is null, if not check if it exists, if so get the name, etc) when it's get() method is called. You can do this either by subclassing StringBinding and putting the logic in the computeValue() method, or by using the utility Bindings.createStringBinding(...) method as in the following:
new SimpleObjectProperty<File>(this, "BGM File", null){
final StringBinding string = Bindings.createStringBinding(() -> {
File file = this.get();
if (file != null && file.exists()) {
return file.getName();
} else {
return "[NONE]";
}
}, this);
#Override public StringBinding asString(){
return string ;
}
}
For what it's worth, I tend to prefer a style in which I avoid subclassing unless necessary. In this case, I'd make the StringBinding a separate object that merely binds to the file property. The choice here depends on the use case, but this will work for most use cases and you never find yourself asking "are my overridden methods interacting in a way that doesn't work", and in general bugs like the one you had are more obvious in this style:
ObjectProperty<File> bgmFile = new SimpleObjectProperty(this, "bgmFile", null);
StringBinding fileName = Bindings.createStringBinding( () -> {
File file = bgmFile.get();
if (file != null && file.exists()) {
return file.getName();
} else return "[NONE]";
}, bgmFile);
And then of course just do playButton.textProperty().bind(fileName);.

Categories